Physical Address:
ChongQing,China.
WebSite:
近期在移植适配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是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);
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模块裁剪的关键。
在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的更多细节,可以参考我的文章,希望对各位有所帮助~