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