系统服务定制:自定义系统服务、ServiceManager 注册、Binder 通信、权限管理

说实话,Android Automotive OS 里最让我着迷的部分,就是系统服务。你想想看,整个车载系统就像一家公司,应用是前台员工,系统服务就是后勤保障。没有后勤,前台啥也干不了。

我在做车载项目时,经常需要一些特殊功能——比如读取车辆CAN总线数据、控制氛围灯、管理多屏交互。这些功能放不到普通App里,必须做成系统服务。为什么?因为系统服务有更高的权限,能常驻后台,还能跨进程通信。

这一章,我就带你手把手走一遍自定义系统服务的完整流程。从服务定义到注册,从Binder通信到权限管控,一个都不落下。

7.1 自定义系统服务的整体架构

先看一张图,帮你建立整体认知。我习惯在动手前先画架构图,这样写代码时心里有底。

自定义系统服务架构图 应用层(Client) 车载应用通过 Context.getSystemService() 获取服务代理 例如:CarSensorManager、VehicleHalManager Binder IPC 通信层 AIDL 接口定义 · 序列化 · 跨进程调用 系统服务层(Server) 自定义 Service 实现 · 继承 SystemService 例如:MyCarService、CustomSensorService ServiceManager 注册中心 addService() · getService() · 服务名管理

这张图其实就说了三件事:应用怎么调、服务怎么接、中间怎么传。下面我们逐个拆解。

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);
}
我的经验:AIDL只支持三种数据类型——基本类型、String、CharSequence、Parcelable、List/Map。别想着传个自定义对象进去,除非它实现了Parcelable接口。

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的关键一步。第一个参数是服务名,应用层通过这个名字来查找服务。

我曾经踩过的坑:服务名不要跟系统已有的服务重名!比如别用 "window"、"activity" 这种。我建议用 "custom_" 或 "vehicle_" 前缀,一眼就能看出是自定义服务。

7.4 ServiceManager 注册机制

说到ServiceManager,它其实是Android系统里一个特殊的Binder服务。所有系统服务启动时,都要去它那里「报到」。

注册流程说白了就三步:

  1. 服务端:调用 ServiceManager.addService(name, binder)
  2. ServiceManager:把服务名和Binder引用存到哈希表里
  3. 客户端:调用 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);
            }
        }
    }
}
我建议:在应用层封装一个Manager类,比如 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 的权限管理分三层:

  1. 声明权限:在 frameworks/base/core/res/AndroidManifest.xml 中定义
  2. 检查权限:在服务方法入口处调用 checkCallingPermission()
  3. 应用申请:应用在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.javarun() 方法里,找到启动车载服务的部分:

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