4. 实现 Service Stub:生成 AIDL Stub 类、override 方法、权限检查、异常处理

4.1 原理:AIDL Stub 类与 Binder 通信模型

Android Framework 中,自定义系统级 Service 必须通过 Binder 对外暴露接口。AIDL(Android Interface Definition Language)编译器会为声明的接口自动生成一个名为 Stub 的抽象内部类,它继承自 android.os.Binder 并实现了你在 .aidl 文件中定义的接口。客户端通过 Stub.asInterface() 拿到代理,发起 IPC 调用;服务端则继承 Stub 类,重写接口方法,完成实际业务逻辑。

关键职责:Stub 类承担了编组/解组(marshalling/unmarshalling)、线程模型切换、事务码分发等底层工作,开发者只需专注于业务方法的实现、权限校验和异常处理。
AIDL Stub 类通信流程图 客户端 调用代理方法 (Proxy) Binder 驱动 事务分发 (内核态) 服务端 Stub onTransact() → 业务方法 说明 Stub 内部 onTransact() 根据事务码调用对应方法,自动完成参数解组与结果编组。 权限检查通常在 onTransact() 分发前或业务方法入口执行。 图:自定义 Service 中 Stub 类参与 IPC 调用的完整路径

4.2 实现流程

步骤操作产出物
1编写 .aidl 接口文件定义方法签名、参数、返回类型
2编译 AIDL 文件,生成 Stub 抽象类IXXXService.Stub 类
3在自己的 Service 类中创建内部类,继承 StubMyService.StubImpl
4Override AIDL 接口的所有方法业务逻辑实现
5在方法入口添加权限检查checkCallingPermission()
6处理 RemoteException 及业务异常try-catch / throw
7在 onBind() 中返回 Stub 实例IBinder

4.3 代码实现

4.3.1 AIDL 接口定义

// frameworks/base/core/java/com/example/IMyService.aidl
package com.example;

interface IMyService {
    String getData(int id);
    int setData(String value) throws android.os.RemoteException;
}

4.3.2 编译后生成的 Stub 类(简化展示)

// 自动生成:out/soong/.../IMyService.java
public interface IMyService extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements IMyService {
        private static final String DESCRIPTOR = "com.example.IMyService";

        public Stub() {
            attachInterface(this, DESCRIPTOR);
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data,
                                  android.os.Parcel reply, int flags)
                throws android.os.RemoteException {
            switch (code) {
                case TRANSACTION_getData:
                    data.enforceInterface(DESCRIPTOR);
                    int _id = data.readInt();
                    // 权限检查可在此处
                    String _result = getData(_id);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                case TRANSACTION_setData:
                    data.enforceInterface(DESCRIPTOR);
                    String _value = data.readString();
                    // 权限检查
                    int _result = setData(_value);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                default:
                    return super.onTransact(code, data, reply, flags);
            }
        }
        // 以下为事务码常量
        static final int TRANSACTION_getData = (IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_setData = (IBinder.FIRST_CALL_TRANSACTION + 1);
    }
}

4.3.3 自定义 Service 继承 Stub 并 override 方法

// MyService.java
public class MyService extends Service {
    private final IMyService.Stub mBinder = new IMyService.Stub() {
        @Override
        public String getData(int id) throws RemoteException {
            // 权限检查
            if (checkCallingOrSelfPermission("com.example.permission.ACCESS_MY_SERVICE")
                    != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException("Permission denied");
            }
            // 业务逻辑
            return "data for id=" + id;
        }

        @Override
        public int setData(String value) throws RemoteException {
            // 权限检查
            enforceCallingPermission("com.example.permission.ACCESS_MY_SERVICE", null);
            // 业务逻辑
            return 0;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

4.4 权限检查详解

Binder 调用方可以是任意进程,必须验证调用者是否具备权限。Framework 为 Service 提供了两种方法:

  • checkCallingOrSelfPermission(String permission) – 返回是否授予权限(不抛异常)
  • enforceCallingPermission(String permission, String message) – 无权限时直接抛出 SecurityException

权限字符串需在 AndroidManifest.xml 中声明(system 级 Service 通常使用 android.permission 或自定义 signature 级权限):

<permission android:name="com.example.permission.ACCESS_MY_SERVICE"
            android:protectionLevel="signature" />
注意:权限检查应在 onTransact() 层或业务方法入口进行,避免在已经处理部分数据后才拒绝,造成不一致状态。同时建议使用 Binder.getCallingUid() 做更精细的授权决策。

4.5 异常处理策略

异常类型说明处理方法
RemoteExceptionBinder 通信失败(死进程、事务过大)通知客户端,记录日志,清理资源
SecurityException权限不足由 enforceCallingPermission 自动抛出
BadParcelableException反序列化数据异常在 onTransact 中 try-catch 并返回失败
业务自定义异常例如参数校验失败通过 reply.writeException() 传播给客户端

推荐在 Stub 的 onTransact() 内包裹 try-catch,统一处理未预期异常:

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
    try {
        return super.onTransact(code, data, reply, flags);
    } catch (Exception e) {
        reply.writeException(e);
        return true;
    }
}

4.6 调试与验证

  • 日志:在 Stub 方法内添加 Log.d("MyService", "getData called from uid=" + Binder.getCallingUid());
  • dumpsys:在 Service 中实现 dump() 方法,通过 adb shell dumpsys activity service <component> 查看状态
  • 测试客户端:编写一个简单的 App 绑定 Service,调用方法并捕获返回或异常

4.7 常见错误

错误现象原因修正
客户端抛出 SecurityException未声明或未授予权限检查 Manifest 权限声明及保护级别
方法返回 null 引发 NPEStub 方法内未处理空指针对返回对象做 Objects.requireNonNull() 检查
客户端一直阻塞Stub 方法执行耗时任务,阻塞 Binder 线程改用 oneway 或异步处理
Parce 读写顺序不一致自定义 Parcelable 字段顺序与 AIDL 声明不同确保 parcel 读写顺序完全一致

4.8 性能与稳定性优化

  • 减少跨进程数据传输:避免在方法中传递大 Bitmap 或长 List,改用文件描述符或 ContentProvider
  • 使用 oneway 声明:在 AIDL 方法前加 oneway 关键字,使调用不阻塞客户端(适用于通知类接口)
  • 线程池隔离:如果 Service 需要执行耗时操作,在 Stub 方法内抛到自己的 HandlerThread 中,避免占用 Binder 线程池
  • 资源释放:在 onTransact() 或方法结束后,确保 Parcel 不会泄露
最佳实践:建议在 Stub 子类中统一实现 getInterfaceHash() 和 getInterfaceVersion()(AIDL 3.0+),便于客户端校验接口一致性,避免运行时因接口不匹配导致死机。

通过以上步骤,你就能在 Framework 层成功实现一个健壮、安全的 Service Stub,为后续注册到 ServiceManager 并提供给其他进程调用打下坚实基础。