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