对Binder机制的一点深入探究(一)

本文探讨了Android中Binder机制在Activity启动过程中的应用,特别是AMS如何创建并传递Token对象给Activity。在Activity与AMS交互时,Token作为Binder对象被传递,AMS能够将接收到的IBinder强转为Token,揭示了Binder机制中的“PY交易”。后续文章将继续剖析从AMS到APP,Token如何转换为native层的JavaBBinder并进行传递。

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写的,感觉还不错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值