基于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层相关流程!

相关推荐
xiangzhihong83 小时前
使用Universal Links与Android App Links实现网页无缝跳转至应用
android·ios
没有了遇见4 小时前
Android 渐变色实现总结
android
雨白6 小时前
Jetpack系列(四):精通WorkManager,让后台任务不再失控
android·android jetpack
mmoyula8 小时前
【RK3568 驱动开发:实现一个最基础的网络设备】
android·linux·驱动开发
sam.li9 小时前
WebView安全实现(一)
android·安全·webview
移动开发者1号10 小时前
Kotlin协程超时控制:深入理解withTimeout与withTimeoutOrNull
android·kotlin
程序员JerrySUN10 小时前
RK3588 Android SDK 实战全解析 —— 架构、原理与开发关键点
android·架构
移动开发者1号10 小时前
Java Phaser:分阶段任务控制的终极武器
android·kotlin
哲科软件19 小时前
跨平台开发的抉择:Flutter vs 原生安卓(Kotlin)的优劣对比与选型建议
android·flutter·kotlin