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