【Android】VehiclePropertyAccess引起CarService崩溃
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
@Override
public 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 propId
Slog.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 listener
Client client = mClientMap.get(listenerBinder);
if (client == null) {
client = new Client(listener);
}
client.addProperty(propId, rate);
// Insert the client into the propId --> clients map
List<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 necessary
if (!mListenerIsSet) {
mHal.setListener(this);
}
// Set the new rate
if (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;
}
}