基于Android14的CarService 启动流程分析

背景

最近公司项目基线切换到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服务,而不是直接启动对应的服务?我觉得有以下考量:

  1. 解耦合:使用服务代理可以将服务的实现与其使用者解耦。这样,当服务的实现方式发生变化时,调用者不需要进行大量修改,只需通过代理进行交互即可。比如上面的两个代理服务都有一个别的实现。CarPerUserServiceImpl extends ProxiedService {}和public class CarPerUserService extends ServiceProxy {} 这样的用户切换时候只需要切换代理服务中的mRealService 变量就行了,实际实现不需要关注。

  2. 灵活性: 通过代理模式,可以在运行时动态地替换服务的实现,这为测试、调试和扩展提供了灵活性。例如,可以在开发阶段使用模拟服务,而在生产环境中使用真实服务。比如:模拟器车控服务和真实车控服务。

接下来,我们看下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层相关流程!

相关推荐
阿巴斯甜19 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker19 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952720 小时前
Andorid Google 登录接入文档
android
黄林晴21 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android