• 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 
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 
VehicleHal(IVehicle vehicle)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
VehicleHal(PowerHalService powerHal, DiagnosticHalService diagnosticHal, HalClient halClient, PropertyHalService propertyHal)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 
vehicleHalReconnected(IVehicle vehicle)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 
init()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 
release()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 
getDiagnosticHal()193     public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
194 
getPowerHal()195     public PowerHalService getPowerHal() {
196         return mPowerHal;
197     }
198 
getPropertyHal()199     public PropertyHalService getPropertyHal() {
200         return mPropertyHal;
201     }
202 
getInputHal()203     public InputHalService getInputHal() {
204         return mInputHal;
205     }
206 
getVmsHal()207     public VmsHalService getVmsHal() { return mVmsHal; }
208 
assertServiceOwnerLocked(HalServiceBase service, int property)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      */
subscribeProperty(HalServiceBase service, int property)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      */
subscribeProperty(HalServiceBase service, int property, float sampleRateHz)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      */
subscribeProperty(HalServiceBase service, int property, float samplingRateHz, int flags)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 
unsubscribeProperty(HalServiceBase service, int property)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 
isPropertySupported(int propertyId)306     public boolean isPropertySupported(int propertyId) {
307         return mAllProperties.containsKey(propertyId);
308     }
309 
getAllPropConfigs()310     public Collection<VehiclePropConfig> getAllPropConfigs() {
311         return mAllProperties.values();
312     }
313 
get(int propertyId)314     public VehiclePropValue get(int propertyId) throws PropertyTimeoutException {
315         return get(propertyId, NO_AREA);
316     }
317 
get(int propertyId, int areaId)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 
get(Class clazz, int propertyId)329     public <T> T get(Class clazz, int propertyId) throws PropertyTimeoutException {
330         return get(clazz, createPropValue(propertyId, NO_AREA));
331     }
332 
get(Class clazz, int propertyId, int areaId)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")
get(Class clazz, VehiclePropValue requestedPropValue)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 
get(VehiclePropValue requestedPropValue)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      */
getSampleRate(int propId)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 
set(VehiclePropValue propValue)390     protected void set(VehiclePropValue propValue) throws PropertyTimeoutException {
391         mHalClient.setValue(propValue);
392     }
393 
394     @CheckResult
set(int propId)395     VehiclePropValueSetter set(int propId) {
396         return new VehiclePropValueSetter(mHalClient, propId, NO_AREA);
397     }
398 
399     @CheckResult
set(int propId, int areaId)400     VehiclePropValueSetter set(int propId, int areaId) {
401         return new VehiclePropValueSetter(mHalClient, propId, areaId);
402     }
403 
isPropertySubscribable(VehiclePropConfig config)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 
dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs)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
onPropertyEvent(ArrayList<VehiclePropValue> propValues)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
onPropertySet(VehiclePropValue value)449     public void onPropertySet(VehiclePropValue value) {
450         // No need to handle on-property-set events in HAL service yet.
451     }
452 
453     @Override
onPropertySetError(int errorCode, int propId, int areaId)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 
dump(PrintWriter writer)465     public void dump(PrintWriter writer) {
466         writer.println("**dump HAL services**");
467         for (HalServiceBase service: mAllServices) {
468             service.dump(writer);
469         }
470         // Dump all VHAL property configure.
471         dumpPropertyConfigs(writer, "");
472         writer.println(String.format("**All Events, now ns:%d**",
473                 SystemClock.elapsedRealtimeNanos()));
474         for (VehiclePropertyEventInfo info : mEventLog.values()) {
475             writer.println(String.format("event count:%d, lastEvent:%s",
476                     info.eventCount, dumpVehiclePropValue(info.lastEvent)));
477         }
478 
479         writer.println("**Property handlers**");
480         for (int i = 0; i < mPropertyHandlers.size(); i++) {
481             int propId = mPropertyHandlers.keyAt(i);
482             HalServiceBase service = mPropertyHandlers.valueAt(i);
483             writer.println(String.format("Prop: 0x%08X, service: %s", propId, service));
484         }
485     }
486 
487     /**
488      * Dumps vehicle property values.
489      * @param writer
490      * @param propId property id, dump all properties' value if it is empty string.
491      * @param areaId areaId of the property, dump the property for all areaIds in the config
492      * if it is empty string.
493      */
dumpPropertyValueByCommend(PrintWriter writer, String propId, String areaId)494     public void dumpPropertyValueByCommend(PrintWriter writer, String propId, String areaId) {
495         if (propId.equals("")) {
496             writer.println("**All property values**");
497             for (VehiclePropConfig config : mAllProperties.values()) {
498                 dumpPropertyValueByConfig(writer, config);
499             }
500         } else if (areaId.equals("")) {
501             VehiclePropConfig config = mAllProperties.get(Integer.parseInt(propId, 16));
502             dumpPropertyValueByConfig(writer, config);
503         } else {
504             int id = Integer.parseInt(propId, 16);
505             int area = Integer.parseInt(areaId);
506             try {
507                 VehiclePropValue value = get(id, area);
508                 writer.println(dumpVehiclePropValue(value));
509             } catch (Exception e) {
510                 writer.println("Can not get property value for propertyId: 0x"
511                         + propId + ", areaId: " + area);
512             }
513         }
514     }
515 
dumpPropertyValueByConfig(PrintWriter writer, VehiclePropConfig config)516     private void dumpPropertyValueByConfig(PrintWriter writer, VehiclePropConfig config) {
517         if (config.areaConfigs.isEmpty()) {
518             try {
519                 VehiclePropValue value = get(config.prop);
520                 writer.println(dumpVehiclePropValue(value));
521             } catch (Exception e) {
522                 writer.println("Can not get property value for propertyId: 0x"
523                         + toHexString(config.prop) + ", areaId: 0");
524             }
525         } else {
526             for (VehicleAreaConfig areaConfig : config.areaConfigs) {
527                 int area = areaConfig.areaId;
528                 try {
529                     VehiclePropValue value = get(config.prop, area);
530                     writer.println(dumpVehiclePropValue(value));
531                 } catch (Exception e) {
532                     writer.println("Can not get property value for propertyId: 0x"
533                             + toHexString(config.prop) + ", areaId: " + area);
534                 }
535             }
536         }
537     }
538 
539     /**
540      * Dump VHAL property configs.
541      *
542      * @param writer
543      * @param propId Property ID in Hex. If propid is empty string, dump all properties.
544      */
dumpPropertyConfigs(PrintWriter writer, String propId)545     public void dumpPropertyConfigs(PrintWriter writer, String propId) {
546         List<VehiclePropConfig> configList;
547         synchronized (this) {
548             configList = new ArrayList<>(mAllProperties.values());
549         }
550 
551         if (propId.equals("")) {
552             writer.println("**All properties**");
553             for (VehiclePropConfig config : configList) {
554                 writer.println(dumpPropertyConfigsHelp(config));
555             }
556             return;
557         }
558         for (VehiclePropConfig config : configList) {
559             if (toHexString(config.prop).equals(propId)) {
560                 writer.println(dumpPropertyConfigsHelp(config));
561                 return;
562             }
563         }
564 
565     }
566 
567     /** Use VehiclePropertyConfig to construct string for dumping */
dumpPropertyConfigsHelp(VehiclePropConfig config)568     private static String dumpPropertyConfigsHelp(VehiclePropConfig config) {
569         StringBuilder builder = new StringBuilder()
570                 .append("Property:0x").append(toHexString(config.prop))
571                 .append(",Property name:").append(VehicleProperty.toString(config.prop))
572                 .append(",access:0x").append(toHexString(config.access))
573                 .append(",changeMode:0x").append(toHexString(config.changeMode))
574                 .append(",config:0x").append(Arrays.toString(config.configArray.toArray()))
575                 .append(",fs min:").append(config.minSampleRate)
576                 .append(",fs max:").append(config.maxSampleRate);
577         for (VehicleAreaConfig area : config.areaConfigs) {
578             builder.append(",areaId :").append(toHexString(area.areaId))
579                     .append(",f min:").append(area.minFloatValue)
580                     .append(",f max:").append(area.maxFloatValue)
581                     .append(",i min:").append(area.minInt32Value)
582                     .append(",i max:").append(area.maxInt32Value)
583                     .append(",i64 min:").append(area.minInt64Value)
584                     .append(",i64 max:").append(area.maxInt64Value);
585         }
586         return builder.toString();
587     }
588     /**
589      * Inject a VHAL event
590      *
591      * @param property the Vehicle property Id as defined in the HAL
592      * @param zone     Zone that this event services
593      * @param value    Data value of the event
594      */
injectVhalEvent(String property, String zone, String value)595     public void injectVhalEvent(String property, String zone, String value)
596             throws NumberFormatException {
597         if (value == null || zone == null || property == null) {
598             return;
599         }
600         int propId = Integer.decode(property);
601         int zoneId = Integer.decode(zone);
602         VehiclePropValue v = createPropValue(propId, zoneId);
603         int propertyType = propId & VehiclePropertyType.MASK;
604         // Values can be comma separated list
605         List<String> dataList = new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER)));
606         switch (propertyType) {
607             case VehiclePropertyType.BOOLEAN:
608                 boolean boolValue = Boolean.valueOf(value);
609                 v.value.int32Values.add(boolValue ? 1 : 0);
610                 break;
611             case VehiclePropertyType.INT32:
612             case VehiclePropertyType.INT32_VEC:
613                 for (String s : dataList) {
614                     v.value.int32Values.add(Integer.decode(s));
615                 }
616                 break;
617             case VehiclePropertyType.FLOAT:
618             case VehiclePropertyType.FLOAT_VEC:
619                 for (String s : dataList) {
620                     v.value.floatValues.add(Float.parseFloat(s));
621                 }
622                 break;
623             default:
624                 Log.e(CarLog.TAG_HAL, "Property type unsupported:" + propertyType);
625                 return;
626         }
627         v.timestamp = SystemClock.elapsedRealtimeNanos();
628         onPropertyEvent(Lists.newArrayList(v));
629     }
630 
631     /**
632      * Inject an error event.
633      *
634      * @param property the Vehicle property Id as defined in the HAL
635      * @param zone Zone for the event to inject
636      * @param errorCode Error code return from HAL
637      */
injectOnPropertySetError(String property, String zone, String errorCode)638     public void injectOnPropertySetError(String property, String zone, String errorCode) {
639         if (zone == null || property == null || errorCode == null) {
640             return;
641         }
642         int propId = Integer.decode(property);
643         int zoneId = Integer.decode(zone);
644         int errorId = Integer.decode(errorCode);
645         onPropertySetError(errorId, propId, zoneId);
646     }
647 
648     private static class VehiclePropertyEventInfo {
649         private int eventCount;
650         private VehiclePropValue lastEvent;
651 
VehiclePropertyEventInfo(VehiclePropValue event)652         private VehiclePropertyEventInfo(VehiclePropValue event) {
653             eventCount = 1;
654             lastEvent = event;
655         }
656 
addNewEvent(VehiclePropValue event)657         private void addNewEvent(VehiclePropValue event) {
658             eventCount++;
659             lastEvent = event;
660         }
661     }
662 
663     final class VehiclePropValueSetter {
664         final WeakReference<HalClient> mClient;
665         final VehiclePropValue mPropValue;
666 
VehiclePropValueSetter(HalClient client, int propId, int areaId)667         private VehiclePropValueSetter(HalClient client, int propId, int areaId) {
668             mClient = new WeakReference<>(client);
669             mPropValue = new VehiclePropValue();
670             mPropValue.prop = propId;
671             mPropValue.areaId = areaId;
672         }
673 
to(boolean value)674         void to(boolean value) throws PropertyTimeoutException {
675             to(value ? 1 : 0);
676         }
677 
to(int value)678         void to(int value) throws PropertyTimeoutException {
679             mPropValue.value.int32Values.add(value);
680             submit();
681         }
682 
to(int[] values)683         void to(int[] values) throws PropertyTimeoutException {
684             for (int value : values) {
685                 mPropValue.value.int32Values.add(value);
686             }
687             submit();
688         }
689 
to(Collection<Integer> values)690         void to(Collection<Integer> values) throws PropertyTimeoutException {
691             mPropValue.value.int32Values.addAll(values);
692             submit();
693         }
694 
submit()695         void submit() throws PropertyTimeoutException {
696             HalClient client =  mClient.get();
697             if (client != null) {
698                 if (DBG) {
699                     Log.i(CarLog.TAG_HAL, "set, property: 0x" + toHexString(mPropValue.prop)
700                             + ", areaId: 0x" + toHexString(mPropValue.areaId));
701                 }
702                 client.setValue(mPropValue);
703             }
704         }
705     }
706 
dumpVehiclePropValue(VehiclePropValue value)707     private static String dumpVehiclePropValue(VehiclePropValue value) {
708         final int MAX_BYTE_SIZE = 20;
709 
710         StringBuilder sb = new StringBuilder()
711                 .append("Property:0x").append(toHexString(value.prop))
712                 .append(",status: ").append(value.status)
713                 .append(",timestamp:").append(value.timestamp)
714                 .append(",zone:0x").append(toHexString(value.areaId))
715                 .append(",floatValues: ").append(Arrays.toString(value.value.floatValues.toArray()))
716                 .append(",int32Values: ").append(Arrays.toString(value.value.int32Values.toArray()))
717                 .append(",int64Values: ")
718                 .append(Arrays.toString(value.value.int64Values.toArray()));
719 
720         if (value.value.bytes.size() > MAX_BYTE_SIZE) {
721             Object[] bytes = Arrays.copyOf(value.value.bytes.toArray(), MAX_BYTE_SIZE);
722             sb.append(",bytes: ").append(Arrays.toString(bytes));
723         } else {
724             sb.append(",bytes: ").append(Arrays.toString(value.value.bytes.toArray()));
725         }
726         sb.append(",string: ").append(value.value.stringValue);
727 
728         return sb.toString();
729     }
730 
createPropValue(int propId, int areaId)731     private static VehiclePropValue createPropValue(int propId, int areaId) {
732         VehiclePropValue propValue = new VehiclePropValue();
733         propValue.prop = propId;
734         propValue.areaId = areaId;
735         return propValue;
736     }
737 }
738