背景
最近公司项目基线切换到Android14 AAOS,应用层业务涉及车控相关比较多,本着对自己负责的态度,我们得了解下Android14 CarService的相关流程,所谓"知己知彼,百战不殆",接下来,让我们一层层揭开CarService的面纱。
CarService启动流程
Android作为车载主流娱乐操作系统之一,CarService是系统中除了AMS、PMS、WMS等核心服务之外最为重要的服务了,汽车相关属性控制都是通过这个服务来操控的。既然CarService和其他Android核心服务同属一个级别,那么FW层面的流程我们就直接从SystemServer开始梳理了,分析前我们先看一下CarService启动的时序,窥探一下全貌。
到这里SystemServer启动CarService服务,最终拿到vhal层IVehicle2.0对象,大致流程基本清晰了,接下来我们结合代码一起看下细节部分,从SystemServer入手:
js
// com.android.server.SystemServer
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
// ...
// 还是先判断是否具有automotiove特性,是否具备某个硬件特性定义在文件
// frameworks/native/data/etc/car_core_hardware.xml中,
//<permissions>
// <feature name="android.hardware.type.automotive" />
// ...
boolean isAutomotive = mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
if (isAutomotive) {
// CAR_SERVICE_HELPER_SERVICE_CLASS = "com.android.internal.car.CarServiceHelperService";
final SystemService cshs = mSystemServiceManager
.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
}
//...
}
// com.android.internal.car.CarServiceHelperService
// 这个类是,车辆服务的系统服务端配套服务。我们可以把他理解成一个管家,
// 用他来启动汽车相关服务并为汽车服务提供必要的API。
// 具体实现在 CarServiceHelperServiceUpdatableImpl中
@Override
public void onStart() {
// 启动Carservice
// CAR_SERVICE_PACKAGE = "android.car.ICar"
// CAR_SERVICE_PACKAGE = "com.android.car"
// 这里绑定了包名为com.android.car,action 为com.android.car的服务
Intent intent = new Intent(CAR_SERVICE_INTERFACE).setPackage(CAR_SERVICE_PACKAGE);
if (!userContext.bindService(intent, Context.BIND_AUTO_CREATE, this,mCarServiceConnection)) {
Slogf.wtf(TAG, "cannot start car service");
}
// ...
}
对应的服务配置的xml文件在/packages/services/Car/service-builtin/AndroidManifest.xml中,可见启动的这服务就是CarService
js
<service android:name=".CarService"
android:singleUser="true"
android:exported="true">
<intent-filter>
<action android:name="android.car.ICar"/>
</intent-filter>
</service>
启动CarService后,我们可以看到,这里使用了一个服务代理取启动真正的服务实现者,这部分代码涉及到一个Service构造方法什么时候调用的概念,因为在CarService构造方法中,把真正需要启动的服务传递给代理服务做启动
js
/** Proxy service for CarServciceImpl */
public class CarService extends ServiceProxy {
public CarService() {
// CAR_SERVICE_IMPL_CLASS = "com.android.car.CarServiceImpl" 把CarService具体实现交给代理服务初始化
super(UpdatablePackageDependency.CAR_SERVICE_IMPL_CLASS);
// ...
}
}
由于平时我们继承Service时,很少写构造方法,一般都是在OnCreate()中初始化,顾这里我们这里稍微展开下Service构造函数调用时机:
js
当调用 Context.startService() 后,最终系统会通过反射调用 Service 类的无参构造方法来创建 Service 实例,大致流程为:
1. 经过 ContextImpl.startService() → ActivityManagerService(AMS) → 进程间 Binder 调用 → 最终到 SystemServer 里的AMS.handleStartService()。
2. AMS 里会根据 ServiceRecord 记录管理所有 Service,决定是否要创建新进程或复用现有进程。AMS 发现你的 Service 所属进程已在运行(比如是同个 app 进程),就调用 ApplicationThread.scheduleCreateService(),通知目标进程的主线程去创建 Service 实例。
具体代码如下:
// android.app.ActivityThread.java
private void handleCreateService(CreateServiceData data) {
// 1. 拿到 Service 的 ComponentName 和 className
LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
// 2. 通过 AppComponentFactory 反射创建 Service 实例
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
// 关键:调用 AppComponentFactory.instantiateService
service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
}
}
// ..后续调用onCreate()
service.attach(context, this, data.info.name, data.token, app,ActivityManager.getService());
service.onCreate();
}
// android.app.AppComponentFactory.java
public @NonNull Service instantiateService(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent) {
// 看到了我们熟悉的newInstance了吧,这里就调用了无参的构造函数了,
return (Service) cl.loadClass(className).newInstance();
}
好,在了解清楚构造方法调用时机后,我们接着看父类ServiceProxy的代码实现:
js
@Override
public void onCreate() {
// 负责完成mRealService的初始化
init();
mRealService.onCreate();
}
private void init() {
//...
try {
// 使用ClassLoader加载器加载 CarServiceImpl类
mRealServiceClass = mUpdatablePackageContext.getClassLoader().loadClass(
mRealServiceClassName);
// Use default constructor always
Constructor constructor = mRealServiceClass.getConstructor();
// 初始化对象 这里把对象转换成ProxiedService,CarServiceImpl是ProxiedService的子类 汽车服务的
mRealService = (ProxiedService) constructor.newInstance();
// ..
} catch (Exception e) {
// ..
}
}
这里我们应该思考一个问题:为什么Google在初始化服务的时候,需要走Proxy服务,而不是直接启动对应的服务?我觉得有以下考量:
-
解耦合:使用服务代理可以将服务的实现与其使用者解耦。这样,当服务的实现方式发生变化时,调用者不需要进行大量修改,只需通过代理进行交互即可。比如上面的两个代理服务都有一个别的实现。CarPerUserServiceImpl extends ProxiedService {}和public class CarPerUserService extends ServiceProxy {} 这样的用户切换时候只需要切换代理服务中的mRealService 变量就行了,实际实现不需要关注。
-
灵活性: 通过代理模式,可以在运行时动态地替换服务的实现,这为测试、调试和扩展提供了灵活性。例如,可以在开发阶段使用模拟服务,而在生产环境中使用真实服务。比如:模拟器车控服务和真实车控服务。
接下来,我们看下CarServiceImpl这个真正的CarServie服务中做了些什么事情:
js
@Override
public void onCreate() {
// 初始化vhal对象,获取vhal层客户端对象IVehicle,后续应用层调用get/set方法时就使用此对象和vhal层交互
mVehicle = VehicleStub.newVehicleStub();
// 初始化 Car服务端对象,应用层获取Car示例时,返回的就是此对象
mICarImpl = new ICarImpl(this,
getBuiltinPackageContext(),
mVehicle,
// ICarImpl里面做了很多车控服务的初始化
mICarImpl.init();
// aidl 死亡监听
mVehicle.linkToDeath(mVehicleDeathRecipient);
// 到这里,尘埃落定,看到了熟悉的addService了,后续应用层获取Car对象就是通过这个"car_service" 枚举和获取AMS、PMS等一个流程了
ServiceManagerHelper.addService("car_service", mICarImpl);
// ...
}
// ICarImpl的构造方法
ICarImpl(...) {
// ...
// 初始化各种车辆控制服务,我们窥一斑,知全貌吧,主要看下CarPropertyService的初始化
mCarPropertyService = constructWithTrace(
t, CarPropertyService.class,
() -> new CarPropertyService(serviceContext, mHal.getPropertyHal()), allServices);
// ...
// 创建VehicleHal对象 在VehicleHal类中,我们初始化了很多和hal层打交道的服务,且提供了对应服务的propertyID和权限的定义,后面我们接着分析下
mHal = constructWithTrace(t, VehicleHal.class,
() -> new VehicleHal(serviceContext, vehicle), allServices);
}
// com.android.car.CarPropertyService
@Override
public void init() {
synchronized (mLock) {
// 本地变量读取PropertyId对应的权限配置,便于后续应用层请求时时候,判断PropertyId合法性和权限问题
mPropertyIdToCarPropertyConfig = mPropertyHalService.getPropertyList();
mPropToPermission = mPropertyHalService.getPermissionsForAllProperties();
}
}
我们单独看下VehicleHal创建之后做了哪些事情,有助于后面我们set流程的理解:
js
VehicleHal(Context context,
PowerHalService powerHal,
PropertyHalService propertyHal,
InputHalService inputHal,
VmsHalService vmsHal,
UserHalService userHal,
DiagnosticHalService diagnosticHal,
ClusterHalService clusterHalService,
TimeHalService timeHalService,
HandlerThread handlerThread,
VehicleStub vehicle) {
// Must be initialized before HalService so that HalService could use this.
// ...
// 其他的我们暂不看了,窥一斑,这里我们初始化了PropertyHalService
mPropertyHal = propertyHal != null ? propertyHal : new PropertyHalService(this);
// ...
}
public class PropertyHalService extends HalServiceBase {
// 类初始化时候,我们可以看到PropertyHalServiceIds初始化了一个对应的变量PropertyHalServiceIds
private final PropertyHalServiceIds mPropertyHalServiceIds = new PropertyHalServiceIds();
}
public class PropertyHalServiceIds {
public PropertyHalServiceIds() {
// Add propertyId and read/write permissions
// Cabin Properties
// 这里定义了相关propertyID和对应的get/set权限 在后续的get/set流程中会设计到权限的校验。
mHalPropIdToPermissions.put(VehicleProperty.DOOR_POS, new Pair<>(
Car.PERMISSION_CONTROL_CAR_DOORS,
Car.PERMISSION_CONTROL_CAR_DOORS));
mHalPropIdToPermissions.put(VehicleProperty.DOOR_MOVE, new Pair<>(
Car.PERMISSION_CONTROL_CAR_DOORS,
Car.PERMISSION_CONTROL_CAR_DOORS));
// ...
}
}
至此,整个CarService的启动基本梳理完成了,相关的准备工作也做好了,静待应用层get/set请求
车辆属性set流程
应用层想要set一个车辆属性,首先需要获取Car对象,所以,我们就从获取Car对象向下分析整个流程,由于get和set流程差不多,我们以set流程为例:
js
//android.car.Car
public static Car createCar(@NonNull Context context,
@Nullable Handler handler, long waitTimeoutMs,
@NonNull CarServiceLifecycleListener statusChangeListener) {
// 根据上面CarService启动的流程时机,它的启动实际是晚于系统的一些其他服务的,如果在系统的其他服务中过早获取车控信息,那么其实服务未启动,所以这里得循环重试获取
// ...省略相关代码
while (true) {
// 如果调用处早于CarService的启动时机,这里获取不到服务
service = ServiceManagerHelper.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
if (car == null) {
// 创建Car对象客户端: 实际服务端为ICarImpl
car = new Car(context, ICar.Stub.asInterface(service), null, statusChangeListener,
handler);
}
if (service != null) {
if (!started) {
// 这里bind一下服务端,获取服务端对象记录在客户端本地变量中,然后返回给上层应用和后续方便获取对应的CarxxxService服务
car.startCarService();
return car;
}
// service available after starting.
break;
}
//如果在CarService还未启动,这里再次主动启动一下,进入到下次循环后,上面的service获取就不为空了
if (!started) {
car.startCarService();
started = true;
}
// ...省略相关代码
}
return car;
}
到这里应用层就能获取到Car对象了,由于CarService里面注册了很多车载类相关服务,例如:CABIN_SERVICE、HVAC_SERVICE等,但是这些服务在14(可能更早的版本就过期了,感兴趣的可以往前面的版本看下)中已经标识过期了,google做了一个归类,使用PROPERTY_SERVICE一个服务就行,顾我们想要设置某个车辆属性,先获取到对应服务的客户端,以PROPERTY_SERVICE服务为例:
js
// 获取PropertyManager 服务
val propertyManager = car.getCarManager(Car.PROPERTY_SERVICE)
public Object getCarManager(String serviceName) {
CarManagerBase manager;
synchronized (mLock) {
// ...取本地map缓存
manager = mServiceMap.get(serviceName);
if (manager == null) {
try {
// 获取CarService启动时注册的服务,如果没有在系统注册,返回null
IBinder binder = mService.getCarService(serviceName);
if (binder == null) {
Log.w(TAG_CAR, "getCarManager could not get binder for service:"
+ serviceName);
return null;
}
// 记录在本地map中
manager = createCarManagerLocked(serviceName, binder);
//...
mServiceMap.put(serviceName, manager);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
}
return manager;
}
得到客户端PropertyManager后调用manager.setProperty()设置车辆属性,相关系统支持的propertyId定义在VehiclePropertyIds类中,当然Vender厂商也可以自定义
js
//android.car.hardware.property.CarPropertyManager
public <E> void setProperty(@NonNull Class<E> clazz, int propertyId, int areaId,
@NonNull E val) {
// 校验ID是否定义:系统或者厂商
assertPropertyIdIsSupported(propertyId);
try {
runSyncOperation(() -> {
// 调用服务端CarPropertyService.setProperty()
mService.setProperty(new CarPropertyValue<>(propertyId, areaId, val),
mCarPropertyEventToService);
return null;
});
}
}
// com.android.car.CarPropertyService
public void setProperty(CarPropertyValue carPropertyValue, ICarPropertyEventListener iCarPropertyEventListener) {
// 这里做了一个同时调用限制,最大为16,如果超过,会抛出异常ServiceSpecificException
// 验证请求参数合法性,包括权限等问题
validateSetParameters(carPropertyValue);
this.runSyncOperationCheckLimit(() -> {
this.mPropertyHalService.setProperty(carPropertyValue);
return null;
});
private void validateSetParameters(CarPropertyValue<?> carPropertyValue) {
requireNonNull(carPropertyValue);
// 在上面的初始化流程中我们分析了mPropertyHalService会加载相关propertyId对应的权限,这里有用到对应的权限检测。
String writePermission = mPropertyHalService.getWritePermission(propertyId);
if (writePermission == null) {
throw new SecurityException(
"Platform does not have permission to write value for property ID: "
+ VehiclePropertyIds.toString(propertyId));
}
}
在权限校验通过后,我们继续看下接下来的流程:
js
// com.android.car.hal.PropertyHalService
// 调用 PropertyHalService.setProperty()
public void setProperty(CarPropertyValue carPropertyValue)
throws IllegalArgumentException, ServiceSpecificException {
synchronized (mLock) {
valueToSet = carPropertyValueToHalPropValueLocked(carPropertyValue);
}
// 调用 VehicleHal.set 而我们的VehicleHal 持有mVehicleStub对象
mVehicleHal.set(valueToSet);
}
// com.android.car.hal.VehicleHal
// 调用set函数
public void set(HalPropValue propValue) throws IllegalArgumentException, ServiceSpecificException {
setValueWithRetry(propValue);
}
private void setValueWithRetry(HalPropValue value) {
// 这里调用了mVehicleStub的set方法
mVehicleStub.set(requestValue);
}
// com.android.car.HidlVehicleStub
// HidlVehicleStub是VehicleStub的子类,还记得我们上面分析的启动流程吧,HidlVehicleStub 持有的服务端对象为vhal层android.hardware.automotive.vehicle@2.0::IVehicle 的服务
@Override
public void set(HalPropValue propValue) throws RemoteException {
// 把传递过来的请求数据封装成 hal层数据结构,调用 Hal层接口把数据传递给hal层
VehiclePropValue hidlPropValue = (VehiclePropValue) propValue.toVehiclePropValue();
// 到这里FW的整个set流程就梳理完了,后面我们在梳理vhal层set流程 int status = mHidlVehicle.set(hidlPropValue);
// ...
}
get的获取流程和set差不多,这里就不做过多梳理了,参考上面set的调用关系即可,到这里我们就基本了解了CarService的启动流程和车控属性的set流程及其相关权限校验流程。后续我们继续分析下vhal层相关流程!