1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.car.hal;
17 
18 import static com.android.car.CarServiceUtils.toByteArray;
19 
20 import android.car.VehicleAreaType;
21 import android.car.vms.VmsAssociatedLayer;
22 import android.car.vms.VmsAvailableLayers;
23 import android.car.vms.VmsClient;
24 import android.car.vms.VmsClientManager.VmsClientCallback;
25 import android.car.vms.VmsLayer;
26 import android.car.vms.VmsLayerDependency;
27 import android.car.vms.VmsSubscriptionHelper;
28 import android.car.vms.VmsSubscriptionState;
29 import android.content.Context;
30 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
31 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
32 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
33 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
34 import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex;
35 import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
36 import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerAndPublisherIdIntegerValuesIndex;
37 import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
38 import android.hardware.automotive.vehicle.V2_0.VmsOfferingMessageIntegerValuesIndex;
39 import android.hardware.automotive.vehicle.V2_0.VmsPublisherInformationIntegerValuesIndex;
40 import android.hardware.automotive.vehicle.V2_0.VmsStartSessionMessageIntegerValuesIndex;
41 import android.os.Build;
42 import android.os.Handler;
43 import android.os.HandlerExecutor;
44 import android.os.HandlerThread;
45 import android.os.RemoteException;
46 import android.os.SystemClock;
47 import android.util.ArraySet;
48 import android.util.Log;
49 
50 import androidx.annotation.GuardedBy;
51 import androidx.annotation.VisibleForTesting;
52 
53 import com.android.car.CarLocalServices;
54 import com.android.car.CarServiceUtils;
55 import com.android.car.vms.VmsBrokerService;
56 
57 import java.io.FileDescriptor;
58 import java.io.FileOutputStream;
59 import java.io.IOException;
60 import java.io.PrintWriter;
61 import java.util.ArrayList;
62 import java.util.Collection;
63 import java.util.HashSet;
64 import java.util.List;
65 import java.util.Set;
66 import java.util.function.BiFunction;
67 import java.util.function.Supplier;
68 
69 /**
70  * VMS client implementation that proxies VmsPublisher/VmsSubscriber API calls to the Vehicle HAL
71  * using HAL-specific message encodings.
72  *
73  * @see android.hardware.automotive.vehicle.V2_0
74  */
75 public class VmsHalService extends HalServiceBase {
76     private static final boolean DBG = false;
77     private static final String TAG = "VmsHalService";
78     private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE;
79     private static final int[] SUPPORTED_PROPERTIES = new int[]{
80             HAL_PROPERTY_ID
81     };
82     private static final int NUM_INTEGERS_IN_VMS_LAYER = 3;
83     private static final int UNKNOWN_CLIENT_ID = -1;
84     private static final byte[] DEFAULT_PUBLISHER_INFO = new byte[0];
85 
86     private final VehicleHal mVehicleHal;
87     private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
88             getClass().getSimpleName());
89     private final Handler mHandler = new Handler(mHandlerThread.getLooper());
90     private final int mCoreId;
91     private final BiFunction<Handler, VmsClientCallback, VmsClient> mInitVmsClient;
92     private final int mClientMetricsProperty;
93     private final boolean mPropagatePropertyException;
94     private final VmsSubscriptionHelper mSubscriptionHelper =
95             new VmsSubscriptionHelper(this::setSubscriptions);
96 
97     private final Object mLock = new Object();
98     @GuardedBy("mLock")
99     private boolean mIsSupported;
100     @GuardedBy("mLock")
101     private VmsClient mClient;
102 
103     private final VmsClientCallback mClientCallback = new VmsClientCallback() {
104         @Override
105         public void onClientConnected(VmsClient client) {
106             Log.wtf(TAG, "onClientConnnected triggered for local client");
107         }
108 
109         @Override
110         public void onSubscriptionStateChanged(VmsSubscriptionState subscriptionState) {
111             if (DBG) Log.d(TAG, "Handling a subscription state change");
112             setPropertyValue(createSubscriptionStateMessage(VmsMessageType.SUBSCRIPTIONS_CHANGE,
113                             subscriptionState));
114         }
115 
116         @Override
117         public void onLayerAvailabilityChanged(VmsAvailableLayers availableLayers) {
118             if (DBG) Log.d(TAG, "Handling a layer availability change");
119             setPropertyValue(createAvailableLayersMessage(VmsMessageType.AVAILABILITY_CHANGE,
120                             availableLayers));
121         }
122 
123         @Override
124         public void onPacketReceived(int providerId, VmsLayer layer, byte[] packet) {
125             if (DBG) Log.d(TAG, "Handling a data message for Layer: " + layer);
126             setPropertyValue(createDataMessage(layer, providerId, packet));
127         }
128     };
129 
130     /**
131      * Constructor used by {@link VehicleHal}
132      */
VmsHalService(Context context, VehicleHal vehicleHal)133     VmsHalService(Context context, VehicleHal vehicleHal) {
134         this(context, vehicleHal, SystemClock::uptimeMillis, VmsHalService::initVmsClient,
135                 Build.IS_DEBUGGABLE);
136     }
137 
138     @VisibleForTesting
VmsHalService(Context context, VehicleHal vehicleHal, Supplier<Long> getCoreId, BiFunction<Handler, VmsClientCallback, VmsClient> initVmsClient, boolean propagatePropertyException)139     VmsHalService(Context context, VehicleHal vehicleHal, Supplier<Long> getCoreId,
140             BiFunction<Handler, VmsClientCallback, VmsClient> initVmsClient,
141             boolean propagatePropertyException) {
142         mVehicleHal = vehicleHal;
143         mCoreId = (int) (getCoreId.get() % Integer.MAX_VALUE);
144         mInitVmsClient = initVmsClient;
145         mClientMetricsProperty = getClientMetricsProperty(context);
146         mPropagatePropertyException = propagatePropertyException;
147     }
148 
getClientMetricsProperty(Context context)149     private static int getClientMetricsProperty(Context context) {
150         int propId = context.getResources().getInteger(
151                 com.android.car.R.integer.vmsHalClientMetricsProperty);
152         if (propId == 0) {
153             Log.i(TAG, "Metrics collection disabled");
154             return 0;
155         }
156         if ((propId & VehiclePropertyGroup.MASK) != VehiclePropertyGroup.VENDOR) {
157             Log.w(TAG, String.format("Metrics collection disabled, non-vendor property: 0x%x",
158                     propId));
159             return 0;
160         }
161 
162         Log.i(TAG, String.format("Metrics collection property: 0x%x", propId));
163         return propId;
164     }
165 
166     /**
167      * Retrieves the callback message handler for use by unit tests.
168      */
169     @VisibleForTesting
getHandler()170     Handler getHandler() {
171         return mHandler;
172     }
173 
174     @Override
getAllSupportedProperties()175     public int[] getAllSupportedProperties() {
176         return SUPPORTED_PROPERTIES;
177     }
178 
179     @Override
takeProperties(Collection<VehiclePropConfig> properties)180     public void takeProperties(Collection<VehiclePropConfig> properties) {
181         if (properties.isEmpty()) {
182             return;
183         }
184         synchronized (mLock) {
185             mIsSupported = true;
186         }
187     }
188 
189     @Override
init()190     public void init() {
191         synchronized (mLock) {
192             if (!mIsSupported) {
193                 Log.i(TAG, "VmsHalService VHAL property not supported");
194                 return; // Do not continue initialization
195             }
196             connectVmsClient();
197         }
198 
199         Log.i(TAG, "Initializing VmsHalService VHAL property");
200         mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID);
201 
202         mHandler.post(() ->
203                 setPropertyValue(createStartSessionMessage(mCoreId, UNKNOWN_CLIENT_ID)));
204     }
205 
206     @Override
release()207     public void release() {
208         synchronized (mLock) {
209             disconnectVmsClient();
210             if (!mIsSupported) {
211                 return;
212             }
213         }
214         if (DBG) {
215             Log.d(TAG, "Releasing VmsHalService VHAL property");
216         }
217         mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID);
218     }
219 
220     @Override
dump(PrintWriter writer)221     public void dump(PrintWriter writer) {
222         synchronized (mLock) {
223             writer.println("*VMS HAL*");
224             writer.printf("VmsProperty: %s\n", mIsSupported ? "supported" : "unsupported");
225             if (mClient == null) {
226                 writer.println("VmsClient: disconnected");
227                 return;
228             }
229             writer.println("VmsClient: connected");
230             writer.printf("Subscriptions: %s\n", mSubscriptionHelper.getSubscriptions());
231             writer.printf("AvailableLayers: %s\n", mClient.getAvailableLayers());
232             writer.printf("SubscriptionState: %s\n", mClient.getSubscriptionState());
233         }
234     }
235 
236     /**
237      * Dumps HAL client metrics obtained by reading the VMS HAL property.
238      *
239      * @param fd Dumpsys file descriptor to write client metrics to.
240      */
dumpMetrics(FileDescriptor fd)241     public void dumpMetrics(FileDescriptor fd) {
242         if (mClientMetricsProperty == 0) {
243             Log.w(TAG, "Metrics collection is disabled");
244             return;
245         }
246 
247         VehiclePropValue vehicleProp = null;
248         try {
249             vehicleProp = mVehicleHal.get(mClientMetricsProperty);
250         } catch (RuntimeException e) {
251             // Failures to retrieve metrics should be non-fatal
252             Log.e(TAG, "While reading metrics from client", e);
253         }
254         if (vehicleProp == null) {
255             if (DBG) Log.d(TAG, "Metrics unavailable");
256             return;
257         }
258 
259         try (FileOutputStream fout = new FileOutputStream(fd)) {
260             fout.write(toByteArray(vehicleProp.value.bytes));
261             fout.flush();
262         } catch (IOException e) {
263             Log.e(TAG, "Error writing metrics to output stream", e);
264         }
265     }
266 
267     /**
268      * Consumes/produces HAL messages.
269      *
270      * The format of these messages is defined in:
271      * hardware/interfaces/automotive/vehicle/2.0/types.hal
272      */
273     @Override
onHalEvents(List<VehiclePropValue> values)274     public void onHalEvents(List<VehiclePropValue> values) {
275         if (DBG) Log.d(TAG, "Handling a VMS property change");
276         for (VehiclePropValue v : values) {
277             ArrayList<Integer> vec = v.value.int32Values;
278             int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE);
279 
280             if (DBG) Log.d(TAG, "Received " + VmsMessageType.toString(messageType) + " message");
281             try {
282                 switch (messageType) {
283                     case VmsMessageType.DATA:
284                         handleDataEvent(vec, toByteArray(v.value.bytes));
285                         break;
286                     case VmsMessageType.SUBSCRIBE:
287                         handleSubscribeEvent(vec);
288                         break;
289                     case VmsMessageType.UNSUBSCRIBE:
290                         handleUnsubscribeEvent(vec);
291                         break;
292                     case VmsMessageType.SUBSCRIBE_TO_PUBLISHER:
293                         handleSubscribeToPublisherEvent(vec);
294                         break;
295                     case VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER:
296                         handleUnsubscribeFromPublisherEvent(vec);
297                         break;
298                     case VmsMessageType.PUBLISHER_ID_REQUEST:
299                         handlePublisherIdRequest(toByteArray(v.value.bytes));
300                         break;
301                     case VmsMessageType.PUBLISHER_INFORMATION_REQUEST:
302                         handlePublisherInfoRequest(vec);
303                     case VmsMessageType.OFFERING:
304                         handleOfferingEvent(vec);
305                         break;
306                     case VmsMessageType.AVAILABILITY_REQUEST:
307                         handleAvailabilityRequestEvent();
308                         break;
309                     case VmsMessageType.SUBSCRIPTIONS_REQUEST:
310                         handleSubscriptionsRequestEvent();
311                         break;
312                     case VmsMessageType.START_SESSION:
313                         handleStartSessionEvent(vec);
314                         break;
315                     default:
316                         Log.e(TAG, "Unexpected message type: " + messageType);
317                 }
318             } catch (IndexOutOfBoundsException e) {
319                 Log.e(TAG, "While handling " + VmsMessageType.toString(messageType), e);
320             }
321         }
322     }
323 
connectVmsClient()324     private void connectVmsClient() {
325         synchronized (mLock) {
326             mClient = mInitVmsClient.apply(mHandler, mClientCallback);
327         }
328     }
329 
disconnectVmsClient()330     private void disconnectVmsClient() {
331         synchronized (mLock) {
332             if (mClient != null) {
333                 try {
334                     mClient.unregister();
335                 } catch (RemoteException e) {
336                     Log.wtf(TAG, "Local broker should not throw RemoteException", e);
337                 }
338                 mClient = null;
339             }
340         }
341     }
342 
initVmsClient(Handler handler, VmsClientCallback callback)343     private static VmsClient initVmsClient(Handler handler, VmsClientCallback callback) {
344         VmsBrokerService brokerService = CarLocalServices.getService(VmsBrokerService.class);
345         if (brokerService == null) {
346             Log.e(TAG, "Broker service is not enabled");
347             return null;
348         }
349         VmsClient client = new VmsClient(brokerService, new HandlerExecutor(handler), callback,
350                 /* legacyClient= */ true, /* autoCloseMemory */ false,
351                 /* exceptionHandler= */ ignored -> { });
352         try {
353             client.register();
354         } catch (RemoteException e) {
355             Log.wtf(TAG, "Local broker should not throw RemoteException", e);
356         }
357         return client;
358     }
359 
getVmsClient()360     private VmsClient getVmsClient() {
361         synchronized (mLock) {
362             if (mClient == null) {
363                 throw new IllegalStateException("VmsClient is not connected");
364             }
365             return mClient;
366         }
367     }
368 
369     /**
370      * SESSION_START message format:
371      * <ul>
372      * <li>Message type
373      * <li>Core ID
374      * <li>Client ID
375      * </ul>
376      */
handleStartSessionEvent(List<Integer> message)377     private void handleStartSessionEvent(List<Integer> message) {
378         int coreId = message.get(VmsStartSessionMessageIntegerValuesIndex.SERVICE_ID);
379         int clientId = message.get(VmsStartSessionMessageIntegerValuesIndex.CLIENT_ID);
380         Log.i(TAG, "Starting new session with coreId: " + coreId + " client: " + clientId);
381 
382         if (coreId != mCoreId) {
383             // Reset VmsClient
384             disconnectVmsClient();
385             connectVmsClient();
386             // Send acknowledgement message
387             setPropertyValue(createStartSessionMessage(mCoreId, clientId));
388         }
389         mClientCallback.onLayerAvailabilityChanged(getVmsClient().getAvailableLayers());
390     }
391 
392     /**
393      * DATA message format:
394      * <ul>
395      * <li>Message type
396      * <li>Layer ID
397      * <li>Layer subtype
398      * <li>Layer version
399      * <li>Publisher ID
400      * <li>Payload
401      * </ul>
402      */
handleDataEvent(List<Integer> message, byte[] payload)403     private void handleDataEvent(List<Integer> message, byte[] payload) {
404         VmsLayer vmsLayer = parseVmsLayerFromMessage(message);
405         int publisherId = parsePublisherIdFromMessage(message);
406         if (DBG) {
407             Log.d(TAG,
408                     "Handling a data event for Layer: " + vmsLayer + " Publisher: " + publisherId);
409         }
410         getVmsClient().publishPacket(publisherId, vmsLayer, payload);
411     }
412 
413     /**
414      * SUBSCRIBE message format:
415      * <ul>
416      * <li>Message type
417      * <li>Layer ID
418      * <li>Layer subtype
419      * <li>Layer version
420      * </ul>
421      */
handleSubscribeEvent(List<Integer> message)422     private void handleSubscribeEvent(List<Integer> message) {
423         VmsLayer vmsLayer = parseVmsLayerFromMessage(message);
424         if (DBG) Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer);
425         mSubscriptionHelper.subscribe(vmsLayer);
426     }
427 
428     /**
429      * SUBSCRIBE_TO_PUBLISHER message format:
430      * <ul>
431      * <li>Message type
432      * <li>Layer ID
433      * <li>Layer subtype
434      * <li>Layer version
435      * <li>Publisher ID
436      * </ul>
437      */
handleSubscribeToPublisherEvent(List<Integer> message)438     private void handleSubscribeToPublisherEvent(List<Integer> message) {
439         VmsLayer vmsLayer = parseVmsLayerFromMessage(message);
440         int publisherId = parsePublisherIdFromMessage(message);
441         if (DBG) {
442             Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer
443                     + " Publisher: " + publisherId);
444         }
445         mSubscriptionHelper.subscribe(vmsLayer, publisherId);
446     }
447 
448     /**
449      * UNSUBSCRIBE message format:
450      * <ul>
451      * <li>Message type
452      * <li>Layer ID
453      * <li>Layer subtype
454      * <li>Layer version
455      * </ul>
456      */
handleUnsubscribeEvent(List<Integer> message)457     private void handleUnsubscribeEvent(List<Integer> message) {
458         VmsLayer vmsLayer = parseVmsLayerFromMessage(message);
459         if (DBG) Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer);
460         mSubscriptionHelper.unsubscribe(vmsLayer);
461     }
462 
463     /**
464      * UNSUBSCRIBE_TO_PUBLISHER message format:
465      * <ul>
466      * <li>Message type
467      * <li>Layer ID
468      * <li>Layer subtype
469      * <li>Layer version
470      * <li>Publisher ID
471      * </ul>
472      */
handleUnsubscribeFromPublisherEvent(List<Integer> message)473     private void handleUnsubscribeFromPublisherEvent(List<Integer> message) {
474         VmsLayer vmsLayer = parseVmsLayerFromMessage(message);
475         int publisherId = parsePublisherIdFromMessage(message);
476         if (DBG) {
477             Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer
478                     + " Publisher: " + publisherId);
479         }
480         mSubscriptionHelper.unsubscribe(vmsLayer, publisherId);
481     }
482 
setSubscriptions(Set<VmsAssociatedLayer> subscriptions)483     private void setSubscriptions(Set<VmsAssociatedLayer> subscriptions) {
484         getVmsClient().setSubscriptions(subscriptions);
485     }
486 
487     /**
488      * PUBLISHER_ID_REQUEST message format:
489      * <ul>
490      * <li>Message type
491      * <li>Publisher info (bytes)
492      * </ul>
493      *
494      * PUBLISHER_ID_RESPONSE message format:
495      * <ul>
496      * <li>Message type
497      * <li>Publisher ID
498      * </ul>
499      */
handlePublisherIdRequest(byte[] payload)500     private void handlePublisherIdRequest(byte[] payload) {
501         if (DBG) {
502             Log.d(TAG, "Handling a publisher id request event");
503         }
504 
505         VehiclePropValue vehicleProp = createVmsMessage(VmsMessageType.PUBLISHER_ID_RESPONSE);
506 
507         // Publisher ID
508         vehicleProp.value.int32Values.add(getVmsClient().registerProvider(payload));
509         setPropertyValue(vehicleProp);
510     }
511 
512 
513     /**
514      * PUBLISHER_INFORMATION_REQUEST message format:
515      * <ul>
516      * <li>Message type
517      * <li>Publisher ID
518      * </ul>
519      *
520      * PUBLISHER_INFORMATION_RESPONSE message format:
521      * <ul>
522      * <li>Message type
523      * <li>Publisher info (bytes)
524      * </ul>
525      */
handlePublisherInfoRequest(List<Integer> message)526     private void handlePublisherInfoRequest(List<Integer> message) {
527         if (DBG) Log.d(TAG, "Handling a publisher info request event");
528         int publisherId = message.get(VmsPublisherInformationIntegerValuesIndex.PUBLISHER_ID);
529 
530         VehiclePropValue vehicleProp =
531                 createVmsMessage(VmsMessageType.PUBLISHER_INFORMATION_RESPONSE);
532 
533         // Publisher Info
534         byte[] publisherInfo = getVmsClient().getProviderDescription(publisherId);
535         appendBytes(vehicleProp.value.bytes,
536                 publisherInfo != null ? publisherInfo : DEFAULT_PUBLISHER_INFO);
537         setPropertyValue(vehicleProp);
538     }
539 
540     /**
541      * OFFERING message format:
542      * <ul>
543      * <li>Message type
544      * <li>Publisher ID
545      * <li>Number of offerings.
546      * <li>Offerings (x number of offerings)
547      * <ul>
548      * <li>Layer ID
549      * <li>Layer subtype
550      * <li>Layer version
551      * <li>Number of layer dependencies.
552      * <li>Layer dependencies (x number of layer dependencies)
553      * <ul>
554      * <li>Layer ID
555      * <li>Layer subtype
556      * <li>Layer version
557      * </ul>
558      * </ul>
559      * </ul>
560      */
handleOfferingEvent(List<Integer> message)561     private void handleOfferingEvent(List<Integer> message) {
562         // Publisher ID for OFFERING is stored at a different index than in other message types
563         int publisherId = message.get(VmsOfferingMessageIntegerValuesIndex.PUBLISHER_ID);
564         int numLayerDependencies =
565                 message.get(
566                         VmsOfferingMessageIntegerValuesIndex.NUMBER_OF_OFFERS);
567         if (DBG) {
568             Log.d(TAG, "Handling an offering event of " + numLayerDependencies
569                     + " layers for Publisher: " + publisherId);
570         }
571 
572         Set<VmsLayerDependency> offeredLayers = new ArraySet<>(numLayerDependencies);
573         int idx = VmsOfferingMessageIntegerValuesIndex.OFFERING_START;
574         for (int i = 0; i < numLayerDependencies; i++) {
575             VmsLayer offeredLayer = parseVmsLayerAtIndex(message, idx);
576             idx += NUM_INTEGERS_IN_VMS_LAYER;
577 
578             int numDependenciesForLayer = message.get(idx++);
579             if (numDependenciesForLayer == 0) {
580                 offeredLayers.add(new VmsLayerDependency(offeredLayer));
581             } else {
582                 Set<VmsLayer> dependencies = new HashSet<>();
583 
584                 for (int j = 0; j < numDependenciesForLayer; j++) {
585                     VmsLayer dependantLayer = parseVmsLayerAtIndex(message, idx);
586                     idx += NUM_INTEGERS_IN_VMS_LAYER;
587                     dependencies.add(dependantLayer);
588                 }
589                 offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies));
590             }
591         }
592         getVmsClient().setProviderOfferings(publisherId, offeredLayers);
593     }
594 
595     /**
596      * AVAILABILITY_REQUEST message format:
597      * <ul>
598      * <li>Message type
599      * </ul>
600      */
handleAvailabilityRequestEvent()601     private void handleAvailabilityRequestEvent() {
602         setPropertyValue(createAvailableLayersMessage(VmsMessageType.AVAILABILITY_RESPONSE,
603                 getVmsClient().getAvailableLayers()));
604     }
605 
606     /**
607      * SUBSCRIPTION_REQUEST message format:
608      * <ul>
609      * <li>Message type
610      * </ul>
611      */
handleSubscriptionsRequestEvent()612     private void handleSubscriptionsRequestEvent() {
613         setPropertyValue(createSubscriptionStateMessage(VmsMessageType.SUBSCRIPTIONS_RESPONSE,
614                 getVmsClient().getSubscriptionState()));
615     }
616 
setPropertyValue(VehiclePropValue vehicleProp)617     private void setPropertyValue(VehiclePropValue vehicleProp) {
618         int messageType = vehicleProp.value.int32Values.get(
619                 VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE);
620 
621         synchronized (mLock) {
622             if (!mIsSupported) {
623                 Log.w(TAG, "HAL unsupported while attempting to send "
624                         + VmsMessageType.toString(messageType));
625                 return;
626             }
627         }
628 
629         try {
630             mVehicleHal.set(vehicleProp);
631         } catch (RuntimeException e) {
632             Log.e(TAG, "While sending " + VmsMessageType.toString(messageType), e);
633             if (mPropagatePropertyException) {
634                 throw new IllegalStateException(e);
635             }
636         }
637     }
638 
639     /**
640      * Creates a SESSION_START type {@link VehiclePropValue}.
641      *
642      * SESSION_START message format:
643      * <ul>
644      * <li>Message type
645      * <li>Core ID
646      * <li>Client ID
647      * </ul>
648      */
createStartSessionMessage(int coreId, int clientId)649     private static VehiclePropValue createStartSessionMessage(int coreId, int clientId) {
650         // Message type + layer
651         VehiclePropValue vehicleProp = createVmsMessage(VmsMessageType.START_SESSION);
652         List<Integer> message = vehicleProp.value.int32Values;
653 
654         // Core ID
655         message.add(coreId);
656 
657         // Client ID
658         message.add(clientId);
659 
660         return vehicleProp;
661     }
662 
663     /**
664      * Creates a DATA type {@link VehiclePropValue}.
665      *
666      * DATA message format:
667      * <ul>
668      * <li>Message type
669      * <li>Layer ID
670      * <li>Layer subtype
671      * <li>Layer version
672      * <li>Publisher ID
673      * <li>Payload
674      * </ul>
675      *
676      * @param layer Layer for which message was published.
677      * @param publisherId Publisher of message
678      * @param payload Data message
679      */
createDataMessage(VmsLayer layer, int publisherId, byte[] payload)680     private static VehiclePropValue createDataMessage(VmsLayer layer, int publisherId,
681             byte[] payload) {
682         // Message type + layer
683         VehiclePropValue vehicleProp = createVmsMessage(VmsMessageType.DATA);
684         appendLayer(vehicleProp.value.int32Values, layer);
685         List<Integer> message = vehicleProp.value.int32Values;
686 
687         // Publisher ID
688         message.add(publisherId);
689 
690         // Payload
691         appendBytes(vehicleProp.value.bytes, payload);
692         return vehicleProp;
693     }
694 
695     /**
696      * Creates a SUBSCRIPTION_CHANGE or SUBSCRIPTION_RESPONSE type {@link VehiclePropValue}.
697      *
698      * Both message types have the same format:
699      * <ul>
700      * <li>Message type
701      * <li>Sequence number
702      * <li>Number of layers
703      * <li>Number of associated layers
704      * <li>Layers (x number of layers) (see {@link #appendLayer})
705      * <li>Associated layers (x number of associated layers) (see {@link #appendAssociatedLayer})
706      * </ul>
707      *
708      * @param messageType       Either SUBSCRIPTIONS_CHANGE or SUBSCRIPTIONS_RESPONSE.
709      * @param subscriptionState The subscription state to encode in the message.
710      */
createSubscriptionStateMessage(int messageType, VmsSubscriptionState subscriptionState)711     private static VehiclePropValue createSubscriptionStateMessage(int messageType,
712             VmsSubscriptionState subscriptionState) {
713         // Message type
714         VehiclePropValue vehicleProp = createVmsMessage(messageType);
715         List<Integer> message = vehicleProp.value.int32Values;
716 
717         // Sequence number
718         message.add(subscriptionState.getSequenceNumber());
719 
720         Set<VmsLayer> layers = subscriptionState.getLayers();
721         Set<VmsAssociatedLayer> associatedLayers = subscriptionState.getAssociatedLayers();
722 
723         // Number of layers
724         message.add(layers.size());
725         // Number of associated layers
726         message.add(associatedLayers.size());
727 
728         // Layers
729         for (VmsLayer layer : layers) {
730             appendLayer(message, layer);
731         }
732 
733         // Associated layers
734         for (VmsAssociatedLayer layer : associatedLayers) {
735             appendAssociatedLayer(message, layer);
736         }
737         return vehicleProp;
738     }
739 
740     /**
741      * Creates an AVAILABILITY_CHANGE or AVAILABILITY_RESPONSE type {@link VehiclePropValue}.
742      *
743      * Both message types have the same format:
744      * <ul>
745      * <li>Message type
746      * <li>Sequence number.
747      * <li>Number of associated layers.
748      * <li>Associated layers (x number of associated layers) (see {@link #appendAssociatedLayer})
749      * </ul>
750      *
751      * @param messageType     Either AVAILABILITY_CHANGE or AVAILABILITY_RESPONSE.
752      * @param availableLayers The available layers to encode in the message.
753      */
createAvailableLayersMessage(int messageType, VmsAvailableLayers availableLayers)754     private static VehiclePropValue createAvailableLayersMessage(int messageType,
755             VmsAvailableLayers availableLayers) {
756         // Message type
757         VehiclePropValue vehicleProp = createVmsMessage(messageType);
758         List<Integer> message = vehicleProp.value.int32Values;
759 
760         // Sequence number
761         message.add(availableLayers.getSequence());
762 
763         // Number of associated layers
764         message.add(availableLayers.getAssociatedLayers().size());
765 
766         // Associated layers
767         for (VmsAssociatedLayer layer : availableLayers.getAssociatedLayers()) {
768             appendAssociatedLayer(message, layer);
769         }
770         return vehicleProp;
771     }
772 
773     /**
774      * Creates a base {@link VehiclePropValue} of the requested message type, with no message fields
775      * populated.
776      *
777      * @param messageType Type of message, from {@link VmsMessageType}
778      */
createVmsMessage(int messageType)779     private static VehiclePropValue createVmsMessage(int messageType) {
780         VehiclePropValue vehicleProp = new VehiclePropValue();
781         vehicleProp.prop = HAL_PROPERTY_ID;
782         vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
783         vehicleProp.value.int32Values.add(messageType);
784         return vehicleProp;
785     }
786 
787     /**
788      * Appends a {@link VmsLayer} to an encoded VMS message.
789      *
790      * Layer format:
791      * <ul>
792      * <li>Layer ID
793      * <li>Layer subtype
794      * <li>Layer version
795      * </ul>
796      *
797      * @param message Message to append to.
798      * @param layer   Layer to append.
799      */
appendLayer(List<Integer> message, VmsLayer layer)800     private static void appendLayer(List<Integer> message, VmsLayer layer) {
801         message.add(layer.getType());
802         message.add(layer.getSubtype());
803         message.add(layer.getVersion());
804     }
805 
806     /**
807      * Appends a {@link VmsAssociatedLayer} to an encoded VMS message.
808      *
809      * AssociatedLayer format:
810      * <ul>
811      * <li>Layer ID
812      * <li>Layer subtype
813      * <li>Layer version
814      * <li>Number of publishers
815      * <li>Publisher ID (x number of publishers)
816      * </ul>
817      *
818      * @param message Message to append to.
819      * @param layer   Layer to append.
820      */
appendAssociatedLayer(List<Integer> message, VmsAssociatedLayer layer)821     private static void appendAssociatedLayer(List<Integer> message, VmsAssociatedLayer layer) {
822         message.add(layer.getVmsLayer().getType());
823         message.add(layer.getVmsLayer().getSubtype());
824         message.add(layer.getVmsLayer().getVersion());
825         message.add(layer.getProviderIds().size());
826         message.addAll(layer.getProviderIds());
827     }
828 
appendBytes(ArrayList<Byte> dst, byte[] src)829     private static void appendBytes(ArrayList<Byte> dst, byte[] src) {
830         dst.ensureCapacity(src.length);
831         for (byte b : src) {
832             dst.add(b);
833         }
834     }
835 
parseVmsLayerFromMessage(List<Integer> message)836     private static VmsLayer parseVmsLayerFromMessage(List<Integer> message) {
837         return parseVmsLayerAtIndex(message,
838                 VmsMessageWithLayerIntegerValuesIndex.LAYER_TYPE);
839     }
840 
parseVmsLayerAtIndex(List<Integer> message, int index)841     private static VmsLayer parseVmsLayerAtIndex(List<Integer> message, int index) {
842         List<Integer> layerValues = message.subList(index, index + NUM_INTEGERS_IN_VMS_LAYER);
843         return new VmsLayer(layerValues.get(0), layerValues.get(1), layerValues.get(2));
844     }
845 
parsePublisherIdFromMessage(List<Integer> message)846     private static int parsePublisherIdFromMessage(List<Integer> message) {
847         return message.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
848     }
849 }
850