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