- 作者:老汪软件技巧
- 发表时间:2024-09-09 17:01
- 浏览量:
前面介绍的通过ServiceManager添加服务和获取服务,这些服务都是有名称的,我们可以通过ServiceManager来获取它。除此之外Android系统中还有一类Binder服务是匿名它,它们如何让客户端获得代理对象,并且使用呢,本文就一探究竟。
AIDL 介绍
AIDL全称为Android接口定义语言,是Android系统提供的一款可供用户用来抽象IPC的工具,它提供了语法让我们来定义跨进程通讯的服务接口,也就是.aidl文件,它也提供了工具,帮助我们把定义文件专程目标语言的代码。
我们自己使用AIDL创建的服务,或者一部分系统内的服务,比如IWindowSession、IApplicationThread等,这些多是运行在App进程,一般都不是系统服务,因此都是匿名的。我们这里以IApplicationThread来分析AIDL创建的匿名服务是怎么传递Binder给使用端的。
以IApplicationThread来分析,则服务端是我们的App进程,而客户端则是system_server进程。作为AIDL创建的Binder,首先会有一个AIDL文件,这里是IApplicationThread.aidl,其中定义了一些跨进程调用的方法,部分内容如下:
package android.app;
...
oneway interface IApplicationThread {
void scheduleReceiver(in Intent intent, in ActivityInfo info,
in CompatibilityInfo compatInfo,
int resultCode, in String data, in Bundle extras, boolean sync,
int sendingUser, int processState);
@UnsupportedAppUsage
void scheduleStopService(IBinder token);
...
}
当前Android AIDL已经支持生成Java、C++、Rust的代码,对于IApplicationThread这里我们只需关注生成Java版本的代码即可。生成的代码在IApplicationThread当中,大概如下所示:
public interface IApplicationThread extends android.os.IInterface {
public static class Default implements android.app.IApplicationThread {
@Override
public void scheduleReceiver(android.content.Intent
...) throws android.os.RemoteException {}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
public abstract static class Stub extends android.os.Binder
implements android.app.IApplicationThread {
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static android.app.IApplicationThread asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof android.app.IApplicationThread))) {
return ((android.app.IApplicationThread) iin);
}
return new android.app.IApplicationThread.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
public static java.lang.String getDefaultTransactionName(int transactionCode) {
switch (transactionCode) {
case TRANSACTION_scheduleReceiver:{
return "scheduleReceiver";
}
case TRANSACTION_scheduleCreateService: {
return "scheduleCreateService";
}
default: {
return null;
}
}
}
public java.lang.String getTransactionName(int transactionCode) {
return this.getDefaultTransactionName(transactionCode);
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION
&& code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code) {
case INTERFACE_TRANSACTION:{
reply.writeString(descriptor);
return true;
}
}
switch (code) {
case TRANSACTION_scheduleReceiver:{
android.content.Intent _arg0;
_arg0 = data.readTypedObject(android.content.Intent.CREATOR);
...
int _arg8;
_arg8 = data.readInt();
data.enforceNoDataAvail();
this.scheduleReceiver(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8);
break;
}
...
default:{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
private static class Proxy implements android.app.IApplicationThread {
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;
}
@Override
public void scheduleReceiver(
android.content.Intent intent,
...
int processState) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
...
_data.writeTypedObject(intent, 0);
boolean _status = mRemote.transact(
Stub.TRANSACTION_scheduleReceiver, _data, null, android.os.IBinder.FLAG_ONEWAY);
} finally {
_data.recycle();
}
}
...
}
public static final java.lang.String DESCRIPTOR = "android.app.IApplicationThread";
static final int TRANSACTION_scheduleReceiver = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_scheduleCreateService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
...
public int getMaxTransactionId() {
return 57;
}
}
...
public void scheduleReceiver(
android.content.Intent intent,
...
int processState)
throws android.os.RemoteException;
}
对于Java代码,AIDL会生成一个跟AIDL同名的接口,同时继承自IInterface,同时还会创建内部类,分别为Default和Stub。Default为默认实现,大多数情况下是没有的,因为我们自己会实现。而Stub为抽象类,我们自己实现的时候会使用它,它以及继承自Binder,AIDL工具帮助我们把Parcel读写相关的代码已经生成,我们只需要去继承它,实现业务逻辑即可。而Stub中还有一个内部类Proxy,这个类用于Binder服务的客户端使用。对于IApplicationThread的实现,在ActivityThread当中。
匿名服务的传输
那么ActivityServiceManager(之后简称AMS)是怎么拿到ApplicationThread的呢,ActivityThread的attach方法则是这一切的入口:
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
代码中,首先拿到AMS的binder客户端类,这里也就是IActivityManager$Stub$Proxy,因为它也用了AIDL,所以跟我们ApplicationThread的类是类似的,具体如何拿到的,这个之前ServiceManager分析getService的时候已经分析过了,这里不看了。我们可以看一下它的attachApplication方法,代码如下:
@Override public void attachApplication(android.app.IApplicationThread app, long startSeq) 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.writeStrongInterface(app);
_data.writeLong(startSeq);
boolean _status = mRemote.transact(Stub.TRANSACTION_attachApplication, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
这里也是跟我们之前addService类似,把binder写入到Parcel中去,因为App进程这里相当于是ApplicationThread它的服务端,因此这里写入的type为BINDER_TYPE_BINDER,而调用mRemote.transact则为调用BinderProxy的同名方法,我们知道最终会调用到IPCThreadState的transact方法,从而调用binder驱动。
类似于getService的方法,AMS所在的system_server进程会收到BR_TRANSACTION命令,在其中解析数据知道调用的是TRANSACTION_attachApplication这个业务命令,进而使用readStrongInterface来获取到binder的代理对象BinderProxy。具体代码在IActivityManager.Stub的onTransact中,代码如下:
case TRANSACTION_attachApplication:
{
android.app.IApplicationThread _arg0;
_arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder());
long _arg1;
_arg1 = data.readLong();
data.enforceNoDataAvail();
this.attachApplication(_arg0, _arg1);
reply.writeNoException();
break;
}
到这样调用AMS服务端的attachApplication的时候就能使用IApplicationThread所提供的方法了。
Binder驱动中binder节点的处理
但是看到这里,有个问题,就是我们是匿名的binder,驱动怎么是处理的呢。这个需要去看一下binder驱动的源码,不过就不具体看调用流程了,直接看生成创建node和handle部分的代码:
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_node *node;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
struct binder_ref_data rdata;
int ret = 0;
node = binder_get_node(proc, fp->binder);
if (!node) {
node = binder_new_node(proc, fp);
if (!node)
return -ENOMEM;
}
...
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
...
if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE;
else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;
...
done:
binder_put_node(node);
return ret;
}
可以看到对于通过Parcel调用经过binder驱动的binder对象,binder驱动都会给他们创建一个binder_node,并且为其设置handle,在传输到客户端的时候还会把type设置为BINDER_TYPE_HANDLE。这样我们就对整给流程有所了解了,如果读者还想窥探更多的细节,则需要自行去阅读binder驱动的源码了。
开发者如何使用匿名服务
这上面介绍的部分是我们使用系统的服务来获取AIDL创建的服务,对于应用开发者来说有什么办法呢。我们可以通过AIDL+Service来实现,Android的四大组件之一的Service,它提供了通过bindService的方式来启动服务。在它的onBind中就可以返回IBinder,Android Framework会帮助我们调用操作代码如下:
private void handleBindService(BindServiceData data) {
CreateServiceData createData = mServicesData.get(data.token);
Service s = mServices.get(data.token);
if (s != null) {
try {
....
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
...
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
....
}
}
}
可以看到在ActivityThread的handleBindService方法 中,我们在拿到Service所提供的IBinder之后,AMS会调用publishService,我们可以在ServiceConnection回调中拿到Binder的代理对象,之后就可以进行跨进程通讯了。
另外Android Framework还为我们提供了Messenger,其实现为Service+AIDL+Handler,让我们不用自己写AIDL,我们自己定义Service的时候使用Messenger和Handler就可以实现跨进程通信了。
总结
到此为止,我们已经分析了Binder服务管家ServiceManager的启动、使用ServiceManger添加服务和查找服务以及匿名服务的传递,在此过程中我们了解了进程是如何与Binder驱动交互的,以及binder调用过程中的会执行的方法等,我们对于Binder就有了一个全面的了解。在本文还简单介绍 了应用开发者如何使用Binder,有了这些基础,我们后面分析Android系统其他部分的代码就会更加容易了。当然关于Binder驱动的代码,BInder线程池的管理这两块还没有分析,读者感性确可以自行阅读,也可查看其他博主的文章。
如果你也对于Android系统源码感兴趣,欢迎与我交流。博文因为个人局限,也难免会出现差错,也欢迎大家指正。