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