1/*
2 * Copyright (C) 2015 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
17package com.android.car.hal;
18
19import static com.android.car.CarServiceUtils.toByteArray;
20import static com.android.car.CarServiceUtils.toFloatArray;
21import static com.android.car.CarServiceUtils.toIntArray;
22import static java.lang.Integer.toHexString;
23
24import android.annotation.CheckResult;
25import android.car.annotation.FutureFeature;
26import android.hardware.automotive.vehicle.V2_0.IVehicle;
27import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
28import android.hardware.automotive.vehicle.V2_0.SubscribeFlags;
29import android.hardware.automotive.vehicle.V2_0.SubscribeOptions;
30import android.hardware.automotive.vehicle.V2_0.VehicleAreaConfig;
31import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
32import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
33import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
34import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
35import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
36import android.os.HandlerThread;
37import android.os.RemoteException;
38import android.os.SystemClock;
39import android.util.ArraySet;
40import android.util.Log;
41import android.util.SparseArray;
42
43import com.google.android.collect.Lists;
44
45import com.android.car.CarLog;
46import com.android.car.internal.FeatureConfiguration;
47import com.android.internal.annotations.VisibleForTesting;
48
49import java.io.PrintWriter;
50import java.lang.ref.WeakReference;
51import java.util.ArrayList;
52import java.util.Arrays;
53import java.util.Collection;
54import java.util.HashMap;
55import java.util.HashSet;
56import java.util.List;
57import java.util.Set;
58
59/**
60 * Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing
61 * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase}
62 * implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding
63 * Car*Service for Car*Manager API.
64 */
65public class VehicleHal extends IVehicleCallback.Stub {
66
67    private static final boolean DBG = false;
68
69    private static final int NO_AREA = -1;
70
71    private final HandlerThread mHandlerThread;
72    private final SensorHalService mSensorHal;
73    private final InfoHalService mInfoHal;
74    private final AudioHalService mAudioHal;
75    private final CabinHalService mCabinHal;
76    private final RadioHalService mRadioHal;
77    private final PowerHalService mPowerHal;
78    private final HvacHalService mHvacHal;
79    private final InputHalService mInputHal;
80    private final VendorExtensionHalService mVendorExtensionHal;
81    @FutureFeature
82    private VmsHalService mVmsHal;
83
84    @FutureFeature
85    private DiagnosticHalService mDiagnosticHal = null;
86
87    /** Might be re-assigned if Vehicle HAL is reconnected. */
88    private volatile HalClient mHalClient;
89
90    /** Stores handler for each HAL property. Property events are sent to handler. */
91    private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>();
92    /** This is for iterating all HalServices with fixed order. */
93    private final ArrayList<HalServiceBase> mAllServices = new ArrayList<>();
94    private final HashMap<Integer, SubscribeOptions> mSubscribedProperties = new HashMap<>();
95    private final HashMap<Integer, VehiclePropConfig> mAllProperties = new HashMap<>();
96    private final HashMap<Integer, VehiclePropertyEventInfo> mEventLog = new HashMap<>();
97
98    public VehicleHal(IVehicle vehicle) {
99        mHandlerThread = new HandlerThread("VEHICLE-HAL");
100        mHandlerThread.start();
101        // passing this should be safe as long as it is just kept and not used in constructor
102        mPowerHal = new PowerHalService(this);
103        mSensorHal = new SensorHalService(this);
104        mInfoHal = new InfoHalService(this);
105        mAudioHal = new AudioHalService(this);
106        mCabinHal = new CabinHalService(this);
107        mRadioHal = new RadioHalService(this);
108        mHvacHal = new HvacHalService(this);
109        mInputHal = new InputHalService(this);
110        mVendorExtensionHal = new VendorExtensionHalService(this);
111        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
112            mVmsHal = new VmsHalService(this);
113        }
114        if(FeatureConfiguration.ENABLE_DIAGNOSTIC) {
115            mDiagnosticHal = new DiagnosticHalService(this);
116        }
117        mAllServices.addAll(Arrays.asList(mPowerHal,
118                mSensorHal,
119                mInfoHal,
120                mAudioHal,
121                mCabinHal,
122                mRadioHal,
123                mHvacHal,
124                mInputHal,
125                mVendorExtensionHal));
126        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
127            mAllServices.add(mVmsHal);
128        }
129        if(FeatureConfiguration.ENABLE_DIAGNOSTIC) {
130            mAllServices.add(mDiagnosticHal);
131        }
132
133        mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/);
134    }
135
136    /** Dummy version only for testing */
137    @VisibleForTesting
138    public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
139            AudioHalService audioHal, CabinHalService cabinHal,
140            RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) {
141        mHandlerThread = null;
142        mPowerHal = powerHal;
143        mSensorHal = sensorHal;
144        mInfoHal = infoHal;
145        mAudioHal = audioHal;
146        mCabinHal = cabinHal;
147        mRadioHal = radioHal;
148        mHvacHal = hvacHal;
149        mInputHal = null;
150        mVendorExtensionHal = null;
151
152        if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
153            // TODO(antoniocortes): do we need a test version of VmsHalService?
154            mVmsHal = null;
155        }
156        if(FeatureConfiguration.ENABLE_DIAGNOSTIC) {
157            mDiagnosticHal = null;
158        }
159
160        mHalClient = halClient;
161    }
162
163    /** Dummy version only for testing */
164    @VisibleForTesting
165    @FutureFeature
166    public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
167            AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal,
168            RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) {
169            mHandlerThread = null;
170            mPowerHal = powerHal;
171            mSensorHal = sensorHal;
172            mInfoHal = infoHal;
173            mAudioHal = audioHal;
174            mCabinHal = cabinHal;
175            mDiagnosticHal = diagnosticHal;
176            mRadioHal = radioHal;
177            mHvacHal = hvacHal;
178            mInputHal = null;
179            mVendorExtensionHal = null;
180            // TODO(antoniocortes): do we need a test version of VmsHalService?
181            mVmsHal = null;
182            mHalClient = halClient;
183            mDiagnosticHal = diagnosticHal;
184    }
185
186    public void vehicleHalReconnected(IVehicle vehicle) {
187        synchronized (this) {
188            mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(),
189                    this /*IVehicleCallback*/);
190
191            SubscribeOptions[] options = mSubscribedProperties.values()
192                    .toArray(new SubscribeOptions[0]);
193
194            try {
195                mHalClient.subscribe(options);
196            } catch (RemoteException e) {
197                throw new RuntimeException("Failed to subscribe: " + Arrays.asList(options), e);
198            }
199        }
200    }
201
202    public void init() {
203        Set<VehiclePropConfig> properties;
204        try {
205            properties = new HashSet<>(mHalClient.getAllPropConfigs());
206        } catch (RemoteException e) {
207            throw new RuntimeException("Unable to retrieve vehicle property configuration", e);
208        }
209
210        synchronized (this) {
211            // Create map of all properties
212            for (VehiclePropConfig p : properties) {
213                mAllProperties.put(p.prop, p);
214            }
215        }
216
217        for (HalServiceBase service: mAllServices) {
218            Collection<VehiclePropConfig> taken = service.takeSupportedProperties(properties);
219            if (taken == null) {
220                continue;
221            }
222            if (DBG) {
223                Log.i(CarLog.TAG_HAL, "HalService " + service + " take properties " + taken.size());
224            }
225            synchronized (this) {
226                for (VehiclePropConfig p: taken) {
227                    mPropertyHandlers.append(p.prop, service);
228                }
229            }
230            properties.removeAll(taken);
231            service.init();
232        }
233    }
234
235    public void release() {
236        // release in reverse order from init
237        for (int i = mAllServices.size() - 1; i >= 0; i--) {
238            mAllServices.get(i).release();
239        }
240        synchronized (this) {
241            for (int p : mSubscribedProperties.keySet()) {
242                try {
243                    mHalClient.unsubscribe(p);
244                } catch (RemoteException e) {
245                    //  Ignore exceptions on shutdown path.
246                    Log.w(CarLog.TAG_HAL, "Failed to unsubscribe", e);
247                }
248            }
249            mSubscribedProperties.clear();
250            mAllProperties.clear();
251        }
252        // keep the looper thread as should be kept for the whole life cycle.
253    }
254
255    public SensorHalService getSensorHal() {
256        return mSensorHal;
257    }
258
259    public InfoHalService getInfoHal() {
260        return mInfoHal;
261    }
262
263    public AudioHalService getAudioHal() {
264        return mAudioHal;
265    }
266
267    public CabinHalService getCabinHal() {
268        return mCabinHal;
269    }
270
271    @FutureFeature
272    public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
273
274    public RadioHalService getRadioHal() {
275        return mRadioHal;
276    }
277
278    public PowerHalService getPowerHal() {
279        return mPowerHal;
280    }
281
282    public HvacHalService getHvacHal() {
283        return mHvacHal;
284    }
285
286    public InputHalService getInputHal() {
287        return mInputHal;
288    }
289
290    public VendorExtensionHalService getVendorExtensionHal() {
291        return mVendorExtensionHal;
292    }
293
294    @FutureFeature
295    public VmsHalService getVmsHal() { return mVmsHal; }
296
297    private void assertServiceOwnerLocked(HalServiceBase service, int property) {
298        if (service != mPropertyHandlers.get(property)) {
299            throw new IllegalArgumentException("Property 0x" + toHexString(property)
300                    + " is not owned by service: " + service);
301        }
302    }
303
304    /**
305     * Subscribes given properties with sampling rate defaults to 0 and no special flags provided.
306     *
307     * @see #subscribeProperty(HalServiceBase, int, float, int)
308     */
309    public void subscribeProperty(HalServiceBase service, int property)
310            throws IllegalArgumentException {
311        subscribeProperty(service, property, 0f, SubscribeFlags.DEFAULT);
312    }
313
314    /**
315     * Subscribes given properties with default subscribe flag.
316     *
317     * @see #subscribeProperty(HalServiceBase, int, float, int)
318     */
319    public void subscribeProperty(HalServiceBase service, int property, float sampleRateHz)
320            throws IllegalArgumentException {
321        subscribeProperty(service, property, sampleRateHz, SubscribeFlags.DEFAULT);
322    }
323
324    /**
325     * Subscribe given property. Only Hal service owning the property can subscribe it.
326     *
327     * @param service HalService that owns this property
328     * @param property property id (VehicleProperty)
329     * @param samplingRateHz sampling rate in Hz for continuous properties
330     * @param flags flags from {@link android.hardware.automotive.vehicle.V2_0.SubscribeFlags}
331     * @throws IllegalArgumentException thrown if property is not supported by VHAL
332     */
333    public void subscribeProperty(HalServiceBase service, int property,
334            float samplingRateHz, int flags) throws IllegalArgumentException {
335        if (DBG) {
336            Log.i(CarLog.TAG_HAL, "subscribeProperty, service:" + service
337                    + ", property: 0x" + toHexString(property));
338        }
339        VehiclePropConfig config;
340        synchronized (this) {
341            config = mAllProperties.get(property);
342        }
343
344        if (config == null) {
345            throw new IllegalArgumentException("subscribe error: config is null for property 0x" +
346                    toHexString(property));
347        } else if (isPropertySubscribable(config)) {
348            SubscribeOptions opts = new SubscribeOptions();
349            opts.propId = property;
350            opts.sampleRate = samplingRateHz;
351            opts.flags = flags;
352            synchronized (this) {
353                assertServiceOwnerLocked(service, property);
354                mSubscribedProperties.put(property, opts);
355            }
356            try {
357                mHalClient.subscribe(opts);
358            } catch (RemoteException e) {
359                Log.e(CarLog.TAG_HAL, "Failed to subscribe to property: 0x" + property, e);
360            }
361        } else {
362            Log.e(CarLog.TAG_HAL, "Cannot subscribe to property: " + property);
363        }
364    }
365
366    public void unsubscribeProperty(HalServiceBase service, int property) {
367        if (DBG) {
368            Log.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service
369                    + ", property: 0x" + toHexString(property));
370        }
371        VehiclePropConfig config;
372        synchronized (this) {
373            config = mAllProperties.get(property);
374        }
375
376        if (config == null) {
377            Log.e(CarLog.TAG_HAL, "unsubscribeProperty: property " + property + " does not exist");
378        } else if (isPropertySubscribable(config)) {
379            synchronized (this) {
380                assertServiceOwnerLocked(service, property);
381                mSubscribedProperties.remove(property);
382            }
383            try {
384                mHalClient.unsubscribe(property);
385            } catch (RemoteException e) {
386                Log.e(CarLog.TAG_SERVICE, "Failed to unsubscribe from property: 0x"
387                        + toHexString(property), e);
388            }
389        } else {
390            Log.e(CarLog.TAG_HAL, "Cannot unsubscribe property: " + property);
391        }
392    }
393
394    public boolean isPropertySupported(int propertyId) {
395        return mAllProperties.containsKey(propertyId);
396    }
397
398    public Collection<VehiclePropConfig> getAllPropConfigs() {
399        return mAllProperties.values();
400    }
401
402    public VehiclePropValue get(int propertyId) throws PropertyTimeoutException {
403        return get(propertyId, NO_AREA);
404    }
405
406    public VehiclePropValue get(int propertyId, int areaId) throws PropertyTimeoutException {
407        if (DBG) {
408            Log.i(CarLog.TAG_HAL, "get, property: 0x" + toHexString(propertyId)
409                    + ", areaId: 0x" + toHexString(areaId));
410        }
411        VehiclePropValue propValue = new VehiclePropValue();
412        propValue.prop = propertyId;
413        propValue.areaId = areaId;
414        return mHalClient.getValue(propValue);
415    }
416
417    public <T> T get(Class clazz, int propertyId) throws PropertyTimeoutException {
418        return get(clazz, createPropValue(propertyId, NO_AREA));
419    }
420
421    public <T> T get(Class clazz, int propertyId, int areaId) throws PropertyTimeoutException {
422        return get(clazz, createPropValue(propertyId, areaId));
423    }
424
425    @SuppressWarnings("unchecked")
426    public <T> T get(Class clazz, VehiclePropValue requestedPropValue)
427            throws PropertyTimeoutException {
428        VehiclePropValue propValue;
429        propValue = mHalClient.getValue(requestedPropValue);
430
431        if (clazz == Integer.class || clazz == int.class) {
432            return (T) propValue.value.int32Values.get(0);
433        } else if (clazz == Boolean.class || clazz == boolean.class) {
434            return (T) Boolean.valueOf(propValue.value.int32Values.get(0) == 1);
435        } else if (clazz == Float.class || clazz == float.class) {
436            return (T) propValue.value.floatValues.get(0);
437        } else if (clazz == Integer[].class) {
438            Integer[] intArray = new Integer[propValue.value.int32Values.size()];
439            return (T) propValue.value.int32Values.toArray(intArray);
440        } else if (clazz == Float[].class) {
441            Float[] floatArray = new Float[propValue.value.floatValues.size()];
442            return (T) propValue.value.floatValues.toArray(floatArray);
443        } else if (clazz == int[].class) {
444            return (T) toIntArray(propValue.value.int32Values);
445        } else if (clazz == float[].class) {
446            return (T) toFloatArray(propValue.value.floatValues);
447        } else if (clazz == byte[].class) {
448            return (T) toByteArray(propValue.value.bytes);
449        } else if (clazz == String.class) {
450            return (T) propValue.value.stringValue;
451        } else {
452            throw new IllegalArgumentException("Unexpected type: " + clazz);
453        }
454    }
455
456    public VehiclePropValue get(VehiclePropValue requestedPropValue)
457            throws PropertyTimeoutException {
458        return mHalClient.getValue(requestedPropValue);
459    }
460
461    void set(VehiclePropValue propValue) throws PropertyTimeoutException {
462        mHalClient.setValue(propValue);
463    }
464
465    @CheckResult
466    VehiclePropValueSetter set(int propId) {
467        return new VehiclePropValueSetter(mHalClient, propId, NO_AREA);
468    }
469
470    @CheckResult
471    VehiclePropValueSetter set(int propId, int areaId) {
472        return new VehiclePropValueSetter(mHalClient, propId, areaId);
473    }
474
475    static boolean isPropertySubscribable(VehiclePropConfig config) {
476        if ((config.access & VehiclePropertyAccess.READ) == 0 ||
477                (config.changeMode == VehiclePropertyChangeMode.STATIC)) {
478            return false;
479        }
480        return true;
481    }
482
483    static void dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs) {
484        for (VehiclePropConfig config : configs) {
485            writer.println(String.format("property 0x%x", config.prop));
486        }
487    }
488
489    private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>();
490
491    @Override
492    public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
493        synchronized (this) {
494            for (VehiclePropValue v : propValues) {
495                HalServiceBase service = mPropertyHandlers.get(v.prop);
496                if(service == null) {
497                    Log.e(CarLog.TAG_HAL, "HalService not found for prop: 0x"
498                        + toHexString(v.prop));
499                    continue;
500                }
501                service.getDispatchList().add(v);
502                mServicesToDispatch.add(service);
503                VehiclePropertyEventInfo info = mEventLog.get(v.prop);
504                if (info == null) {
505                    info = new VehiclePropertyEventInfo(v);
506                    mEventLog.put(v.prop, info);
507                } else {
508                    info.addNewEvent(v);
509                }
510            }
511        }
512        for (HalServiceBase s : mServicesToDispatch) {
513            s.handleHalEvents(s.getDispatchList());
514            s.getDispatchList().clear();
515        }
516        mServicesToDispatch.clear();
517    }
518
519    @Override
520    public void onPropertySet(VehiclePropValue value) {
521        // No need to handle on-property-set events in HAL service yet.
522    }
523
524    @Override
525    public void onPropertySetError(int errorCode, int propId, int areaId) {
526        Log.e(CarLog.TAG_HAL, String.format("onPropertySetError, errorCode: %d, prop: 0x%x, "
527                + "area: 0x%x", errorCode, propId, areaId));
528        if (propId != VehicleProperty.INVALID) {
529            HalServiceBase service = mPropertyHandlers.get(propId);
530            if (service != null) {
531                service.handlePropertySetError(propId, areaId);
532            }
533        }
534    }
535
536    public void dump(PrintWriter writer) {
537        writer.println("**dump HAL services**");
538        for (HalServiceBase service: mAllServices) {
539            service.dump(writer);
540        }
541
542        List<VehiclePropConfig> configList;
543        synchronized (this) {
544            configList = new ArrayList<>(mAllProperties.values());
545        }
546
547        writer.println("**All properties**");
548        for (VehiclePropConfig config : configList) {
549            StringBuilder builder = new StringBuilder()
550                    .append("Property:0x").append(toHexString(config.prop))
551                    .append(",access:0x").append(toHexString(config.access))
552                    .append(",changeMode:0x").append(toHexString(config.changeMode))
553                    .append(",areas:0x").append(toHexString(config.supportedAreas))
554                    .append(",config:0x").append(Arrays.toString(config.configArray.toArray()))
555                    .append(",fs min:").append(config.minSampleRate)
556                    .append(",fs max:").append(config.maxSampleRate);
557            for (VehicleAreaConfig area : config.areaConfigs) {
558                builder.append(",areaId :").append(toHexString(area.areaId))
559                        .append(",f min:").append(area.minFloatValue)
560                        .append(",f max:").append(area.maxFloatValue)
561                        .append(",i min:").append(area.minInt32Value)
562                        .append(",i max:").append(area.maxInt32Value)
563                        .append(",i64 min:").append(area.minInt64Value)
564                        .append(",i64 max:").append(area.maxInt64Value);
565            }
566            writer.println(builder.toString());
567        }
568        writer.println(String.format("**All Events, now ns:%d**",
569                SystemClock.elapsedRealtimeNanos()));
570        for (VehiclePropertyEventInfo info : mEventLog.values()) {
571            writer.println(String.format("event count:%d, lastEvent:%s",
572                    info.eventCount, dumpVehiclePropValue(info.lastEvent)));
573        }
574
575        writer.println("**Property handlers**");
576        for (int i = 0; i < mPropertyHandlers.size(); i++) {
577            int propId = mPropertyHandlers.keyAt(i);
578            HalServiceBase service = mPropertyHandlers.valueAt(i);
579            writer.println(String.format("Prop: 0x%08X, service: %s", propId, service));
580        }
581    }
582
583    /**
584     * Inject a fake boolean HAL event - for testing purposes.
585     * @param propId - VehicleProperty ID
586     * @param areaId - Vehicle Area ID
587     * @param value - true/false to inject
588     */
589    public void injectBooleanEvent(int propId, int areaId, boolean value) {
590        VehiclePropValue v = createPropValue(propId, areaId);
591        v.value.int32Values.add(value? 1 : 0);
592        onPropertyEvent(Lists.newArrayList(v));
593    }
594
595    /**
596     * Inject a fake Integer HAL event - for testing purposes.
597     * @param propId - VehicleProperty ID
598     * @param value - Integer value to inject
599     */
600    public void injectIntegerEvent(int propId, int value) {
601        VehiclePropValue v = createPropValue(propId, 0);
602        v.value.int32Values.add(value);
603        v.timestamp = SystemClock.elapsedRealtimeNanos();
604        onPropertyEvent(Lists.newArrayList(v));
605    }
606
607    private static class VehiclePropertyEventInfo {
608        private int eventCount;
609        private VehiclePropValue lastEvent;
610
611        private VehiclePropertyEventInfo(VehiclePropValue event) {
612            eventCount = 1;
613            lastEvent = event;
614        }
615
616        private void addNewEvent(VehiclePropValue event) {
617            eventCount++;
618            lastEvent = event;
619        }
620    }
621
622    final class VehiclePropValueSetter {
623        final WeakReference<HalClient> mClient;
624        final VehiclePropValue mPropValue;
625
626        private VehiclePropValueSetter(HalClient client, int propId, int areaId) {
627            mClient = new WeakReference<>(client);
628            mPropValue = new VehiclePropValue();
629            mPropValue.prop = propId;
630            mPropValue.areaId = areaId;
631        }
632
633        void to(boolean value) throws PropertyTimeoutException {
634            to(value ? 1 : 0);
635        }
636
637        void to(int value) throws PropertyTimeoutException {
638            mPropValue.value.int32Values.add(value);
639            submit();
640        }
641
642        void to(int[] values) throws PropertyTimeoutException {
643            for (int value : values) {
644                mPropValue.value.int32Values.add(value);
645            }
646            submit();
647        }
648
649        void to(Collection<Integer> values) throws PropertyTimeoutException {
650            mPropValue.value.int32Values.addAll(values);
651            submit();
652        }
653
654        void submit() throws PropertyTimeoutException {
655            HalClient client =  mClient.get();
656            if (client != null) {
657                if (DBG) {
658                    Log.i(CarLog.TAG_HAL, "set, property: 0x" + toHexString(mPropValue.prop)
659                            + ", areaId: 0x" + toHexString(mPropValue.areaId));
660                }
661                client.setValue(mPropValue);
662            }
663        }
664    }
665
666    private static String dumpVehiclePropValue(VehiclePropValue value) {
667        final int MAX_BYTE_SIZE = 20;
668
669        StringBuilder sb = new StringBuilder()
670                .append("Property:0x").append(toHexString(value.prop))
671                .append(",timestamp:").append(value.timestamp)
672                .append(",zone:0x").append(toHexString(value.areaId))
673                .append(",floatValues: ").append(Arrays.toString(value.value.floatValues.toArray()))
674                .append(",int32Values: ").append(Arrays.toString(value.value.int32Values.toArray()))
675                .append(",int64Values: ")
676                .append(Arrays.toString(value.value.int64Values.toArray()));
677
678        if (value.value.bytes.size() > MAX_BYTE_SIZE) {
679            Object[] bytes = Arrays.copyOf(value.value.bytes.toArray(), MAX_BYTE_SIZE);
680            sb.append(",bytes: ").append(Arrays.toString(bytes));
681        } else {
682            sb.append(",bytes: ").append(Arrays.toString(value.value.bytes.toArray()));
683        }
684        sb.append(",string: ").append(value.value.stringValue);
685
686        return sb.toString();
687    }
688
689    private static VehiclePropValue createPropValue(int propId, int areaId) {
690        VehiclePropValue propValue = new VehiclePropValue();
691        propValue.prop = propId;
692        propValue.areaId = areaId;
693        return propValue;
694    }
695}
696