Physical Address:
ChongQing,China.
WebSite:
根据需要对CarService中的功能模块进行裁剪
近期在移植适配Android14的过程中,发现系统在启动阶段老是在Console中打印类似的日志:
[ 13.303945] servicemanager: Could not find android.hardware.automotive.IEvsEnumerator/default in the VINTF manifest.[ 13.303945] servicemanager: Could not find android.hardware.automotive.IEvsEnumerator/default in the VINTF manifest.
出现这份日志时,我第一时间检查了对应的服务,确实没有集成相应的EVS HAL实现。但本身这些实现在Android12.1中也没有,在Android12.1启动过程中却并不会有相关的错误日志。从日志信息来看,可能还与其他配置相关联。跟随着日志中的线索,简单了解了一下VINTF配置,即使在清理完VINTF配置后仍存在这类错误,这说明问题的根因并不在这里。
当我使用adb进入Android系统并通过logcat查看日志时,发现CarService会频繁打印与CarEvsService相关联的日志,从日志来看存在报错提示未能成功连接EVS HAL服务,所以问题大概率就出现在这里。
在我的这篇文章中,详细介绍了EVS的框架,其除了在Native层拥有三个服务(EVS HAL,EVS Manager,EVS APP)之外,在CarService中还提供了一个CarEvsManager与CarEvsService;在CarEvsService中便会尝试连接EVS Manager;我们在系统启动阶段看到的日志信息,就是由于CarEvsService启动后无法连接到EVS Manager导致的。
那么问题来了,如果我们的系统在定制时没有集成相关的框架能力,如何在CarService中也对相应的模块进行屏蔽呢。针对这个问题,我们首先需要对CarService进行分析。
CarService是Android Automotive所提供的核心服务,众多的Automotive特性都依赖于该服务;Android12.1中CarService源码位于/packages/services/Car/service/,Android14.0中源码位于packages/services/Car/service-builtin,编译产物均为CarService.apk.
编译时与Car相关的所有编译配置都在packages/services/Car/car_product/buil/car.mk中,在该makefile中又include了car_base.mk,关于CarService的整体应用框架,如下图所示:
CarService所提供的绝大部分功能位于androi.car库,其源码位置/packages/services/Car/car-lib,在该目录下的Android.bp通过java_library字段定义了名为android.car的Java库,从而供应用使用。
在CarService中,包含了众多的子Service,如下所示:
服务字段 | 作用 |
AUDIO_SERVICE | 音频相关 |
SENSOR_SERVICE | 车身传感器信息 |
INFO_SERVICE | |
APP_FOCUS_SERVICE | |
PACKAGE_SERVICE | 包管理相关 |
CAR_OCCUPANT_ZONE_SERVICE | |
CAR_NAVIGATION_SERVICE | 导航相关 |
CABIN_SERVICE | 座舱内相关服务 |
DIAGNOSTIC_SERVICE | 诊断相关服务 |
HVAC_SERVICE | 空调相关 |
POWER_SERVICE | 电源管理相关 |
PROJECTION_SERVICE | |
PROVERTY_SERVICE | 获取和管理设备属性相关 |
VENDOR_EXTENSION_SERVICE | |
CAR_INSTRUMENT_CLUSTER_SERVICE | 仪表交互相关 |
BLUETOOTH_SERVICE | 蓝牙相关 |
STORAGE_MONITORING_SERVICE | 存储相关 |
CAR_MEDIA_SERVICE | 多媒体相关 |
CAR_INPUT_SERVICE | 按键、触摸等Input相关 |
CAR_ACTIVITY_SERVICE | 车载应用中的Activity管理相关 |
CAR_USER_SERVICE | 多用户管理相关 |
CAR_EVS_SERVICE | EVS框架接入 |
我们在使用CarService时,可以通过其提供的getCarManager方法获取对应的子Manager,从而连接对应的子Service;如下所示:
CarEvsManager evsManager = (CarEvsManager) car.getCarManager(Car.CAR_EVS_SERVICE);CarEvsManager evsManager = (CarEvsManager) car.getCarManager(Car.CAR_EVS_SERVICE);
CarService在Android Automotive中属于系统级别服务,由SystemServer拉起,拉起过程中主要依赖于CarServiceHelperService服务,首先我们看看如何在SystemServer中启动CarServiceHelperService:
// frameworks/base/services/java/com/android/server/SystemServer.javaprivate static final String CAR_SERVICE_HELPER_SERVICE_CLASS ="com.android.internal.car.CarServiceHelperService";private void startOtherServices(@NonNull TimingsTraceAndSlog t) {……if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {t.traceBegin("StartCarServiceHelperService");mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);t.traceEnd();}}// frameworks/base/services/java/com/android/server/SystemServer.java private static final String CAR_SERVICE_HELPER_SERVICE_CLASS = "com.android.internal.car.CarServiceHelperService"; private void startOtherServices(@NonNull TimingsTraceAndSlog t) { …… if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { t.traceBegin("StartCarServiceHelperService"); mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS); t.traceEnd(); } }
这里我们可以看到CarServiceHelperService的启动是在system_server的startOtherServices中进行,调用的是SystemServiceManager.java中提供的startService方法。
/*** Starts a service by class name.** @return The service instance.*/public SystemService startService(String className) {final Class<SystemService> serviceClass = loadClassFromLoader(className,this.getClass().getClassLoader());return startService(serviceClass);}/** Loads and initializes a class from the given classLoader. Returns the class.*/@SuppressWarnings("unchecked")private static Class<SystemService> loadClassFromLoader(String className,ClassLoader classLoader) {try {return (Class<SystemService>) Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {throw new RuntimeException("Failed to create service " + className+ " from class loader " + classLoader.toString() + ": service class not "+ "found, usually indicates that the caller should "+ "have called PackageManager.hasSystemFeature() to check whether the "+ "feature is available on this device before trying to start the "+ "services that implement it. Also ensure that the correct path for the "}}/** * Starts a service by class name. * * @return The service instance. */ public SystemService startService(String className) { final Class<SystemService> serviceClass = loadClassFromLoader(className, this.getClass().getClassLoader()); return startService(serviceClass); } /* * Loads and initializes a class from the given classLoader. Returns the class. */ @SuppressWarnings("unchecked") private static Class<SystemService> loadClassFromLoader(String className,ClassLoader classLoader) { try { return (Class<SystemService>) Class.forName(className, true, classLoader); } catch (ClassNotFoundException ex) { throw new RuntimeException("Failed to create service " + className + " from class loader " + classLoader.toString() + ": service class not " + "found, usually indicates that the caller should " + "have called PackageManager.hasSystemFeature() to check whether the " + "feature is available on this device before trying to start the " + "services that implement it. Also ensure that the correct path for the " } }
这里通过classLoader拿到CarServiceHelperService的class对象,进而通过startService重载调用对应service的onStart方法:
//frameworks/base/services/core/java/com/android/server/SystemServiceManager.java//startService重载,通过调用对应service的onStart方法启动对应的Servicepublic void startService(@NonNull final SystemService service) {// Register it.将对应的service对象保存至mService所对应的list中mServices.add(service);// Start it.long time = SystemClock.elapsedRealtime();try {//通过onStart进行启动service.onStart();} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + service.getClass().getName()+ ": onStart threw an exception", ex);}warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");}//frameworks/base/services/core/java/com/android/server/SystemServiceManager.java //startService重载,通过调用对应service的onStart方法启动对应的Service public void startService(@NonNull final SystemService service) { // Register it.将对应的service对象保存至mService所对应的list中 mServices.add(service); // Start it. long time = SystemClock.elapsedRealtime(); try { //通过onStart进行启动 service.onStart(); } catch (RuntimeException ex) { throw new RuntimeException("Failed to start service " + service.getClass().getName() + ": onStart threw an exception", ex); } warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart"); }
接下来我们进入CarServiceHelperService,看看其onStart具体做了哪些事情:
//frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java@Overridepublic void onStart() {EventLog.writeEvent(EventLogTags.CAR_HELPER_START);IntentFilter filter = new IntentFilter(Intent.ACTION_REBOOT);filter.addAction(Intent.ACTION_SHUTDOWN);mContext.registerReceiverForAllUsers(mShutdownEventReceiver, filter, null, null);mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);mCarWatchdogDaemonHelper.connect();Intent intent = new Intent();intent.setPackage("com.android.car");intent.setAction(CAR_SERVICE_INTERFACE);//通过intent启动car serviceif (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,mHandler, UserHandle.SYSTEM)) {Slogf.wtf(TAG, "cannot start car service");}loadNativeLibrary();}//frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java @Override public void onStart() { EventLog.writeEvent(EventLogTags.CAR_HELPER_START); IntentFilter filter = new IntentFilter(Intent.ACTION_REBOOT); filter.addAction(Intent.ACTION_SHUTDOWN); mContext.registerReceiverForAllUsers(mShutdownEventReceiver, filter, null, null); mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener); mCarWatchdogDaemonHelper.connect(); Intent intent = new Intent(); intent.setPackage("com.android.car"); intent.setAction(CAR_SERVICE_INTERFACE); //通过intent启动car service if (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,mHandler, UserHandle.SYSTEM)) { Slogf.wtf(TAG, "cannot start car service"); } loadNativeLibrary(); }
这里我们可以看到,CarServiceHelperService的onStart中会通过intent去启动我们的CarService,其对应的包名为com.android.car,通过Context.BIND_AUTO_CREATE标志来自启动服务(会自动调用服务的onCreate方法)。
紧接着我们来分析CarService的onBind,其内部实现很简单,就是返回了一个名为mICarImpl的实例对象,该实例对象是在CarService类的onCreate进行实例化的:
@Overridepublic void onCreate() {.....//获取Vehicle服务对象mVehicle = getVehicle();//CarService实现 实例化mICarImpl = new ICarImpl(this,mVehicle,SystemInterface.Builder.defaultSystemInterface(this).build(),mVehicleInterfaceName);//通过init方法进行初始化mICarImpl.init();linkToDeath(mVehicle, mVehicleDeathRecipient);//添加到servicemanagerServiceManager.addService("car_service", mICarImpl);SystemProperties.set("boot.car_service_created", "1");super.onCreate();}@Override public void onCreate() { ..... //获取Vehicle服务对象 mVehicle = getVehicle(); //CarService实现 实例化 mICarImpl = new ICarImpl(this, mVehicle, SystemInterface.Builder.defaultSystemInterface(this).build(), mVehicleInterfaceName); //通过init方法进行初始化 mICarImpl.init(); linkToDeath(mVehicle, mVehicleDeathRecipient); //添加到servicemanager ServiceManager.addService("car_service", mICarImpl); SystemProperties.set("boot.car_service_created", "1"); super.onCreate(); }
接着我们来到ICarImpl类的实现,看看其init方法:
@MainThreadvoid init() {LimitedTimingsTraceLog t = new LimitedTimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG,Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);t.traceBegin("ICarImpl.init");t.traceBegin("VHAL.init");mHal.init();t.traceEnd();t.traceBegin("CarService.initAllServices");//初始化各个子service,调用各子service的init方法for (CarServiceBase service : mAllServices) {t.traceBegin(service.getClass().getSimpleName());service.init();t.traceEnd();}t.traceEnd(); // "CarService.initAllServices"t.traceEnd(); // "ICarImpl.init"}@MainThread void init() { LimitedTimingsTraceLog t = new LimitedTimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS); t.traceBegin("ICarImpl.init"); t.traceBegin("VHAL.init"); mHal.init(); t.traceEnd(); t.traceBegin("CarService.initAllServices"); //初始化各个子service,调用各子service的init方法 for (CarServiceBase service : mAllServices) { t.traceBegin(service.getClass().getSimpleName()); service.init(); t.traceEnd(); } t.traceEnd(); // "CarService.initAllServices" t.traceEnd(); // "ICarImpl.init" }
这里我们可以看到,CarService启动时会通过for循环去调用每个子service的init方法,从而完成启动过程中的初始化;其中mAllService是一个数组,其包含的元素为CarService中对应的各个子service实例,由此完成CarService的初始化流程
在了解完其启动流程后,我们自然会想到mAllService数组内的元素到底是从哪里来的呢,如果我们控制可以控制该数组内的元素,是不是就能实现CarService的裁剪,这就是我们进行CarService模块裁剪的关键。
在CarService中,CarFeatureController用于解析配置,从而控制初始化阶段需要进行初始化的子服务;比如CarEvsService的初始化就依赖于CarFeatureController:
if (mFeatureController.isFeatureEnabled(Car.CAR_EVS_SERVICE)) {mCarEvsService = constructWithTrace(t, CarEvsService.class,() -> new CarEvsService(serviceContext, mHal.getEvsHal(), mCarPropertyService));} else {mCarEvsService = null;}CarFeatureController由ICarImpl进行实例化:mFeatureController = constructWithTrace(t, CarFeatureController.class,() -> new CarFeatureController(serviceContext, defaultEnabledFeatures,disabledFromVhal, mSystemInterface.getSystemCarDir()));if (mFeatureController.isFeatureEnabled(Car.CAR_EVS_SERVICE)) { mCarEvsService = constructWithTrace(t, CarEvsService.class, () -> new CarEvsService(serviceContext, mHal.getEvsHal(), mCarPropertyService)); } else { mCarEvsService = null; } CarFeatureController由ICarImpl进行实例化: mFeatureController = constructWithTrace(t, CarFeatureController.class, () -> new CarFeatureController(serviceContext, defaultEnabledFeatures, disabledFromVhal, mSystemInterface.getSystemCarDir()));
其中defaultEnabledFeatures代表CarService中默认支持的特性,可以通过/packages/services/Car/service/res/values/config.xml进行配置:
defaultEnabledFeatures = res.getStringArray(R.array.config_allowed_optional_car_features);defaultEnabledFeatures = res.getStringArray(R.array.config_allowed_optional_car_features);
在Android12与14中,其具体配置如下:
<string-array translatable="false" name="config_allowed_optional_car_features"><item>car_navigation_service</item><item>cluster_service</item><item>com.android.car.user.CarUserNoticeService</item><item>diagnostic</item><item>storage_monitoring</item><item>vehicle_map_service</item><item>car_evs_service</item><item>car_telemetry_service</item></string-array><string-array translatable="false" name="config_allowed_optional_car_features"> <item>car_navigation_service</item> <item>cluster_service</item> <item>com.android.car.user.CarUserNoticeService</item> <item>diagnostic</item> <item>storage_monitoring</item> <item>vehicle_map_service</item> <item>car_evs_service</item> <item>car_telemetry_service</item> </string-array>
在CarFeatureController初始化时,会传入默认支持的feature特性,还会读取一个名为car_feature_config.txt的配置文件(如果存在),在CarFeatureController中,其内部成员变量mEnabledFeatures是一个HashSet,用于存储记录需要支持的特性,这些特性都有相对应的子Service;这些特性分为强制的(MANDATORY_FEATURES)和可选的(OPTIONAL_FEATURES)两种;最后完整支持的feature都会被记录在SUPPORT_FEATURES内;
所以当我们需要对CarService进行模块裁剪时,我们需要先确认该模块是否是optional的,如果是optional的模块,我们可以通过修改原生配置或者Overlay原生配置的方式覆盖config.xml中关于config_allowed_optional_car_features的配置;如果是原生强制支持的特性,可以通过修改源码或者定制car_feature_config.txt的配置来实现。
这里还存在一个小细节,即/packages/services/Car/service/res/values/config.xml并不是打包到CarService中,而是打包到了CarServiceUpdatableNonModule中,在我们完成修改后用aapt2进行初步确认时,需要检查的是CarServiceUpdatableNonModule.apk而非CarService.apk。
在我遇到的场景里,CarEvsService属于可选项,为了避免对原生系统造成侵入式修改,我选择使用Overlay的形式来做定制化配置,具体步骤分为如下几步:
1.在device目录下创建Overlay的配置文件夹,放入Overlay的配置
2.在device.mk中通过DEVICE_PACKAGE_OVERLAYS配置Overlay
3.重新进行编译打包,测试验证;初步验证时我们可以借助aapt2,如下所示:
$aapt dump strings CarServiceUpdatableNonModule.apkString pool of 15786 unique UTF-8 non-sorted strings, 15786 entries and 0 styles using 1280560 bytes:String #0 : car_navigation_serviceString #1 : cluster_serviceString #2 : com.android.car.user.CarUserNoticeServiceString #3 : diagnosticString #4 : storage_monitoringString #5 : vehicle_map_serviceString #6 : car_telemetry_service$aapt dump strings CarServiceUpdatableNonModule.apk String pool of 15786 unique UTF-8 non-sorted strings, 15786 entries and 0 styles using 1280560 bytes: String #0 : car_navigation_service String #1 : cluster_service String #2 : com.android.car.user.CarUserNoticeService String #3 : diagnostic String #4 : storage_monitoring String #5 : vehicle_map_service String #6 : car_telemetry_service
我们可以看到,在进行Overlay之后,car_evs_service配置已经不在了,这样CarService也就不会再去初始化CarEvsService,我们也就实现了对CarService的裁剪。
关于Overlay的更多细节,可以参考我的文章,希望对各位有所帮助~