Android 系统服务的添加
Android 中如何添加一个系统服务
一、这道题主要考察什么
“如何添加一个系统服务”主要考察以下几个方面:
是否理解系统服务的使用方式
应用通常通过Context.getSystemService()获取系统服务。是否理解系统服务的发现机制
服务端需要将自己的 Binder 对象注册到ServiceManager,客户端才能根据服务名称找到它。是否理解 Binder 跨进程通信
应用进程获取到系统服务的 Binder 代理对象后,通过 Binder IPC 调用服务端。是否理解系统服务的启动位置
系统服务可以运行在system_server进程中,也可以运行在单独的 Native 进程中。
可以将核心概括为三个关键词:
方便使用、注册开放、Binder IPC
二、系统服务的整体调用链
应用获取和调用系统服务的大致流程是:
应用程序 ↓ Context.getSystemService(name) SystemServiceRegistry ↓ 根据 name 查找 ServiceFetcher 创建对应的 Manager 对象 ↓ ServiceManager.getService(name) ↓ 获得服务端 Binder 代理对象 ↓ AIDL 接口代理 Stub.Proxy ↓ Binder IPC 系统服务端例如应用获取电源服务:
PowerManagerpowerManager=(PowerManager)context.getSystemService(Context.POWER_SERVICE);底层大致经历:
Context.getSystemService(POWER_SERVICE) ↓ SystemServiceRegistry ↓ PowerManager 对应的 ServiceFetcher ↓ ServiceManager.getService("power") ↓ 获得 IPowerManager 的 Binder 代理 ↓ 封装成 PowerManager 返回给应用三、为什么还要封装一个 Manager 类
客户端从ServiceManager中取得的,本质上是系统服务的 Binder 接口代理,例如:
IPowerManager但 Android 通常不会直接把 AIDL 接口暴露给普通应用,而是再封装一层:
PowerManager调用关系为:
应用 ↓ PowerManager ↓ IPowerManager.Stub.Proxy ↓ Binder IPC PowerManagerServicePowerManager相当于客户端代理或者业务封装层,主要作用是:
- 隐藏 Binder 和 AIDL 实现细节;
- 统一处理
RemoteException; - 对外提供更容易使用的业务 API;
- 对参数、权限和兼容性进行额外处理。
所以,添加一个完整的系统服务时,不能只考虑服务端,还应该考虑应用如何方便地使用该服务。
四、ServiceManager 的作用
ServiceManager可以理解为 Binder 系统中的服务注册中心。
系统服务启动后,需要把自己的 Binder 实体注册到ServiceManager:
ServiceManager.addService("my_service",binder);客户端使用时,根据服务名称查询:
IBinderbinder=ServiceManager.getService("my_service");整体关系为:
注册 Binder 系统服务 ----------------------> ServiceManager ↑ │ 根据名称查询 │ 客户端 -----------------------------+客户端并不需要提前知道服务对象所在的进程,只需要知道服务名称。
查询到 Binder 对象后,客户端便可以通过 Binder IPC 调用服务端。
五、如何添加一个系统服务
添加系统服务需要同时处理服务端和客户端。
1. 定义跨进程接口
通常使用 AIDL 定义服务接口:
interface IMyService { int calculate(int value); }编译后会生成:
IMyService IMyService.Stub IMyService.Stub.Proxy其中:
Stub用于服务端接收 Binder 请求;Proxy用于客户端发起 Binder 请求。
2. 实现系统服务
服务端实现 AIDL 生成的Stub:
publicclassMySystemServiceextendsIMyService.Stub{@Overridepublicintcalculate(intvalue){returnvalue*2;}}此时MySystemService本身就是一个 Binder 实体对象。
实际系统服务通常还会包含:
- 资源初始化;
- 系统状态监听;
- 权限检查;
- Binder 接口实现;
- 生命周期管理。
3. 选择系统服务运行的位置
系统服务主要有两种运行方式。
方式一:运行在 system_server 中
大多数 Java 系统服务运行在system_server进程,例如:
ActivityManagerService PackageManagerService PowerManagerService WindowManagerService这类服务通常在SystemServer启动过程中创建。
大致过程:
MySystemServiceservice=newMySystemService();ServiceManager.addService("my_service",service);如果运行在system_server中,不需要自己初始化 Binder 驱动和 Binder 线程池,因为system_server已经完成这些工作。
方式二:运行在独立进程中
部分 Native 系统服务运行在独立进程中,例如:
SurfaceFlinger media 服务 ServiceManager独立进程的服务通常需要:
- 在
init.rc中配置进程启动方式; - 提供进程入口函数;
- 初始化 Binder 通信环境;
- 创建系统服务 Binder 对象;
- 注册到
ServiceManager; - 启动 Binder 线程池并进入循环。
大致流程:
init 读取 rc 配置 ↓ 启动系统服务进程 ↓ 打开 Binder 驱动 ↓ 进行 mmap 内存映射 ↓ 启动 Binder 线程池 ↓ 创建服务对象 ↓ ServiceManager.addService() ↓ 进入 Binder 线程循环4. 注册到 ServiceManager
无论系统服务运行在system_server,还是运行在独立进程中,都需要向ServiceManager注册。
ServiceManager.addService("my_service",myService);注册时传入:
- 服务名称;
- 服务的 Binder 实体对象。
注册之后,其他进程才能通过名称查询:
IBinderbinder=ServiceManager.getService("my_service");因此,注册的本质是:
把服务名称和 Binder 对象之间的映射关系保存到 ServiceManager 中。
5. 编写客户端 Manager 类
为了避免应用直接操作 AIDL 接口,通常再封装一个 Manager 类:
publicclassMyManager{privatefinalIMyServiceservice;publicMyManager(IMyServiceservice){this.service=service;}publicintcalculate(intvalue){try{returnservice.calculate(value);}catch(RemoteExceptione){throwe.rethrowFromSystemServer();}}}调用链为:
MyManager ↓ IMyService.Proxy ↓ Binder IPC MySystemService6. 注册到 SystemServiceRegistry
为了让应用能够像使用其他系统服务一样调用:
context.getSystemService("my_service");还需要在客户端框架层注册对应的ServiceFetcher。
概念代码如下:
registerService("my_service",MyManager.class,newCachedServiceFetcher<MyManager>(){@OverridepublicMyManagercreateService(ContextImplcontext){IBinderbinder=ServiceManager.getService("my_service");IMyServiceservice=IMyService.Stub.asInterface(binder);returnnewMyManager(service);}});之后应用便可以通过:
MyManagermanager=(MyManager)context.getSystemService("my_service");获取服务。
这里完成了三层转换:
服务名称 ↓ IBinder ↓ IMyService ↓ MyManager六、完整添加流程
完整流程可以总结为:
1. 使用 AIDL 定义 Binder 接口 ↓ 2. 服务端继承 Stub,实现业务逻辑 ↓ 3. 决定服务运行在 system_server 还是独立进程 ↓ 4. 在合适的系统启动阶段创建服务 ↓ 5. 使用 ServiceManager.addService() 注册 Binder ↓ 6. 客户端实现对应的 Manager 封装类 ↓ 7. 在 SystemServiceRegistry 中注册 ServiceFetcher ↓ 8. 应用通过 Context.getSystemService() 获取服务 ↓ 9. 通过 Binder IPC 调用服务端七、除了代码,还需要处理什么
真正将一个系统服务加入 Android 系统,通常还需要处理:
- Android.bp 等编译配置;
- AIDL 接口编译配置;
- SELinux 权限和策略;
service_contexts服务名称映射;- Binder 调用权限检查;
- 系统 API 的公开或隐藏配置;
- 独立进程对应的
init.rc; - 系统启动顺序和依赖关系;
- 服务异常和 Binder 死亡处理。
因此,添加系统服务不仅是写一个 Binder 类,还涉及:
Framework、Binder、SystemServer、ServiceManager、编译系统以及 SELinux。
八、逻辑梳理
Android 中添加一个系统服务,需要同时考虑服务端注册、Binder 通信和客户端使用方式。
首先,可以通过 AIDL 定义跨进程接口,然后在服务端继承 AIDL 生成的Stub,实现具体业务逻辑。
接着要确定服务运行的位置。如果服务运行在system_server中,可以在SystemServer启动系统服务的阶段创建它;如果运行在独立进程中,还需要通过init.rc启动进程,并初始化 Binder 驱动、内存映射和 Binder 线程池。
服务创建完成后,需要调用ServiceManager.addService(),将服务名称和 Binder 实体注册到ServiceManager。这样客户端才能通过ServiceManager.getService()根据名称获得 Binder 代理对象,并通过 Binder IPC 调用服务。
为了让应用使用起来更方便,通常还会编写一个 Manager 类,对 AIDL 接口进行封装,并在SystemServiceRegistry中注册对应的ServiceFetcher。这样应用就可以像使用其他系统服务一样,通过Context.getSystemService()获取该服务。
此外,真正加入 Android 系统还需要处理编译配置、SELinux 权限、service_contexts、接口权限检查和系统启动顺序等问题。
九、十句话以内的面试回答
添加 Android 系统服务,首先要使用 AIDL 定义跨进程接口,并在服务端继承Stub实现业务逻辑。
然后确定服务运行在system_server进程,还是运行在独立进程中。
如果运行在system_server中,通常在SystemServer启动系统服务的阶段创建。
如果运行在独立进程中,还需要配置init.rc,并初始化 Binder 线程池。
服务创建后,通过ServiceManager.addService()注册服务名称和 Binder 实体。
客户端通过ServiceManager.getService()根据名称取得 Binder 代理。
之后通过 AIDL 生成的 Proxy 发起 Binder IPC 调用。
为了方便应用使用,还应封装一个对应的 Manager 类。
再在SystemServiceRegistry中注册,使应用可以通过Context.getSystemService()获取。
最后还需要配置编译规则、SELinux、service_contexts和调用权限。
十、最核心的一句话
添加系统服务的本质,是创建一个 Binder 服务,将它注册到 ServiceManager,并在客户端提供通过 Context.getSystemService() 获取和调用该服务的封装。
第一是添加时机。如果服务运行在 system_server 中,就在 SystemServer 启动系统服务的阶段创建;如果运行在独立进程中,就需要配置 init.rc,并提供进程入口函数。
第二是服务端工作。服务端需要支持 Binder 通信、完成自身初始化,并通过 ServiceManager.addService() 注册自己的 Binder 对象。如果运行在 system_server 中,Binder 环境已经由系统初始化;如果运行在独立进程中,则需要自己启动 Binder 线程池。
第三是应用端工作。为了让应用能够像使用其他系统服务一样,通过 Context.getSystemService() 获取服务,需要在 SystemServiceRegistry 中注册对应的 ServiceFetcher,并封装客户端 Manager 类。