Android 系统服务的添加

Android 中如何添加一个系统服务

一、这道题主要考察什么

"如何添加一个系统服务"主要考察以下几个方面:

  1. 是否理解系统服务的使用方式

    应用通常通过 Context.getSystemService() 获取系统服务。

  2. 是否理解系统服务的发现机制

    服务端需要将自己的 Binder 对象注册到 ServiceManager,客户端才能根据服务名称找到它。

  3. 是否理解 Binder 跨进程通信

    应用进程获取到系统服务的 Binder 代理对象后,通过 Binder IPC 调用服务端。

  4. 是否理解系统服务的启动位置

    系统服务可以运行在 system_server 进程中,也可以运行在单独的 Native 进程中。

可以将核心概括为三个关键词:

方便使用、注册开放、Binder IPC


二、系统服务的整体调用链

应用获取和调用系统服务的大致流程是:

text 复制代码
应用程序
   ↓ Context.getSystemService(name)
SystemServiceRegistry
   ↓ 根据 name 查找 ServiceFetcher
创建对应的 Manager 对象
   ↓
ServiceManager.getService(name)
   ↓
获得服务端 Binder 代理对象
   ↓
AIDL 接口代理 Stub.Proxy
   ↓ Binder IPC
系统服务端

例如应用获取电源服务:

java 复制代码
PowerManager powerManager =
        (PowerManager) context.getSystemService(Context.POWER_SERVICE);

底层大致经历:

text 复制代码
Context.getSystemService(POWER_SERVICE)
        ↓
SystemServiceRegistry
        ↓
PowerManager 对应的 ServiceFetcher
        ↓
ServiceManager.getService("power")
        ↓
获得 IPowerManager 的 Binder 代理
        ↓
封装成 PowerManager 返回给应用

三、为什么还要封装一个 Manager 类

客户端从 ServiceManager 中取得的,本质上是系统服务的 Binder 接口代理,例如:

text 复制代码
IPowerManager

但 Android 通常不会直接把 AIDL 接口暴露给普通应用,而是再封装一层:

text 复制代码
PowerManager

调用关系为:

text 复制代码
应用
  ↓
PowerManager
  ↓
IPowerManager.Stub.Proxy
  ↓ Binder IPC
PowerManagerService

PowerManager 相当于客户端代理或者业务封装层,主要作用是:

  • 隐藏 Binder 和 AIDL 实现细节;
  • 统一处理 RemoteException
  • 对外提供更容易使用的业务 API;
  • 对参数、权限和兼容性进行额外处理。

所以,添加一个完整的系统服务时,不能只考虑服务端,还应该考虑应用如何方便地使用该服务。


四、ServiceManager 的作用

ServiceManager 可以理解为 Binder 系统中的服务注册中心。

系统服务启动后,需要把自己的 Binder 实体注册到 ServiceManager

java 复制代码
ServiceManager.addService("my_service", binder);

客户端使用时,根据服务名称查询:

java 复制代码
IBinder binder = ServiceManager.getService("my_service");

整体关系为:

text 复制代码
                注册 Binder
系统服务  ----------------------> ServiceManager
                                      ↑
                                      │ 根据名称查询
                                      │
客户端   -----------------------------+

客户端并不需要提前知道服务对象所在的进程,只需要知道服务名称。

查询到 Binder 对象后,客户端便可以通过 Binder IPC 调用服务端。


五、如何添加一个系统服务

添加系统服务需要同时处理服务端和客户端。

1. 定义跨进程接口

通常使用 AIDL 定义服务接口:

aidl 复制代码
interface IMyService {
    int calculate(int value);
}

编译后会生成:

text 复制代码
IMyService
IMyService.Stub
IMyService.Stub.Proxy

其中:

  • Stub 用于服务端接收 Binder 请求;
  • Proxy 用于客户端发起 Binder 请求。

2. 实现系统服务

服务端实现 AIDL 生成的 Stub

java 复制代码
public class MySystemService extends IMyService.Stub {

    @Override
    public int calculate(int value) {
        return value * 2;
    }
}

此时 MySystemService 本身就是一个 Binder 实体对象。

实际系统服务通常还会包含:

  • 资源初始化;
  • 系统状态监听;
  • 权限检查;
  • Binder 接口实现;
  • 生命周期管理。

3. 选择系统服务运行的位置

系统服务主要有两种运行方式。

方式一:运行在 system_server 中

