系统服务定制:自定义系统服务、ServiceManager 注册、Binder 通信、权限管理
说实话,Android Automotive OS 里最让我着迷的部分,就是系统服务。你想想看,整个车载系统就像一家公司,应用是前台员工,系统服务就是后勤保障。没有后勤,前台啥也干不了。
我在做车载项目时,经常需要一些特殊功能——比如读取车辆CAN总线数据、控制氛围灯、管理多屏交互。这些功能放不到普通App里,必须做成系统服务。为什么?因为系统服务有更高的权限,能常驻后台,还能跨进程通信。
这一章,我就带你手把手走一遍自定义系统服务的完整流程。从服务定义到注册,从Binder通信到权限管控,一个都不落下。
7.1 自定义系统服务的整体架构
先看一张图,帮你建立整体认知。我习惯在动手前先画架构图,这样写代码时心里有底。
这张图其实就说了三件事:应用怎么调、服务怎么接、中间怎么传。下面我们逐个拆解。
7.2 定义 AIDL 接口
所有Binder通信的第一步,就是定义接口。AIDL(Android Interface Definition Language)就是我们的契约。
我个人习惯把AIDL文件放在 frameworks/base/core/java/android/car/ 目录下,这样跟系统源码风格一致。
// ICustomVehicleService.aidl
package android.car;
import android.car.CustomVehicleInfo;
interface ICustomVehicleService {
/** 获取车辆VIN码 */
String getVin();
/** 设置氛围灯颜色 */
void setAmbientLightColor(int color);
/** 注册车辆状态回调 */
void registerCallback(ICustomVehicleCallback callback);
/** 注销回调 */
void unregisterCallback(ICustomVehicleCallback callback);
}
这里有个坑——回调接口也必须用AIDL定义。我见过有人直接在AIDL里用Java接口,编译直接报错。回调的AIDL长这样:
// ICustomVehicleCallback.aidl
package android.car;
interface ICustomVehicleCallback {
void onVehicleSpeedChanged(float speed);
void onDoorStatusChanged(int doorId, boolean isOpen);
}
7.3 实现系统服务类
接口定义好了,接下来就是实现。在Android Automotive OS中,系统服务要继承 SystemService 类。
// CustomVehicleService.java
package com.android.server.car;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.SystemService;
import android.car.ICustomVehicleService;
import android.car.ICustomVehicleCallback;
import java.util.ArrayList;
import java.util.List;
public class CustomVehicleService extends SystemService {
private static final String TAG = "CustomVehicleService";
private final BinderServiceImpl mBinderService;
private final List<ICustomVehicleCallback> mCallbacks = new ArrayList<>();
public CustomVehicleService(Context context) {
super(context);
mBinderService = new BinderServiceImpl();
}
@Override
public void onStart() {
// 注册到ServiceManager
publishBinderService("custom_vehicle", mBinderService);
Slog.i(TAG, "CustomVehicleService started");
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// 系统服务就绪后,可以初始化硬件
Slog.i(TAG, "System services ready, init hardware");
}
}
private class BinderServiceImpl extends ICustomVehicleService.Stub {
@Override
public String getVin() {
// 实际项目中这里会调用HAL层
return "LSVAU2A38N2101234";
}
@Override
public void setAmbientLightColor(int color) {
// 控制氛围灯硬件
Slog.i(TAG, "Set ambient light color: " + color);
}
@Override
public void registerCallback(ICustomVehicleCallback callback) {
if (callback != null && !mCallbacks.contains(callback)) {
mCallbacks.add(callback);
Slog.i(TAG, "Callback registered");
}
}
@Override
public void unregisterCallback(ICustomVehicleCallback callback) {
mCallbacks.remove(callback);
Slog.i(TAG, "Callback unregistered");
}
}
// 通知所有回调,车速变化
public void notifySpeedChange(float speed) {
for (ICustomVehicleCallback cb : mCallbacks) {
try {
cb.onVehicleSpeedChanged(speed);
} catch (RemoteException e) {
Slog.w(TAG, "Callback failed", e);
mCallbacks.remove(cb);
}
}
}
}
注意看 onStart() 方法里的 publishBinderService。这就是把服务注册到ServiceManager的关键一步。第一个参数是服务名,应用层通过这个名字来查找服务。
7.4 ServiceManager 注册机制
说到ServiceManager,它其实是Android系统里一个特殊的Binder服务。所有系统服务启动时,都要去它那里「报到」。
注册流程说白了就三步:
- 服务端:调用
ServiceManager.addService(name, binder) - ServiceManager:把服务名和Binder引用存到哈希表里
- 客户端:调用
ServiceManager.getService(name)获取Binder代理
在Android Automotive OS中,我们通常用 publishBinderService() 这个封装方法。它内部其实调的就是 ServiceManager.addService()。
// SystemService.java 中的源码片段
protected final void publishBinderService(String name, IBinder service) {
publishBinderService(name, service, false);
}
protected final void publishBinderService(String name, IBinder service,
boolean allowIsolated) {
ServiceManager.addService(name, service, allowIsolated);
}
你可能会问:为什么不用直接调ServiceManager?嗯,因为 publishBinderService 帮我们处理了权限检查和隔离进程的问题。直接用ServiceManager的话,这些细节都得自己管。
7.5 客户端调用服务
服务端写好了,客户端怎么用?应用层通过 Context.getSystemService() 来获取服务代理。
// 在车载应用中调用
public class MainActivity extends Activity {
private ICustomVehicleService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取服务
IBinder binder = ServiceManager.getService("custom_vehicle");
if (binder != null) {
mService = ICustomVehicleService.Stub.asInterface(binder);
}
// 调用服务方法
if (mService != null) {
try {
String vin = mService.getVin();
mService.setAmbientLightColor(0xFFFF0000);
} catch (RemoteException e) {
Log.e(TAG, "Service call failed", e);
}
}
}
}
CustomVehicleManager。这样上层代码不用直接跟Binder打交道,调用起来更优雅。Google的CarService就是这么设计的。
7.6 Binder 通信原理与优化
Binder通信,说白了就是一次跨进程的函数调用。但这里有几个性能要点你得知道:
| 场景 | 数据量 | 建议方式 | 原因 |
|---|---|---|---|
| 获取VIN码 | < 1KB | 同步调用 | 数据小,一次拷贝即可 |
| 设置氛围灯颜色 | 4字节 | 同步调用 | 无返回值,快速 |
| 批量上传日志 | > 1MB | 异步 + 文件共享 | Binder缓冲区默认1MB,超了会抛异常 |
| 实时车速推送 | 8字节 | 回调(oneway) | 高频数据,避免阻塞 |
我在项目中遇到过一个问题:车速回调太频繁,导致Binder线程池满了,其他服务调用被阻塞。后来怎么解决的?把回调接口声明为 oneway。
// oneway 表示异步调用,不等待返回
oneway interface ICustomVehicleCallback {
void onVehicleSpeedChanged(float speed);
void onDoorStatusChanged(int doorId, boolean isOpen);
}
加了 oneway 之后,服务端调用回调时不会阻塞,客户端收到消息后在自己的线程池里处理。嗯,这个优化很关键。
7.7 权限管理
系统服务暴露给应用,权限必须管好。你不能让一个音乐App随便读取车辆VIN码,对吧?
Android Automotive OS 的权限管理分三层:
- 声明权限:在
frameworks/base/core/res/AndroidManifest.xml中定义 - 检查权限:在服务方法入口处调用
checkCallingPermission() - 应用申请:应用在AndroidManifest.xml中声明
<uses-permission>
先看权限定义:
<!-- frameworks/base/core/res/AndroidManifest.xml -->
<permission android:name="android.car.permission.ACCESS_VEHICLE_INFO"
android:protectionLevel="signature" />
<permission android:name="android.car.permission.CONTROL_AMBIENT_LIGHT"
android:protectionLevel="signature|privileged" />
protectionLevel 这里我解释一下:
- signature:只有跟系统签名的应用才能用。适合敏感信息,比如VIN码
- signature|privileged:系统应用和特权应用可用。适合氛围灯这种
- normal:普通应用也能申请。但车载系统里我建议少用
然后在服务实现里加权限检查:
private class BinderServiceImpl extends ICustomVehicleService.Stub {
@Override
public String getVin() {
// 检查调用方是否有权限
if (checkCallingPermission(
"android.car.permission.ACCESS_VEHICLE_INFO")
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("需要 ACCESS_VEHICLE_INFO 权限");
}
return "LSVAU2A38N2101234";
}
@Override
public void setAmbientLightColor(int color) {
if (checkCallingPermission(
"android.car.permission.CONTROL_AMBIENT_LIGHT")
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("需要 CONTROL_AMBIENT_LIGHT 权限");
}
// 执行硬件控制
}
}
normal,结果第三方应用随便调用我的服务,差点把车辆数据泄露了。后来全部改成 signature 级别,才安心。记住:车载数据无小事,权限能严就别松。
7.8 完整注册流程
最后,把服务注册到系统启动流程中。这一步很多人会忘。
在 SystemServer.java 的 run() 方法里,找到启动车载服务的部分:
// SystemServer.java
private void run() {
// ... 其他服务启动 ...
// 启动自定义车载服务
try {
Slog.i(TAG, "Starting CustomVehicleService");
mSystemServiceManager.startService(CustomVehicleService.class);
} catch (Throwable e) {
Slog.e(TAG, "Failed to start CustomVehicleService", e);
}
// ... 继续启动其他服务 ...
}
这样,系统启动时就会自动加载我们的服务,注册到ServiceManager中。应用层通过 "custom_vehicle" 这个服务名就能找到它。
核心要点回顾:
- AIDL 是Binder通信的契约,回调接口也要用AIDL定义
- 服务类继承
SystemService,在onStart()中调用publishBinderService() - ServiceManager 是全局服务注册中心,服务名要唯一
- 高频回调用
oneway避免线程池阻塞 - 权限控制用
signature级别,别偷懒用normal
说实话,自定义系统服务这件事,第一次做会觉得步骤多、流程复杂。但只要你按这个套路走一遍,后面再做新服务就轻车熟路了。我在项目里前前后后写了七八个自定义服务,从车辆诊断到多屏互动,都是这套模板。
嗯,这一章的内容就到这儿。代码都在上面了,建议你照着敲一遍,遇到问题再回来看。实践出真知嘛。
公众号:蓝海资料掘金营,微信deep3321