• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.fakevhal;
18 
19 import static com.android.car.internal.property.CarPropertyErrorCodes.ERROR_CODES_INTERNAL;
20 import static com.android.car.internal.property.CarPropertyErrorCodes.ERROR_CODES_NOT_AVAILABLE;
21 import static com.android.car.internal.property.CarPropertyErrorCodes.createFromVhalStatusCode;
22 
23 import android.annotation.Nullable;
24 import android.car.builtin.util.Slogf;
25 import android.car.hardware.CarPropertyValue;
26 import android.hardware.automotive.vehicle.RawPropValues;
27 import android.hardware.automotive.vehicle.StatusCode;
28 import android.hardware.automotive.vehicle.VehicleArea;
29 import android.hardware.automotive.vehicle.VehiclePropValue;
30 import android.hardware.automotive.vehicle.VehiclePropertyAccess;
31 import android.hardware.automotive.vehicle.VehiclePropertyType;
32 import android.os.Handler;
33 import android.os.RemoteException;
34 import android.os.ServiceSpecificException;
35 import android.os.SystemClock;
36 import android.util.Pair;
37 import android.util.SparseArray;
38 
39 import com.android.car.CarLog;
40 import com.android.car.CarServiceUtils;
41 import com.android.car.IVehicleDeathRecipient;
42 import com.android.car.VehicleStub;
43 import com.android.car.hal.HalAreaConfig;
44 import com.android.car.hal.HalPropConfig;
45 import com.android.car.hal.HalPropValue;
46 import com.android.car.hal.HalPropValueBuilder;
47 import com.android.car.internal.property.CarPropertyErrorCodes;
48 import com.android.car.internal.util.IndentingPrintWriter;
49 import com.android.car.internal.util.PairSparseArray;
50 import com.android.internal.annotations.GuardedBy;
51 
52 import java.io.FileDescriptor;
53 import java.io.FileOutputStream;
54 import java.io.PrintWriter;
55 import java.util.ArrayList;
56 import java.util.List;
57 
58 public abstract class VehicleStubWrapper extends VehicleStub {
59 
60     private static final String TAG = CarLog.tagFor(VehicleStubWrapper.class);
61 
62     static final int AREA_ID_GLOBAL = 0;
63 
64     final SparseArray<HalPropConfig> mPropConfigsByPropId;
65     final VehicleStub mRealVehicle;
66     @GuardedBy("mLock")
67     private final PairSparseArray<HalPropValue> mPropValuesByPropIdAreaId;
68     final Handler mHandler;
69     private final Object mLock = new Object();
70 
VehicleStubWrapper(VehicleStub vehicleStub, Pair<SparseArray<HalPropConfig>, PairSparseArray<HalPropValue>> propConfigsByPropIdPropValuesByPropIdAreaIdPair)71     public VehicleStubWrapper(VehicleStub vehicleStub, Pair<SparseArray<HalPropConfig>,
72             PairSparseArray<HalPropValue>> propConfigsByPropIdPropValuesByPropIdAreaIdPair) {
73         mRealVehicle = vehicleStub;
74         mHandler = new Handler(CarServiceUtils.getHandlerThread(getClass().getSimpleName())
75                 .getLooper());
76         mPropConfigsByPropId = propConfigsByPropIdPropValuesByPropIdAreaIdPair.first;
77         mPropValuesByPropIdAreaId = propConfigsByPropIdPropValuesByPropIdAreaIdPair.second;
78     }
79 
80     /**
81      * Checks if a property is a global property.
82      *
83      * @param propId The property to be checked.
84      * @return {@code true} if this property is a global property.
85      */
isPropertyGlobal(int propId)86     /* package */ static boolean isPropertyGlobal(int propId) {
87         return (propId & VehicleArea.MASK) == VehicleArea.GLOBAL;
88     }
89 
90     /**
91      * Puts the {@code HalPropValue} based on the propId and areaId.
92      *
93      * @param propId The given propId
94      * @param areaId The given areaId
95      * @param halPropValue The given HalPropValue
96      */
putPropValue(int propId, int areaId, HalPropValue halPropValue)97     /* package */ void putPropValue(int propId, int areaId, HalPropValue halPropValue) {
98         synchronized (mLock) {
99             mPropValuesByPropIdAreaId.put(propId, areaId, halPropValue);
100         }
101     }
102 
103     /**
104      * Gets the current {@code HalPropValue} given the propId and areaId.
105      *
106      * @param propId The given propId
107      * @param areaId The Given areaId
108      * @return The HalPropValue if it exists.
109      */
110     @Nullable
getPropValue(int propId, int areaId)111     /* package */ HalPropValue getPropValue(int propId, int areaId) {
112         synchronized (mLock) {
113             return mPropValuesByPropIdAreaId.get(propId, areaId);
114         }
115     }
116 
117     /**
118      * Generates a list of all supported areaId for a certain property.
119      *
120      * @param propId The property to get all supported areaIds.
121      * @return A {@link List} of all supported areaId.
122      */
getAllSupportedAreaId(int propId)123     /* package */ List<Integer> getAllSupportedAreaId(int propId) {
124         return getAllSupportedAreaId(propId, mPropConfigsByPropId);
125     }
126 
127     /**
128      * Generates a list of all supported areaId for a certain property from the given configs by
129      * propId.
130      *
131      * @param propId The property to get all supported areaIds.
132      * @param allProperties The given configs by propId to get all supported areaIds for.
133      * @return A {@link List} of all supported areaId.
134      */
getAllSupportedAreaId(int propId, SparseArray<HalPropConfig> allProperties)135     /* package */ static List<Integer> getAllSupportedAreaId(int propId,
136             SparseArray<HalPropConfig> allProperties) {
137         List<Integer> allSupportedAreaId = new ArrayList<>();
138         HalAreaConfig[] areaConfigs = allProperties.get(propId).getAreaConfigs();
139         for (int i = 0; i < areaConfigs.length; i++) {
140             allSupportedAreaId.add(areaConfigs[i].getAreaId());
141         }
142         return allSupportedAreaId;
143     }
144 
145     /**
146      * Checks if a property is supported. If not, throw a {@link ServiceSpecificException}.
147      *
148      * @param propId The property to be checked.
149      */
checkPropIdSupported(int propId)150     /* package */ void checkPropIdSupported(int propId) {
151         // Check if the property config exists.
152         if (!mPropConfigsByPropId.contains(propId)) {
153             throw new ServiceSpecificException(StatusCode.INVALID_ARG, "The propId: " + propId
154                     + " is not supported.");
155         }
156     }
157 
158     /**
159      * Checks if an areaId of a property is supported.
160      *
161      * @param propId The property to be checked.
162      * @param areaId The area to be checked.
163      */
checkAreaIdSupported(int propId, int areaId)164     /* package */ void checkAreaIdSupported(int propId, int areaId) {
165         List<Integer> supportedAreaIds = getAllSupportedAreaId(propId);
166         // For global property, areaId will be ignored if the area config array is empty.
167         if ((isPropertyGlobal(propId) && supportedAreaIds.isEmpty())
168                 || supportedAreaIds.contains(areaId)) {
169             return;
170         }
171         throw new ServiceSpecificException(StatusCode.INVALID_ARG, "The areaId: " + areaId
172                 + " is not supported.");
173     }
174 
175     /**
176      * Gets the HalPropValue based on propId and areaId  with checks.
177      * @param propId The given propId
178      * @param areaId The given areaId
179      * @return The given HalPropValue.
180      */
getFakeHalPropValue(int propId, int areaId)181     /* package */ HalPropValue getFakeHalPropValue(int propId, int areaId) {
182         // PropId config exists but the value map doesn't have this propId, this may be caused by:
183         // 1. This property is a global property, and it doesn't have default prop value.
184         // 2. This property has area configs, and it has neither default prop value nor area value.
185         synchronized (mLock) {
186             HalPropValue halPropValue = mPropValuesByPropIdAreaId.get(propId, areaId);
187             if (halPropValue == null) {
188                 if (isPropertyGlobal(propId)) {
189                     throw new ServiceSpecificException(StatusCode.NOT_AVAILABLE,
190                             "propId: " + propId + " has no property value.");
191                 }
192                 throw new ServiceSpecificException(StatusCode.NOT_AVAILABLE,
193                         "propId: " + propId + ", areaId: " + areaId + " has no property value.");
194             }
195             return halPropValue;
196         }
197     }
198 
199     /**
200      * Gets the access of the given propId and areaId.
201      * @param propId The given propId.
202      * @param areaId The given areaId.
203      * @return The access type of the given propId areaId.
204      */
getAccess(int propId, int areaId)205     /* package */ int getAccess(int propId, int areaId) {
206         HalPropConfig halPropConfig = mPropConfigsByPropId.get(propId);
207         HalAreaConfig[] halAreaConfigs = halPropConfig.getAreaConfigs();
208         for (int i = 0; i < halAreaConfigs.length; i++) {
209             if (halAreaConfigs[i].getAreaId() != areaId) {
210                 continue;
211             }
212             int areaAccess = halAreaConfigs[i].getAccess();
213             if (areaAccess != VehiclePropertyAccess.NONE) {
214                 return areaAccess;
215             }
216             break;
217         }
218         return halPropConfig.getAccess();
219     }
220 
221     /**
222      * Checks if the set value is within the value range.
223      *
224      * @return {@code true} if set value is within the prop config range.
225      */
isWithinRange(int propId, int areaId, RawPropValues rawPropValues)226     /* package */ boolean isWithinRange(int propId, int areaId, RawPropValues rawPropValues) {
227         // For global property without areaId.
228         if (isPropertyGlobal(propId) && getAllSupportedAreaId(propId).isEmpty()) {
229             return true;
230         }
231 
232         // For non-global properties and global properties with areaIds.
233         int index = getAllSupportedAreaId(propId).indexOf(areaId);
234 
235         HalAreaConfig areaConfig = mPropConfigsByPropId.get(propId).getAreaConfigs()[index];
236 
237         int[] int32Values = rawPropValues.int32Values;
238         long[] int64Values = rawPropValues.int64Values;
239         float[] floatValues = rawPropValues.floatValues;
240         // If max and min values exists, then check the boundaries. If max and min values are all
241         // 0s, return true.
242         switch (getPropType(propId)) {
243             case VehiclePropertyType.INT32:
244             case VehiclePropertyType.INT32_VEC:
245                 int minInt32Value = areaConfig.getMinInt32Value();
246                 int maxInt32Value = areaConfig.getMaxInt32Value();
247                 if (minInt32Value != maxInt32Value || minInt32Value != 0) {
248                     for (int int32Value : int32Values) {
249                         if (int32Value > maxInt32Value || int32Value < minInt32Value) {
250                             Slogf.e(TAG, "For propId: %d, areaId: %d, the valid min value is: "
251                                             + "%d, max value is: %d, but the given value is: %d.",
252                                     propId, areaId, minInt32Value, maxInt32Value, int32Value);
253                             return false;
254                         }
255                     }
256                 }
257                 break;
258             case VehiclePropertyType.INT64:
259             case VehiclePropertyType.INT64_VEC:
260                 long minInt64Value = areaConfig.getMinInt64Value();
261                 long maxInt64Value = areaConfig.getMaxInt64Value();
262                 if (minInt64Value != maxInt64Value || minInt64Value != 0) {
263                     for (long int64Value : int64Values) {
264                         if (int64Value > maxInt64Value || int64Value < minInt64Value) {
265                             Slogf.e(TAG, "For propId: %d, areaId: %d, the valid min value is: "
266                                             + "%d, max value is: %d, but the given value is: %d.",
267                                     propId, areaId, minInt64Value, maxInt64Value, int64Value);
268                             return false;
269                         }
270                     }
271                 }
272                 break;
273             case VehiclePropertyType.FLOAT:
274             case VehiclePropertyType.FLOAT_VEC:
275                 float minFloatValue = areaConfig.getMinFloatValue();
276                 float maxFloatValue = areaConfig.getMaxFloatValue();
277                 if (minFloatValue != maxFloatValue || minFloatValue != 0) {
278                     for (float floatValue : floatValues) {
279                         if (floatValue > maxFloatValue || floatValue < minFloatValue) {
280                             Slogf.e(TAG, "For propId: %d, areaId: %d, the valid min value is: "
281                                             + "%f, max value is: %f, but the given value is: %d.",
282                                     propId, areaId, minFloatValue, maxFloatValue, floatValue);
283                             return false;
284                         }
285                     }
286                 }
287                 break;
288             default:
289                 Slogf.d(TAG, "Skip checking range for propId: %d because it is mixed type.",
290                         propId);
291         }
292         return true;
293     }
294 
295     /**
296      * Verifies the propId areaId has read access.
297      * @param propId The given propId.
298      * @param areaId The given areaId.
299      */
verifyReadAccess(int propId, int areaId)300     /* package */ void verifyReadAccess(int propId, int areaId) {
301         int access = getAccess(propId, areaId);
302         if (access != VehiclePropertyAccess.READ && access != VehiclePropertyAccess.READ_WRITE) {
303             throw new ServiceSpecificException(StatusCode.ACCESS_DENIED, "This property " + propId
304                     + " doesn't have read permission.");
305         }
306     }
307 
308     /**
309      * Verifies the propId areaId has write access.
310      * @param propId The given propId.
311      * @param areaId The given areaId.
312      */
verifyWriteAccess(int propId, int areaId)313     /* package */ void verifyWriteAccess(int propId, int areaId) {
314         int access = getAccess(propId, areaId);
315         if (access != VehiclePropertyAccess.WRITE && access != VehiclePropertyAccess.READ_WRITE) {
316             throw new ServiceSpecificException(StatusCode.ACCESS_DENIED, "This property " + propId
317                     + " doesn't have write permission.");
318         }
319     }
320 
321     /**
322      * Gets the type of property.
323      *
324      * @param propId The property to get the type.
325      * @return The type.
326      */
getPropType(int propId)327     /* package */  static int getPropType(int propId) {
328         return propId & VehiclePropertyType.MASK;
329     }
330 
331     /**
332      * Builds a {@link HalPropValue} from the given {@link HalPropValue} and checks if the
333      * raw values are within the allowed range.
334      *
335      * @param propValue the {@link HalPropValue} to build from
336      * @return the built {@link HalPropValue}
337      * @throws ServiceSpecificException if the raw values are not within the allowed range
338      */
buildRawPropValueAndCheckRange(HalPropValue propValue)339     /* package */ HalPropValue buildRawPropValueAndCheckRange(HalPropValue propValue) {
340         int propId = propValue.getPropId();
341         int areaId = propValue.getAreaId();
342         RawPropValues rawPropValues = ((VehiclePropValue) propValue.toVehiclePropValue()).value;
343 
344         // Check if the set values are within the value config range.
345         if (!isWithinRange(propId, areaId, rawPropValues)) {
346             throw new ServiceSpecificException(StatusCode.INVALID_ARG,
347                     "The set value is outside the range.");
348         }
349 
350         return buildHalPropValue(propId, areaId,
351                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
352     }
353 
354     /**
355      * Builds a {@link HalPropValue}.
356      *
357      * @param propId The propId of the prop value to be built.
358      * @param areaId The areaId of the prop value to be built.
359      * @param timestamp The elapsed time in nanoseconds when mPropConfigsByPropId is initialized.
360      * @param rawPropValues The {@link RawPropValues} contains property values.
361      * @return a {@link HalPropValue} built by propId, areaId, timestamp and value.
362      */
buildHalPropValue(int propId, int areaId, long timestamp, RawPropValues rawPropValues)363     /* package */ HalPropValue buildHalPropValue(int propId, int areaId, long timestamp,
364             RawPropValues rawPropValues) {
365         return buildHalPropValue(propId, areaId, timestamp, rawPropValues,
366                 getHalPropValueBuilder());
367     }
368 
369     /**
370      * Builds a {@link HalPropValue} from the given parameters using a {@link HalPropValueBuilder}.
371      *
372      * @param propId the property ID
373      * @param areaId the area ID
374      * @param timestamp the timestamp
375      * @param rawPropValues the raw property values
376      * @param halPropValueBuilder the {@link HalPropValueBuilder} to use
377      * @return the built {@link HalPropValue}
378      */
buildHalPropValue(int propId, int areaId, long timestamp, RawPropValues rawPropValues, HalPropValueBuilder halPropValueBuilder)379     /* package */ static HalPropValue buildHalPropValue(int propId, int areaId, long timestamp,
380             RawPropValues rawPropValues, HalPropValueBuilder halPropValueBuilder) {
381         VehiclePropValue propValue = new VehiclePropValue();
382         propValue.prop = propId;
383         propValue.areaId = areaId;
384         propValue.timestamp = timestamp;
385         propValue.value = rawPropValues;
386         return halPropValueBuilder.build(propValue);
387     }
388 
389     /**
390      * Builds a {@link HalPropValue} from the given parameters using a {@link HalPropValueBuilder}
391      *
392      */
buildHalPropValue(CarPropertyValue carPropertyValue, int halPropId, long timestamp)393     /* package */ HalPropValue buildHalPropValue(CarPropertyValue carPropertyValue,
394             int halPropId, long timestamp) {
395         return getHalPropValueBuilder().build(carPropertyValue, halPropId, timestamp,
396                 mPropConfigsByPropId.get(halPropId));
397     }
398 
399     /**
400      * Gets properties asynchronously.
401      *
402      * @param getVehicleStubAsyncRequests The async request list.
403      * @param getVehicleStubAsyncCallback The callback for getting property values.
404      */
405     @Override
getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests, VehicleStubCallbackInterface getVehicleStubAsyncCallback)406     public void getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests,
407             VehicleStubCallbackInterface getVehicleStubAsyncCallback) {
408         List<GetVehicleStubAsyncResult> onGetAsyncResultList = new ArrayList<>();
409         for (int i = 0; i < getVehicleStubAsyncRequests.size(); i++) {
410             AsyncGetSetRequest request = getVehicleStubAsyncRequests.get(i);
411             GetVehicleStubAsyncResult result;
412             try {
413                 HalPropValue halPropValue = get(request.getHalPropValue());
414                 result = new GetVehicleStubAsyncResult(request.getServiceRequestId(),
415                         halPropValue);
416                 if (halPropValue == null) {
417                     result = new GetVehicleStubAsyncResult(request.getServiceRequestId(),
418                             ERROR_CODES_NOT_AVAILABLE);
419                 }
420             } catch (ServiceSpecificException e) {
421                 CarPropertyErrorCodes carPropertyErrorCodes =
422                         createFromVhalStatusCode(e.errorCode);
423                 result = new GetVehicleStubAsyncResult(request.getServiceRequestId(),
424                         carPropertyErrorCodes);
425             } catch (RemoteException e) {
426                 result = new GetVehicleStubAsyncResult(request.getServiceRequestId(),
427                         ERROR_CODES_INTERNAL);
428             }
429             onGetAsyncResultList.add(result);
430         }
431         mHandler.post(() -> {
432             getVehicleStubAsyncCallback.onGetAsyncResults(onGetAsyncResultList);
433         });
434     }
435 
436     /**
437      * Sets properties asynchronously.
438      *
439      * @param setVehicleStubAsyncRequests The async request list.
440      * @param setVehicleStubAsyncCallback the callback for setting property values.
441      */
442     @Override
setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests, VehicleStubCallbackInterface setVehicleStubAsyncCallback)443     public void setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests,
444             VehicleStubCallbackInterface setVehicleStubAsyncCallback) {
445         List<SetVehicleStubAsyncResult> onSetAsyncResultsList = new ArrayList<>();
446         for (int i = 0; i < setVehicleStubAsyncRequests.size(); i++) {
447             AsyncGetSetRequest setRequest = setVehicleStubAsyncRequests.get(i);
448             int serviceRequestId = setRequest.getServiceRequestId();
449             SetVehicleStubAsyncResult result;
450             try {
451                 set(setRequest.getHalPropValue());
452                 result = new SetVehicleStubAsyncResult(serviceRequestId);
453             } catch (RemoteException e) {
454                 result = new SetVehicleStubAsyncResult(serviceRequestId,
455                         ERROR_CODES_INTERNAL);
456             } catch (ServiceSpecificException e) {
457                 CarPropertyErrorCodes carPropertyErrorCodes =
458                         createFromVhalStatusCode(e.errorCode);
459                 result = new SetVehicleStubAsyncResult(serviceRequestId, carPropertyErrorCodes);
460             }
461             onSetAsyncResultsList.add(result);
462         }
463         mHandler.post(() -> {
464             setVehicleStubAsyncCallback.onSetAsyncResults(onSetAsyncResultsList);
465         });
466     }
467 
468     /**
469      * Checks if FakeVehicleStub connects to a valid Vhal.
470      *
471      * @return {@code true} if connects to a valid Vhal.
472      */
473     @Override
isValid()474     public boolean isValid() {
475         return mRealVehicle.isValid();
476     }
477 
478     /**
479      * Registers a death recipient that would be called when Vhal died.
480      *
481      * @param recipient A death recipient.
482      * @throws IllegalStateException If unable to register the death recipient.
483      */
484     @Override
linkToDeath(IVehicleDeathRecipient recipient)485     public void linkToDeath(IVehicleDeathRecipient recipient) throws IllegalStateException {
486         mRealVehicle.linkToDeath(recipient);
487     }
488 
489     /**
490      * Unlinks a previously linked death recipient.
491      *
492      * @param recipient A previously linked death recipient.
493      */
494     @Override
unlinkToDeath(IVehicleDeathRecipient recipient)495     public void unlinkToDeath(IVehicleDeathRecipient recipient) {
496         mRealVehicle.unlinkToDeath(recipient);
497     }
498 
499     /**
500      * @return {@code true} if car service is connected to FakeVehicleStub.
501      */
502     @Override
isFakeModeEnabled()503     public boolean isFakeModeEnabled() {
504         return false;
505     }
506 
507     @Override
dump(FileDescriptor fd, List<String> args)508     public void dump(FileDescriptor fd, List<String> args) throws RemoteException,
509             ServiceSpecificException {
510         IndentingPrintWriter writer = new IndentingPrintWriter(new PrintWriter(
511                 new FileOutputStream(fd)));
512         synchronized (mLock) {
513             writer.println("Fake values: ");
514             writer.increaseIndent();
515             for (int i = 0; i < mPropValuesByPropIdAreaId.size(); i++) {
516                 HalPropValue propValue = mPropValuesByPropIdAreaId.valueAt(i);
517                 writer.println("HalPropValue: " + propValue);
518             }
519             writer.decreaseIndent();
520         }
521         mRealVehicle.dump(fd, args);
522     }
523 
524     /**
525      * @return The real vehicle stub.
526      */
527     @Override
getRealVehicleStub()528     public VehicleStub getRealVehicleStub() {
529         return mRealVehicle;
530     }
531 }
532