大多数 Java 系统服务运行在 system_server 进程,例如:

text 复制代码
ActivityManagerService
PackageManagerService
PowerManagerService
WindowManagerService

这类服务通常在 SystemServer 启动过程中创建。

大致过程:

java 复制代码
MySystemService service = new MySystemService();
ServiceManager.addService("my_service", service);

如果运行在 system_server 中,不需要自己初始化 Binder 驱动和 Binder 线程池,因为 system_server 已经完成这些工作。


方式二:运行在独立进程中

部分 Native 系统服务运行在独立进程中,例如:

text 复制代码
SurfaceFlinger
media 服务
ServiceManager

独立进程的服务通常需要:

  1. init.rc 中配置进程启动方式;
  2. 提供进程入口函数;
  3. 初始化 Binder 通信环境;
  4. 创建系统服务 Binder 对象;
  5. 注册到 ServiceManager
  6. 启动 Binder 线程池并进入循环。

大致流程:

text 复制代码
init 读取 rc 配置
        ↓
启动系统服务进程
        ↓
打开 Binder 驱动
        ↓
进行 mmap 内存映射
        ↓
启动 Binder 线程池
        ↓
创建服务对象
        ↓
ServiceManager.addService()
        ↓
进入 Binder 线程循环

4. 注册到 ServiceManager

无论系统服务运行在 system_server,还是运行在独立进程中,都需要向 ServiceManager 注册。

java 复制代码
ServiceManager.addService("my_service", myService);

注册时传入:

  • 服务名称;
  • 服务的 Binder 实体对象。

注册之后,其他进程才能通过名称查询:

java 复制代码
IBinder binder = ServiceManager.getService("my_service");

因此,注册的本质是:

把服务名称和 Binder 对象之间的映射关系保存到 ServiceManager 中。


5. 编写客户端 Manager 类

为了避免应用直接操作 AIDL 接口,通常再封装一个 Manager 类:

java 复制代码
public class MyManager {

    private final IMyService service;

    public MyManager(IMyService service) {
        this.service = service;
    }

    public int calculate(int value) {
        try {
            return service.calculate(value);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}

调用链为:

text 复制代码
MyManager
   ↓
IMyService.Proxy
   ↓ Binder IPC
MySystemService

6. 注册到 SystemServiceRegistry

为了让应用能够像使用其他系统服务一样调用:

java 复制代码
context.getSystemService("my_service");

还需要在客户端框架层注册对应的 ServiceFetcher

概念代码如下:

java 复制代码
registerService(
        "my_service",
        MyManager.class,
        new CachedServiceFetcher<MyManager>() {
            @Override
            public MyManager createService(ContextImpl context) {
                IBinder binder =
                        ServiceManager.getService("my_service");

                IMyService service =
                        IMyService.Stub.asInterface(binder);

                return new MyManager(service);
            }
        }
);

之后应用便可以通过:

java 复制代码
MyManager manager =
        (MyManager) context.getSystemService("my_service");

获取服务。

这里完成了三层转换:

text 复制代码
服务名称
   ↓
IBinder
   ↓
IMyService
   ↓
MyManager

六、完整添加流程

完整流程可以总结为:

text 复制代码
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 类。

相关推荐
杉氧2 小时前
用 Compose 挑战交互与动效天花板:ComposeCraftLab 开源实验室全解析
android·前端·kotlin
朝与同歌暮同酒3 小时前
冒泡社区《幻想三国》还能玩吗?安卓手机与电脑模拟器试玩记录
android·智能手机·电脑
音视频牛哥3 小时前
从数字孪生到机器人操控:Android Unity3D下RTMP/RTSP多路低延迟播放实践
android·unity·音视频·unity rtsp播放器·unity rtmp播放器·rtsp player·rtmp player
用户69371750013844 小时前
了解一下 Agent Harness
android·前端·后端
淡淡的香烟5 小时前
Android15适配16kb完整版
android
杉氧5 小时前
跨平台持久化:Flutter 本地数据库的多线程安全与架构设计实践
android·前端·flutter
小孔龙5 小时前
Compose 布局与绘制:LayoutNode、DisplayList 与 GraphicsLayer
android·android jetpack
淡淡的香烟6 小时前
Android常用adb命令
android·adb
李白你好6 小时前
强大的 Frida 重打包工具,用于 iOS 和 Android。轻松修改 Frida 特征
android·ios