对Binder机制的一点深入探究(一)
1.背景
之所以研究这个问题是因为看到应用程序和AMS交互的过程中,有些代码让我觉得很神奇如下所示:
Activity的启动过程
我们知道系统在启动一个Activity的时候会先创建一个ActivityRecord对象,然后在ActivityRecord的构造函数中又会创建一个Token对象
appToken = new Token(this, service);
static class Token extends IApplicationToken.Stub{
...
}
可以看出Token是个Binder类型的对象.然后在启动Activity的时候,会把该Token通过binder机制传递给应用,在performLaunchActivity方法中会实例化Activity对象,然后调用Activity的attarch方法,最终Activity会把Token保存起来。
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);
总结:在该过程中AMS会创建一个Token对象,把该Token对象传递给Activity。
Activity主动与AMS交互过程
我们以Activity的getTaskId方法为例说明该过程
public int getTaskId() {
try {
return ActivityManagerNative.getDefault()
.getTaskForActivity(mToken, false);
} catch (RemoteException e) {
return -1;
}
}
public int getTaskForActivity(IBinder token, boolean onlyRoot) {
synchronized(this) {
return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
}
}
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return INVALID_TASK_ID;
}
final TaskRecord task = r.task;
final int activityNdx = task.mActivities.indexOf(r);
if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
return INVALID_TASK_ID;
}
return task.taskId;
}
static ActivityRecord forTokenLocked(IBinder token) {
try {
return Token.tokenToActivityRecordLocked((Token)token);
} catch (ClassCastException e) {
Slog.w(TAG, "Bad activity token: " + token, e);
return null;
}
}
getTaskId是Activity中的方法,该activity在与AMS打交道的时候传递了Token对象,用来识别自己身份,在forTokenLocked中,我们发现,骚操作出现了,竟然把IBinder类型的对象强转为Token类型的对象。
大家不觉得这个很神奇么,Token的本尊在system_process,通过binder机制,app侧拿到的只是代理对象,该app又把代理对象通过binder机制传回给system_process,AMS中居然可以强转该代理对象为Token的本尊,为什么不是代理对象的代理对象呢?这中间肯定有见不得人的PY交易。本系列文章就打算研究一下这个PY交易。
我们就研究此Token对象传递的过程,来解密binder神秘的面纱,看看java层的Binder和BinderProxy对象,native层的BpBinder和BBbinder到底是怎么回事。
2.从AMS到APP,Token的传递过程
Activity的启动流程我们不关注,所以这部分知识大家可以从别的地方了解,本文重点关注Token的传递流程。
Activity启动的最后一步是realStartActivityLocked方法,该方法中有如下调用:
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
接着调用到ApplicationThreadProxy的scheduleLaunchActivity方法
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
data.writeStrongBinder(token);
data.writeInt(ident);
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
if (overrideConfig != null) {
data.writeInt(1);
overrideConfig.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
compatInfo.writeToParcel(data, 0);
data.writeString(referrer);
data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
data.writeInt(procState);
data.writeBundle(state);
data.writePersistableBundle(persistentState);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
data.writeInt(notResumed ? 1 : 0);
data.writeInt(isForward ? 1 : 0);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
重点是调用data.writeStrongBinder(token);
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
此时出现了一个重要的方法ibinderForJavaObject,从名字来看该方法是要把java的对象转换成native层的ibinder对象,实际作用也确实如此。
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
if (obj == NULL) return NULL;
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
return jbh != NULL ? jbh->get(env, obj) : NULL;
}
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
}
ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
class JavaBBinderHolder : public RefBase
{
public:
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, obj);
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
}
return b;
}
sp<JavaBBinder> getExisting()
{
AutoMutex _l(mLock);
return mBinder.promote();
}
private:
Mutex mLock;
wp<JavaBBinder> mBinder;
};
让我们对上面的代码做一下解释。ibinderForJavaObject方法中会调用IsInstanceOf对传下来的java对象做判断,判断结果有两种类型。
其中gBinderProxyOffsets.mClass对应android/os/BinderProxy,gBinderOffsets.mClass对应android/os/Binder,回到我们上面的内容static class Token extends IApplicationToken.Stub,Token继承子Stub类,熟悉Binder的同学都知道,Stub类是编译器根据aidl自动为我们生成的对象,而Stub是继承自android.os.Binder,所以会走进去第一个判断的逻辑。
其实每一个java层的Binder对象在初始化的时候同时会在navive初始化一个JavaBBinderHolder对象,然后native层要操作java层的Binder对象时候会把该对象包装成一个JavaBBinder对象,保存在JavaBBinderHolder中,其中会用到智能指针等知识,这个我不太懂,不影响我们分析流程,从JavaBBinderHolder的get方法可以看到其中的逻辑。
总结:Token类是继承自Binder,从AMS传递给APP层的时候,首先先把该对象转换成native层的对象JavaBBinder,然后native层操作的是JavaBBinder对象
这篇文章先到这里,说太多,不太好。另外这篇文章是用MarkDown写的,感觉还不错