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