VehiclePropertyAccess引起CarService崩潰
VehiclePropertyAccess
VehiclePropertyAccess屬性,用于定義車輛屬性的訪問權限。權限包括
- 讀:READ,只可以讀取,不能寫入。
VehiclePropertyAccess:READ
- 寫:WRITE,只可以寫入,不能讀取。
VehiclePropertyAccess:WRITE
- 讀寫
VehiclePropertyAccess:READ_WRITE
這些車輛屬性被定義在Vechile的types.hal中。編譯時,會被轉成VehiclPropConfig,記錄到每個車輛屬性中。
對于車輛屬性的操作,在Android11版本,調用CarService注冊監聽屬性,如果違反了其權限規定,會導致CarService崩潰。
原生CarService因為屬性注冊崩潰
違反VehiclePropertyAccess權限,導致的CarService崩潰
某應用調用 CarPropertyManager的registerCallback接口,注冊監聽屬性ID。該操作,導致CarService反復崩潰。
崩潰代碼流程分析
- CarPropertyService.java,應用調用registerListener注冊監聽屬性ID
@Overridepublic void registerListener(int propertyId, float updateRateHz,ICarPropertyEventListener iCarPropertyEventListener) throws IllegalArgumentException{}
- CarPropertyService.java,服務端對應registerListener的實現
@Override
public void registerListener(int propId, float rate, ICarPropertyEventListener listener)
{if (DBG) {Slog.d(TAG, "registerListener: propId=0x" + toHexString(propId) + " rate=" + rate);}if (listener == null) {Slog.e(TAG, "registerListener: Listener is null.");throw new IllegalArgumentException("listener cannot be null.");}IBinder listenerBinder = listener.asBinder();CarPropertyConfig propertyConfig;Client finalClient;synchronized (mLock) {propertyConfig = mConfigs.get(propId);if (propertyConfig == null) {// Do not attempt to register an invalid propIdSlog.e(TAG, "registerListener: propId is not in config list: 0x" + toHexString(propId));return;}ICarImpl.assertPermission(mContext, mHal.getReadPermission(propId));// Get or create the client for this listenerClient client = mClientMap.get(listenerBinder);if (client == null) {client = new Client(listener);}client.addProperty(propId, rate);// Insert the client into the propId --> clients mapList<Client> clients = mPropIdClientMap.get(propId);if (clients == null) {clients = new CopyOnWriteArrayList<Client>();mPropIdClientMap.put(propId, clients);}if (!clients.contains(client)) {clients.add(client);}// Set the HAL listener if necessaryif (!mListenerIsSet) {mHal.setListener(this);}// Set the new rateif (rate > mHal.getSampleRate(propId)) {mHal.subscribeProperty(propId, rate);}finalClient = client;}// propertyConfig and client are NonNull.mHandler.post(() ->getAndDispatchPropertyInitValue(propertyConfig, finalClient));
}
- 兩個主要流程:注冊屬性,獲取屬性初始值并派發
public void subscribeProperty(HalServiceBase service, int property,float samplingRateHz, int flags) throws IllegalArgumentException {if (DBG) {Slog.i(CarLog.TAG_HAL, "subscribeProperty, service:" + service+ ", " + toCarPropertyLog(property));}VehiclePropConfig config;synchronized (mLock) {config = mAllProperties.get(property);}if (config == null) {throw new IllegalArgumentException("subscribe error: config is null for property 0x"+ toHexString(property));} else if (isPropertySubscribable(config)) {SubscribeOptions opts = new SubscribeOptions();opts.propId = property;opts.sampleRate = samplingRateHz;opts.flags = flags;synchronized (mLock) {assertServiceOwnerLocked(service, property);mSubscribedProperties.put(property, opts);}try {mHalClient.subscribe(opts);} catch (RemoteException e) {Slog.e(CarLog.TAG_HAL, "Failed to subscribe to " + toCarPropertyLog(property), e);}} else {Slog.e(CarLog.TAG_HAL, "Cannot subscribe to " + toCarPropertyLog(property));}
}
- VehicleHal.java, isPropertySubscribable判斷車輛屬性是否可讀,如果不可讀不能注冊。比如屬性ID是"VehiclePropertyAccess:READ",就不能注冊了。
static boolean isPropertySubscribable(VehiclePropConfig config) {if ((config.access & VehiclePropertyAccess.READ) == 0|| (config.changeMode == VehiclePropertyChangeMode.STATIC)) {return false;}return true;
}
- 接下來,獲取屬性初始值。哪怕不能注冊的屬性ID,也會去獲取,所以導致了上面的CarService崩潰問題。CarPropertyService.java
private void getAndDispatchPropertyInitValue(CarPropertyConfig config, Client client) { List<CarPropertyEvent> events = new LinkedList<>();int propId = config.getPropertyId();if (config.isGlobalProperty()) {CarPropertyValue value = mHal.getPropertySafe(propId, 0);if (value != null) {CarPropertyEvent event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);events.add(event);}} else {for (int areaId : config.getAreaIds()) {CarPropertyValue value = mHal.getPropertySafe(propId, areaId);if (value != null) {CarPropertyEvent event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);events.add(event);}}}try {client.getListener().onEvent(events);} catch (RemoteException ex) {// If we cannot send a record, its likely the connection snapped. Let the binder// death handle the situation.Slog.e(TAG, "onEvent calling failed: " + ex);}
}
- HalClient.java,getValue,獲取屬性ID的值。
VehiclePropValue getValue(VehiclePropValue requestedPropValue) {final ObjectWrapper<VehiclePropValue> valueWrapper = new ObjectWrapper<>();int status = invokeRetriable(() -> {ValueResult res = internalGet(requestedPropValue);valueWrapper.object = res.propValue;return res.status;}, mWaitCapMs, mSleepMs);if (StatusCode.INVALID_ARG == status) {throw new IllegalArgumentException(getValueErrorMessage("get", requestedPropValue));}if (StatusCode.OK != status || valueWrapper.object == null) {// If valueWrapper.object is null and status is StatusCode.Ok, change the status to be// NOT_AVAILABLE.if (StatusCode.OK == status) {status = StatusCode.NOT_AVAILABLE;}Log.e(TAG, getPropertyErrorMessage("get", requestedPropValue, status));throw new ServiceSpecificException(status,"Failed to get property: 0x" + Integer.toHexString(requestedPropValue.prop)+ " in areaId: 0x" + Integer.toHexString(requestedPropValue.areaId));}return valueWrapper.object;
}
- 如果是VehiclePropertyAccess:READ的屬性,上述代碼會拋出ServiceSpecificException異常。導致CarService崩潰。code 4,ACCESS_DENIED。
Android12修復方式
android12已修復該問題。使用了getPropertySafe,捕獲異常。
/*** Return property or null if property is not ready yet or there is an exception in HAL.*/
@Nullable
public CarPropertyValue getPropertySafe(int mgrPropId, int areaId) {
try {return getProperty(mgrPropId, areaId);} catch (Exception e) {Slog.e(TAG, "get property value failed for property id: 0x "+ toHexString(mgrPropId) + " area id: 0x" + toHexString(areaId)+ " exception: " + e);return null;}
}