1 package android.car.vms;
2 
3 import android.annotation.SystemApi;
4 import android.util.Log;
5 
6 import com.android.internal.annotations.VisibleForTesting;
7 
8 import org.json.JSONArray;
9 import org.json.JSONException;
10 import org.json.JSONObject;
11 
12 /**
13  * Records VMS operations using the Android Log.
14  *
15  * This class records VMS operations. The recorded messages include the VMS operations and its
16  * arguments encoded as JSON text so that the string can be both read as a log message and easily
17  * parsed. VmsOperationRecorder is intended to be called after successful state change.
18  *
19  * Access the VmsOperationRecorder using the {@link #get()} method, which returns a singleton
20  * instance. Each VMS operation has a corresponding VmsOperationRecorder method. For instance:
21  * <pre>{@code
22  *   VmsOperationRecorder.get().subscribe(layer);
23  * }</pre>
24  *
25  * @hide
26  */
27 @SystemApi
28 public final class VmsOperationRecorder {
29     private static final String TAG = "VmsOperationRecorder";
30     private static final VmsOperationRecorder INSTANCE = new VmsOperationRecorder(new Writer());
31     private final Writer mWriter;
32 
33     /** @hide */
34     @VisibleForTesting
VmsOperationRecorder(Writer writer)35     public VmsOperationRecorder(Writer writer) {
36         mWriter = writer;
37     }
38 
39     /** Return the singleton instance. */
get()40     public static VmsOperationRecorder get() {
41         return INSTANCE;
42     }
43 
44     // VMS Client operations.
45 
subscribe(VmsLayer layer)46     public void subscribe(VmsLayer layer) {
47         recordOp("subscribe", layer);
48     }
49 
unsubscribe(VmsLayer layer)50     public void unsubscribe(VmsLayer layer) {
51         recordOp("unsubscribe", layer);
52     }
53 
subscribe(VmsLayer layer, int publisherId)54     public void subscribe(VmsLayer layer, int publisherId) {
55         recordOp("subscribe", "publisherId", publisherId, layer);
56     }
57 
unsubscribe(VmsLayer layer, int publisherId)58     public void unsubscribe(VmsLayer layer, int publisherId) {
59         recordOp("unsubscribe", "publisherId", publisherId, layer);
60     }
61 
startMonitoring()62     public void startMonitoring() {
63         recordOp("startMonitoring");
64     }
65 
stopMonitoring()66     public void stopMonitoring() {
67         recordOp("stopMonitoring");
68     }
69 
setLayersOffering(VmsLayersOffering layersOffering)70     public void setLayersOffering(VmsLayersOffering layersOffering) {
71         recordOp("setLayersOffering", layersOffering);
72     }
73 
getPublisherId(int publisherId)74     public void getPublisherId(int publisherId) {
75         recordOp("getPublisherId", "publisherId", publisherId);
76     }
77 
78     // VMS Service operations.
79 
addSubscription(int sequenceNumber, VmsLayer layer)80     public void addSubscription(int sequenceNumber, VmsLayer layer) {
81         recordOp("addSubscription", "sequenceNumber", sequenceNumber, layer);
82     }
83 
removeSubscription(int sequenceNumber, VmsLayer layer)84     public void removeSubscription(int sequenceNumber, VmsLayer layer) {
85         recordOp("removeSubscription", "sequenceNumber", sequenceNumber, layer);
86     }
87 
addPromiscuousSubscription(int sequenceNumber)88     public void addPromiscuousSubscription(int sequenceNumber) {
89         recordOp("addPromiscuousSubscription", "sequenceNumber", sequenceNumber);
90     }
91 
removePromiscuousSubscription(int sequenceNumber)92     public void removePromiscuousSubscription(int sequenceNumber) {
93         recordOp("removePromiscuousSubscription", "sequenceNumber", sequenceNumber);
94     }
95 
addHalSubscription(int sequenceNumber, VmsLayer layer)96     public void addHalSubscription(int sequenceNumber, VmsLayer layer) {
97         recordOp("addHalSubscription", "sequenceNumber", sequenceNumber, layer);
98     }
99 
removeHalSubscription(int sequenceNumber, VmsLayer layer)100     public void removeHalSubscription(int sequenceNumber, VmsLayer layer) {
101         recordOp("removeHalSubscription", "sequenceNumber", sequenceNumber, layer);
102     }
103 
setPublisherLayersOffering(VmsLayersOffering layersOffering)104     public void setPublisherLayersOffering(VmsLayersOffering layersOffering) {
105         recordOp("setPublisherLayersOffering", layersOffering);
106     }
107 
setHalPublisherLayersOffering(VmsLayersOffering layersOffering)108     public void setHalPublisherLayersOffering(VmsLayersOffering layersOffering) {
109         recordOp("setHalPublisherLayersOffering", layersOffering);
110     }
111 
recordOp(String operation)112     private void recordOp(String operation) {
113         if (isEnabled()) {
114             try {
115                 write(new JSONObject().put(operation, new JSONObject()));
116             } catch (JSONException e) {
117                 Log.e(TAG, e.toString());
118             }
119         }
120     }
121 
recordOp(String operation, VmsLayer layer)122     private void recordOp(String operation, VmsLayer layer) {
123         if (isEnabled()) {
124             try {
125                 recordOp(operation, new JSONObject().put("layer", toJson(layer)));
126             } catch (JSONException e) {
127                 Log.e(TAG, e.toString());
128             }
129         }
130     }
131 
recordOp(String operation, VmsLayersOffering layersOffering)132     private void recordOp(String operation, VmsLayersOffering layersOffering) {
133         if (isEnabled()) {
134             try {
135                 JSONObject args = new JSONObject();
136                 args.put("publisherId", layersOffering.getPublisherId());
137                 JSONArray offering = toJson(layersOffering);
138                 if (offering.length() > 0) {
139                     args.put("layerDependency", offering);
140                 }
141                 recordOp(operation, args);
142             } catch (JSONException e) {
143                 Log.e(TAG, e.toString());
144             }
145         }
146     }
147 
recordOp(String operation, String intArgName, int arg)148     private void recordOp(String operation, String intArgName, int arg) {
149         if (isEnabled()) {
150             try {
151                 recordOp(operation, new JSONObject().put(intArgName, arg));
152             } catch (JSONException e) {
153                 Log.e(TAG, e.toString());
154             }
155         }
156     }
157 
recordOp(String operation, String intArgName, int arg, VmsLayer layer)158     private void recordOp(String operation, String intArgName, int arg, VmsLayer layer) {
159         if (isEnabled()) {
160             try {
161                 recordOp(operation,
162                         new JSONObject().put(intArgName, arg).put("layer", toJson(layer)));
163             } catch (JSONException e) {
164                 Log.e(TAG, e.toString());
165             }
166         }
167     }
168 
recordOp(String operation, JSONObject args)169     private void recordOp(String operation, JSONObject args) {
170         if (isEnabled()) {
171             try {
172                 write(new JSONObject().put(operation, args));
173             } catch (JSONException e) {
174                 Log.e(TAG, e.toString());
175             }
176         }
177     }
178 
toJson(VmsLayer layer)179     private static JSONObject toJson(VmsLayer layer) throws JSONException {
180         return new JSONObject()
181                 .put("type", layer.getType())
182                 .put("subtype", layer.getSubtype())
183                 .put("version", layer.getVersion());
184     }
185 
toJson(VmsLayerDependency layerDependency)186     private static JSONObject toJson(VmsLayerDependency layerDependency) throws JSONException {
187         JSONObject dep = new JSONObject();
188         dep.put("layer", toJson(layerDependency.getLayer()));
189         if (!layerDependency.getDependencies().isEmpty()) {
190             JSONArray dependencies = new JSONArray();
191             for (VmsLayer dependency : layerDependency.getDependencies()) {
192                 dependencies.put(toJson(dependency));
193             }
194             dep.put("dependency", dependencies);
195         }
196         return dep;
197     }
198 
toJson(VmsLayersOffering layersOffering)199     private static JSONArray toJson(VmsLayersOffering layersOffering) throws JSONException {
200         JSONArray offerings = new JSONArray();
201         for (VmsLayerDependency layerDependency : layersOffering.getDependencies()) {
202             offerings.put(toJson(layerDependency));
203         }
204         return offerings;
205     }
206 
isEnabled()207     private boolean isEnabled() {
208         return mWriter.isEnabled();
209     }
210 
write(JSONObject object)211     private void write(JSONObject object) {
212         mWriter.write(object.toString());
213     }
214 
215     /** @hide */
216     @VisibleForTesting
217     public static class Writer {
218         private static final String TAG = "VMS.RECORD.EVENT";
219         private static final int LEVEL = Log.DEBUG;
220 
isEnabled()221         public boolean isEnabled() {
222             return Log.isLoggable(TAG, LEVEL);
223         }
224 
write(String msg)225         public void write(String msg) {
226             Log.println(LEVEL, TAG, msg);
227         }
228     }
229 }
230