• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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;
18 
19 import static com.android.car.CarServiceUtils.subscribeOptionsToHidl;
20 import static com.android.car.internal.property.CarPropertyErrorCodes.createFromVhalStatusCode;
21 
22 import android.annotation.Nullable;
23 import android.car.builtin.util.Slogf;
24 import android.hardware.automotive.vehicle.SubscribeOptions;
25 import android.hardware.automotive.vehicle.V2_0.IVehicle;
26 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
27 import android.hardware.automotive.vehicle.V2_0.StatusCode;
28 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
29 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
30 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyStatus;
31 import android.hardware.automotive.vehicle.VehiclePropError;
32 import android.os.NativeHandle;
33 import android.os.RemoteException;
34 import android.os.ServiceSpecificException;
35 import android.os.SystemProperties;
36 
37 import com.android.car.hal.HalPropConfig;
38 import com.android.car.hal.HalPropValue;
39 import com.android.car.hal.HalPropValueBuilder;
40 import com.android.car.hal.HidlHalPropConfig;
41 import com.android.car.hal.VehicleHalCallback;
42 import com.android.car.internal.property.CarPropertyErrorCodes;
43 import com.android.car.internal.property.PropIdAreaId;
44 import com.android.internal.annotations.VisibleForTesting;
45 
46 import java.io.FileDescriptor;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.NoSuchElementException;
50 import java.util.concurrent.Executor;
51 import java.util.concurrent.Executors;
52 
53 final class HidlVehicleStub extends VehicleStub {
54 
55     private static final String TAG = CarLog.tagFor(HidlVehicleStub.class);
56 
57     // The property ID for "SUPPORTED_PROPRETY_IDS". This is the same as SUPPORTED_PROPERTY_IDS as
58     // defined in
59     // {@code platform/hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl}.
60     private static final int VHAL_PROP_SUPPORTED_PROPERTY_IDS = 0x11410F48;
61 
62     private final IVehicle mHidlVehicle;
63     private final HalPropValueBuilder mPropValueBuilder;
64     private final Executor mExecutor = Executors.newFixedThreadPool(5);
65 
HidlVehicleStub()66     HidlVehicleStub() {
67         this(getHidlVehicle());
68     }
69 
70     @VisibleForTesting
HidlVehicleStub(IVehicle hidlVehicle)71     HidlVehicleStub(IVehicle hidlVehicle) {
72         mHidlVehicle = hidlVehicle;
73         mPropValueBuilder = new HalPropValueBuilder(/*isAidl=*/false);
74     }
75 
76     /**
77      * Checks whether we are connected to AIDL VHAL: {@code true} or HIDL VHAL: {@code false}.
78      */
79     @Override
isAidlVhal()80     public boolean isAidlVhal() {
81         return false;
82     }
83 
84     /**
85      * Gets a HalPropValueBuilder that could be used to build a HalPropValue.
86      *
87      * @return a builder to build HalPropValue.
88      */
89     @Override
getHalPropValueBuilder()90     public HalPropValueBuilder getHalPropValueBuilder() {
91         return mPropValueBuilder;
92     }
93 
94     /**
95      * Returns whether this vehicle stub is connecting to a valid vehicle HAL.
96      *
97      * @return Whether this vehicle stub is connecting to a valid vehicle HAL.
98      */
99     @Override
isValid()100     public boolean isValid() {
101         return mHidlVehicle != null;
102     }
103 
104     /**
105      * Gets the interface descriptor for the connecting vehicle HAL.
106      *
107      * @return the interface descriptor.
108      * @throws IllegalStateException If unable to get the descriptor.
109      */
110     @Override
getInterfaceDescriptor()111     public String getInterfaceDescriptor() throws IllegalStateException {
112         try {
113             return mHidlVehicle.interfaceDescriptor();
114         } catch (RemoteException e) {
115             throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e);
116         }
117     }
118 
119     /**
120      * Registers a death recipient that would be called when vehicle HAL died.
121      *
122      * @param recipient A death recipient.
123      * @throws IllegalStateException If unable to register the death recipient.
124      */
125     @Override
linkToDeath(IVehicleDeathRecipient recipient)126     public void linkToDeath(IVehicleDeathRecipient recipient) throws IllegalStateException {
127         try {
128             mHidlVehicle.linkToDeath(recipient, /* cookie= */ 0);
129         } catch (RemoteException e) {
130             throw new IllegalStateException("Failed to linkToDeath Vehicle HAL");
131         }
132     }
133 
134     /**
135      * Unlinks a previously linked death recipient.
136      *
137      * @param recipient A previously linked death recipient.
138      */
139     @Override
unlinkToDeath(IVehicleDeathRecipient recipient)140     public void unlinkToDeath(IVehicleDeathRecipient recipient) {
141         try {
142             mHidlVehicle.unlinkToDeath(recipient);
143         } catch (RemoteException ignored) {
144             // Ignore errors on shutdown path.
145         }
146     }
147 
148     /**
149      * Gets all property configs.
150      *
151      * @return All the property configs.
152      */
153     @Override
getAllPropConfigs()154     public HalPropConfig[] getAllPropConfigs() throws RemoteException {
155         ArrayList<VehiclePropConfig> configForSupportedProps;
156         try {
157             configForSupportedProps = getPropConfigs(new ArrayList<>(
158                     List.of(VHAL_PROP_SUPPORTED_PROPERTY_IDS)));
159         } catch (Exception e) {
160             Slogf.d(TAG, "Use getAllPropConfigs to fetch all property configs");
161 
162             // If the VHAL_PROP_SUPPORTED_PROPERTY_IDS is not supported, fallback to normal API.
163             return vehiclePropConfigsToHalPropConfigs(mHidlVehicle.getAllPropConfigs());
164         }
165 
166         if (configForSupportedProps.size() == 0) {
167             Slogf.w(TAG, "getPropConfigs[VHAL_PROP_SUPPORTED_IDS] returns 0 config"
168                     + "assume it is not supported, fall back to getAllPropConfigs.");
169             return vehiclePropConfigsToHalPropConfigs(mHidlVehicle.getAllPropConfigs());
170         }
171 
172         // If the VHAL_PROP_SUPPORTED_PROPERTY_IDS is supported, VHAL has
173         // too many property configs that cannot be returned in getAllPropConfigs() in one binder
174         // transaction.
175         // We need to get the property list and then divide the list into smaller requests.
176         Slogf.d(TAG, "VHAL_PROP_SUPPORTED_PROPERTY_IDS is supported, "
177                 + "use multiple getPropConfigs to fetch all property configs");
178 
179         return getAllPropConfigsThroughMultipleRequests(configForSupportedProps.get(0));
180     }
181 
182     /**
183      * Gets a new {@code SubscriptionClient} that could be used to subscribe/unsubscribe.
184      *
185      * @param callback A callback that could be used to receive events.
186      * @return a {@code SubscriptionClient} that could be used to subscribe/unsubscribe.
187      */
188     @Override
newSubscriptionClient(VehicleHalCallback callback)189     public SubscriptionClient newSubscriptionClient(VehicleHalCallback callback) {
190         return new HidlSubscriptionClient(callback, mPropValueBuilder);
191     }
192 
193     private static class GetValueResult {
194         public int status;
195         public VehiclePropValue value;
196     }
197 
198     /**
199      * Gets a property.
200      *
201      * @param requestedPropValue The property to get.
202      * @return The vehicle property value.
203      * @throws RemoteException if the remote operation fails.
204      * @throws ServiceSpecificException if VHAL returns service specific error.
205      */
206     @Override
207     @Nullable
get(HalPropValue requestedPropValue)208     public HalPropValue get(HalPropValue requestedPropValue)
209             throws RemoteException, ServiceSpecificException {
210         VehiclePropValue hidlPropValue = (VehiclePropValue) requestedPropValue.toVehiclePropValue();
211         GetValueResult result = new GetValueResult();
212         mHidlVehicle.get(
213                 hidlPropValue,
214                 (s, p) -> {
215                     result.status = s;
216                     result.value = p;
217                 });
218 
219         if (result.status != android.hardware.automotive.vehicle.V2_0.StatusCode.OK) {
220             throw new ServiceSpecificException(
221                     result.status,
222                     "failed to get value for property: " + Integer.toString(hidlPropValue.prop));
223         }
224 
225         if (result.value == null) {
226             return null;
227         }
228 
229         return getHalPropValueBuilder().build(result.value);
230     }
231 
232     @Override
getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests, VehicleStubCallbackInterface getVehicleStubAsyncCallback)233     public void getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests,
234             VehicleStubCallbackInterface getVehicleStubAsyncCallback) {
235         mExecutor.execute(() -> {
236             for (int i = 0; i < getVehicleStubAsyncRequests.size(); i++) {
237                 AsyncGetSetRequest getVehicleStubAsyncRequest = getVehicleStubAsyncRequests.get(i);
238                 int serviceRequestId = getVehicleStubAsyncRequest.getServiceRequestId();
239                 HalPropValue halPropValue;
240                 try {
241                     halPropValue = get(getVehicleStubAsyncRequest.getHalPropValue());
242                 } catch (ServiceSpecificException e) {
243                     CarPropertyErrorCodes carPropertyErrorCodes =
244                             createFromVhalStatusCode(e.errorCode);
245                     callGetAsyncErrorCallback(carPropertyErrorCodes, serviceRequestId,
246                             getVehicleStubAsyncCallback);
247                     continue;
248                 } catch (RemoteException e) {
249                     Slogf.w(CarLog.TAG_SERVICE,
250                             "Received RemoteException from VHAL. VHAL is likely dead.");
251                     callGetAsyncErrorCallback(
252                             CarPropertyErrorCodes.ERROR_CODES_INTERNAL,
253                             serviceRequestId, getVehicleStubAsyncCallback);
254                     continue;
255                 }
256 
257                 if (halPropValue == null) {
258                     callGetAsyncErrorCallback(
259                             CarPropertyErrorCodes.ERROR_CODES_NOT_AVAILABLE,
260                             serviceRequestId, getVehicleStubAsyncCallback);
261                     continue;
262                 }
263 
264                 getVehicleStubAsyncCallback.onGetAsyncResults(
265                         List.of(new GetVehicleStubAsyncResult(serviceRequestId, halPropValue)));
266             }
267         });
268     }
269 
270     @Override
setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests, VehicleStubCallbackInterface setVehicleStubAsyncCallback)271     public void setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests,
272             VehicleStubCallbackInterface setVehicleStubAsyncCallback) {
273         mExecutor.execute(() -> {
274             for (int i = 0; i < setVehicleStubAsyncRequests.size(); i++) {
275                 AsyncGetSetRequest setVehicleStubAsyncRequest = setVehicleStubAsyncRequests.get(i);
276                 int serviceRequestId = setVehicleStubAsyncRequest.getServiceRequestId();
277                 try {
278                     set(setVehicleStubAsyncRequest.getHalPropValue());
279                     setVehicleStubAsyncCallback.onSetAsyncResults(
280                             List.of(new SetVehicleStubAsyncResult(serviceRequestId)));
281                 } catch (ServiceSpecificException e) {
282                     CarPropertyErrorCodes carPropertyErrorCodes =
283                             createFromVhalStatusCode(e.errorCode);
284                     callSetAsyncErrorCallback(
285                             carPropertyErrorCodes,
286                             serviceRequestId,
287                             setVehicleStubAsyncCallback);
288                 } catch (RemoteException e) {
289                     Slogf.w(CarLog.TAG_SERVICE,
290                             "Received RemoteException from VHAL. VHAL is likely dead.");
291                     callSetAsyncErrorCallback(
292                             CarPropertyErrorCodes.ERROR_CODES_INTERNAL,
293                             serviceRequestId, setVehicleStubAsyncCallback);
294                 }
295             }
296         });
297     }
298 
callGetAsyncErrorCallback(CarPropertyErrorCodes errorCodes, int serviceRequestId, VehicleStubCallbackInterface callback)299     private void callGetAsyncErrorCallback(CarPropertyErrorCodes errorCodes, int serviceRequestId,
300             VehicleStubCallbackInterface callback) {
301         callback.onGetAsyncResults(
302                 List.of(new GetVehicleStubAsyncResult(serviceRequestId, errorCodes)));
303     }
304 
callSetAsyncErrorCallback(CarPropertyErrorCodes errorCodes, int serviceRequestId, VehicleStubCallbackInterface callback)305     private void callSetAsyncErrorCallback(CarPropertyErrorCodes errorCodes, int serviceRequestId,
306             VehicleStubCallbackInterface callback) {
307         callback.onSetAsyncResults(
308                 List.of(new SetVehicleStubAsyncResult(serviceRequestId, errorCodes)));
309     }
310 
311     /**
312      * Sets a property.
313      *
314      * @param propValue The property to set.
315      * @throws RemoteException if the remote operation fails.
316      * @throws ServiceSpecificException if VHAL returns service specific error.
317      */
318     @Override
set(HalPropValue propValue)319     public void set(HalPropValue propValue) throws RemoteException {
320         VehiclePropValue hidlPropValue = (VehiclePropValue) propValue.toVehiclePropValue();
321         int status = mHidlVehicle.set(hidlPropValue);
322         if (status != StatusCode.OK) {
323             throw new ServiceSpecificException(status, "failed to set value for property: "
324                     + Integer.toString(hidlPropValue.prop));
325         }
326     }
327 
328     @Override
dump(FileDescriptor fd, List<String> args)329     public void dump(FileDescriptor fd, List<String> args) throws RemoteException {
330         mHidlVehicle.debug(new NativeHandle(fd, /* own= */ false), new ArrayList<String>(args));
331     }
332 
333     @Nullable
getHidlVehicle()334     private static IVehicle getHidlVehicle() {
335         String instanceName = SystemProperties.get("ro.vehicle.hal", "default");
336 
337         try {
338             // Wait for HIDL VHAL to be ready if it is declared.
339             return IVehicle.getService(instanceName, /* retry= */ true);
340         } catch (RemoteException e) {
341             Slogf.e(TAG, e, "Failed to get IVehicle/" + instanceName + " service");
342         } catch (NoSuchElementException e) {
343             Slogf.e(TAG, "IVehicle/" + instanceName + " service not registered yet");
344         }
345         return null;
346     }
347 
348     private class HidlSubscriptionClient extends IVehicleCallback.Stub
349             implements SubscriptionClient {
350         private final VehicleHalCallback mCallback;
351         private final HalPropValueBuilder mBuilder;
352 
HidlSubscriptionClient(VehicleHalCallback callback, HalPropValueBuilder builder)353         HidlSubscriptionClient(VehicleHalCallback callback, HalPropValueBuilder builder) {
354             mCallback = callback;
355             mBuilder = builder;
356         }
357 
358         @Override
onPropertyEvent(ArrayList<VehiclePropValue> propValues)359         public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
360             ArrayList<HalPropValue> values = new ArrayList<>();
361             for (VehiclePropValue value : propValues) {
362                 values.add(mBuilder.build(value));
363             }
364             mCallback.onPropertyEvent(values);
365         }
366 
367         @Override
onPropertySet(VehiclePropValue propValue)368         public void onPropertySet(VehiclePropValue propValue) {
369             // Deprecated, do nothing.
370         }
371 
372         @Override
onPropertySetError(int errorCode, int propId, int areaId)373         public void onPropertySetError(int errorCode, int propId, int areaId) {
374             VehiclePropError error = new VehiclePropError();
375             error.propId = propId;
376             error.areaId = areaId;
377             error.errorCode = errorCode;
378             ArrayList<VehiclePropError> errors = new ArrayList<VehiclePropError>();
379             errors.add(error);
380             mCallback.onPropertySetError(errors);
381         }
382 
383         @Override
subscribe(SubscribeOptions[] options)384         public void subscribe(SubscribeOptions[] options) throws RemoteException {
385             ArrayList<android.hardware.automotive.vehicle.V2_0.SubscribeOptions> hidlOptions =
386                     new ArrayList<>();
387             for (SubscribeOptions option : options) {
388                 hidlOptions.add(subscribeOptionsToHidl(option));
389             }
390             mHidlVehicle.subscribe(this, hidlOptions);
391         }
392 
393         @Override
unsubscribe(int prop)394         public void unsubscribe(int prop) throws RemoteException {
395             mHidlVehicle.unsubscribe(this, prop);
396         }
397 
398         @Override
registerSupportedValuesChange(List<PropIdAreaId> propIdAreaIds)399         public void registerSupportedValuesChange(List<PropIdAreaId> propIdAreaIds) {
400             // Do nothing.
401             return;
402         }
403 
404         @Override
unregisterSupportedValuesChange(List<PropIdAreaId> propIdAreaIds)405         public void unregisterSupportedValuesChange(List<PropIdAreaId> propIdAreaIds) {
406             // Do nothing.
407             return;
408         }
409     }
410 
vehiclePropConfigsToHalPropConfigs( List<VehiclePropConfig> hidlConfigs)411     private static HalPropConfig[] vehiclePropConfigsToHalPropConfigs(
412             List<VehiclePropConfig> hidlConfigs) {
413         int configSize = hidlConfigs.size();
414         HalPropConfig[] configs = new HalPropConfig[configSize];
415         for (int i = 0; i < configSize; i++) {
416             configs[i] = new HidlHalPropConfig(hidlConfigs.get(i));
417         }
418         return configs;
419     }
420 
421     private static final class GetPropConfigsResult {
422         public int status;
423         public ArrayList<VehiclePropConfig> propConfigs;
424     }
425 
getAllPropConfigsThroughMultipleRequests( VehiclePropConfig configForSupportedProps)426     private HalPropConfig[] getAllPropConfigsThroughMultipleRequests(
427             VehiclePropConfig configForSupportedProps)
428             throws RemoteException, ServiceSpecificException {
429         if (configForSupportedProps.configArray.size() < 1) {
430             throw new IllegalArgumentException(
431                     "VHAL Property: SUPPORTED_PROPERTY_IDS must have one element: "
432                     + "[num_of_configs_per_request] in the config array");
433         }
434 
435         int numConfigsPerRequest = configForSupportedProps.configArray.get(0);
436         if (numConfigsPerRequest <= 0) {
437             throw new IllegalArgumentException("Number of configs per request must be > 0");
438         }
439         HalPropValue propIdsRequestValue = mPropValueBuilder.build(
440                 VHAL_PROP_SUPPORTED_PROPERTY_IDS, /* areaId= */ 0);
441         HalPropValue propIdsResultValue;
442         try {
443             propIdsResultValue = get(propIdsRequestValue);
444         } catch (Exception e) {
445             Slogf.e(TAG, e, "failed to get SUPPORTED_PROPRETY_IDS");
446             throw e;
447         }
448         int status = propIdsResultValue.getStatus();
449         if (status != VehiclePropertyStatus.AVAILABLE) {
450             throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
451                     "got non-okay status: "  + StatusCode.toString(status)
452                     + " for SUPPORTED_PROPERTY_IDS");
453         }
454         int propCount = propIdsResultValue.getInt32ValuesSize();
455         ArrayList<VehiclePropConfig> allConfigs = new ArrayList<>();
456         ArrayList<Integer> requestPropIds = new ArrayList<Integer>();
457         for (int i = 0; i < propCount; i++) {
458             requestPropIds.add(propIdsResultValue.getInt32Value(i));
459             if (requestPropIds.size() == numConfigsPerRequest || (i + 1) == propCount) {
460                 ArrayList<VehiclePropConfig> subConfigs = getPropConfigs(requestPropIds);
461                 allConfigs.addAll(subConfigs);
462                 requestPropIds.clear();
463             }
464         }
465         return vehiclePropConfigsToHalPropConfigs(allConfigs);
466     }
467 
getPropConfigs(ArrayList<Integer> propIds)468     private ArrayList<VehiclePropConfig> getPropConfigs(ArrayList<Integer> propIds)
469             throws RemoteException {
470         GetPropConfigsResult result = new GetPropConfigsResult();
471         mHidlVehicle.getPropConfigs(propIds,
472                 (status, propConfigs) -> {
473                     result.status = status;
474                     result.propConfigs = propConfigs;
475                 });
476         if (result.status != StatusCode.OK) {
477             throw new IllegalArgumentException("Part of the property IDs: " + propIds
478                     + " is not supported");
479         }
480         return result.propConfigs;
481     }
482 }
483