• 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 android.os.SystemClock.uptimeMillis;
20 
21 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
22 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
23 
24 import android.annotation.CheckResult;
25 import android.annotation.Nullable;
26 import android.car.VehiclePropertyIds;
27 import android.car.builtin.os.TraceHelper;
28 import android.car.builtin.util.Slogf;
29 import android.content.Context;
30 import android.hardware.automotive.vehicle.StatusCode;
31 import android.hardware.automotive.vehicle.SubscribeOptions;
32 import android.hardware.automotive.vehicle.VehiclePropError;
33 import android.hardware.automotive.vehicle.VehicleProperty;
34 import android.hardware.automotive.vehicle.VehiclePropertyAccess;
35 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode;
36 import android.hardware.automotive.vehicle.VehiclePropertyStatus;
37 import android.hardware.automotive.vehicle.VehiclePropertyType;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.ParcelFileDescriptor;
41 import android.os.RemoteException;
42 import android.os.ServiceSpecificException;
43 import android.os.SystemClock;
44 import android.os.Trace;
45 import android.util.ArrayMap;
46 import android.util.ArraySet;
47 import android.util.Pair;
48 import android.util.SparseArray;
49 
50 import com.android.car.CarLog;
51 import com.android.car.CarServiceUtils;
52 import com.android.car.CarSystemService;
53 import com.android.car.VehicleStub;
54 import com.android.car.VehicleStub.SubscriptionClient;
55 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
56 import com.android.car.internal.util.IndentingPrintWriter;
57 import com.android.car.internal.util.Lists;
58 import com.android.internal.annotations.GuardedBy;
59 import com.android.internal.annotations.VisibleForTesting;
60 
61 import java.io.PrintWriter;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collection;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Timer;
68 import java.util.TimerTask;
69 import java.util.concurrent.TimeUnit;
70 import java.util.stream.Collectors;
71 
72 /**
73  * Abstraction for vehicle HAL. This class handles interface with native HAL and does basic parsing
74  * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase}
75  * implementation. It is the responsibility of {@link HalServiceBase} to convert data to
76  * corresponding Car*Service for Car*Manager API.
77  */
78 public class VehicleHal implements VehicleHalCallback, CarSystemService {
79 
80     private static final boolean DBG = false;
81     private static final long TRACE_TAG = TraceHelper.TRACE_TAG_CAR_SERVICE;
82 
83     /**
84      * Used in {@link VehicleHal#dumpPropValue} method when copying
85      * {@link HalPropValue}.
86      */
87     private static final int MAX_BYTE_SIZE = 20;
88 
89     public static final int NO_AREA = -1;
90     public static final float NO_SAMPLE_RATE = -1;
91 
92     /**
93      * If call to vehicle HAL returns StatusCode.TRY_AGAIN, we will retry to invoke that method
94      * again for this amount of milliseconds.
95      */
96     private static final int MAX_DURATION_FOR_RETRIABLE_RESULT_MS = 2000;
97 
98     private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 100;
99     private static final float PRECISION_THRESHOLD = 0.001f;
100 
101     private final HandlerThread mHandlerThread;
102     private final Handler mHandler;
103     private final SubscriptionClient mSubscriptionClient;
104 
105     private final PowerHalService mPowerHal;
106     private final PropertyHalService mPropertyHal;
107     private final InputHalService mInputHal;
108     private final VmsHalService mVmsHal;
109     private final UserHalService mUserHal;
110     private final DiagnosticHalService mDiagnosticHal;
111     private final ClusterHalService mClusterHalService;
112     private final EvsHalService mEvsHal;
113     private final TimeHalService mTimeHalService;
114     private final HalPropValueBuilder mPropValueBuilder;
115     private final VehicleStub mVehicleStub;
116 
117     private final Object mLock = new Object();
118 
119     // Only changed for test.
120     private int mMaxDurationForRetryMs = MAX_DURATION_FOR_RETRIABLE_RESULT_MS;
121     // Only changed for test.
122     private int mSleepBetweenRetryMs = SLEEP_BETWEEN_RETRIABLE_INVOKES_MS;
123 
124     /** Stores handler for each HAL property. Property events are sent to handler. */
125     @GuardedBy("mLock")
126     private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>();
127     /** This is for iterating all HalServices with fixed order. */
128     @GuardedBy("mLock")
129     private final List<HalServiceBase> mAllServices;
130     @GuardedBy("mLock")
131     private final ArrayMap<Pair<Integer, Integer>, Float> mUpdateRateByPropIdAreadId =
132             new ArrayMap<>();
133     @GuardedBy("mLock")
134     private final SparseArray<HalPropConfig> mAllProperties = new SparseArray<>();
135 
136     @GuardedBy("mLock")
137     private final SparseArray<VehiclePropertyEventInfo> mEventLog = new SparseArray<>();
138 
139     // Used by injectVHALEvent for testing purposes.  Delimiter for an array of data
140     private static final String DATA_DELIMITER = ",";
141 
142     /**
143      * Constructs a new {@link VehicleHal} object given the {@link Context} and {@link IVehicle}
144      * both passed as parameters.
145      */
VehicleHal(Context context, VehicleStub vehicle)146     public VehicleHal(Context context, VehicleStub vehicle) {
147         this(context, /* powerHal= */ null, /* propertyHal= */ null,
148                 /* inputHal= */ null, /* vmsHal= */ null, /* userHal= */ null,
149                 /* diagnosticHal= */ null, /* clusterHalService= */ null,
150                 /* timeHalService= */ null,
151                 CarServiceUtils.getHandlerThread(VehicleHal.class.getSimpleName()), vehicle);
152     }
153 
154     /**
155      * Constructs a new {@link VehicleHal} object given the services passed as parameters.
156      * This method must be used by tests only.
157      */
158     @VisibleForTesting
VehicleHal(Context context, PowerHalService powerHal, PropertyHalService propertyHal, InputHalService inputHal, VmsHalService vmsHal, UserHalService userHal, DiagnosticHalService diagnosticHal, ClusterHalService clusterHalService, TimeHalService timeHalService, HandlerThread handlerThread, VehicleStub vehicle)159     VehicleHal(Context context,
160             PowerHalService powerHal,
161             PropertyHalService propertyHal,
162             InputHalService inputHal,
163             VmsHalService vmsHal,
164             UserHalService userHal,
165             DiagnosticHalService diagnosticHal,
166             ClusterHalService clusterHalService,
167             TimeHalService timeHalService,
168             HandlerThread handlerThread,
169             VehicleStub vehicle) {
170         // Must be initialized before HalService so that HalService could use this.
171         mPropValueBuilder = vehicle.getHalPropValueBuilder();
172         mHandlerThread = handlerThread;
173         mHandler = new Handler(mHandlerThread.getLooper());
174         mPowerHal = powerHal != null ? powerHal : new PowerHalService(context, this);
175         mPropertyHal = propertyHal != null ? propertyHal : new PropertyHalService(this);
176         mInputHal = inputHal != null ? inputHal : new InputHalService(this);
177         mVmsHal = vmsHal != null ? vmsHal : new VmsHalService(context, this);
178         mUserHal = userHal != null ? userHal :  new UserHalService(this);
179         mDiagnosticHal = diagnosticHal != null ? diagnosticHal : new DiagnosticHalService(this);
180         mClusterHalService = clusterHalService != null
181                 ? clusterHalService : new ClusterHalService(this);
182         mEvsHal = new EvsHalService(this);
183         mTimeHalService = timeHalService != null
184                 ? timeHalService : new TimeHalService(context, this);
185         mAllServices = List.of(
186                 mPowerHal,
187                 mInputHal,
188                 mDiagnosticHal,
189                 mVmsHal,
190                 mUserHal,
191                 mClusterHalService,
192                 mEvsHal,
193                 mTimeHalService,
194                 // mPropertyHal must be the last so that on init/release it can be used for all
195                 // other HAL services properties.
196                 mPropertyHal);
197         mVehicleStub = vehicle;
198         mSubscriptionClient = vehicle.newSubscriptionClient(this);
199     }
200 
201     @VisibleForTesting
setMaxDurationForRetryMs(int maxDurationForRetryMs)202     void setMaxDurationForRetryMs(int maxDurationForRetryMs) {
203         mMaxDurationForRetryMs = maxDurationForRetryMs;
204     }
205 
206     @VisibleForTesting
setSleepBetweenRetryMs(int sleepBetweenRetryMs)207     void setSleepBetweenRetryMs(int sleepBetweenRetryMs) {
208         mSleepBetweenRetryMs = sleepBetweenRetryMs;
209     }
210 
fetchAllPropConfigs()211     private void fetchAllPropConfigs() {
212         synchronized (mLock) {
213             if (mAllProperties.size() != 0) { // already set
214                 Slogf.i(CarLog.TAG_HAL, "fetchAllPropConfigs already fetched");
215                 return;
216             }
217         }
218         HalPropConfig[] configs;
219         try {
220             configs = mVehicleStub.getAllPropConfigs();
221             if (configs == null || configs.length == 0) {
222                 Slogf.e(CarLog.TAG_HAL, "getAllPropConfigs returned empty configs");
223                 return;
224             }
225         } catch (RemoteException | ServiceSpecificException e) {
226             throw new RuntimeException("Unable to retrieve vehicle property configuration", e);
227         }
228 
229         synchronized (mLock) {
230             // Create map of all properties
231             for (HalPropConfig p : configs) {
232                 if (DBG) {
233                     Slogf.i(CarLog.TAG_HAL, "Add config for prop: 0x%x config: %s", p.getPropId(),
234                             p.toString());
235                 }
236                 mAllProperties.put(p.getPropId(), p);
237             }
238         }
239     }
240 
handleOnPropertyEvent(List<HalPropValue> propValues)241     private void handleOnPropertyEvent(List<HalPropValue> propValues) {
242         synchronized (mLock) {
243             for (int i = 0; i < propValues.size(); i++) {
244                 HalPropValue v = propValues.get(i);
245                 int propId = v.getPropId();
246                 HalServiceBase service = mPropertyHandlers.get(propId);
247                 if (service == null) {
248                     Slogf.e(CarLog.TAG_HAL,
249                             "handleOnPropertyEvent: HalService not found for prop: 0x%x", propId);
250                     continue;
251                 }
252                 service.getDispatchList().add(v);
253                 mServicesToDispatch.add(service);
254                 VehiclePropertyEventInfo info = mEventLog.get(propId);
255                 if (info == null) {
256                     info = new VehiclePropertyEventInfo(v);
257                     mEventLog.put(propId, info);
258                 } else {
259                     info.addNewEvent(v);
260                 }
261             }
262         }
263         for (HalServiceBase s : mServicesToDispatch) {
264             s.onHalEvents(s.getDispatchList());
265             s.getDispatchList().clear();
266         }
267         mServicesToDispatch.clear();
268     }
269 
handleOnPropertySetError(List<VehiclePropError> errors)270     private void handleOnPropertySetError(List<VehiclePropError> errors) {
271         SparseArray<ArrayList<VehiclePropError>> errorsByPropId =
272                 new SparseArray<ArrayList<VehiclePropError>>();
273         for (int i = 0; i < errors.size(); i++) {
274             VehiclePropError error = errors.get(i);
275             int errorCode = error.errorCode;
276             int propId = error.propId;
277             int areaId = error.areaId;
278             Slogf.w(CarLog.TAG_HAL, "onPropertySetError, errorCode: %d, prop: 0x%x, area: 0x%x",
279                     errorCode, propId, areaId);
280             if (propId == VehicleProperty.INVALID) {
281                 continue;
282             }
283 
284             ArrayList<VehiclePropError> propErrors;
285             if (errorsByPropId.get(propId) == null) {
286                 propErrors = new ArrayList<VehiclePropError>();
287                 errorsByPropId.put(propId, propErrors);
288             } else {
289                 propErrors = errorsByPropId.get(propId);
290             }
291             propErrors.add(error);
292         }
293 
294         for (int i = 0; i < errorsByPropId.size(); i++) {
295             int propId = errorsByPropId.keyAt(i);
296             HalServiceBase service;
297             synchronized (mLock) {
298                 service = mPropertyHandlers.get(propId);
299             }
300             if (service == null) {
301                 Slogf.e(CarLog.TAG_HAL,
302                         "handleOnPropertySetError: HalService not found for prop: 0x%x", propId);
303                 continue;
304             }
305 
306             ArrayList<VehiclePropError> propErrors = errorsByPropId.get(propId);
307             service.onPropertySetError(propErrors);
308         }
309     }
310 
errorMessage(String action, HalPropValue propValue, String errorMsg)311     private static String errorMessage(String action, HalPropValue propValue, String errorMsg) {
312         return String.format("Failed to %s value for: 0x%x, areaId: 0x%x, error: %s", action,
313                 propValue.getPropId(), propValue.getAreaId(), errorMsg);
314     }
315 
getValueWithRetry(HalPropValue value)316     private HalPropValue getValueWithRetry(HalPropValue value) {
317         return getValueWithRetry(value, /* maxRetries= */ 0);
318     }
319 
getValueWithRetry(HalPropValue value, int maxRetries)320     private HalPropValue getValueWithRetry(HalPropValue value, int maxRetries) {
321         HalPropValue result;
322         Trace.traceBegin(TRACE_TAG, "VehicleStub#getValueWithRetry");
323         try {
324             result = invokeRetriable((requestValue) -> {
325                 Trace.traceBegin(TRACE_TAG, "VehicleStub#get");
326                 try {
327                     return mVehicleStub.get(requestValue);
328                 } finally {
329                     Trace.traceEnd(TRACE_TAG);
330                 }
331             }, "get", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, maxRetries);
332         } finally {
333             Trace.traceEnd(TRACE_TAG);
334         }
335 
336         if (result == null) {
337             // If VHAL returns null result, but the status is OKAY. We treat that as NOT_AVAILABLE.
338             throw new ServiceSpecificException(StatusCode.NOT_AVAILABLE,
339                     errorMessage("get", value, "VHAL returns null for property value"));
340         }
341         return result;
342     }
343 
setValueWithRetry(HalPropValue value)344     private void setValueWithRetry(HalPropValue value)  {
345         invokeRetriable((requestValue) -> {
346             Trace.traceBegin(TRACE_TAG, "VehicleStub#set");
347             mVehicleStub.set(requestValue);
348             Trace.traceEnd(TRACE_TAG);
349             return null;
350         }, "set", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, /* maxRetries= */ 0);
351     }
352 
353     /**
354      * Inits the vhal configurations.
355      */
356     @Override
init()357     public void init() {
358         // nothing to init as everything was done on priorityInit
359     }
360 
361     /**
362      * PriorityInit for the vhal configurations.
363      */
priorityInit()364     public void priorityInit() {
365         fetchAllPropConfigs();
366 
367         // PropertyHalService will take most properties, so make it big enough.
368         ArrayMap<HalServiceBase, ArrayList<HalPropConfig>> configsForAllServices;
369         synchronized (mLock) {
370             configsForAllServices = new ArrayMap<>(mAllServices.size());
371             for (int i = 0; i < mAllServices.size(); i++) {
372                 ArrayList<HalPropConfig> configsForService = new ArrayList();
373                 HalServiceBase service = mAllServices.get(i);
374                 configsForAllServices.put(service, configsForService);
375                 int[] supportedProps = service.getAllSupportedProperties();
376                 if (supportedProps.length == 0) {
377                     for (int j = 0; j < mAllProperties.size(); j++) {
378                         Integer propId = mAllProperties.keyAt(j);
379                         if (service.isSupportedProperty(propId)) {
380                             HalPropConfig config = mAllProperties.get(propId);
381                             mPropertyHandlers.append(propId, service);
382                             configsForService.add(config);
383                         }
384                     }
385                 } else {
386                     for (int prop : supportedProps) {
387                         HalPropConfig config = mAllProperties.get(prop);
388                         if (config == null) {
389                             continue;
390                         }
391                         mPropertyHandlers.append(prop, service);
392                         configsForService.add(config);
393                     }
394                 }
395             }
396         }
397 
398         for (Map.Entry<HalServiceBase, ArrayList<HalPropConfig>> entry
399                 : configsForAllServices.entrySet()) {
400             HalServiceBase service = entry.getKey();
401             ArrayList<HalPropConfig> configsForService = entry.getValue();
402             service.takeProperties(configsForService);
403             service.init();
404         }
405     }
406 
407     /**
408      * Releases all connected services (power management service, input service, etc).
409      */
410     @Override
release()411     public void release() {
412         ArraySet<Integer> subscribedProperties = new ArraySet<>();
413         synchronized (mLock) {
414             // release in reverse order from init
415             for (int i = mAllServices.size() - 1; i >= 0; i--) {
416                 mAllServices.get(i).release();
417             }
418             for (int i = 0; i < mUpdateRateByPropIdAreadId.size(); i++) {
419                 subscribedProperties.add(mUpdateRateByPropIdAreadId.keyAt(i).first);
420             }
421             mUpdateRateByPropIdAreadId.clear();
422             mAllProperties.clear();
423         }
424         for (int i = 0; i < subscribedProperties.size(); i++) {
425             try {
426                 mSubscriptionClient.unsubscribe(subscribedProperties.valueAt(i));
427             } catch (RemoteException | ServiceSpecificException e) {
428                 //  Ignore exceptions on shutdown path.
429                 Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe", e);
430             }
431         }
432         // keep the looper thread as should be kept for the whole life cycle.
433     }
434 
getDiagnosticHal()435     public DiagnosticHalService getDiagnosticHal() {
436         return mDiagnosticHal;
437     }
438 
getPowerHal()439     public PowerHalService getPowerHal() {
440         return mPowerHal;
441     }
442 
getPropertyHal()443     public PropertyHalService getPropertyHal() {
444         return mPropertyHal;
445     }
446 
getInputHal()447     public InputHalService getInputHal() {
448         return mInputHal;
449     }
450 
getUserHal()451     public UserHalService getUserHal() {
452         return mUserHal;
453     }
454 
getVmsHal()455     public VmsHalService getVmsHal() {
456         return mVmsHal;
457     }
458 
getClusterHal()459     public ClusterHalService getClusterHal() {
460         return mClusterHalService;
461     }
462 
getEvsHal()463     public EvsHalService getEvsHal() {
464         return mEvsHal;
465     }
466 
getTimeHalService()467     public TimeHalService getTimeHalService() {
468         return mTimeHalService;
469     }
470 
getHalPropValueBuilder()471     public HalPropValueBuilder getHalPropValueBuilder() {
472         return mPropValueBuilder;
473     }
474 
475     @GuardedBy("mLock")
assertServiceOwnerLocked(HalServiceBase service, int property)476     private void assertServiceOwnerLocked(HalServiceBase service, int property) {
477         if (service != mPropertyHandlers.get(property)) {
478             throw new IllegalArgumentException(String.format(
479                     "Property 0x%x  is not owned by service: %s", property, service));
480         }
481     }
482 
483     /**
484      * Subscribes given properties with sampling rate defaults to 0 and no special flags provided.
485      *
486      * @throws IllegalArgumentException thrown if property is not supported by VHAL
487      * @see #subscribeProperty(HalServiceBase, int, float)
488      */
subscribeProperty(HalServiceBase service, int property)489     public void subscribeProperty(HalServiceBase service, int property)
490             throws IllegalArgumentException {
491         subscribeProperty(service, property, /* samplingRateHz= */ 0f);
492     }
493 
494     /**
495      * Subscribe given property. Only Hal service owning the property can subscribe it.
496      *
497      * @param service HalService that owns this property
498      * @param property property id (VehicleProperty)
499      * @param samplingRateHz sampling rate in Hz for continuous properties
500      * @throws IllegalArgumentException thrown if property is not supported by VHAL
501      */
subscribeProperty(HalServiceBase service, int property, float samplingRateHz)502     public void subscribeProperty(HalServiceBase service, int property, float samplingRateHz)
503             throws IllegalArgumentException {
504         subscribeProperty(service, property, samplingRateHz, new int[0]);
505     }
506 
507     /**
508      * Subscribe given property. Only Hal service owning the property can subscribe it.
509      *
510      * @param service HalService that owns this property
511      * @param property property id (VehicleProperty)
512      * @param samplingRateHz sampling rate in Hz for continuous properties
513      * @param areaIds The areaId that is being subscribed to, if empty subscribe to all areas
514      * @throws IllegalArgumentException thrown if property is not supported by VHAL
515      */
subscribeProperty(HalServiceBase service, int property, float samplingRateHz, int[] areaIds)516     public void subscribeProperty(HalServiceBase service, int property, float samplingRateHz,
517             int[] areaIds) {
518         if (DBG) {
519             Slogf.d(CarLog.TAG_HAL, "subscribeProperty, service, areaIds, SamplingRateHz:"
520                     + toCarPropertyLog(property) + ", " + service + ", "
521                     + CarServiceUtils.asList(areaIds) + ", " + samplingRateHz);
522         }
523         HalPropConfig config;
524         synchronized (mLock) {
525             config = mAllProperties.get(property);
526         }
527 
528         if (config == null) {
529             throw new IllegalArgumentException(
530                     String.format("subscribe error: config is null for property 0x%x", property));
531         } else if (isPropertySubscribable(config)) {
532             if (areaIds.length == 0) {
533                 areaIds = getAllAreaIdsFromPropertyId(config);
534             }
535             SubscribeOptions opts = new SubscribeOptions();
536             opts.propId = property;
537             opts.sampleRate = samplingRateHz;
538             int[] filteredAreaIds = checkAlreadySubscribed(property, areaIds, samplingRateHz);
539             opts.areaIds = filteredAreaIds;
540             if (opts.areaIds.length == 0) {
541                 Slogf.w(CarLog.TAG_HAL, "property: " + VehiclePropertyIds.toString(property)
542                         + " is already subscribed at rate: " + samplingRateHz + " hz");
543                 return;
544             }
545             synchronized (mLock) {
546                 assertServiceOwnerLocked(service, property);
547                 for (int i = 0; i < filteredAreaIds.length; i++) {
548                     mUpdateRateByPropIdAreadId.put(Pair.create(property,
549                                     filteredAreaIds[i]), samplingRateHz);
550                 }
551             }
552             try {
553                 mSubscriptionClient.subscribe(new SubscribeOptions[]{opts});
554             } catch (RemoteException | ServiceSpecificException e) {
555                 Slogf.w(CarLog.TAG_HAL, "Failed to subscribe to " + toCarPropertyLog(property),
556                         e);
557             }
558         } else {
559             Slogf.w(CarLog.TAG_HAL, "Cannot subscribe to " + toCarPropertyLog(property));
560         }
561     }
562 
checkAlreadySubscribed(int property, int[] areaIds, float sampleRateHz)563     private int[] checkAlreadySubscribed(int property, int[] areaIds, float sampleRateHz) {
564         List<Integer> areaIdList = new ArrayList<>();
565         synchronized (mLock) {
566             for (int i = 0; i < areaIds.length; i++) {
567                 Pair<Integer, Integer> propertyAndAreadId = Pair.create(property, areaIds[i]);
568                 Float savedSampleRateHz = mUpdateRateByPropIdAreadId.get(propertyAndAreadId);
569                 if (savedSampleRateHz != null
570                         && savedSampleRateHz - sampleRateHz < PRECISION_THRESHOLD) {
571                     continue;
572                 }
573                 areaIdList.add(areaIds[i]);
574             }
575         }
576         return CarServiceUtils.toIntArray(areaIdList);
577     }
578 
getAllAreaIdsFromPropertyId(HalPropConfig config)579     private int[] getAllAreaIdsFromPropertyId(HalPropConfig config) {
580         HalAreaConfig[] allAreaConfigs = config.getAreaConfigs();
581         if (allAreaConfigs.length == 0) {
582             return new int[]{/* areaId= */ 0};
583         }
584         int[] areaId = new int[allAreaConfigs.length];
585         for (int i = 0; i < allAreaConfigs.length; i++) {
586             areaId[i] = allAreaConfigs[i].getAreaId();
587         }
588         return areaId;
589     }
590 
591     /**
592      * Unsubscribes from receiving notifications for the property and HAL services passed
593      * as parameters.
594      */
unsubscribeProperty(HalServiceBase service, int property)595     public void unsubscribeProperty(HalServiceBase service, int property) {
596         if (DBG) {
597             Slogf.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service
598                     + ", " + toCarPropertyLog(property));
599         }
600         HalPropConfig config;
601         synchronized (mLock) {
602             config = mAllProperties.get(property);
603         }
604 
605         if (config == null) {
606             Slogf.w(CarLog.TAG_HAL, "unsubscribeProperty " + toCarPropertyLog(property)
607                     + " does not exist");
608         } else if (isPropertySubscribable(config)) {
609             synchronized (mLock) {
610                 assertServiceOwnerLocked(service, property);
611                 int[] areaIds = getAllAreaIdsFromPropertyId(config);
612                 for (int i = 0; i < areaIds.length; i++) {
613                     mUpdateRateByPropIdAreadId.remove(Pair.create(property, areaIds[i]));
614                 }
615             }
616             try {
617                 mSubscriptionClient.unsubscribe(property);
618             } catch (RemoteException | ServiceSpecificException e) {
619                 Slogf.w(CarLog.TAG_SERVICE, "Failed to unsubscribe: "
620                         + toCarPropertyLog(property), e);
621             }
622         } else {
623             Slogf.w(CarLog.TAG_HAL, "Cannot unsubscribe " + toCarPropertyLog(property));
624         }
625     }
626 
627     /**
628      * Indicates if the property passed as parameter is supported.
629      */
isPropertySupported(int propertyId)630     public boolean isPropertySupported(int propertyId) {
631         synchronized (mLock) {
632             return mAllProperties.contains(propertyId);
633         }
634     }
635 
636     /**
637      * Gets given property with retries.
638      *
639      * <p>If getting the property fails after all retries, it will throw
640      * {@code IllegalStateException}. If the property is not supported, it will simply return
641      * {@code null}.
642      */
643     @Nullable
getIfSupportedOrFail(int propertyId, int maxRetries)644     public HalPropValue getIfSupportedOrFail(int propertyId, int maxRetries) {
645         if (!isPropertySupported(propertyId)) {
646             return null;
647         }
648         try {
649             return getValueWithRetry(mPropValueBuilder.build(propertyId, NO_AREA), maxRetries);
650         } catch (Exception e) {
651             throw new IllegalStateException(e);
652         }
653     }
654 
655     /**
656      * This works similar to {@link #getIfSupportedOrFail(int, int)} except that this can be called
657      * before {@code init()} is called.
658      *
659      * <p>This call will check if requested vhal property is supported by querying directly to vhal
660      * and can have worse performance. Use this only for accessing vhal properties before
661      * {@code ICarImpl.init()} phase.
662      */
663     @Nullable
getIfSupportedOrFailForEarlyStage(int propertyId, int maxRetries)664     public HalPropValue getIfSupportedOrFailForEarlyStage(int propertyId, int maxRetries) {
665         fetchAllPropConfigs();
666         return getIfSupportedOrFail(propertyId, maxRetries);
667     }
668 
669     /**
670      * Returns the property's {@link HalPropValue} for the property id passed as parameter and
671      * not specified area.
672      *
673      * @throws IllegalArgumentException if argument is invalid
674      * @throws ServiceSpecificException if VHAL returns error
675      */
get(int propertyId)676     public HalPropValue get(int propertyId)
677             throws IllegalArgumentException, ServiceSpecificException {
678         return get(propertyId, NO_AREA);
679     }
680 
681     /**
682      * Returns the property's {@link HalPropValue} for the property id and area id passed as
683      * parameters.
684      *
685      * @throws IllegalArgumentException if argument is invalid
686      * @throws ServiceSpecificException if VHAL returns error
687      */
get(int propertyId, int areaId)688     public HalPropValue get(int propertyId, int areaId)
689             throws IllegalArgumentException, ServiceSpecificException {
690         if (DBG) {
691             Slogf.i(CarLog.TAG_HAL, "get, " + toCarPropertyLog(propertyId)
692                     + toCarAreaLog(areaId));
693         }
694         return getValueWithRetry(mPropValueBuilder.build(propertyId, areaId));
695     }
696 
697     /**
698      * Returns the property object value for the class and property id passed as parameter and
699      * no area specified.
700      *
701      * @throws IllegalArgumentException if argument is invalid
702      * @throws ServiceSpecificException if VHAL returns error
703      */
get(Class clazz, int propertyId)704     public <T> T get(Class clazz, int propertyId)
705             throws IllegalArgumentException, ServiceSpecificException {
706         return get(clazz, propertyId, NO_AREA);
707     }
708 
709     /**
710      * Returns the property object value for the class, property id, and area id passed as
711      * parameter.
712      *
713      * @throws IllegalArgumentException if argument is invalid
714      * @throws ServiceSpecificException if VHAL returns error
715      */
get(Class clazz, int propertyId, int areaId)716     public <T> T get(Class clazz, int propertyId, int areaId)
717             throws IllegalArgumentException, ServiceSpecificException {
718         return get(clazz, mPropValueBuilder.build(propertyId, areaId));
719     }
720 
721     /**
722      * Returns the property object value for the class and requested property value passed as
723      * parameter.
724      *
725      * @throws IllegalArgumentException if argument is invalid
726      * @throws ServiceSpecificException if VHAL returns error
727      */
728     @SuppressWarnings("unchecked")
get(Class clazz, HalPropValue requestedPropValue)729     public <T> T get(Class clazz, HalPropValue requestedPropValue)
730             throws IllegalArgumentException, ServiceSpecificException {
731         HalPropValue propValue;
732         propValue = getValueWithRetry(requestedPropValue);
733 
734         if (clazz == Long.class || clazz == long.class) {
735             Long value = propValue.getInt64Value(0);
736             return (T) value;
737         } else if (clazz == Integer.class || clazz == int.class) {
738             Integer value = propValue.getInt32Value(0);
739             return (T) value;
740         } else if (clazz == Boolean.class || clazz == boolean.class) {
741             Boolean value = Boolean.valueOf(propValue.getInt32Value(0) == 1);
742             return (T) value;
743         } else if (clazz == Float.class || clazz == float.class) {
744             Float value = propValue.getFloatValue(0);
745             return (T) value;
746         } else if (clazz == Long[].class) {
747             int size = propValue.getInt64ValuesSize();
748             Long[] longArray = new Long[size];
749             for (int i = 0; i < size; i++) {
750                 longArray[i] = propValue.getInt64Value(i);
751             }
752             return (T) longArray;
753         } else if (clazz == Integer[].class) {
754             int size = propValue.getInt32ValuesSize();
755             Integer[] intArray = new Integer[size];
756             for (int i = 0; i < size; i++) {
757                 intArray[i] = propValue.getInt32Value(i);
758             }
759             return (T) intArray;
760         } else if (clazz == Float[].class) {
761             int size = propValue.getFloatValuesSize();
762             Float[] floatArray = new Float[size];
763             for (int i = 0; i < size; i++) {
764                 floatArray[i] = propValue.getFloatValue(i);
765             }
766             return (T) floatArray;
767         } else if (clazz == long[].class) {
768             int size = propValue.getInt64ValuesSize();
769             long[] longArray = new long[size];
770             for (int i = 0; i < size; i++) {
771                 longArray[i] = propValue.getInt64Value(i);
772             }
773             return (T) longArray;
774         } else if (clazz == int[].class) {
775             int size = propValue.getInt32ValuesSize();
776             int[] intArray = new int[size];
777             for (int i = 0; i < size; i++) {
778                 intArray[i] = propValue.getInt32Value(i);
779             }
780             return (T) intArray;
781         } else if (clazz == float[].class) {
782             int size = propValue.getFloatValuesSize();
783             float[] floatArray = new float[size];
784             for (int i = 0; i < size; i++) {
785                 floatArray[i] = propValue.getFloatValue(i);
786             }
787             return (T) floatArray;
788         } else if (clazz == byte[].class) {
789             return (T) propValue.getByteArray();
790         } else if (clazz == String.class) {
791             return (T) propValue.getStringValue();
792         } else {
793             throw new IllegalArgumentException("Unexpected type: " + clazz);
794         }
795     }
796 
797     /**
798      * Returns the vehicle's {@link HalPropValue} for the requested property value passed
799      * as parameter.
800      *
801      * @throws IllegalArgumentException if argument is invalid
802      * @throws ServiceSpecificException if VHAL returns error
803      */
get(HalPropValue requestedPropValue)804     public HalPropValue get(HalPropValue requestedPropValue)
805             throws IllegalArgumentException, ServiceSpecificException {
806         return getValueWithRetry(requestedPropValue);
807     }
808 
809     /**
810      * Set property.
811      *
812      * @throws IllegalArgumentException if argument is invalid
813      * @throws ServiceSpecificException if VHAL returns error
814      */
set(HalPropValue propValue)815     public void set(HalPropValue propValue)
816             throws IllegalArgumentException, ServiceSpecificException {
817         setValueWithRetry(propValue);
818     }
819 
820     @CheckResult
set(int propId)821     HalPropValueSetter set(int propId) {
822         return set(propId, NO_AREA);
823     }
824 
825     @CheckResult
set(int propId, int areaId)826     HalPropValueSetter set(int propId, int areaId) {
827         return new HalPropValueSetter(propId, areaId);
828     }
829 
isPropertySubscribable(HalPropConfig config)830     static boolean isPropertySubscribable(HalPropConfig config) {
831         return (config.getAccess() & VehiclePropertyAccess.READ) != 0
832                 && (config.getChangeMode() != VehiclePropertyChangeMode.STATIC);
833     }
834 
835     /**
836      * Sets a property passed from the shell command.
837      *
838      * @param property Property ID in hex or decimal.
839      * @param areaId Area ID
840      * @param data Comma-separated value.
841      */
setPropertyFromCommand(int property, int areaId, String data, IndentingPrintWriter writer)842     public void setPropertyFromCommand(int property, int areaId, String data,
843             IndentingPrintWriter writer) throws IllegalArgumentException, ServiceSpecificException {
844         long timestamp = SystemClock.elapsedRealtimeNanos();
845         HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, areaId,
846                 List.of(data.split(DATA_DELIMITER)), timestamp);
847         if (v == null) {
848             throw new IllegalArgumentException("Unsupported property type: property=" + property
849                     + ", areaId=" + areaId);
850         }
851         set(v);
852     }
853 
854     private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>();
855 
856     @Override
onPropertyEvent(ArrayList<HalPropValue> propValues)857     public void onPropertyEvent(ArrayList<HalPropValue> propValues) {
858         mHandler.post(() -> handleOnPropertyEvent(propValues));
859     }
860 
861     @Override
onPropertySetError(ArrayList<VehiclePropError> errors)862     public void onPropertySetError(ArrayList<VehiclePropError> errors) {
863         mHandler.post(() -> handleOnPropertySetError(errors));
864     }
865 
866     @Override
867     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)868     public void dump(IndentingPrintWriter writer) {
869         synchronized (mLock) {
870             writer.println("**dump HAL services**");
871             for (int i = 0; i < mAllServices.size(); i++) {
872                 mAllServices.get(i).dump(writer);
873             }
874             // Dump all VHAL property configure.
875             dumpPropertyConfigs(writer, -1);
876             writer.printf("**All Events, now ns:%d**\n",
877                     SystemClock.elapsedRealtimeNanos());
878             for (int i = 0; i < mEventLog.size(); i++) {
879                 VehiclePropertyEventInfo info = mEventLog.valueAt(i);
880                 writer.printf("event count:%d, lastEvent: ", info.mEventCount);
881                 dumpPropValue(writer, info.mLastEvent);
882             }
883             writer.println("**Property handlers**");
884             for (int i = 0; i < mPropertyHandlers.size(); i++) {
885                 int propId = mPropertyHandlers.keyAt(i);
886                 HalServiceBase service = mPropertyHandlers.valueAt(i);
887                 writer.printf("Property Id: %d // 0x%x name: %s, service: %s\n", propId, propId,
888                         VehiclePropertyIds.toString(propId), service);
889             }
890         }
891     }
892 
893      /**
894      * Dumps or debug VHAL.
895      */
896     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpVhal(ParcelFileDescriptor fd, List<String> options)897     public void dumpVhal(ParcelFileDescriptor fd, List<String> options) throws RemoteException {
898         mVehicleStub.dump(fd.getFileDescriptor(), options);
899     }
900 
901     /**
902      * Dumps the list of HALs.
903      */
dumpListHals(PrintWriter writer)904     public void dumpListHals(PrintWriter writer) {
905         synchronized (mLock) {
906             for (int i = 0; i < mAllServices.size(); i++) {
907                 writer.println(mAllServices.get(i).getClass().getName());
908             }
909         }
910     }
911 
912     /**
913      * Dumps the given HALs.
914      */
dumpSpecificHals(PrintWriter writer, String... halNames)915     public void dumpSpecificHals(PrintWriter writer, String... halNames) {
916         synchronized (mLock) {
917             Map<String, HalServiceBase> byName = mAllServices.stream()
918                     .collect(Collectors.toMap(s -> s.getClass().getSimpleName(), s -> s));
919             for (String halName : halNames) {
920                 HalServiceBase service = byName.get(halName);
921                 if (service == null) {
922                     writer.printf("No HAL named %s. Valid options are: %s\n",
923                             halName, byName.keySet());
924                     continue;
925                 }
926                 service.dump(writer);
927             }
928         }
929     }
930 
931     /**
932      * Dumps vehicle property values.
933      *
934      * @param propId property id, dump all properties' value if it is empty string
935      * @param areaId areaId of the property, dump the property for all areaIds in the config
936      *               if it is empty string
937      */
dumpPropertyValueByCommand(PrintWriter writer, int propId, int areaId)938     public void dumpPropertyValueByCommand(PrintWriter writer, int propId, int areaId) {
939         if (propId == -1) {
940             writer.println("**All property values**");
941             synchronized (mLock) {
942                 for (int i = 0; i < mAllProperties.size(); i++) {
943                     HalPropConfig config = mAllProperties.valueAt(i);
944                     dumpPropertyValueByConfig(writer, config);
945                 }
946             }
947         } else if (areaId == -1) {
948             synchronized (mLock) {
949                 HalPropConfig config = mAllProperties.get(propId);
950                 if (config == null) {
951                     writer.print("Property ");
952                     dumpPropHelper(writer, propId);
953                     writer.print(" not supported by HAL\n");
954                     return;
955                 }
956                 dumpPropertyValueByConfig(writer, config);
957             }
958         } else {
959             try {
960                 HalPropValue value = get(propId, areaId);
961                 dumpPropValue(writer, value);
962             } catch (RuntimeException e) {
963                 writer.printf("Can not get property value for property: %d // 0x%x "
964                         + "in areaId: %d // 0x%x.\n", propId, propId, areaId, areaId);
965             }
966         }
967     }
968 
969     /**
970      * Gets all property configs from VHAL.
971      */
getAllPropConfigs()972     public HalPropConfig[] getAllPropConfigs() throws RemoteException, ServiceSpecificException {
973         return mVehicleStub.getAllPropConfigs();
974     }
975 
976     /**
977      * Gets the property config for a property, returns {@code null} if not supported.
978      */
getPropConfig(int propId)979     public @Nullable HalPropConfig getPropConfig(int propId) {
980         synchronized (mLock) {
981             return mAllProperties.get(propId);
982         }
983     }
984 
985     /**
986      * Checks whether we are connected to AIDL VHAL: {@code true} or HIDL VHAL: {@code false}.
987      */
isAidlVhal()988     public boolean isAidlVhal() {
989         return mVehicleStub.isAidlVhal();
990     }
991 
992     /**
993      * Checks if fake VHAL mode is enabled.
994      *
995      * @return {@code true} if car service is connected to FakeVehicleStub.
996      */
isFakeModeEnabled()997     public boolean isFakeModeEnabled() {
998         return mVehicleStub.isFakeModeEnabled();
999     }
1000 
dumpPropHelper(PrintWriter pw, int propId)1001     private static void dumpPropHelper(PrintWriter pw, int propId) {
1002         pw.printf("Id: %d // 0x%x, name: %s ", propId, propId, VehiclePropertyIds.toString(propId));
1003     }
1004 
dumpPropertyValueByConfig(PrintWriter writer, HalPropConfig config)1005     private void dumpPropertyValueByConfig(PrintWriter writer, HalPropConfig config) {
1006         int propId = config.getPropId();
1007         HalAreaConfig[] areaConfigs = config.getAreaConfigs();
1008         if (areaConfigs == null || areaConfigs.length == 0) {
1009             try {
1010                 HalPropValue value = get(config.getPropId());
1011                 dumpPropValue(writer, value);
1012             } catch (RuntimeException e) {
1013                 writer.printf("Can not get property value for property: %d // 0x%x,"
1014                         + " areaId: 0 \n", propId, propId);
1015             }
1016         } else {
1017             for (HalAreaConfig areaConfig : areaConfigs) {
1018                 int areaId = areaConfig.getAreaId();
1019                 try {
1020                     HalPropValue value = get(propId, areaId);
1021                     dumpPropValue(writer, value);
1022                 } catch (RuntimeException e) {
1023                     writer.printf("Can not get property value for property: %d // 0x%x "
1024                             + "in areaId: %d // 0x%x\n", propId, propId, areaId, areaId);
1025                 }
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Dump VHAL property configs.
1032      * Dump all properties if propid param is empty.
1033      *
1034      * @param propId the property ID
1035      */
dumpPropertyConfigs(PrintWriter writer, int propId)1036     public void dumpPropertyConfigs(PrintWriter writer, int propId) {
1037         HalPropConfig[] configs;
1038         synchronized (mLock) {
1039             configs = new HalPropConfig[mAllProperties.size()];
1040             for (int i = 0; i < mAllProperties.size(); i++) {
1041                 configs[i] = mAllProperties.valueAt(i);
1042             }
1043         }
1044 
1045         if (propId == -1) {
1046             writer.println("**All properties**");
1047             for (HalPropConfig config : configs) {
1048                 dumpPropertyConfigsHelp(writer, config);
1049             }
1050             return;
1051         }
1052         for (HalPropConfig config : configs) {
1053             if (config.getPropId() == propId) {
1054                 dumpPropertyConfigsHelp(writer, config);
1055                 return;
1056             }
1057         }
1058 
1059     }
1060 
1061     /** Dumps VehiclePropertyConfigs */
dumpPropertyConfigsHelp(PrintWriter writer, HalPropConfig config)1062     private static void dumpPropertyConfigsHelp(PrintWriter writer, HalPropConfig config) {
1063         int propId = config.getPropId();
1064         writer.printf("Property:0x%x, Property name:%s, access:0x%x, changeMode:0x%x, "
1065                         + "config:%s, fs min:%f, fs max:%f\n",
1066                 propId, VehiclePropertyIds.toString(propId), config.getAccess(),
1067                 config.getChangeMode(), Arrays.toString(config.getConfigArray()),
1068                 config.getMinSampleRate(), config.getMaxSampleRate());
1069         if (config.getAreaConfigs() == null) {
1070             return;
1071         }
1072         for (HalAreaConfig area : config.getAreaConfigs()) {
1073             writer.printf("\tareaId:0x%x, f min:%f, f max:%f, i min:%d, i max:%d,"
1074                             + " i64 min:%d, i64 max:%d\n",
1075                     area.getAreaId(), area.getMinFloatValue(), area.getMaxFloatValue(),
1076                     area.getMinInt32Value(), area.getMaxInt32Value(), area.getMinInt64Value(),
1077                     area.getMaxInt64Value());
1078         }
1079     }
1080 
1081     /**
1082      * Inject a VHAL event
1083      *
1084      * @param property the Vehicle property Id as defined in the HAL
1085      * @param zone the zone that this event services
1086      * @param value the data value of the event
1087      * @param delayTime add a certain duration to event timestamp
1088      */
injectVhalEvent(int property, int zone, String value, int delayTime)1089     public void injectVhalEvent(int property, int zone, String value, int delayTime)
1090             throws NumberFormatException {
1091         long timestamp = SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos(delayTime);
1092         HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone,
1093                 Arrays.asList(value.split(DATA_DELIMITER)), timestamp);
1094         if (v == null) {
1095             return;
1096         }
1097         mHandler.post(() -> handleOnPropertyEvent(Lists.newArrayList(v)));
1098     }
1099 
1100     /**
1101      * Injects continuous VHAL events.
1102      *
1103      * @param property the Vehicle property Id as defined in the HAL
1104      * @param zone the zone that this event services
1105      * @param value the data value of the event
1106      * @param sampleRate the sample rate for events in Hz
1107      * @param timeDurationInSec the duration for injecting events in seconds
1108      */
injectContinuousVhalEvent(int property, int zone, String value, float sampleRate, long timeDurationInSec)1109     public void injectContinuousVhalEvent(int property, int zone, String value,
1110             float sampleRate, long timeDurationInSec) {
1111 
1112         HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone,
1113                 new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))), 0);
1114         if (v == null) {
1115             return;
1116         }
1117         // rate in Hz
1118         if (sampleRate <= 0) {
1119             Slogf.e(CarLog.TAG_HAL, "Inject events at an invalid sample rate: " + sampleRate);
1120             return;
1121         }
1122         long period = (long) (1000 / sampleRate);
1123         long stopTime = timeDurationInSec * 1000 + SystemClock.elapsedRealtime();
1124         Timer timer = new Timer();
1125         timer.schedule(new TimerTask() {
1126             @Override
1127             public void run() {
1128                 if (stopTime < SystemClock.elapsedRealtime()) {
1129                     timer.cancel();
1130                     timer.purge();
1131                 } else {
1132                     // Avoid the fake events be covered by real Event
1133                     long timestamp = SystemClock.elapsedRealtimeNanos()
1134                             + TimeUnit.SECONDS.toNanos(timeDurationInSec);
1135                     HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone,
1136                             new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))), timestamp);
1137                     mHandler.post(() -> handleOnPropertyEvent(Lists.newArrayList(v)));
1138                 }
1139             }
1140         }, /* delay= */0, period);
1141     }
1142 
1143     // Returns null if the property type is unsupported.
1144     @Nullable
createPropValueForInjecting(HalPropValueBuilder builder, int propId, int zoneId, List<String> dataList, long timestamp)1145     private static HalPropValue createPropValueForInjecting(HalPropValueBuilder builder,
1146             int propId, int zoneId, List<String> dataList, long timestamp) {
1147         int propertyType = propId & VehiclePropertyType.MASK;
1148         // Values can be comma separated list
1149         switch (propertyType) {
1150             case VehiclePropertyType.BOOLEAN:
1151                 boolean boolValue = Boolean.parseBoolean(dataList.get(0));
1152                 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE,
1153                         boolValue ? 1 : 0);
1154             case VehiclePropertyType.INT64:
1155             case VehiclePropertyType.INT64_VEC:
1156                 long[] longValues = new long[dataList.size()];
1157                 for (int i = 0; i < dataList.size(); i++) {
1158                     longValues[i] = Long.decode(dataList.get(i));
1159                 }
1160                 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE,
1161                         longValues);
1162             case VehiclePropertyType.INT32:
1163             case VehiclePropertyType.INT32_VEC:
1164                 int[] intValues = new int[dataList.size()];
1165                 for (int i = 0; i < dataList.size(); i++) {
1166                     intValues[i] = Integer.decode(dataList.get(i));
1167                 }
1168                 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE,
1169                         intValues);
1170             case VehiclePropertyType.FLOAT:
1171             case VehiclePropertyType.FLOAT_VEC:
1172                 float[] floatValues = new float[dataList.size()];
1173                 for (int i = 0; i < dataList.size(); i++) {
1174                     floatValues[i] = Float.parseFloat(dataList.get(i));
1175                 }
1176                 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE,
1177                         floatValues);
1178             default:
1179                 Slogf.e(CarLog.TAG_HAL, "Property type unsupported:" + propertyType);
1180                 return null;
1181         }
1182     }
1183 
1184     private static class VehiclePropertyEventInfo {
1185         private int mEventCount;
1186         private HalPropValue mLastEvent;
1187 
VehiclePropertyEventInfo(HalPropValue event)1188         private VehiclePropertyEventInfo(HalPropValue event) {
1189             mEventCount = 1;
1190             mLastEvent = event;
1191         }
1192 
addNewEvent(HalPropValue event)1193         private void addNewEvent(HalPropValue event) {
1194             mEventCount++;
1195             mLastEvent = event;
1196         }
1197     }
1198 
1199     final class HalPropValueSetter {
1200         final int mPropId;
1201         final int mAreaId;
1202 
HalPropValueSetter(int propId, int areaId)1203         private HalPropValueSetter(int propId, int areaId) {
1204             mPropId = propId;
1205             mAreaId = areaId;
1206         }
1207 
1208         /**
1209          * Set the property to the given value.
1210          *
1211          * @throws IllegalArgumentException if argument is invalid
1212          * @throws ServiceSpecificException if VHAL returns error
1213          */
to(boolean value)1214         void to(boolean value) throws IllegalArgumentException, ServiceSpecificException {
1215             to(value ? 1 : 0);
1216         }
1217 
1218         /**
1219          * Set the property to the given value.
1220          *
1221          * @throws IllegalArgumentException if argument is invalid
1222          * @throws ServiceSpecificException if VHAL returns error
1223          */
to(int value)1224         void to(int value) throws IllegalArgumentException, ServiceSpecificException {
1225             HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, value);
1226             submit(propValue);
1227         }
1228 
1229         /**
1230          * Set the property to the given values.
1231          *
1232          * @throws IllegalArgumentException if argument is invalid
1233          * @throws ServiceSpecificException if VHAL returns error
1234          */
to(int[] values)1235         void to(int[] values) throws IllegalArgumentException, ServiceSpecificException {
1236             HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, values);
1237             submit(propValue);
1238         }
1239 
1240         /**
1241          * Set the property to the given values.
1242          *
1243          * @throws IllegalArgumentException if argument is invalid
1244          * @throws ServiceSpecificException if VHAL returns error
1245          */
to(Collection<Integer> values)1246         void to(Collection<Integer> values)
1247                 throws IllegalArgumentException, ServiceSpecificException {
1248             int[] intValues = new int[values.size()];
1249             int i = 0;
1250             for (int value : values) {
1251                 intValues[i] = value;
1252                 i++;
1253             }
1254             HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, intValues);
1255             submit(propValue);
1256         }
1257 
submit(HalPropValue propValue)1258         void submit(HalPropValue propValue)
1259                 throws IllegalArgumentException, ServiceSpecificException {
1260             if (DBG) {
1261                 Slogf.i(CarLog.TAG_HAL, "set, " + toCarPropertyLog(mPropId)
1262                         + toCarAreaLog(mAreaId));
1263             }
1264             setValueWithRetry(propValue);
1265         }
1266     }
1267 
dumpPropValue(PrintWriter writer, HalPropValue value)1268     private static void dumpPropValue(PrintWriter writer, HalPropValue value) {
1269         String bytesString = "";
1270         byte[] byteValues = value.getByteArray();
1271         if (byteValues.length > MAX_BYTE_SIZE) {
1272             byte[] bytes = Arrays.copyOf(byteValues, MAX_BYTE_SIZE);
1273             bytesString = Arrays.toString(bytes);
1274         } else {
1275             bytesString = Arrays.toString(byteValues);
1276         }
1277 
1278         writer.printf("Property:0x%x, status: %d, timestamp: %d, zone: 0x%x, "
1279                         + "floatValues: %s, int32Values: %s, int64Values: %s, bytes: %s, string: "
1280                         + "%s\n",
1281                 value.getPropId(), value.getStatus(), value.getTimestamp(), value.getAreaId(),
1282                 value.dumpFloatValues(), value.dumpInt32Values(), value.dumpInt64Values(),
1283                 bytesString, value.getStringValue());
1284     }
1285 
toCarPropertyLog(int propId)1286     private static String toCarPropertyLog(int propId) {
1287         return String.format("property Id: %d // 0x%x, property name: %s ", propId, propId,
1288                 VehiclePropertyIds.toString(propId));
1289     }
1290 
1291     @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
toCarAreaLog(int areaId)1292     private static String toCarAreaLog(int areaId) {
1293         return String.format("areaId: %d // 0x%x", areaId, areaId);
1294     }
1295 
1296     interface RetriableAction {
run(HalPropValue requestValue)1297         @Nullable HalPropValue run(HalPropValue requestValue)
1298                 throws ServiceSpecificException, RemoteException;
1299     }
1300 
invokeRetriable(RetriableAction action, String operation, HalPropValue requestValue, long maxDurationForRetryMs, long sleepBetweenRetryMs, int maxRetries)1301     private static HalPropValue invokeRetriable(RetriableAction action,
1302             String operation, HalPropValue requestValue, long maxDurationForRetryMs,
1303             long sleepBetweenRetryMs, int maxRetries)
1304             throws ServiceSpecificException, IllegalArgumentException {
1305         Retrier retrier = new Retrier(action, operation, requestValue, maxDurationForRetryMs,
1306                 sleepBetweenRetryMs, maxRetries);
1307         return retrier.invokeAction();
1308     }
1309 
1310     private static final class Retrier {
1311         private final RetriableAction mAction;
1312         private final String mOperation;
1313         private final HalPropValue mRequestValue;
1314         private final long mMaxDurationForRetryMs;
1315         private final long mSleepBetweenRetryMs;
1316         private final int mMaxRetries;
1317         private final long mStartTime;
1318         private int mRetryCount = 0;
1319 
Retrier(RetriableAction action, String operation, HalPropValue requestValue, long maxDurationForRetryMs, long sleepBetweenRetryMs, int maxRetries)1320         Retrier(RetriableAction action,
1321                 String operation, HalPropValue requestValue, long maxDurationForRetryMs,
1322                 long sleepBetweenRetryMs, int maxRetries) {
1323             mAction = action;
1324             mOperation = operation;
1325             mRequestValue = requestValue;
1326             mMaxDurationForRetryMs = maxDurationForRetryMs;
1327             mSleepBetweenRetryMs = sleepBetweenRetryMs;
1328             mMaxRetries = maxRetries;
1329             mStartTime = uptimeMillis();
1330         }
1331 
invokeAction()1332         HalPropValue invokeAction()
1333                 throws ServiceSpecificException, IllegalArgumentException {
1334             mRetryCount++;
1335 
1336             try {
1337                 return mAction.run(mRequestValue);
1338             } catch (ServiceSpecificException e) {
1339                 switch (e.errorCode) {
1340                     case StatusCode.INVALID_ARG:
1341                         throw new IllegalArgumentException(errorMessage(mOperation, mRequestValue,
1342                             e.toString()));
1343                     case StatusCode.TRY_AGAIN:
1344                         return sleepAndTryAgain(e);
1345                     default:
1346                         throw e;
1347                 }
1348             } catch (RemoteException e) {
1349                 return sleepAndTryAgain(e);
1350             }
1351         }
1352 
sleepAndTryAgain(Exception e)1353         private HalPropValue sleepAndTryAgain(Exception e)
1354                 throws ServiceSpecificException, IllegalArgumentException {
1355             Slogf.d(CarLog.TAG_HAL, "trying the request: "
1356                     + toCarPropertyLog(mRequestValue.getPropId()) + ", "
1357                     + toCarAreaLog(mRequestValue.getAreaId()) + " again...");
1358             try {
1359                 Thread.sleep(mSleepBetweenRetryMs);
1360             } catch (InterruptedException interruptedException) {
1361                 Thread.currentThread().interrupt();
1362                 Slogf.w(CarLog.TAG_HAL, "Thread was interrupted while waiting for vehicle HAL.",
1363                         interruptedException);
1364                 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
1365                         errorMessage(mOperation, mRequestValue, interruptedException.toString()));
1366             }
1367 
1368             if (mMaxRetries != 0) {
1369                 // If mMaxRetries is specified, check the retry count.
1370                 if (mMaxRetries == mRetryCount) {
1371                     throw new ServiceSpecificException(StatusCode.TRY_AGAIN,
1372                             errorMessage(mOperation, mRequestValue,
1373                                     "cannot get property after " + mRetryCount + " retires, "
1374                                     + "last exception: " + e));
1375                 }
1376             } else if ((uptimeMillis() - mStartTime) >= mMaxDurationForRetryMs) {
1377                 // Otherwise, check whether we have reached timeout.
1378                 throw new ServiceSpecificException(StatusCode.TRY_AGAIN,
1379                         errorMessage(mOperation, mRequestValue,
1380                                 "cannot get property within " + mMaxDurationForRetryMs
1381                                 + "ms, last exception: " + e));
1382             }
1383             return invokeAction();
1384         }
1385     }
1386 
1387 
1388     /**
1389      * Queries HalPropValue with list of GetVehicleHalRequest objects.
1390      *
1391      * <p>This method gets the HalPropValue using async methods.
1392      */
getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests, VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback)1393     public void getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests,
1394             VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback) {
1395         mVehicleStub.getAsync(getVehicleStubAsyncRequests, getVehicleStubAsyncCallback);
1396     }
1397 
1398     /**
1399      * Sets vehicle property value asynchronously.
1400      */
setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests, VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback)1401     public void setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests,
1402             VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback) {
1403         mVehicleStub.setAsync(setVehicleStubAsyncRequests, setVehicleStubAsyncCallback);
1404     }
1405 
1406     /**
1407      * Cancels all the on-going async requests with the given request IDs.
1408      */
cancelRequests(List<Integer> vehicleStubRequestIds)1409     public void cancelRequests(List<Integer> vehicleStubRequestIds) {
1410         mVehicleStub.cancelRequests(vehicleStubRequestIds);
1411     }
1412 }
1413