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