• Home
  • History
  • Annotate
  • only in /external/autotest/client/site_tests/power_Thermal/
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 */
16package com.android.car.hal;
17
18import static com.android.car.CarServiceUtils.toByteArray;
19
20import static java.lang.Integer.toHexString;
21
22import android.annotation.SystemApi;
23import android.car.VehicleAreaType;
24import android.car.vms.IVmsSubscriberClient;
25import android.car.vms.VmsAssociatedLayer;
26import android.car.vms.VmsAvailableLayers;
27import android.car.vms.VmsLayer;
28import android.car.vms.VmsLayerDependency;
29import android.car.vms.VmsLayersOffering;
30import android.car.vms.VmsOperationRecorder;
31import android.car.vms.VmsSubscriptionState;
32import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
33import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
34import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
35import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex;
36import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
37import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerAndPublisherIdIntegerValuesIndex;
38import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
39import android.hardware.automotive.vehicle.V2_0.VmsOfferingMessageIntegerValuesIndex;
40import android.os.Binder;
41import android.os.IBinder;
42import android.util.Log;
43
44import com.android.car.CarLog;
45import com.android.car.VmsLayersAvailability;
46import com.android.car.VmsPublishersInfo;
47import com.android.car.VmsRouting;
48import com.android.internal.annotations.GuardedBy;
49
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.Arrays;
53import java.util.Collection;
54import java.util.Collections;
55import java.util.HashMap;
56import java.util.HashSet;
57import java.util.LinkedList;
58import java.util.List;
59import java.util.Map;
60import java.util.Set;
61import java.util.concurrent.CopyOnWriteArrayList;
62
63/**
64 * This is a glue layer between the VehicleHal and the VmsService. It sends VMS properties back and
65 * forth.
66 */
67@SystemApi
68public class VmsHalService extends HalServiceBase {
69
70    private static final boolean DBG = true;
71    private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE;
72    private static final String TAG = "VmsHalService";
73
74    private final static List<Integer> AVAILABILITY_MESSAGE_TYPES = Collections.unmodifiableList(
75            Arrays.asList(
76                    VmsMessageType.AVAILABILITY_RESPONSE,
77                    VmsMessageType.AVAILABILITY_CHANGE));
78
79    private boolean mIsSupported = false;
80    private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners =
81            new CopyOnWriteArrayList<>();
82    private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners =
83            new CopyOnWriteArrayList<>();
84
85    private final IBinder mHalPublisherToken = new Binder();
86    private final VehicleHal mVehicleHal;
87
88    private final Object mLock = new Object();
89    private final VmsRouting mRouting = new VmsRouting();
90    @GuardedBy("mLock")
91    private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>();
92    @GuardedBy("mLock")
93    private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
94    private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
95
96    /**
97     * The VmsPublisherService implements this interface to receive data from the HAL.
98     */
99    public interface VmsHalPublisherListener {
100        void onChange(VmsSubscriptionState subscriptionState);
101    }
102
103    /**
104     * The VmsSubscriberService implements this interface to receive data from the HAL.
105     */
106    public interface VmsHalSubscriberListener {
107        // Notifies the listener on a data Message from a publisher.
108        void onDataMessage(VmsLayer layer, int publisherId, byte[] payload);
109
110        // Notifies the listener on a change in available layers.
111        void onLayersAvaiabilityChange(VmsAvailableLayers availableLayers);
112    }
113
114    /**
115     * The VmsService implements this interface to receive data from the HAL.
116     */
117    protected VmsHalService(VehicleHal vehicleHal) {
118        mVehicleHal = vehicleHal;
119        if (DBG) {
120            Log.d(TAG, "started VmsHalService!");
121        }
122    }
123
124    public void addPublisherListener(VmsHalPublisherListener listener) {
125        mPublisherListeners.add(listener);
126    }
127
128    public void addSubscriberListener(VmsHalSubscriberListener listener) {
129        mSubscriberListeners.add(listener);
130    }
131
132    public void removePublisherListener(VmsHalPublisherListener listener) {
133        mPublisherListeners.remove(listener);
134    }
135
136    public void removeSubscriberListener(VmsHalSubscriberListener listener) {
137        mSubscriberListeners.remove(listener);
138    }
139
140    public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
141        boolean firstSubscriptionForLayer = false;
142        if (DBG) {
143            Log.d(TAG, "Checking for first subscription. Layer: " + layer);
144        }
145        synchronized (mLock) {
146            // Check if publishers need to be notified about this change in subscriptions.
147            firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
148
149            // Add the listeners subscription to the layer
150            mRouting.addSubscription(listener, layer);
151        }
152        if (firstSubscriptionForLayer) {
153            notifyHalPublishers(layer, true);
154            notifyClientPublishers();
155        }
156    }
157
158    public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
159        boolean layerHasSubscribers = true;
160        synchronized (mLock) {
161            if (!mRouting.hasLayerSubscriptions(layer)) {
162                if (DBG) {
163                    Log.d(TAG, "Trying to remove a layer with no subscription: " + layer);
164                }
165                return;
166            }
167
168            // Remove the listeners subscription to the layer
169            mRouting.removeSubscription(listener, layer);
170
171            // Check if publishers need to be notified about this change in subscriptions.
172            layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
173        }
174        if (!layerHasSubscribers) {
175            notifyHalPublishers(layer, false);
176            notifyClientPublishers();
177        }
178    }
179
180    public void addSubscription(IVmsSubscriberClient listener) {
181        synchronized (mLock) {
182            mRouting.addSubscription(listener);
183        }
184    }
185
186    public void removeSubscription(IVmsSubscriberClient listener) {
187        synchronized (mLock) {
188            mRouting.removeSubscription(listener);
189        }
190    }
191
192    public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer, int publisherId) {
193        boolean firstSubscriptionForLayer = false;
194        synchronized (mLock) {
195            // Check if publishers need to be notified about this change in subscriptions.
196            firstSubscriptionForLayer = !(mRouting.hasLayerSubscriptions(layer) ||
197                    mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId));
198
199            // Add the listeners subscription to the layer
200            mRouting.addSubscription(listener, layer, publisherId);
201        }
202        if (firstSubscriptionForLayer) {
203            notifyHalPublishers(layer, true);
204            notifyClientPublishers();
205        }
206    }
207
208    public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer, int publisherId) {
209        boolean layerHasSubscribers = true;
210        synchronized (mLock) {
211            if (!mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId)) {
212                Log.i(TAG, "Trying to remove a layer with no subscription: " +
213                        layer + ", publisher ID:" + publisherId);
214                return;
215            }
216
217            // Remove the listeners subscription to the layer
218            mRouting.removeSubscription(listener, layer, publisherId);
219
220            // Check if publishers need to be notified about this change in subscriptions.
221            layerHasSubscribers = mRouting.hasLayerSubscriptions(layer) ||
222                    mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId);
223        }
224        if (!layerHasSubscribers) {
225            notifyHalPublishers(layer, false);
226            notifyClientPublishers();
227        }
228    }
229
230    public void removeDeadSubscriber(IVmsSubscriberClient listener) {
231        synchronized (mLock) {
232            mRouting.removeDeadSubscriber(listener);
233        }
234    }
235
236    public Set<IVmsSubscriberClient> getSubscribersForLayerFromPublisher(VmsLayer layer,
237                                                                         int publisherId) {
238        synchronized (mLock) {
239            return mRouting.getSubscribersForLayerFromPublisher(layer, publisherId);
240        }
241    }
242
243    public Set<IVmsSubscriberClient> getAllSubscribers() {
244        synchronized (mLock) {
245            return mRouting.getAllSubscribers();
246        }
247    }
248
249    public boolean isHalSubscribed(VmsLayer layer) {
250        synchronized (mLock) {
251            return mRouting.isHalSubscribed(layer);
252        }
253    }
254
255    public VmsSubscriptionState getSubscriptionState() {
256        synchronized (mLock) {
257            return mRouting.getSubscriptionState();
258        }
259    }
260
261    /**
262     * Assigns an idempotent ID for publisherInfo and stores it. The idempotency in this case means
263     * that the same publisherInfo will always, within a trip of the vehicle, return the same ID.
264     * The publisherInfo should be static for a binary and should only change as part of a software
265     * update. The publisherInfo is a serialized proto message which VMS clients can interpret.
266     */
267    public int getPublisherId(byte[] publisherInfo) {
268        if (DBG) {
269            Log.i(TAG, "Getting publisher static ID");
270        }
271        synchronized (mLock) {
272            return mPublishersInfo.getIdForInfo(publisherInfo);
273        }
274    }
275
276    public byte[] getPublisherInfo(int publisherId) {
277        if (DBG) {
278            Log.i(TAG, "Getting information for publisher ID: " + publisherId);
279        }
280        synchronized (mLock) {
281            return mPublishersInfo.getPublisherInfo(publisherId);
282        }
283    }
284
285    private void addHalSubscription(VmsLayer layer) {
286        boolean firstSubscriptionForLayer = true;
287        synchronized (mLock) {
288            // Check if publishers need to be notified about this change in subscriptions.
289            firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
290
291            // Add the listeners subscription to the layer
292            mRouting.addHalSubscription(layer);
293        }
294        if (firstSubscriptionForLayer) {
295            notifyHalPublishers(layer, true);
296            notifyClientPublishers();
297        }
298    }
299
300    private void addHalSubscriptionToPublisher(VmsLayer layer, int publisherId) {
301        boolean firstSubscriptionForLayer = true;
302        synchronized (mLock) {
303            // Check if publishers need to be notified about this change in subscriptions.
304            firstSubscriptionForLayer = !(mRouting.hasLayerSubscriptions(layer) ||
305                    mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId));
306
307            // Add the listeners subscription to the layer
308            mRouting.addHalSubscriptionToPublisher(layer, publisherId);
309        }
310        if (firstSubscriptionForLayer) {
311            notifyHalPublishers(layer, publisherId, true);
312            notifyClientPublishers();
313        }
314    }
315
316    private void removeHalSubscription(VmsLayer layer) {
317        boolean layerHasSubscribers = true;
318        synchronized (mLock) {
319            if (!mRouting.hasLayerSubscriptions(layer)) {
320                Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
321                return;
322            }
323
324            // Remove the listeners subscription to the layer
325            mRouting.removeHalSubscription(layer);
326
327            // Check if publishers need to be notified about this change in subscriptions.
328            layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
329        }
330        if (!layerHasSubscribers) {
331            notifyHalPublishers(layer, false);
332            notifyClientPublishers();
333        }
334    }
335
336    public void removeHalSubscriptionFromPublisher(VmsLayer layer, int publisherId) {
337        boolean layerHasSubscribers = true;
338        synchronized (mLock) {
339            if (!mRouting.hasLayerSubscriptions(layer)) {
340                Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
341                return;
342            }
343
344            // Remove the listeners subscription to the layer
345            mRouting.removeHalSubscriptionToPublisher(layer, publisherId);
346
347            // Check if publishers need to be notified about this change in subscriptions.
348            layerHasSubscribers = mRouting.hasLayerSubscriptions(layer) ||
349                    mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId);
350        }
351        if (!layerHasSubscribers) {
352            notifyHalPublishers(layer, publisherId, false);
353            notifyClientPublishers();
354        }
355    }
356
357    public boolean containsSubscriber(IVmsSubscriberClient subscriber) {
358        synchronized (mLock) {
359            return mRouting.containsSubscriber(subscriber);
360        }
361    }
362
363    public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering) {
364        synchronized (mLock) {
365            updateOffering(publisherToken, offering);
366            VmsOperationRecorder.get().setPublisherLayersOffering(offering);
367        }
368    }
369
370    public VmsAvailableLayers getAvailableLayers() {
371        synchronized (mLock) {
372            return mAvailableLayers.getAvailableLayers();
373        }
374    }
375
376    /**
377     * Notify all the publishers and the HAL on subscription changes regardless of who triggered
378     * the change.
379     *
380     * @param layer          layer which is being subscribed to or unsubscribed from.
381     * @param hasSubscribers indicates if the notification is for subscription or unsubscription.
382     */
383    private void notifyHalPublishers(VmsLayer layer, boolean hasSubscribers) {
384        // notify the HAL
385        setSubscriptionRequest(layer, hasSubscribers);
386    }
387
388    private void notifyHalPublishers(VmsLayer layer, int publisherId, boolean hasSubscribers) {
389        // notify the HAL
390        setSubscriptionToPublisherRequest(layer, publisherId, hasSubscribers);
391    }
392
393    private void notifyClientPublishers() {
394        // Notify the App publishers
395        for (VmsHalPublisherListener listener : mPublisherListeners) {
396            // Besides the list of layers, also a timestamp is provided to the clients.
397            // They should ignore any notification with a timestamp that is older than the most
398            // recent timestamp they have seen.
399            listener.onChange(getSubscriptionState());
400        }
401    }
402
403    /**
404     * Notify all the subscribers and the HAL on layers availability change.
405     *
406     * @param availableLayers the layers which publishers claim they made publish.
407     */
408    private void notifyOfAvailabilityChange(VmsAvailableLayers availableLayers) {
409        // notify the HAL
410        notifyAvailabilityChangeToHal(availableLayers);
411
412        // Notify the App subscribers
413        for (VmsHalSubscriberListener listener : mSubscriberListeners) {
414            listener.onLayersAvaiabilityChange(availableLayers);
415        }
416    }
417
418    @Override
419    public void init() {
420        if (DBG) {
421            Log.d(TAG, "init()");
422        }
423        if (mIsSupported) {
424            mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID);
425        }
426    }
427
428    @Override
429    public void release() {
430        if (DBG) {
431            Log.d(TAG, "release()");
432        }
433        if (mIsSupported) {
434            mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID);
435        }
436        mPublisherListeners.clear();
437        mSubscriberListeners.clear();
438    }
439
440    @Override
441    public Collection<VehiclePropConfig> takeSupportedProperties(
442            Collection<VehiclePropConfig> allProperties) {
443        List<VehiclePropConfig> taken = new LinkedList<>();
444        for (VehiclePropConfig p : allProperties) {
445            if (p.prop == HAL_PROPERTY_ID) {
446                taken.add(p);
447                mIsSupported = true;
448                if (DBG) {
449                    Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop));
450                }
451                break;
452            }
453        }
454        return taken;
455    }
456
457    /**
458     * Consumes/produces HAL messages. The format of these messages is defined in:
459     * hardware/interfaces/automotive/vehicle/2.1/types.hal
460     */
461    @Override
462    public void handleHalEvents(List<VehiclePropValue> values) {
463        if (DBG) {
464            Log.d(TAG, "Handling a VMS property change");
465        }
466        for (VehiclePropValue v : values) {
467            ArrayList<Integer> vec = v.value.int32Values;
468            int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE);
469
470            if (DBG) {
471                Log.d(TAG, "Handling VMS message type: " + messageType);
472            }
473            switch (messageType) {
474                case VmsMessageType.DATA:
475                    handleDataEvent(vec, toByteArray(v.value.bytes));
476                    break;
477                case VmsMessageType.SUBSCRIBE:
478                    handleSubscribeEvent(vec);
479                    break;
480                case VmsMessageType.UNSUBSCRIBE:
481                    handleUnsubscribeEvent(vec);
482                    break;
483                case VmsMessageType.SUBSCRIBE_TO_PUBLISHER:
484                    handleSubscribeToPublisherEvent(vec);
485                    break;
486                case VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER:
487                    handleUnsubscribeFromPublisherEvent(vec);
488                    break;
489                case VmsMessageType.OFFERING:
490                    handleOfferingEvent(vec);
491                    break;
492                case VmsMessageType.AVAILABILITY_REQUEST:
493                    handleHalAvailabilityRequestEvent();
494                    break;
495                case VmsMessageType.SUBSCRIPTIONS_REQUEST:
496                    handleSubscriptionsRequestEvent();
497                    break;
498                default:
499                    throw new IllegalArgumentException("Unexpected message type: " + messageType);
500            }
501        }
502    }
503
504    private VmsLayer parseVmsLayerFromSimpleMessageIntegerValues(List<Integer> integerValues) {
505        return new VmsLayer(integerValues.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_TYPE),
506                integerValues.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_SUBTYPE),
507                integerValues.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_VERSION));
508    }
509
510    private VmsLayer parseVmsLayerFromDataMessageIntegerValues(List<Integer> integerValues) {
511        return parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
512    }
513
514    private int parsePublisherIdFromDataMessageIntegerValues(List<Integer> integerValues) {
515        return integerValues.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
516    }
517
518
519    /**
520     * Data message format:
521     * <ul>
522     * <li>Message type.
523     * <li>Layer id.
524     * <li>Layer version.
525     * <li>Layer subtype.
526     * <li>Publisher ID.
527     * <li>Payload.
528     * </ul>
529     */
530    private void handleDataEvent(List<Integer> integerValues, byte[] payload) {
531        VmsLayer vmsLayer = parseVmsLayerFromDataMessageIntegerValues(integerValues);
532        int publisherId = parsePublisherIdFromDataMessageIntegerValues(integerValues);
533        if (DBG) {
534            Log.d(TAG, "Handling a data event for Layer: " + vmsLayer);
535        }
536
537        // Send the message.
538        for (VmsHalSubscriberListener listener : mSubscriberListeners) {
539            listener.onDataMessage(vmsLayer, publisherId, payload);
540        }
541    }
542
543    /**
544     * Subscribe message format:
545     * <ul>
546     * <li>Message type.
547     * <li>Layer id.
548     * <li>Layer version.
549     * <li>Layer subtype.
550     * </ul>
551     */
552    private void handleSubscribeEvent(List<Integer> integerValues) {
553        VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
554        if (DBG) {
555            Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer);
556        }
557        addHalSubscription(vmsLayer);
558    }
559
560    /**
561     * Subscribe message format:
562     * <ul>
563     * <li>Message type.
564     * <li>Layer id.
565     * <li>Layer version.
566     * <li>Layer subtype.
567     * <li>Publisher ID
568     * </ul>
569     */
570    private void handleSubscribeToPublisherEvent(List<Integer> integerValues) {
571        VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
572        if (DBG) {
573            Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer);
574        }
575        int publisherId =
576                integerValues.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
577        addHalSubscriptionToPublisher(vmsLayer, publisherId);
578    }
579
580    /**
581     * Unsubscribe message format:
582     * <ul>
583     * <li>Message type.
584     * <li>Layer id.
585     * <li>Layer version.
586     * </ul>
587     */
588    private void handleUnsubscribeEvent(List<Integer> integerValues) {
589        VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
590        if (DBG) {
591            Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer);
592        }
593        removeHalSubscription(vmsLayer);
594    }
595
596    /**
597     * Unsubscribe message format:
598     * <ul>
599     * <li>Message type.
600     * <li>Layer id.
601     * <li>Layer version.
602     * </ul>
603     */
604    private void handleUnsubscribeFromPublisherEvent(List<Integer> integerValues) {
605        VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
606        int publisherId =
607                integerValues.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
608        if (DBG) {
609            Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer);
610        }
611        removeHalSubscriptionFromPublisher(vmsLayer, publisherId);
612    }
613
614    private static int NUM_INTEGERS_IN_VMS_LAYER = 3;
615
616    private VmsLayer parseVmsLayerFromIndex(List<Integer> integerValues, int index) {
617        int layerType = integerValues.get(index++);
618        int layerSutype = integerValues.get(index++);
619        int layerVersion = integerValues.get(index++);
620        return new VmsLayer(layerType, layerSutype, layerVersion);
621    }
622
623    /**
624     * Offering message format:
625     * <ul>
626     * <li>Message type.
627     * <li>Publisher ID.
628     * <li>Number of offerings.
629     * <li>Each offering consists of:
630     * <ul>
631     * <li>Layer id.
632     * <li>Layer version.
633     * <li>Number of layer dependencies.
634     * <li>Layer type/subtype/version.
635     * </ul>
636     * </ul>
637     */
638    private void handleOfferingEvent(List<Integer> integerValues) {
639        int publisherId = integerValues.get(VmsOfferingMessageIntegerValuesIndex.PUBLISHER_ID);
640        int numLayersDependencies =
641                integerValues.get(
642                        VmsOfferingMessageIntegerValuesIndex.NUMBER_OF_OFFERS);
643        int idx = VmsOfferingMessageIntegerValuesIndex.OFFERING_START;
644
645        Set<VmsLayerDependency> offeredLayers = new HashSet<>();
646
647        // An offering is layerId, LayerVersion, LayerType, NumDeps, <LayerId, LayerVersion> X NumDeps.
648        for (int i = 0; i < numLayersDependencies; i++) {
649            VmsLayer offeredLayer = parseVmsLayerFromIndex(integerValues, idx);
650            idx += NUM_INTEGERS_IN_VMS_LAYER;
651
652            int numDependenciesForLayer = integerValues.get(idx++);
653            if (numDependenciesForLayer == 0) {
654                offeredLayers.add(new VmsLayerDependency(offeredLayer));
655            } else {
656                Set<VmsLayer> dependencies = new HashSet<>();
657
658                for (int j = 0; j < numDependenciesForLayer; j++) {
659                    VmsLayer dependantLayer = parseVmsLayerFromIndex(integerValues, idx);
660                    idx += NUM_INTEGERS_IN_VMS_LAYER;
661                    dependencies.add(dependantLayer);
662                }
663                offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies));
664            }
665        }
666        // Store the HAL offering.
667        VmsLayersOffering offering = new VmsLayersOffering(offeredLayers, publisherId);
668        synchronized (mLock) {
669            updateOffering(mHalPublisherToken, offering);
670            VmsOperationRecorder.get().setHalPublisherLayersOffering(offering);
671        }
672    }
673
674    /**
675     * Availability message format:
676     * <ul>
677     * <li>Message type.
678     * <li>Number of layers.
679     * <li>Layer type/subtype/version.
680     * </ul>
681     */
682    private void handleHalAvailabilityRequestEvent() {
683        synchronized (mLock) {
684            VmsAvailableLayers availableLayers = mAvailableLayers.getAvailableLayers();
685            VehiclePropValue vehiclePropertyValue =
686                    toAvailabilityUpdateVehiclePropValue(
687                            availableLayers,
688                            VmsMessageType.AVAILABILITY_RESPONSE);
689
690            setPropertyValue(vehiclePropertyValue);
691        }
692    }
693
694    /**
695     * VmsSubscriptionRequestFormat:
696     * <ul>
697     * <li>Message type.
698     * </ul>
699     * <p>
700     * VmsSubscriptionResponseFormat:
701     * <ul>
702     * <li>Message type.
703     * <li>Sequence number.
704     * <li>Number of layers.
705     * <li>Layer type/subtype/version.
706     * </ul>
707     */
708    private void handleSubscriptionsRequestEvent() {
709        VmsSubscriptionState subscription = getSubscriptionState();
710        VehiclePropValue vehicleProp =
711                toTypedVmsVehiclePropValue(VmsMessageType.SUBSCRIPTIONS_RESPONSE);
712        VehiclePropValue.RawValue v = vehicleProp.value;
713        v.int32Values.add(subscription.getSequenceNumber());
714        Set<VmsLayer> layers = subscription.getLayers();
715        v.int32Values.add(layers.size());
716
717        //TODO(asafro): get the real number of associated layers in the subscriptions
718        //              state and send the associated layers themselves.
719        v.int32Values.add(0);
720
721        for (VmsLayer layer : layers) {
722            v.int32Values.add(layer.getType());
723            v.int32Values.add(layer.getSubtype());
724            v.int32Values.add(layer.getVersion());
725        }
726        setPropertyValue(vehicleProp);
727    }
728
729    private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) {
730        VmsAvailableLayers availableLayers;
731        synchronized (mLock) {
732            mOfferings.put(publisherToken, offering);
733
734            // Update layers availability.
735            mAvailableLayers.setPublishersOffering(mOfferings.values());
736
737            availableLayers = mAvailableLayers.getAvailableLayers();
738        }
739        notifyOfAvailabilityChange(availableLayers);
740    }
741
742    @Override
743    public void dump(PrintWriter writer) {
744        writer.println(TAG);
745        writer.println("VmsProperty " + (mIsSupported ? "" : "not") + " supported.");
746    }
747
748    /**
749     * Updates the VMS HAL property with the given value.
750     *
751     * @param layer          layer data to update the hal property.
752     * @param hasSubscribers if it is a subscribe or unsubscribe message.
753     * @return true if the call to the HAL to update the property was successful.
754     */
755    public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) {
756        VehiclePropValue vehiclePropertyValue = toTypedVmsVehiclePropValueWithLayer(
757                hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
758        return setPropertyValue(vehiclePropertyValue);
759    }
760
761    public boolean setSubscriptionToPublisherRequest(VmsLayer layer,
762                                                     int publisherId,
763                                                     boolean hasSubscribers) {
764        VehiclePropValue vehiclePropertyValue = toTypedVmsVehiclePropValueWithLayer(
765                hasSubscribers ?
766                        VmsMessageType.SUBSCRIBE_TO_PUBLISHER :
767                        VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER, layer);
768        vehiclePropertyValue.value.int32Values.add(publisherId);
769        return setPropertyValue(vehiclePropertyValue);
770    }
771
772    public boolean setDataMessage(VmsLayer layer, byte[] payload) {
773        VehiclePropValue vehiclePropertyValue =
774                toTypedVmsVehiclePropValueWithLayer(VmsMessageType.DATA, layer);
775        VehiclePropValue.RawValue v = vehiclePropertyValue.value;
776        v.bytes.ensureCapacity(payload.length);
777        for (byte b : payload) {
778            v.bytes.add(b);
779        }
780        return setPropertyValue(vehiclePropertyValue);
781    }
782
783    public boolean notifyAvailabilityChangeToHal(VmsAvailableLayers availableLayers) {
784        VehiclePropValue vehiclePropertyValue =
785                toAvailabilityUpdateVehiclePropValue(
786                        availableLayers,
787                        VmsMessageType.AVAILABILITY_CHANGE);
788
789        return setPropertyValue(vehiclePropertyValue);
790    }
791
792    public boolean setPropertyValue(VehiclePropValue vehiclePropertyValue) {
793        try {
794            mVehicleHal.set(vehiclePropertyValue);
795            return true;
796        } catch (PropertyTimeoutException e) {
797            Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(HAL_PROPERTY_ID));
798        }
799        return false;
800    }
801
802    private static VehiclePropValue toTypedVmsVehiclePropValue(int messageType) {
803        VehiclePropValue vehicleProp = new VehiclePropValue();
804        vehicleProp.prop = HAL_PROPERTY_ID;
805        vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
806        VehiclePropValue.RawValue v = vehicleProp.value;
807
808        v.int32Values.add(messageType);
809        return vehicleProp;
810    }
811
812    /**
813     * Creates a {@link VehiclePropValue}
814     */
815    private static VehiclePropValue toTypedVmsVehiclePropValueWithLayer(
816            int messageType, VmsLayer layer) {
817        VehiclePropValue vehicleProp = toTypedVmsVehiclePropValue(messageType);
818        VehiclePropValue.RawValue v = vehicleProp.value;
819        v.int32Values.add(layer.getType());
820        v.int32Values.add(layer.getSubtype());
821        v.int32Values.add(layer.getVersion());
822        return vehicleProp;
823    }
824
825    private static VehiclePropValue toAvailabilityUpdateVehiclePropValue(
826            VmsAvailableLayers availableLayers, int messageType) {
827
828        if (!AVAILABILITY_MESSAGE_TYPES.contains(messageType)) {
829            throw new IllegalArgumentException("Unsupported availability type: " + messageType);
830        }
831        VehiclePropValue vehicleProp =
832                toTypedVmsVehiclePropValue(messageType);
833        populateAvailabilityPropValueFields(availableLayers, vehicleProp);
834        return vehicleProp;
835
836    }
837
838    private static void populateAvailabilityPropValueFields(
839            VmsAvailableLayers availableAssociatedLayers,
840            VehiclePropValue vehicleProp) {
841        VehiclePropValue.RawValue v = vehicleProp.value;
842        v.int32Values.add(availableAssociatedLayers.getSequence());
843        int numLayers = availableAssociatedLayers.getAssociatedLayers().size();
844        v.int32Values.add(numLayers);
845        for (VmsAssociatedLayer layer : availableAssociatedLayers.getAssociatedLayers()) {
846            v.int32Values.add(layer.getVmsLayer().getType());
847            v.int32Values.add(layer.getVmsLayer().getSubtype());
848            v.int32Values.add(layer.getVmsLayer().getVersion());
849            v.int32Values.add(layer.getPublisherIds().size());
850            for (int publisherId : layer.getPublisherIds()) {
851                v.int32Values.add(publisherId);
852            }
853        }
854    }
855}
856