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