• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
VehicleHal(IVehicle vehicle)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
VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, AudioHalService audioHal, CabinHalService cabinHal, RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient)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
VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal, RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient)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 
vehicleHalReconnected(IVehicle vehicle)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 
init()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 
release()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 
getSensorHal()233     public SensorHalService getSensorHal() {
234         return mSensorHal;
235     }
236 
getInfoHal()237     public InfoHalService getInfoHal() {
238         return mInfoHal;
239     }
240 
getAudioHal()241     public AudioHalService getAudioHal() {
242         return mAudioHal;
243     }
244 
getCabinHal()245     public CabinHalService getCabinHal() {
246         return mCabinHal;
247     }
248 
getDiagnosticHal()249     public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
250 
getRadioHal()251     public RadioHalService getRadioHal() {
252         return mRadioHal;
253     }
254 
getPowerHal()255     public PowerHalService getPowerHal() {
256         return mPowerHal;
257     }
258 
getHvacHal()259     public HvacHalService getHvacHal() {
260         return mHvacHal;
261     }
262 
getInputHal()263     public InputHalService getInputHal() {
264         return mInputHal;
265     }
266 
getVendorExtensionHal()267     public VendorExtensionHalService getVendorExtensionHal() {
268         return mVendorExtensionHal;
269     }
270 
assertServiceOwnerLocked(HalServiceBase service, int property)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      */
subscribeProperty(HalServiceBase service, int property)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      */
subscribeProperty(HalServiceBase service, int property, float sampleRateHz)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      */
subscribeProperty(HalServiceBase service, int property, float samplingRateHz, int flags)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 
unsubscribeProperty(HalServiceBase service, int property)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 
isPropertySupported(int propertyId)368     public boolean isPropertySupported(int propertyId) {
369         return mAllProperties.containsKey(propertyId);
370     }
371 
getAllPropConfigs()372     public Collection<VehiclePropConfig> getAllPropConfigs() {
373         return mAllProperties.values();
374     }
375 
get(int propertyId)376     public VehiclePropValue get(int propertyId) throws PropertyTimeoutException {
377         return get(propertyId, NO_AREA);
378     }
379 
get(int propertyId, int areaId)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 
get(Class clazz, int propertyId)391     public <T> T get(Class clazz, int propertyId) throws PropertyTimeoutException {
392         return get(clazz, createPropValue(propertyId, NO_AREA));
393     }
394 
get(Class clazz, int propertyId, int areaId)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")
get(Class clazz, VehiclePropValue requestedPropValue)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 
get(VehiclePropValue requestedPropValue)430     public VehiclePropValue get(VehiclePropValue requestedPropValue)
431             throws PropertyTimeoutException {
432         return mHalClient.getValue(requestedPropValue);
433     }
434 
set(VehiclePropValue propValue)435     void set(VehiclePropValue propValue) throws PropertyTimeoutException {
436         mHalClient.setValue(propValue);
437     }
438 
439     @CheckResult
set(int propId)440     VehiclePropValueSetter set(int propId) {
441         return new VehiclePropValueSetter(mHalClient, propId, NO_AREA);
442     }
443 
444     @CheckResult
set(int propId, int areaId)445     VehiclePropValueSetter set(int propId, int areaId) {
446         return new VehiclePropValueSetter(mHalClient, propId, areaId);
447     }
448 
isPropertySubscribable(VehiclePropConfig config)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 
dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs)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
onPropertyEvent(ArrayList<VehiclePropValue> propValues)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
onPropertySet(VehiclePropValue value)494     public void onPropertySet(VehiclePropValue value) {
495         // No need to handle on-property-set events in HAL service yet.
496     }
497 
498     @Override
onPropertySetError(int errorCode, int propId, int areaId)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 
dump(PrintWriter writer)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      */
injectBooleanEvent(int propId, int areaId, boolean value)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      */
injectIntegerEvent(int propId, int value)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 
VehiclePropertyEventInfo(VehiclePropValue event)585         private VehiclePropertyEventInfo(VehiclePropValue event) {
586             eventCount = 1;
587             lastEvent = event;
588         }
589 
addNewEvent(VehiclePropValue event)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 
VehiclePropValueSetter(HalClient client, int propId, int areaId)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 
to(boolean value)607         void to(boolean value) throws PropertyTimeoutException {
608             to(value ? 1 : 0);
609         }
610 
to(int value)611         void to(int value) throws PropertyTimeoutException {
612             mPropValue.value.int32Values.add(value);
613             submit();
614         }
615 
to(int[] values)616         void to(int[] values) throws PropertyTimeoutException {
617             for (int value : values) {
618                 mPropValue.value.int32Values.add(value);
619             }
620             submit();
621         }
622 
to(Collection<Integer> values)623         void to(Collection<Integer> values) throws PropertyTimeoutException {
624             mPropValue.value.int32Values.addAll(values);
625             submit();
626         }
627 
submit()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 
dumpVehiclePropValue(VehiclePropValue value)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 
createPropValue(int propId, int areaId)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