Android aidl
通常我们在做开发时,实现进程间通信用的最多的就是 AIDL。当我们定义好 AIDL 文件,在编译时编译器会帮我们生成代码实现 IPC 通信。借助 AIDL 编译以后的代码能帮助我们进一步理解 Binder IPC 的通信原理。
但是无论是从可读性还是可理解性上来看,编译器生成的代码对开发者并不友好。比如一个 BookManager.aidl 文件对应会生成一个 BookManager.java 文件,这个 java 文件包含了一个 BookManager 接口、一个 Stub 静态的抽象类和一个 Proxy 静态类。Proxy 是 Stub 的静态内部类,Stub 又是 BookManager 的静态内部类,这就造成了可读性和可理解性的问题。
aidl
IBinder : IBinder 是一个接口,代表了一种跨进程通信的能力。只要实现了这个借口,这个对象就能跨进程传输。
IInterface : IInterface 代表的就是 Server 进程对象具备什么样的能力(能提供哪些方法,其实对应的就是 AIDL 文件中定义的接口)
Binder : Java 层的 Binder 类,代表的其实就是 Binder 本地对象。BinderProxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理;这两个类都继承自 IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。
Stub : AIDL 的时候,编译工具会给我们生成一个名为 Stub 的静态内部类;这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要开发者自己实现。
示例code:
IInterface 代表服务端进程具备的能力
/**
* 这个类用来定义服务端 RemoteService 具备什么样的能力
*/
public interface BookManager extends IInterface {
void addBook(Book book) throws RemoteException;
}
Stub 继承 Binder, 说明它是一个 Binder 本地对象;实现 IInterface 接口,表明具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要调用方自己实现。
public abstract class Stub extends Binder implements BookManager {
...
//通过asInterface将IBinder转化为BookManager这个IInterface对象
//service会在onServiceConnected时传给client一个IBinder对象
//接下来通过asInterface进行转换就可以进行IPC了
public static BookManager asInterface(IBinder binder) {
if (binder == null)
return null;
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof BookManager)
return (BookManager) iin;
return new Proxy(binder);
}
...
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSAVTION_addBook:
data.enforceInterface(DESCRIPTOR);
Book arg0 = null;
if (data.readInt() != 0) {
arg0 = Book.CREATOR.createFromParcel(data);
}
this.addBook(arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
...
}
asInterface会去调用 binder.queryLocalInterface() 去查找 Binder 本地对象,如果找到了就说明 Client 和 Server 在同一进程,那么这个 binder 本身就是 Binder 本地对象,可以直接使用。否则说需要我们创建一个代理对象 Proxy,通过这个代理对象来是实现远程访问。
public class Proxy implements BookManager {
...
//IBinder是onServiceConnected服务端传进来的
public Proxy(IBinder remote) {
this.remote = remote;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0);
replay.readException();
} finally {
replay.recycle();
data.recycle();
}
}
...
}
我们看看 addBook() 的实现;在 Stub 类中,addBook(Book book) 是一个抽象方法,Client 端需要继承并实现它。
由于queryLocalInterface的存在,如果 Client 和 Server 在同一个进程,那么直接就是调用这个方法,如果是远程调用,Client 想要调用 Server 的方法就需要通过 Proxy来实现
在 Proxy 中的 addBook() 方法中首先将数据序列化,然后调用 remote.transact()。这里的 remote 是个 BinderProxy 对象。最终通过一系列的函数调用,Client 进程通过系统调用陷入内核态,Client 进程中执行 addBook() 的线程挂起等待返回;驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact()。最终又走到了 Stub 中的 onTransact() 中,onTransact() 根据函数编号调用相关函数(在 Stub 类中为 BookManager 接口中的每个函数中定义了一个编号,只不过上面的源码中我们简化掉了;在跨进程调用的时候,不会传递函数而是传递编号来指明要调用哪个函数);我们这个例子里面,调用了 Binder 本地对象的 addBook() 并将结果返回给驱动,驱动唤醒 Client 进程里刚刚挂起的线程并将结果返回。这样一次跨进程调用就完成了。
server进程 Service类
public class RemoteService extends Service {
private List<Book> books = new ArrayList<>();
public RemoteService() {
}
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("三体");
book.setPrice(88);
books.add(book);
}
//在这里返回给client IBinder对象
@Override
public IBinder onBind(Intent intent) {
return bookManager;
}
private final Stub bookManager = new Stub() {
@Override
public List<Book> getBooks() throws RemoteException {
synchronized (this) {
if (books != null) {
return books;
}
return new ArrayList<>();
}
}
@Override
public void addBook(Book book) throws RemoteException {
synchronized (this) {
if (books == null) {
books = new ArrayList<>();
}
if (book == null)
return;
book.setPrice(book.getPrice() * 2);
books.add(book);
Log.e("Server", "books: " + book.toString());
}
}
};
}
client的使用
private BookManager bookManager;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConnection = true;
bookManager = Stub.asInterface(service);
if (bookManager != null) {
try {
List<Book> books = bookManager.getBooks();
Log.d("ClientActivity", books.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isConnection = false;
}
};
AMS里的IPC
AOSP21
以AOSP21 的finish Activity为例
Activity类
/**
* Finishes the current activity and specifies whether to remove the task associated with this
* activity.
*/
private void finish(boolean finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
resultData.prepareToLeaveProcess();
}
//在这里调用了ActivityManagerNative.getDefault()
if (ActivityManagerNative.getDefault()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
} else {
mParent.finishFromChild(this);
}
}
调用ActivityManagerNative.getDefault()返回结果后然后调用finishActivity,这个过程就是一个IPC过程,最终会调用到ActivityManagerService的finishActivity
来看看ActivityManagerNative.getDefault()
/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
Singleton类是一个用来保存单例数据的类,泛型即为需要保存的单例对象
这里保存的为IActivityManager对象,定义了客户端和服务端方法调用的协议
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* System private API for talking with the activity manager service. This
* provides calls from the application back to the activity manager.
*
* {@hide}
*/
public interface IActivityManager extends IInterface {
public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
throws RemoteException;
/** Information returned after waiting for an activity start. */
public static class WaitResult implements Parcelable {
public int result;
public boolean timeout;
public ComponentName who;
public long thisTime;
public long totalTime;
public WaitResult() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
dest.writeInt(timeout ? 1 : 0);
ComponentName.writeToParcel(who, dest);
dest.writeLong(thisTime);
dest.writeLong(totalTime);
}
public static final Parcelable.Creator<WaitResult> CREATOR
= new Parcelable.Creator<WaitResult>() {
@Override
public WaitResult createFromParcel(Parcel source) {
return new WaitResult(source);
}
@Override
public WaitResult[] newArray(int size) {
return new WaitResult[size];
}
};
private WaitResult(Parcel source) {
result = source.readInt();
timeout = source.readInt() != 0;
who = ComponentName.readFromParcel(source);
thisTime = source.readLong();
totalTime = source.readLong();
}
}
String descriptor = "android.app.IActivityManager";
// Remaining non-native transaction codes.
int FINISH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+10;
}
在create方法里,ServiceManager.getService会返回对应名字的远端服务进程的Stub对象,再通过asInterface进行转换
/**
* Cast a Binder object into an activity manager interface, generating
* a proxy if needed.
*/
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
重点看ActivityManagerProxy,这里定义了client如何远程调用server的方法
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeInt(resultCode);
if (resultData != null) {
data.writeInt(1);
resultData.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
data.writeInt(finishTask ? 1 : 0);
mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
return res;
}
}
接下来Client 进程通过系统调用陷入内核态,Client 进程中执行 finishActivity() 的线程挂起等待返回;驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact()。最终又走到了 Stub 中的 onTransact() 中
这里的server即为ActivityManagerService,真正执行命令在ActivityManagerService的finishActivity方法
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
/**
* This is the internal entry point for handling Activity.finish().
*
* @param token The Binder token referencing the Activity we want to finish.
* @param resultCode Result code, if any, from this Activity.
* @param resultData Result data (Intent), if any, from this Activity.
* @param finishTask Whether to finish the task associated with this Activity. Only applies to
* the root Activity in the task.
*
* @return Returns true if the activity successfully finished, or false if it is still running.
*/
@Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
boolean finishTask) {
// Refuse possible leaked file descriptors
if (resultData != null && resultData.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return true;
}
// Keep track of the root activity of the task before we finish it
TaskRecord tr = r.task;
ActivityRecord rootR = tr.getRootActivity();
// Do not allow task to finish in Lock Task mode.
if (tr == mStackSupervisor.mLockTaskModeTask) {
if (rootR == r) {
mStackSupervisor.showLockTaskToast();
return false;
}
}
if (mController != null) {
// Find the first activity that is not finishing.
ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
try {
resumeOK = mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mController = null;
Watchdog.getInstance().setActivityController(null);
}
if (!resumeOK) {
return false;
}
}
}
final long origId = Binder.clearCallingIdentity();
try {
boolean res;
if (finishTask && r == rootR) {
// If requested, remove the task that is associated to this activity only if it
// was the root activity in the task. The result code and data is ignored because
// we don't support returning them across task boundaries.
res = removeTaskByIdLocked(tr.taskId, 0);
} else {
res = tr.stack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
}
return res;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
分析一下如果我们要手动通过client端的AMS来finish Activity怎么做
override fun finishLaunchActivity(message: Message) {
try {
val activityClientRecord = message.obj
val tokenField = activityClientRecord.javaClass.getDeclaredField("token")
tokenField.isAccessible = true
val binder = tokenField[activityClientRecord] as IBinder
finish(binder)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
private fun finish(binder: IBinder) {
//这里的binder即为识别activity的token
val activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative")
val getDefaultMethod = activityManagerNativeClass.getDeclaredMethod("getDefault")
val activityManager = getDefaultMethod.invoke(null)
val finishActivityMethod = activityManager.javaClass.getDeclaredMethod(
"finishActivity",
IBinder::class.java,
Int::class.javaPrimitiveType,
Intent::class.java,
Boolean::class.javaPrimitiveType
)
finishActivityMethod.invoke(
activityManager,
binder, Activity.RESULT_CANCELED, null, false
)
}
AOSP 30
/**
* Finishes the current activity and specifies whether to remove the task associated with this
* activity.
*/
@UnsupportedAppUsage
private void finish(int finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
//在这里finish Activity
if (ActivityTaskManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
} else {
mParent.finishFromChild(this);
}
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.
if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
restoreAutofillSaveUi();
}
}
接着分析ActivityTaskManager
@SystemService(Context.ACTIVITY_TASK_SERVICE)
public class ActivityTaskManager {
/** @hide */
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
}
最终会调用到ActivityTaskManagerService的finishActivity
/**
* This is the internal entry point for handling Activity.finish().
*
* @param token The Binder token referencing the Activity we want to finish.
* @param resultCode Result code, if any, from this Activity.
* @param resultData Result data (Intent), if any, from this Activity.
* @param finishTask Whether to finish the task associated with this Activity.
*
* @return Returns true if the activity successfully finished, or false if it is still running.
*/
@Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
// Refuse possible leaked file descriptors
if (resultData != null && resultData.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
final ActivityRecord r;
synchronized (mGlobalLock) {
r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return true;
}
}
// Carefully collect grants without holding lock
final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);
synchronized (mGlobalLock) {
// Sanity check in case activity was removed before entering global lock.
if (!r.isInHistory()) {
return true;
}
// Keep track of the root activity of the task before we finish it
final Task tr = r.getTask();
final ActivityRecord rootR = tr.getRootActivity();
if (rootR == null) {
Slog.w(TAG, "Finishing task with all activities already finished");
}
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
// finish.
if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}
// TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
// We should consolidate.
if (mController != null) {
// Find the first activity that is not finishing.
final ActivityRecord next =
r.getRootTask().topRunningActivity(token, INVALID_TASK_ID);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
try {
resumeOK = mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mController = null;
Watchdog.getInstance().setActivityController(null);
}
if (!resumeOK) {
Slog.i(TAG, "Not finishing activity because controller resumed");
return false;
}
}
}
// note down that the process has finished an activity and is in background activity
// starts grace period
if (r.app != null) {
r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis());
}
final long origId = Binder.clearCallingIdentity();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
try {
boolean res;
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
// If requested, remove the task that is associated to this activity only if it
// was the root activity in the task. The result code and data is ignored
// because we don't support returning them across task boundaries. Also, to
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
mStackSupervisor.removeTask(tr, false /*killProcess*/,
finishWithRootActivity, "finish-activity");
res = true;
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
r.finishIfPossible(resultCode, resultData, resultGrants,
"app-request", true /* oomAdj */);
res = r.finishing;
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
}
return res;
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
}