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 实体注册到ServiceManagerServiceManager.addService(my_service,binder);客户端使用时根据服务名称查询IBinderbinderServiceManager.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 生成的StubpublicclassMySystemServiceextendsIMyService.Stub{Overridepublicintcalculate(intvalue){returnvalue*2;}}此时MySystemService本身就是一个 Binder 实体对象。实际系统服务通常还会包含资源初始化系统状态监听权限检查Binder 接口实现生命周期管理。3. 选择系统服务运行的位置系统服务主要有两种运行方式。方式一运行在 system_server 中大多数 Java 系统服务运行在system_server进程例如ActivityManagerService PackageManagerService PowerManagerService WindowManagerService这类服务通常在SystemServer启动过程中创建。大致过程MySystemServiceservicenewMySystemService();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 实体对象。注册之后其他进程才能通过名称查询IBinderbinderServiceManager.getService(my_service);因此注册的本质是把服务名称和 Binder 对象之间的映射关系保存到 ServiceManager 中。5. 编写客户端 Manager 类为了避免应用直接操作 AIDL 接口通常再封装一个 Manager 类publicclassMyManager{privatefinalIMyServiceservice;publicMyManager(IMyServiceservice){this.serviceservice;}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,newCachedServiceFetcherMyManager(){OverridepublicMyManagercreateService(ContextImplcontext){IBinderbinderServiceManager.getService(my_service);IMyServiceserviceIMyService.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 类。