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