Android Automotive之CarService模块裁剪

根据需要对CarService中的功能模块进行裁剪
Views: 214
3 0
Read Time:4 Minute, 24 Second

近期在移植适配Android14的过程中,发现系统在启动阶段老是在Console中打印类似的日志:

[   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简介

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_SERVICEEVS框架接入

我们在使用CarService时,可以通过其提供的getCarManager方法获取对应的子Manager,从而连接对应的子Service;如下所示:

CarEvsManager evsManager = (CarEvsManager) car.getCarManager(Car.CAR_EVS_SERVICE);

CarService启动

CarService在Android Automotive中属于系统级别服务,由SystemServer拉起,拉起过程中主要依赖于CarServiceHelperService服务,首先我们看看如何在SystemServer中启动CarServiceHelperService:

// 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 "
    }
}

这里通过classLoader拿到CarServiceHelperService的class对象,进而通过startService重载调用对应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
@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进行实例化的:

@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方法:

@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模块裁剪的关键。

CarFeatureController

在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()));

其中defaultEnabledFeatures代表CarService中默认支持的特性,可以通过/packages/services/Car/service/res/values/config.xml进行配置:

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>

在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.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的更多细节,可以参考我的文章,希望对各位有所帮助~

Happy
Happy
100 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

Articles: 86

Leave a Reply

Your email address will not be published. Required fields are marked *

en_USEN