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