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