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.annotation.Nullable;
27 import android.car.hardware.property.CarPropertyManager;
28 import android.content.Context;
29 import android.hardware.automotive.vehicle.V2_0.IVehicle;
30 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
31 import android.hardware.automotive.vehicle.V2_0.SubscribeFlags;
32 import android.hardware.automotive.vehicle.V2_0.SubscribeOptions;
33 import android.hardware.automotive.vehicle.V2_0.VehicleAreaConfig;
34 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
35 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
36 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
37 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
38 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
39 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
40 import android.os.HandlerThread;
41 import android.os.RemoteException;
42 import android.os.ServiceSpecificException;
43 import android.os.SystemClock;
44 import android.util.ArraySet;
45 import android.util.Log;
46 import android.util.SparseArray;
47 
48 import com.android.car.CarLog;
49 import com.android.car.CarServiceUtils;
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.annotations.VisibleForTesting;
52 
53 import com.google.android.collect.Lists;
54 
55 import java.io.PrintWriter;
56 import java.lang.ref.WeakReference;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Collection;
60 import java.util.HashMap;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.concurrent.TimeUnit;
64 import java.util.stream.Collectors;
65 
66 /**
67  * Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing
68  * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase}
69  * implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding
70  * Car*Service for Car*Manager API.
71  */
72 public class VehicleHal extends IVehicleCallback.Stub {
73 
74     private static final boolean DBG = false;
75 
76     private static final int NO_AREA = -1;
77 
78     private final HandlerThread mHandlerThread;
79     private final PowerHalService mPowerHal;
80     private final PropertyHalService mPropertyHal;
81     private final InputHalService mInputHal;
82     private final VmsHalService mVmsHal;
83     private final UserHalService mUserHal;
84     private DiagnosticHalService mDiagnosticHal = null;
85 
86     private final Object mLock = new Object();
87 
88     /** Might be re-assigned if Vehicle HAL is reconnected. */
89     private volatile HalClient mHalClient;
90 
91     /** Stores handler for each HAL property. Property events are sent to handler. */
92     @GuardedBy("mLock")
93     private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>();
94     /** This is for iterating all HalServices with fixed order. */
95     @GuardedBy("mLock")
96     private final ArrayList<HalServiceBase> mAllServices = new ArrayList<>();
97     @GuardedBy("mLock")
98     private final HashMap<Integer, SubscribeOptions> mSubscribedProperties = new HashMap<>();
99     @GuardedBy("mLock")
100     private final HashMap<Integer, VehiclePropConfig> mAllProperties = new HashMap<>();
101 
102     @GuardedBy("mLock")
103     private final HashMap<Integer, VehiclePropertyEventInfo> mEventLog = new HashMap<>();
104 
105     // Used by injectVHALEvent for testing purposes.  Delimiter for an array of data
106     private static final String DATA_DELIMITER = ",";
107 
VehicleHal(Context context, IVehicle vehicle)108     public VehicleHal(Context context, IVehicle vehicle) {
109         mHandlerThread = CarServiceUtils.getHandlerThread(VehicleHal.class.getSimpleName());
110         // passing this should be safe as long as it is just kept and not used in constructor
111         mPowerHal = new PowerHalService(this);
112         mPropertyHal = new PropertyHalService(this);
113         mInputHal = new InputHalService(this);
114         mVmsHal = new VmsHalService(context, this);
115         mDiagnosticHal = new DiagnosticHalService(this);
116         mUserHal = new UserHalService(this);
117         mAllServices.addAll(Arrays.asList(mPowerHal,
118                 mInputHal,
119                 mDiagnosticHal,
120                 mVmsHal,
121                 mUserHal,
122                 mPropertyHal)); // mPropertyHal should be the last.
123 
124         mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/);
125     }
126 
127     /** Dummy version only for testing */
128     @VisibleForTesting
VehicleHal(PowerHalService powerHal, DiagnosticHalService diagnosticHal, HalClient halClient, PropertyHalService propertyHal)129     public VehicleHal(PowerHalService powerHal, DiagnosticHalService diagnosticHal,
130             HalClient halClient, PropertyHalService propertyHal) {
131         mHandlerThread = null;
132         mPowerHal = powerHal;
133         mPropertyHal = propertyHal;
134         mDiagnosticHal = diagnosticHal;
135         mInputHal = null;
136         mVmsHal = null;
137         mHalClient = halClient;
138         mDiagnosticHal = diagnosticHal;
139         mUserHal = null;
140     }
141 
vehicleHalReconnected(IVehicle vehicle)142     public void vehicleHalReconnected(IVehicle vehicle) {
143         synchronized (mLock) {
144             mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(),
145                     this /*IVehicleCallback*/);
146 
147             SubscribeOptions[] options = mSubscribedProperties.values()
148                     .toArray(new SubscribeOptions[0]);
149 
150             try {
151                 mHalClient.subscribe(options);
152             } catch (RemoteException e) {
153                 throw new RuntimeException("Failed to subscribe: " + Arrays.asList(options), e);
154             }
155         }
156     }
157 
fetchAllPropConfigs()158     private void fetchAllPropConfigs() {
159         synchronized (mLock) {
160             if (!mAllProperties.isEmpty()) { // already set
161                 Log.i(CarLog.TAG_HAL, "fetchAllPropConfigs already fetched");
162                 return;
163             }
164         }
165         ArrayList<VehiclePropConfig> configs;
166         try {
167             configs = mHalClient.getAllPropConfigs();
168             if (configs == null || configs.size() == 0) {
169                 Log.e(CarLog.TAG_HAL, "getAllPropConfigs returned empty configs");
170                 return;
171             }
172         } catch (RemoteException e) {
173             throw new RuntimeException("Unable to retrieve vehicle property configuration", e);
174         }
175 
176         synchronized (mLock) {
177             // Create map of all properties
178             for (VehiclePropConfig p : configs) {
179                 if (DBG) {
180                     Log.i(CarLog.TAG_HAL, "Add config for prop:" + Integer.toHexString(p.prop)
181                             + " config:" + p);
182                 }
183                 mAllProperties.put(p.prop, p);
184             }
185         }
186     }
187 
188     /**
189      * Inits the vhal configurations.
190      *
191      * <p><Note that {@link #getIfAvailableOrFailForEarlyStage(int, int)}
192      * can be called before {@code init()}.
193      */
init()194     public void init() {
195         fetchAllPropConfigs();
196 
197         // PropertyHalService will take most properties, so make it big enough.
198         ArrayList<VehiclePropConfig> configsForService = new ArrayList<>(mAllServices.size());
199         for (int i = 0; i < mAllServices.size(); i++) {
200             HalServiceBase service = mAllServices.get(i);
201             int[] supportedProps =  service.getAllSupportedProperties();
202             configsForService.clear();
203             synchronized (mLock) {
204                 if (supportedProps.length == 0) {
205                     for (Integer propId : mAllProperties.keySet()) {
206                         if (service.isSupportedProperty(propId)) {
207                             VehiclePropConfig config = mAllProperties.get(propId);
208                             mPropertyHandlers.append(propId, service);
209                             configsForService.add(config);
210                         }
211                     }
212                 } else {
213                     for (int prop : supportedProps) {
214                         VehiclePropConfig config = mAllProperties.get(prop);
215                         if (config == null) {
216                             continue;
217                         }
218                         mPropertyHandlers.append(prop, service);
219                         configsForService.add(config);
220                     }
221                 }
222             }
223             service.takeProperties(configsForService);
224             service.init();
225         }
226     }
227 
release()228     public void release() {
229         // release in reverse order from init
230         for (int i = mAllServices.size() - 1; i >= 0; i--) {
231             mAllServices.get(i).release();
232         }
233         synchronized (mLock) {
234             for (int p : mSubscribedProperties.keySet()) {
235                 try {
236                     mHalClient.unsubscribe(p);
237                 } catch (RemoteException e) {
238                     //  Ignore exceptions on shutdown path.
239                     Log.w(CarLog.TAG_HAL, "Failed to unsubscribe", e);
240                 }
241             }
242             mSubscribedProperties.clear();
243             mAllProperties.clear();
244         }
245         // keep the looper thread as should be kept for the whole life cycle.
246     }
247 
getDiagnosticHal()248     public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
249 
getPowerHal()250     public PowerHalService getPowerHal() {
251         return mPowerHal;
252     }
253 
getPropertyHal()254     public PropertyHalService getPropertyHal() {
255         return mPropertyHal;
256     }
257 
getInputHal()258     public InputHalService getInputHal() {
259         return mInputHal;
260     }
261 
getUserHal()262     public UserHalService getUserHal() {
263         return mUserHal;
264     }
265 
getVmsHal()266     public VmsHalService getVmsHal() { return mVmsHal; }
267 
assertServiceOwnerLocked(HalServiceBase service, int property)268     private void assertServiceOwnerLocked(HalServiceBase service, int property) {
269         if (service != mPropertyHandlers.get(property)) {
270             throw new IllegalArgumentException("Property 0x" + toHexString(property)
271                     + " is not owned by service: " + service);
272         }
273     }
274 
275     /**
276      * Subscribes given properties with sampling rate defaults to 0 and no special flags provided.
277      *
278      * @see #subscribeProperty(HalServiceBase, int, float, int)
279      */
subscribeProperty(HalServiceBase service, int property)280     public void subscribeProperty(HalServiceBase service, int property)
281             throws IllegalArgumentException {
282         subscribeProperty(service, property, 0f, SubscribeFlags.EVENTS_FROM_CAR);
283     }
284 
285     /**
286      * Subscribes given properties with default subscribe flag.
287      *
288      * @see #subscribeProperty(HalServiceBase, int, float, int)
289      */
subscribeProperty(HalServiceBase service, int property, float sampleRateHz)290     public void subscribeProperty(HalServiceBase service, int property, float sampleRateHz)
291             throws IllegalArgumentException {
292         subscribeProperty(service, property, sampleRateHz, SubscribeFlags.EVENTS_FROM_CAR);
293     }
294 
295     /**
296      * Subscribe given property. Only Hal service owning the property can subscribe it.
297      *
298      * @param service HalService that owns this property
299      * @param property property id (VehicleProperty)
300      * @param samplingRateHz sampling rate in Hz for continuous properties
301      * @param flags flags from {@link android.hardware.automotive.vehicle.V2_0.SubscribeFlags}
302      * @throws IllegalArgumentException thrown if property is not supported by VHAL
303      */
subscribeProperty(HalServiceBase service, int property, float samplingRateHz, int flags)304     public void subscribeProperty(HalServiceBase service, int property,
305             float samplingRateHz, int flags) throws IllegalArgumentException {
306         if (DBG) {
307             Log.i(CarLog.TAG_HAL, "subscribeProperty, service:" + service
308                     + ", property: 0x" + toHexString(property));
309         }
310         VehiclePropConfig config;
311         synchronized (mLock) {
312             config = mAllProperties.get(property);
313         }
314 
315         if (config == null) {
316             throw new IllegalArgumentException("subscribe error: config is null for property 0x" +
317                     toHexString(property));
318         } else if (isPropertySubscribable(config)) {
319             SubscribeOptions opts = new SubscribeOptions();
320             opts.propId = property;
321             opts.sampleRate = samplingRateHz;
322             opts.flags = flags;
323             synchronized (mLock) {
324                 assertServiceOwnerLocked(service, property);
325                 mSubscribedProperties.put(property, opts);
326             }
327             try {
328                 mHalClient.subscribe(opts);
329             } catch (RemoteException e) {
330                 Log.e(CarLog.TAG_HAL, "Failed to subscribe to property: 0x" + property, e);
331             }
332         } else {
333             Log.e(CarLog.TAG_HAL, "Cannot subscribe to property: " + property);
334         }
335     }
336 
unsubscribeProperty(HalServiceBase service, int property)337     public void unsubscribeProperty(HalServiceBase service, int property) {
338         if (DBG) {
339             Log.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service
340                     + ", property: 0x" + toHexString(property));
341         }
342         VehiclePropConfig config;
343         synchronized (mLock) {
344             config = mAllProperties.get(property);
345         }
346 
347         if (config == null) {
348             Log.e(CarLog.TAG_HAL, "unsubscribeProperty: property " + property + " does not exist");
349         } else if (isPropertySubscribable(config)) {
350             synchronized (mLock) {
351                 assertServiceOwnerLocked(service, property);
352                 mSubscribedProperties.remove(property);
353             }
354             try {
355                 mHalClient.unsubscribe(property);
356             } catch (RemoteException e) {
357                 Log.e(CarLog.TAG_SERVICE, "Failed to unsubscribe from property: 0x"
358                         + toHexString(property), e);
359             }
360         } else {
361             Log.e(CarLog.TAG_HAL, "Cannot unsubscribe property: " + property);
362         }
363     }
364 
isPropertySupported(int propertyId)365     public boolean isPropertySupported(int propertyId) {
366         synchronized (mLock) {
367             return mAllProperties.containsKey(propertyId);
368         }
369     }
370 
371     /**
372      * Gets given property with retries.
373      *
374      * <p>If getting the property fails after all retries, it will throw
375      * {@code IllegalStateException}. If the property does not exist, it will simply return
376      * {@code null}.
377      */
getIfAvailableOrFail(int propertyId, int numberOfRetries)378     public @Nullable VehiclePropValue getIfAvailableOrFail(int propertyId, int numberOfRetries) {
379         if (!isPropertySupported(propertyId)) {
380             return null;
381         }
382         VehiclePropValue value;
383         for (int i = 0; i < numberOfRetries; i++) {
384             try {
385                 return get(propertyId);
386             } catch (ServiceSpecificException e) {
387                 Log.e(CarLog.TAG_HAL, "Cannot get property:" + propertyId, e);
388             }
389         }
390         throw new IllegalStateException("Cannot get property:" + propertyId
391                 + " after " + numberOfRetries + " retries");
392     }
393 
394     /**
395      * This works similar to {@link #getIfAvailableOrFail(int, int)} except that this can be called
396      * before {@code init()} is called.
397      *
398      * <p>This call will check if requested vhal property is supported by querying directly to vhal
399      * and can have worse performance. Use this only for accessing vhal properties before
400      * {@code ICarImpl.init()} phase.
401      */
getIfAvailableOrFailForEarlyStage(int propertyId, int numberOfRetries)402     public @Nullable VehiclePropValue getIfAvailableOrFailForEarlyStage(int propertyId,
403             int numberOfRetries) {
404         fetchAllPropConfigs();
405         return getIfAvailableOrFail(propertyId, numberOfRetries);
406     }
407 
get(int propertyId)408     public VehiclePropValue get(int propertyId) {
409         return get(propertyId, NO_AREA);
410     }
411 
get(int propertyId, int areaId)412     public VehiclePropValue get(int propertyId, int areaId) {
413         if (DBG) {
414             Log.i(CarLog.TAG_HAL, "get, property: 0x" + toHexString(propertyId)
415                     + ", areaId: 0x" + toHexString(areaId));
416         }
417         VehiclePropValue propValue = new VehiclePropValue();
418         propValue.prop = propertyId;
419         propValue.areaId = areaId;
420         return mHalClient.getValue(propValue);
421     }
422 
get(Class clazz, int propertyId)423     public <T> T get(Class clazz, int propertyId) {
424         return get(clazz, createPropValue(propertyId, NO_AREA));
425     }
426 
get(Class clazz, int propertyId, int areaId)427     public <T> T get(Class clazz, int propertyId, int areaId) {
428         return get(clazz, createPropValue(propertyId, areaId));
429     }
430 
431     @SuppressWarnings("unchecked")
get(Class clazz, VehiclePropValue requestedPropValue)432     public <T> T get(Class clazz, VehiclePropValue requestedPropValue) {
433         VehiclePropValue propValue;
434         propValue = mHalClient.getValue(requestedPropValue);
435 
436         if (clazz == Integer.class || clazz == int.class) {
437             return (T) propValue.value.int32Values.get(0);
438         } else if (clazz == Boolean.class || clazz == boolean.class) {
439             return (T) Boolean.valueOf(propValue.value.int32Values.get(0) == 1);
440         } else if (clazz == Float.class || clazz == float.class) {
441             return (T) propValue.value.floatValues.get(0);
442         } else if (clazz == Integer[].class) {
443             Integer[] intArray = new Integer[propValue.value.int32Values.size()];
444             return (T) propValue.value.int32Values.toArray(intArray);
445         } else if (clazz == Float[].class) {
446             Float[] floatArray = new Float[propValue.value.floatValues.size()];
447             return (T) propValue.value.floatValues.toArray(floatArray);
448         } else if (clazz == int[].class) {
449             return (T) toIntArray(propValue.value.int32Values);
450         } else if (clazz == float[].class) {
451             return (T) toFloatArray(propValue.value.floatValues);
452         } else if (clazz == byte[].class) {
453             return (T) toByteArray(propValue.value.bytes);
454         } else if (clazz == String.class) {
455             return (T) propValue.value.stringValue;
456         } else {
457             throw new IllegalArgumentException("Unexpected type: " + clazz);
458         }
459     }
460 
get(VehiclePropValue requestedPropValue)461     public VehiclePropValue get(VehiclePropValue requestedPropValue) {
462         return mHalClient.getValue(requestedPropValue);
463     }
464 
465     /**
466      *
467      * @param propId Property ID to return the current sample rate for.
468      *
469      * @return float Returns the current sample rate of the specified propId, or -1 if the
470      *                  property is not currently subscribed.
471      */
getSampleRate(int propId)472     public float getSampleRate(int propId) {
473         SubscribeOptions opts = mSubscribedProperties.get(propId);
474         if (opts == null) {
475             // No sample rate for this property
476             return -1;
477         } else {
478             return opts.sampleRate;
479         }
480     }
481 
set(VehiclePropValue propValue)482     protected void set(VehiclePropValue propValue) {
483         mHalClient.setValue(propValue);
484     }
485 
486     @CheckResult
set(int propId)487     VehiclePropValueSetter set(int propId) {
488         return new VehiclePropValueSetter(mHalClient, propId, NO_AREA);
489     }
490 
491     @CheckResult
set(int propId, int areaId)492     VehiclePropValueSetter set(int propId, int areaId) {
493         return new VehiclePropValueSetter(mHalClient, propId, areaId);
494     }
495 
isPropertySubscribable(VehiclePropConfig config)496     static boolean isPropertySubscribable(VehiclePropConfig config) {
497         if ((config.access & VehiclePropertyAccess.READ) == 0 ||
498                 (config.changeMode == VehiclePropertyChangeMode.STATIC)) {
499             return false;
500         }
501         return true;
502     }
503 
dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs)504     static void dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs) {
505         for (VehiclePropConfig config : configs) {
506             writer.println(String.format("property 0x%x", config.prop));
507         }
508     }
509 
510     private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>();
511 
512     @Override
onPropertyEvent(ArrayList<VehiclePropValue> propValues)513     public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
514         synchronized (mLock) {
515             for (VehiclePropValue v : propValues) {
516                 HalServiceBase service = mPropertyHandlers.get(v.prop);
517                 if(service == null) {
518                     Log.e(CarLog.TAG_HAL, "HalService not found for prop: 0x"
519                         + toHexString(v.prop));
520                     continue;
521                 }
522                 service.getDispatchList().add(v);
523                 mServicesToDispatch.add(service);
524                 VehiclePropertyEventInfo info = mEventLog.get(v.prop);
525                 if (info == null) {
526                     info = new VehiclePropertyEventInfo(v);
527                     mEventLog.put(v.prop, info);
528                 } else {
529                     info.addNewEvent(v);
530                 }
531             }
532         }
533         for (HalServiceBase s : mServicesToDispatch) {
534             s.onHalEvents(s.getDispatchList());
535             s.getDispatchList().clear();
536         }
537         mServicesToDispatch.clear();
538     }
539 
540     @Override
onPropertySet(VehiclePropValue value)541     public void onPropertySet(VehiclePropValue value) {
542         // No need to handle on-property-set events in HAL service yet.
543     }
544 
545     @Override
onPropertySetError(@arPropertyManager.CarSetPropertyErrorCode int errorCode, int propId, int areaId)546     public void onPropertySetError(@CarPropertyManager.CarSetPropertyErrorCode int errorCode,
547             int propId, int areaId) {
548         Log.e(CarLog.TAG_HAL, String.format("onPropertySetError, errorCode: %d, prop: 0x%x, "
549                 + "area: 0x%x", errorCode, propId, areaId));
550         if (propId != VehicleProperty.INVALID) {
551             HalServiceBase service = mPropertyHandlers.get(propId);
552             if (service != null) {
553                 service.onPropertySetError(propId, areaId, errorCode);
554             }
555         }
556     }
557 
dump(PrintWriter writer)558     public void dump(PrintWriter writer) {
559         writer.println("**dump HAL services**");
560         for (HalServiceBase service: mAllServices) {
561             service.dump(writer);
562         }
563         // Dump all VHAL property configure.
564         dumpPropertyConfigs(writer, "");
565         writer.println(String.format("**All Events, now ns:%d**",
566                 SystemClock.elapsedRealtimeNanos()));
567         for (VehiclePropertyEventInfo info : mEventLog.values()) {
568             writer.println(String.format("event count:%d, lastEvent:%s",
569                     info.eventCount, dumpVehiclePropValue(info.lastEvent)));
570         }
571 
572         writer.println("**Property handlers**");
573         for (int i = 0; i < mPropertyHandlers.size(); i++) {
574             int propId = mPropertyHandlers.keyAt(i);
575             HalServiceBase service = mPropertyHandlers.valueAt(i);
576             writer.println(String.format("Prop: 0x%08X, service: %s", propId, service));
577         }
578     }
579 
580     /**
581      * Dumps the list of HALs.
582      */
dumpListHals(PrintWriter writer)583     public void dumpListHals(PrintWriter writer) {
584         for (HalServiceBase service: mAllServices) {
585             writer.println(service.getClass().getName());
586         }
587     }
588 
589     /**
590      * Dumps the given HALs.
591      */
dumpSpecificHals(PrintWriter writer, String... halNames)592     public void dumpSpecificHals(PrintWriter writer, String... halNames) {
593         Map<String, HalServiceBase> byName = mAllServices.stream()
594                 .collect(Collectors.toMap(s -> s.getClass().getSimpleName(), s -> s));
595         for (String halName : halNames) {
596             HalServiceBase service = byName.get(halName);
597             if (service == null) {
598                 writer.printf("No HAL named %s. Valid options are: %s\n", halName, byName.keySet());
599                 continue;
600             }
601             service.dump(writer);
602         }
603     }
604 
605     /**
606      * Dumps vehicle property values.
607      * @param writer
608      * @param propId property id, dump all properties' value if it is empty string.
609      * @param areaId areaId of the property, dump the property for all areaIds in the config
610      * if it is empty string.
611      */
dumpPropertyValueByCommend(PrintWriter writer, String propId, String areaId)612     public void dumpPropertyValueByCommend(PrintWriter writer, String propId, String areaId) {
613         if (propId.equals("")) {
614             writer.println("**All property values**");
615             for (VehiclePropConfig config : mAllProperties.values()) {
616                 dumpPropertyValueByConfig(writer, config);
617             }
618         } else if (areaId.equals("")) {
619             VehiclePropConfig config = mAllProperties.get(Integer.parseInt(propId, 16));
620             if (config == null) {
621                 writer.printf("Property 0x%s not supported by HAL\n", propId);
622                 return;
623             }
624             dumpPropertyValueByConfig(writer, config);
625         } else {
626             int id = Integer.parseInt(propId, 16);
627             int area = Integer.parseInt(areaId);
628             try {
629                 VehiclePropValue value = get(id, area);
630                 writer.println(dumpVehiclePropValue(value));
631             } catch (Exception e) {
632                 writer.println("Can not get property value for propertyId: 0x"
633                         + propId + ", areaId: " + area);
634             }
635         }
636     }
637 
dumpPropertyValueByConfig(PrintWriter writer, VehiclePropConfig config)638     private void dumpPropertyValueByConfig(PrintWriter writer, VehiclePropConfig config) {
639         if (config.areaConfigs.isEmpty()) {
640             try {
641                 VehiclePropValue value = get(config.prop);
642                 writer.println(dumpVehiclePropValue(value));
643             } catch (Exception e) {
644                 writer.println("Can not get property value for propertyId: 0x"
645                         + toHexString(config.prop) + ", areaId: 0");
646             }
647         } else {
648             for (VehicleAreaConfig areaConfig : config.areaConfigs) {
649                 int area = areaConfig.areaId;
650                 try {
651                     VehiclePropValue value = get(config.prop, area);
652                     writer.println(dumpVehiclePropValue(value));
653                 } catch (Exception e) {
654                     writer.println("Can not get property value for propertyId: 0x"
655                             + toHexString(config.prop) + ", areaId: " + area);
656                 }
657             }
658         }
659     }
660 
661     /**
662      * Dump VHAL property configs.
663      *
664      * @param writer
665      * @param propId Property ID in Hex. If propid is empty string, dump all properties.
666      */
dumpPropertyConfigs(PrintWriter writer, String propId)667     public void dumpPropertyConfigs(PrintWriter writer, String propId) {
668         List<VehiclePropConfig> configList;
669         synchronized (mLock) {
670             configList = new ArrayList<>(mAllProperties.values());
671         }
672 
673         if (propId.equals("")) {
674             writer.println("**All properties**");
675             for (VehiclePropConfig config : configList) {
676                 writer.println(dumpPropertyConfigsHelp(config));
677             }
678             return;
679         }
680         for (VehiclePropConfig config : configList) {
681             if (toHexString(config.prop).equals(propId)) {
682                 writer.println(dumpPropertyConfigsHelp(config));
683                 return;
684             }
685         }
686 
687     }
688 
689     /** Use VehiclePropertyConfig to construct string for dumping */
dumpPropertyConfigsHelp(VehiclePropConfig config)690     private static String dumpPropertyConfigsHelp(VehiclePropConfig config) {
691         StringBuilder builder = new StringBuilder()
692                 .append("Property:0x").append(toHexString(config.prop))
693                 .append(", Property name:").append(VehicleProperty.toString(config.prop))
694                 .append(", access:0x").append(toHexString(config.access))
695                 .append(", changeMode:0x").append(toHexString(config.changeMode))
696                 .append(", config:0x").append(Arrays.toString(config.configArray.toArray()))
697                 .append(", fs min:").append(config.minSampleRate)
698                 .append(", fs max:").append(config.maxSampleRate);
699         for (VehicleAreaConfig area : config.areaConfigs) {
700             builder.append("\n\tareaId:0x").append(toHexString(area.areaId))
701                     .append(", f min:").append(area.minFloatValue)
702                     .append(", f max:").append(area.maxFloatValue)
703                     .append(", i min:").append(area.minInt32Value)
704                     .append(", i max:").append(area.maxInt32Value)
705                     .append(", i64 min:").append(area.minInt64Value)
706                     .append(", i64 max:").append(area.maxInt64Value);
707         }
708         return builder.toString();
709     }
710     /**
711      * Inject a VHAL event
712      *
713      * @param property the Vehicle property Id as defined in the HAL
714      * @param zone     Zone that this event services
715      * @param value    Data value of the event
716      * @param delayTime Add a certain duration to event timestamp
717      */
injectVhalEvent(String property, String zone, String value, String delayTime)718     public void injectVhalEvent(String property, String zone, String value, String delayTime)
719             throws NumberFormatException {
720         if (value == null || zone == null || property == null) {
721             return;
722         }
723         int propId = Integer.decode(property);
724         int zoneId = Integer.decode(zone);
725         int duration = Integer.decode(delayTime);
726         VehiclePropValue v = createPropValue(propId, zoneId);
727         int propertyType = propId & VehiclePropertyType.MASK;
728         // Values can be comma separated list
729         List<String> dataList = new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER)));
730         switch (propertyType) {
731             case VehiclePropertyType.BOOLEAN:
732                 boolean boolValue = Boolean.valueOf(value);
733                 v.value.int32Values.add(boolValue ? 1 : 0);
734                 break;
735             case VehiclePropertyType.INT32:
736             case VehiclePropertyType.INT32_VEC:
737                 for (String s : dataList) {
738                     v.value.int32Values.add(Integer.decode(s));
739                 }
740                 break;
741             case VehiclePropertyType.FLOAT:
742             case VehiclePropertyType.FLOAT_VEC:
743                 for (String s : dataList) {
744                     v.value.floatValues.add(Float.parseFloat(s));
745                 }
746                 break;
747             default:
748                 Log.e(CarLog.TAG_HAL, "Property type unsupported:" + propertyType);
749                 return;
750         }
751         v.timestamp = SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos(duration);
752         onPropertyEvent(Lists.newArrayList(v));
753     }
754 
755     /**
756      * Inject an error event.
757      *
758      * @param property the Vehicle property Id as defined in the HAL
759      * @param zone Zone for the event to inject
760      * @param errorCode Error code return from HAL
761      */
injectOnPropertySetError(String property, String zone, String errorCode)762     public void injectOnPropertySetError(String property, String zone, String errorCode) {
763         if (zone == null || property == null || errorCode == null) {
764             return;
765         }
766         int propId = Integer.decode(property);
767         int zoneId = Integer.decode(zone);
768         int errorId = Integer.decode(errorCode);
769         onPropertySetError(errorId, propId, zoneId);
770     }
771 
772     private static class VehiclePropertyEventInfo {
773         private int eventCount;
774         private VehiclePropValue lastEvent;
775 
VehiclePropertyEventInfo(VehiclePropValue event)776         private VehiclePropertyEventInfo(VehiclePropValue event) {
777             eventCount = 1;
778             lastEvent = event;
779         }
780 
addNewEvent(VehiclePropValue event)781         private void addNewEvent(VehiclePropValue event) {
782             eventCount++;
783             lastEvent = event;
784         }
785     }
786 
787     final class VehiclePropValueSetter {
788         final WeakReference<HalClient> mClient;
789         final VehiclePropValue mPropValue;
790 
VehiclePropValueSetter(HalClient client, int propId, int areaId)791         private VehiclePropValueSetter(HalClient client, int propId, int areaId) {
792             mClient = new WeakReference<>(client);
793             mPropValue = new VehiclePropValue();
794             mPropValue.prop = propId;
795             mPropValue.areaId = areaId;
796         }
797 
to(boolean value)798         void to(boolean value) {
799             to(value ? 1 : 0);
800         }
801 
to(int value)802         void to(int value) {
803             mPropValue.value.int32Values.add(value);
804             submit();
805         }
806 
to(int[] values)807         void to(int[] values) {
808             for (int value : values) {
809                 mPropValue.value.int32Values.add(value);
810             }
811             submit();
812         }
813 
to(Collection<Integer> values)814         void to(Collection<Integer> values) {
815             mPropValue.value.int32Values.addAll(values);
816             submit();
817         }
818 
submit()819         void submit() {
820             HalClient client =  mClient.get();
821             if (client != null) {
822                 if (DBG) {
823                     Log.i(CarLog.TAG_HAL, "set, property: 0x" + toHexString(mPropValue.prop)
824                             + ", areaId: 0x" + toHexString(mPropValue.areaId));
825                 }
826                 client.setValue(mPropValue);
827             }
828         }
829     }
830 
dumpVehiclePropValue(VehiclePropValue value)831     private static String dumpVehiclePropValue(VehiclePropValue value) {
832         final int MAX_BYTE_SIZE = 20;
833 
834         StringBuilder sb = new StringBuilder()
835                 .append("Property:0x").append(toHexString(value.prop))
836                 .append(",status: ").append(value.status)
837                 .append(",timestamp:").append(value.timestamp)
838                 .append(",zone:0x").append(toHexString(value.areaId))
839                 .append(",floatValues: ").append(Arrays.toString(value.value.floatValues.toArray()))
840                 .append(",int32Values: ").append(Arrays.toString(value.value.int32Values.toArray()))
841                 .append(",int64Values: ")
842                 .append(Arrays.toString(value.value.int64Values.toArray()));
843 
844         if (value.value.bytes.size() > MAX_BYTE_SIZE) {
845             Object[] bytes = Arrays.copyOf(value.value.bytes.toArray(), MAX_BYTE_SIZE);
846             sb.append(",bytes: ").append(Arrays.toString(bytes));
847         } else {
848             sb.append(",bytes: ").append(Arrays.toString(value.value.bytes.toArray()));
849         }
850         sb.append(",string: ").append(value.value.stringValue);
851 
852         return sb.toString();
853     }
854 
createPropValue(int propId, int areaId)855     private static VehiclePropValue createPropValue(int propId, int areaId) {
856         VehiclePropValue propValue = new VehiclePropValue();
857         propValue.prop = propId;
858         propValue.areaId = areaId;
859         return propValue;
860     }
861 }
862