声明
- 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾…
- 文中参考了很多书籍及博客内容,可能涉及的比较多先不具体列出来了;
- 本文使用的代码是LineageOS的cm-14.1,对应Android 7.1.2,可以参考我的另一篇博客:cm-14.1 Android系统启动过程分析(1)-如何下载Nexus5的LineageOS14.1(cm-14.1)系统源码并编译、刷机
1 Java 层的 Binder 框架
Java框架库中提供了一套 Binder 相关的类,让Java 代码可以直接使用 Binder 构建IPC。其中主要的类在 core/java/android/os/ 目录中,是 android.os 包的一部分。除此之外 core/java/com/android/internal/os/ 目录提供了 com.android.internal.os 包中的 BinderIntermal 类,它是 Binder 的一个支持类。
Java层 Binder 支持的内容来自本地,通过JNI封装到 Java 层。Java 层的 Binder 和本地层的 Binder 实现的基础相同。
1.1 Binder 机制相关的类
Binder 主要的 API 在 android.os 包中,它们是:IInterface 接口、IBinder 接口、Binder 类、Parcel 类。
lInterface 是一个接口,由实现者赋予它实际的方法。其中只有方法:
static sp<IBinder> asBinder(const IInterface*);
static sp<IBinder> asBinder(const sp<IInterface>&);
asBinder() 方法用于将一个 IInterface 转化成一个 IBinder 类型。IBinder 也是一个接口,用于充当 IPC 两端通信的句柄,其中主要的内容是 transact() 方法,如下所示:
virtual status_t transact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0) = 0;
transact() 用于和远程的一个 Binder 通信,它包含4个参数为整数类型的通信码 code,Parcel 类型的参数data 和 返回值 reply,整数类型 flags。IBinder 中的其他几个重要的方法如下所示:
virtual const String16& getInterfaceDescriptor() const = 0;
virtual sp<IInterface> queryLocalInterface(const String16& descriptor);
virtual status_t pingBinder() = 0;
getInterfaceDescriptor() 用于获得此 Binder 对应的接口,queryLocalInterface() 用于获得这个Binder 对象所对应的 IInterface 的实现句柄。pingBinder() 用于查询这个 Binder 是否还存在。Binder 是实现 IBinder 接口的一个类,这个类依然是一个抽象类。主要在于 Binder 类中的onTransact() 方法是没有实现的,如下所示:
protected:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
IBinder.transact() 和 Binder.onTransact() 是具有对应关系的,在 IBinder.transact() 调用的方法,将从Binder.onTransact() 中返回,并且这是一个同步调用的方式。
在程序中,主要可以使用 IInterface 接口的实现者来表示一个具体的接口;不会直接继承 IBinder 接口,继承 Binder 抽象类。
Parcel 是一个用于封装传输数据的类。Parcel 类中支持的内容,也就是使用 Binder 在进程间通信的时候能够使用的参数和返回值的类型。Parcel 中支持基本类型,字符串类型Binder 对象类型,Parcelable 类型,还有各种类型的数组。关于Parcel细节可参考:Android系统中的Binder通信机制分析(5)- 进程间数据传递载体 Parcel
对于一个IInterface 的实现者,可以调用其中的 asBinder(),将其转化成 IBinder 类型就可以通过 Parcel 传递了。实际上 Parcel 中一个名称为 writeStrongInterface() 就是利用这种方法,并通过调用 writeStrongBinder() 来实现的。
1.2 aidl描述文件
AIDL 的全称是 Android Interface Definition Language (Android 接口描述语言)。IDL的作用就是使用特殊的语法来描述接口,接口是方法(函数)的集合。
Android 中的 aidl 工具,可以根据 aidl 文件来自动生成 Java 代码文件,内容为一个具体使用 Binder 进行 IPC 的框架。
在Java层使用 Binder 机制构建一个具体的通信框架的基本过程为:
- 使用aidl文件声明接口 IXXX,并加编译;
- 在服务段通过继承 IXXX.Stub 来实现真正的功能;
- 在客户端通过 IXXXStub.asInterface() 来获得 IXXX 类型的接口,并调用。
在程序中,具体实现需定义接口 IXXX.aidl 。IXXX.aidl 加入编译之后,将会被 Android 中的 aidl 工具处理,自动生成一个名称为 XXX.java 的文件。其中包括了 IXXX 接口、IXXXStub 抽象类,它们就是具体 Binder 结构的框架。
由于具有自动生成机制,具体 Binder 的框架只需要写一个比较简单的 aidl 文件,而不需要再编写通过 Binder IPC 进行通信的代码细节。对于程序的开发者,需要做的事情有三件:aidl文件、服务端继承 IXXX.Stub、实现客户端通过 IXX.Stub.asInterface() 得到 IXXX 类型调用。自动生成的 IXXX.Stub 是一个 Binder 类的继承者。
Binder 和 aidl 结合的使用方式如下图所示:
它表示 aidl 文件中语法格式和一个 Java 接口的定义。但是它表示的不是一个简单的接口,而是具有很多附加的内容。例如,一个名称为 IAidITest.aidl 的文件的片断如下所示:
package android.os; //接口所属于的包
import android.os.AidlTest; //需要导入的类
interface IAidlTest {
//各个方法的集合
int intMethod(int a);
boolean[] booleanArray(in boolean[] a0, out boolean[] al, inout boolean[] a2);
char[] charArray(in char[] a0, out char[] al, inout char[] a2);
String[] stringArray(in String[] a0, out String[] al, inout Stringl] a2);
void voidSecurityException();
int intSecurityException();
//省略部分内容
}
interface 是 aidl 文件中一个关键字,用于定义其表示接口的名称,主要内容是各个方法的集合。除了参数和返回值的类型有所限制之外,aidl 中的语法和 Java 中的接口定义基本相同。in、out 和 inout 表示参数传递的方向。
aidl 文件中另有一个 oneway 的关键字,附加在 interface 前面使用。它表示调用者不会等接口中方法的返回结果,因此使用 oneway 修饰接口中的方法的返回类都是 void。oneway通常用于表示 “回调” 功能的接口。
在Android 源代码的开发环境中,aidl 文件需要手动加入 LOCAL_SRC_FILES 中才会被进行处理。编译系统根据 aidl 扩展名,将使用特殊的 aidl 工具对其进行处理,自动生成 Java 代码文件。如下所示:
LOCAL SRC FILES += \
{path}/*.aidl \
1.3 框架层和应用层对Binder的使用
Java 层基于 Binder 的通信机制既可以在 Java 框架层使用,也可以在 Java 应用层使用者都需要使用aidl 文件定义接口,区别在于服务器端和客户端的结构不同。在Java 框架层使用 Binder 典型的情况是:服务器端完成一个IXXXStub类之后,需要使用ServiceManager类将其增加成某个名称的一个服务;客户端同样通过 ServiceManager根据名称获得这个服务,转换成IXXX类型的接口,进行调用。
在Java 应用程序层中使用 Binder 典型的情况是:服务器端声明一个 Service 组件,在其onBind0方法中,将一个IXXXStub 的继承者返回成IBinder类型;客户端的组件中通过bindService0连接到Service 组件后,可以从 ServiceConnection 接口(android.content包)的实现者中得到IBinder 的句柄,将其转化成IXXX类型,然后进行调用。
以上的两种情况是 Binder 的典型使用情况,服务器和客户端都在框架层,或者都在应用层。除此之外,还有一些特殊情况。
Java 框架层可以定义 Service 组件,在资源包的AndroidMenifest.xml 文件中对其声明即可。如果客户端调用框架层的 Service 组件,与典型应用程序层的调用方式基本相同。Java 框架层定义的aidl 方式的接口后,通常在 Java 服务包中实现,应用程序包中可以直接引用 aidl 文件,直接调用框架层中的服务实现。这种方式可以执行,但是并不常用。
2 Java框架层的Binder使用
2.1 ServiceManager和服务的注册
Java 框架层使用 Binder 的特点是需要通过 ServiceManager 进行服务的注册和获取。其中服务的名字是客户端和服务器端联系的纽带。
ServiceManager 类是 android.os 中的一个内部类,代码位置:frameworks/base/core/java/android/os/ServiceManager.java
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
//获得服务
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
//增加服务
public static void addService(String name, IBinder service) {
try {
getIServiceManager().addService(name, service, false);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
public static void addService(String name, IBinder service, boolean allowIsolated) {
try {
getIServiceManager().addService(name, service, allowIsolated);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
//检查服务
public static IBinder checkService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().checkService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in checkService", e);
return null;
}
}
//列出服务
public static String[] listServices() {
try {
return getIServiceManager().listServices();
} catch (RemoteException e) {
Log.e(TAG, "error in listServices", e);
return null;
}
}
public static void initServiceCache(Map<String, IBinder> cache) {
if (sCache.size() != 0) {
throw new IllegalStateException("setServiceCache may only be called once");
}
sCache.putAll(cache);
}
}
ServiceManager 类当中包含了一些静态的方法。其中所谓的服务是 IBinder 类型,通常情况下就是一个 IXXX.Stub 类型的继承者,表示一个服务的实现。
通常情况下,服务器端使用 addService() 增加服务,客户端使用 getService() 得到服务并使用。服务端通常是服务库实现并注册的。一般情况下,一个服务也就是 Java 服务库 com.android.server 包中的一个类。
Java 层的 ServiceManager 类实际上是基于本地层的服务管理器实现的,其中注册的内容统一被本地的 servicemanager 守护进程管理。
2.2 Binder的构建细节
Java 框架层中使用 Binder 的主体内容是 aidl 文件、服务端和客户端3 个部分,再加上服务的增加和获得两个环境。
Binder的使用细节以及aidl 文件的处理和自动类的生成如图 7-4 所示。
图中虚线中的内容是 Android 框架层已经有的类,IInterface 和 IBinder 是两个接口,Binder 是实现了IBinder 接口的抽象类。
首先需要写一个名称为 IXXX.aidl 的接口描述文件,其中定义了一系列的方法。这个 aidl 文件被处理之后,将自动生成 IXXX.java 文件。IXXX.java 文件中的 IXXX 是一个接口,这个接口实现了 IInterface,并且包含了 IXXX 中定义的方法;IXXX.Stub 是一个抽象类,一方面它继承了 Binder 类,另一方面它实现了 IXXX 接口,但是 IXXX 中的方法并没有实现。
服务器端需要提供一个类,继承 IXXX.Stub,并实现 IXXX 中定义的各个方法。因此这个类实际上也间接实现 IBinder 接口,可以通过 ServiceManager 的 addService() 方法将这个类的实例注册成某个名称的服务。
客户端需要通过 ServiceManager 的 getService() 方法根据名称获得 IBinder 的句柄,然后通过IXXX.Stub 中的静态方法 asInterface() 将这个 IBinder 转化成 IXXX 类型,就可以调用其中的方法。
以Java 框架层中的 AlarmManagerService 为例,服务器端是服务库的一部分,而客户端是框架层的一部分。其接口的定义在框架层中,文件的路径是 frameworks/base/core/java/android/app/IAlarmManager.aidl
package android.app;
import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.WorkSource;
/**
* System private API for talking with the alarm manager service.
*
* {@hide}
*/
interface IAlarmManager {
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
void set(String callingPackage, int type, long triggerAtTime, long windowLength,
long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
boolean setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation, in IAlarmListener listener);
long getNextWakeFromIdleTime();
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
// update the uids being synchronized by network socket request manager
void updateBlockedUids(int uid, boolean isBlocked);
}
这里的内容定义了名称为 IAlarmManager 的接口,它位于android.app 包当中。注意!这不是一个简单的 Java 中的接口,它将生成复杂的内容。
服务端的实现在服务器包中,路径为:frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
class AlarmManagerService extends SystemService {
......
private final IBinder mService = new IAlarmManager.Stub() {
......
@Override
public boolean setTime(long millis) {
getContext().enforceCallingOrSelfPermission(
"android.permission.SET_TIME",
"setTime");
if (mNativeData == 0) {
Slog.w(TAG, "Not setting time since no alarm driver is available.");
return false;
}
synchronized (mLock) {
return setKernelTime(mNativeData, millis) == 0;
}
}
@Override
public void setTimeZone(String tz) {
getContext().enforceCallingOrSelfPermission(
"android.permission.SET_TIME_ZONE",
"setTimeZone");
final long oldId = Binder.clearCallingIdentity();
try {
setTimeZoneImpl(tz);
} finally {
Binder.restoreCallingIdentity(oldId);
}
}
......
}
......
}
AlarmManagerService 中private final IBinder mService实现了 IAlarmManager.Stub 类,其中具有 AlarmManager.aidl 文件中定义的几个方法,这些方法是在 AarmManagerService 类中实现的。
AlarmManagerService 的注册在 frameworks/base/services/java/com/android/server/SystemServer.java 文件中完成,如下所示:
traceBeginAndSlog("StartAlarmManagerService");
mSystemServiceManager.startService(AlarmManagerService.class);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
客户端方面是 Java 框架库的一部分,根据服务名称得到接口过程在core/java/android/app/目录的ContextImpl.java文件中,其中的内容片断如下所示:
AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
客户端调用 Context 的 getSystemService() 方法,通过 Context.ALARM_SERVICE 参数得到一个 AlarmManager 类型的句柄。客户端得到AlarmManager 类型的句柄后就可以调用其中的方法了。
AlarmManager 实际上只是对 IAlarmManager 的一个封装。其中具有同名的方法,并通过调用 IAlarmManager 中的内容实现,AlarmManager 也是警报器管理器对外的 API。各个部分调用 AlarmManager 的时候,实际上就和服务库中实现 AlarmManagerService 产生了远程调用关系。
以上的几个部分内容就是 Java 架层中使用 Binder 机制的所需要手动构建的所有内容。除此之外,根据IAlarmManager.adil 文件还将自动生成 Java 源代码文件,其路径中如下所示:
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app/IAlarmManager.java
IAlarmManager.java 文件的基本结构如下所示:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: frameworks/base/core/java/android/app/IAlarmManager.aidl
*/
package android.app;
/**
* System private API for talking with the alarm manager service.
*
* {@hide}
*/
public interface IAlarmManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.app.IAlarmManager
{
private static final java.lang.String DESCRIPTOR = "android.app.IAlarmManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.app.IAlarmManager interface,
* generating a proxy if needed.
*/
public static android.app.IAlarmManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.app.IAlarmManager))) {
return ((android.app.IAlarmManager)iin);
}
return new android.app.IAlarmManager.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_set:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
long _arg2;
_arg2 = data.readLong();
long _arg3;
_arg3 = data.readLong();
long _arg4;
_arg4 = data.readLong();
int _arg5;
_arg5 = data.readInt();
android.app.PendingIntent _arg6;
if ((0!=data.readInt())) {
_arg6 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
_arg6 = null;
}
android.app.IAlarmListener _arg7;
_arg7 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
java.lang.String _arg8;
_arg8 = data.readString();
android.os.WorkSource _arg9;
if ((0!=data.readInt())) {
_arg9 = android.os.WorkSource.CREATOR.createFromParcel(data);
}
else {
_arg9 = null;
}
android.app.AlarmManager.AlarmClockInfo _arg10;
if ((0!=data.readInt())) {
_arg10 = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(data);
}
else {
_arg10 = null;
}
this.set(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
reply.writeNoException();
return true;
}
case TRANSACTION_setTime:
{
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
boolean _result = this.setTime(_arg0);
reply.writeNoException();
reply.writeInt(((_result)?(1):(0)));
return true;
}
case TRANSACTION_setTimeZone:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setTimeZone(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_remove:
{
data.enforceInterface(DESCRIPTOR);
android.app.PendingIntent _arg0;
if ((0!=data.readInt())) {
_arg0 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
android.app.IAlarmListener _arg1;
_arg1 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
this.remove(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_getNextWakeFromIdleTime:
{
data.enforceInterface(DESCRIPTOR);
long _result = this.getNextWakeFromIdleTime();
reply.writeNoException();
reply.writeLong(_result);
return true;
}
case TRANSACTION_getNextAlarmClock:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
android.app.AlarmManager.AlarmClockInfo _result = this.getNextAlarmClock(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.app.IAlarmManager
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
@Override public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(callingPackage);
_data.writeInt(type);
_data.writeLong(triggerAtTime);
_data.writeLong(windowLength);
_data.writeLong(interval);
_data.writeInt(flags);
if ((operation!=null)) {
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
_data.writeString(listenerTag);
if ((workSource!=null)) {
_data.writeInt(1);
workSource.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
if ((alarmClock!=null)) {
_data.writeInt(1);
alarmClock.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_set, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public boolean setTime(long millis) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(millis);
mRemote.transact(Stub.TRANSACTION_setTime, _data, _reply, 0);
_reply.readException();
_result = (0!=_reply.readInt());
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setTimeZone(java.lang.String zone) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(zone);
mRemote.transact(Stub.TRANSACTION_setTimeZone, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((operation!=null)) {
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_remove, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public long getNextWakeFromIdleTime() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
long _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getNextWakeFromIdleTime, _data, _reply, 0);
_reply.readException();
_result = _reply.readLong();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.app.AlarmManager.AlarmClockInfo _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_getNextAlarmClock, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_set = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setTimeZone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_remove = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_getNextWakeFromIdleTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getNextAlarmClock = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) throws android.os.RemoteException;
public boolean setTime(long millis) throws android.os.RemoteException;
public void setTimeZone(java.lang.String zone) throws android.os.RemoteException;
public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException;
public long getNextWakeFromIdleTime() throws android.os.RemoteException;
public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException;
}
IAlarmManager.java 文件定义的内容主要有以下的部分:
- IAlarmManager 接口实现了 android.os.IInterface,并且声明了 IAlarmManager.aidl 中定义的 set()、setRepeating() 等方法。
- IAlarmManager.Stub 是一个抽象的静态类,它继承自 android.os.Binder,并实现了 IAlarmManager 接口本身。
- IAlarmManager.Stub 中有一个固定方法 asInterface(),参数类型为 IBinder,返回类型为 IAlarmListener。
- IAlarmManager.Stub 类的内部具有众多的方法,实现了基于 Binder 机制的通信。自动生成文件的过程,有工具来完成,并不需要开发者处理。因此在 Java 层中使用 Binder 机制不需要处理最为复杂的通信部分。
3 Binder 中参数和返回值的类型
3.1 类型概述
由于 Java 层具体 Binder 接口通常使用 aidl 文件来描述。其中接口定义的方法涉及参数和返回值类型问题。aidl 接口的方法并不能适用于所有 Java 类型,本质上是受到 Parcel 中能传输类型的限制。
aidl 中能够使用的类型就是以下的几个方面:
- Java 语言的基本类型 (boolean、byte、char、short、int、float 和 double)
- java.lang 中的 CharSequence 和 String
- java.util 中的 List 和 Map<K, V>
- 其他 aidl 文件中定义的接口(需要import)
- 通过 Parcelable 实现的类(需要import)
Java 基本类型是 Parcel 中可以直接支持的,可以作为 aidl 接口的方法的参数和返回值类型,不需要 import 导入。显然,Java 也可以使用它们的数组类型。
CharSequence 和 String 是表示字符串的类,可以作为 aidl 接口的方法的参数和返回值类型,不需要import 导入。List 和 Map<K, V> 是容器类型的模板类,可以使用这两个模板封装其他的类型,作为 aidl 接口的方法的参数和返回值类型,不需要 import 导入。
除了以上的几种情况,还可以使用其他 aidl 文件中定义的接口和通过 Parcelable 实现的类,这两种情况都是需要 import 类才能够使用的。
3.2 其他 aidl 文件中定义的接口作为类型
aidl 文件中定义的接口在本质上将形成一个 IInterface 接口的实现者,并可以转化成 IBinder 类型来使用。因此使用“其他 aidl 文件中定义的接口”作为 aidl 接口的方法的参数和返回值类型的本质是使用 IBinder 类型在 Parcel 中进行传递。
在 Binder 的跨进程通信中,如果需要使用类似回调方面的机制,通常就需要使用另一个 aidl 文件表示回调接口,并在主接口的 aidl 文件中作为方法的参数类型。
例如,在 Java 框架库的代码中的frameworks/core/java/android/app/目录中,具有两个和墙纸管理器相关的 aidl 文件,一个表示主接口(IWallpaperManager.aidl),另一个表示回调接口(IWallpaperManagerCallback.aidl)。
package android.app;
oneway interface IWallpaperManagerCallback {
/**
* Called when the wallpaper has changed
*/
void onWallpaperChanged();
}
IWallpaperManager.aidl对这个回调类型的引用的片断如下所示:
package android.app;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
import android.app.WallpaperInfo;
import android.content.ComponentName;
/** @hide */
interface IWallpaperManager {
......
/**
* Get the wallpaper for a given user.
*/
ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, int which,
out Bundle outParams, int userId);
......
}
由于使用 IWallpaperManagerCallback 作为参数类型,因此需要使用 import 导入这个接口,导入的名称是包名加 aidl 接口的名称。
IWallpaperManager 接口实际上是由 Java 服务库中的一个类实现的,这点和框架层中其他的 aidl 内容相融。IWallpaperManagerCallback 则是在 Java 框架库中实现( WallpaperManager.java ),并作为参数传递给 IWallpaperManager 接口。在实际的运行过程中,Java 服务库中 IWallpaperManager 的实现是通过 IWallpaperManagerCallback 调用 Java 框架库中实现的 onWallpaperChanged() 方法,起到了通知的作用。
3.3 Parcelable 协议作为类型
另外一种可以作为 aidl 接口的方法的参数和返回值类型的内容是 “基于 Parcelable 协议的类”。 Parcelable 协议表示可以使用 Parcel 写入和恢复,实现了 Parcelable 接口就实现了 Parcelable 协议。
Parcelable 是 android.os 包中 Parcelable 接口,其基本的结构如下所示:
public interface Parcelable {
public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
public int describeContents();
public void writeToParcel(Parcel dest, int flags); // 写入Parcel的方法
public interface Creator<T> { //表示“创建者”的子类
public T createFromParcel(Parcel source);
public T[] newArray(int size);
}
public interface ClassLoaderCreator<T> extends Creator<T> {
public T createFromParcel(Parcel source, ClassLoader loader);
}
}
让一个类支持 Parcelable 协议,主要具有以下的步骤:
- 类实现 Parcelable 接口。
- 使用 void writeToParcel(Parcel out)方法完成参数写操作。
- 通过实现 Parcelable.Creator 接口构建一个 Creator,实现静态的创建功能
- 可以创建一个 aidl 文件作为接口(可选)。
在实际的应用过程中,基于 Parcelable 协议的类通常用于传递一个表示复杂数据结构的类。一个实现的实例如下所示:
public final class Rect implements Parcelable {
public int left; //各个类的域
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator<Rect> CREATOR
= new Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in)( //实现创建方法
return new Rect(in);
}
public Rect[] newArray(int size) { //实现建立数组方法
return new Rect[sizel;
}
};
public Rect() {}
public void writeToParcel(Parcelout) {
//依次写入各个成员
out.writeInt(top);
out.writeInt(left);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
//依次读出各个成员
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
Rect 类的本质是一个表示矩形类,其中包含了 4 个整数类型的域(属性)。这个类的结构虽然并不复杂。但是如果希望在 Binder 传递的过程中传递这样一个类,显然 Parcel 并不能直接知道它当中要传递的具体内容是什么。让 Rect 实现 Parcelable 接口的目的,就是为了建立一个 Parcel 的协议,根据这个协议传递具体的数据结构。Parcelabl 协议的本质就是通过调用 writeToParcel() 方法,让实现 Parcelable 接口的类自已决定应该传递的内容是什么。
Android 框架层中具有众多实现了 Parcelable 接口的类,它们可以直接在 aidl 文件中作为参数和返回值的类型使用。
android.os 包中的 Bundle 类就是一个 Parcelable 接口的实现者,其结构如下所示:
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
......
//实现Creator<>
public static final Parcelable.Creator<Bundle> CREATOR =
new Parcelable.Creator<Bundle>() {
@Override
public Bundle createFromParcel(Parcel in) {
return in.readBundle();
}
@Override
public Bundle[] newArray(int size) {
return new Bundle[size];
}
};
@Override
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
try {
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}
......
}
为了让其他的类引用,为 Bundle 类建立一个 frameworks/base/core/java/android/os/Bundle.aidl 文件,内容如下所示:
package android.os;
parcelable Bundle;
以上的内容表示了 android.os 包中的 Bundle 类是 Parcelable 的实现者。在 aidl 文件中类可以通过以下语句来导入 Bundle 作为其参数和返回值的类型:
import android.os.Bundle;
由此,可以直接在 aidl 文件中使用 Android 框架层中实现了 Parcelable 接口的类在实际的开发过程中,真正需要构建一个实现 Parcelable 协议的类的情况并不多见。主要的原因是:
- 对于复杂数据类型的传递,可以拆散成方法中的若干个参数;
- 使用 Bundle、Uri、Bitmap 等已经实现了 Parcelable 的类,尤其 Bundle,可以通过键-值的方式包含任意多的信息,可以完成对复杂数据结构的描述。只有当程序中的确有一个比较特殊的 “数据结构” 需要单独描述的时候,才需要实现 Parcelable 协议。