AMS系列②—Binder通信的双向代理绑定

Binder进程见通信是通过远程代理来实现的,SystemServer进程APP用户进程使用Binder进行通信,必须建立双向的代理绑定,即:一个进程即使客户端也是服务端,这样才能实现双工通道


只有持有对方进程的代理对象,才能通过代理对象控制真实对象做事情,代理对象必须是一个Binder接口,因为它要依赖Binder帮我们做底层的进程间的数据传递,下面从源码分析下,这个双向代理的建立过程:

App进程创建AMS代理 (APP作为客户端)

上一篇文章介绍了ams的创建的具体细节,在 startBootstrapServices ()开启引导服务这个方法的分析4中:调用了AMS的setSystemProcess()这个方法,在这个方法中,将我们的SystemServer进程(从APP进程的角度:远程进程,服务端进程)的各种服务(包括AMS本身)都添加到ServiceManager这个类中:

    public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_HIGH);
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this),
                        /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));
    }

不知道大家在学习Bidner的时候也没有关注到ServiceManager这个东西,简单在过一下Binder吧:

Binder通信模型中有四个东西:
Binder驱动:谷歌在linux层面嵌入的一个插件/驱动,实现进程间通信的底层细节,比如调用mmap实现内存映射,调用copy_from_user实现内存拷贝;
ServiceManager:本质是一个进程,由Binder驱动控制,它的作用是维护了一张表service_manager表,这张表里面存放了所有的服务端进程的记录(具体记录的是什么我忘了,类似具柄引用什么的)
客户端进程:进程通信的C端进程
服务端进程:进程通信的S端进程
首先服务端进程向Binder驱动申请成为服务端,那么服务端进程就会存放到ServiceManager这张表中,当客户端需要通信时,可以向Binder驱动申请查询对应的服务端,如果查询到,就会返回一个服务端的代理给客户端,那么客户端就可以通过这个代理去让服务端的真实对象做事情;

在这里的Servicemanager添加的各种服务,就会被添加到ServiceManager进程的service_manager表中,这样就完成了服务端(SystemServer进程的服务)的注册;

App创建AMS代理:

在AMS的setSystemProcess中想ServiceManager表中注册了之后,就可以通过ServiceManager.getService()向Binder驱动申请获取这个服务的IBinder对象:

    IBinder b = ServiceManager.getService("activity");

在APP进程,AMS的代理是一个叫做iActivityManager的对象,这个玩意儿在7.0和8.0还不一样,我们先看7.0的版本:

7.0 Nougat

在7.0中又一个ActivityManagerNative类,用户进程在startActivity的时候(具体是在Instrumentation.execStartActivity的时候,不清楚的可以看前面的文章),会调用AMN的getDefault()获取AMS的代理:ActivityManagerProxy对象;(这个ActivityManagerProxy是IActivityManager的子类)

Instrumentation.execStartActivity():
int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
ActivityManagerNative.getDefault():
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

   private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            IActivityManager am = asInterface(b);
            return am;
        }
    };

    static public IActivityManager asInterface(IBinder obj) {
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        return new ActivityManagerProxy(obj);
    }

这里的逻辑也非常简单,首先通过ServiceManager.getService()向底层的Binder注册表service_manager查询AMS的IBinder对象,然后将AMS的iBinder对象封装到ActivityManagerProxy中:

   class ActivityManagerProxy implements IActivityManager{
        public ActivityManagerProxy(IBinder remote)
        {
        mRemote = remote;
        }
  }
  • AMN是服务端进程(SystemServer)的,它实现了Binder接口,iActivityManager接口,AMS是其的子类;AMN作为服务端被代理的对象,所有的实际功能都是尤其子类AMS实现的;
  • AMP是客户端进程的,拥有一个Binder成员变量(AMS的iBinder对象,从ServiceManager表中查询到的代理对象),AMP作为客户端的代理对象,它通过mRemote调用的方法都会代理到服务端的AMN中,而AMS继承了AMN,所以最终都是由AMS进程处理的;
8.0 Oreo
Instrumentation.execStartActivity():
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0, null, options);
ActivityManager.getService():
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

这里的处理方式和7.0如出一辙,首先通过ServiceManager获取ams的iBinder对象,然后通过这个对象构造出一个iActivityManager对象;

  • IActivityManager是一个aidl接口,它编译后会生成一个对应的java类,这个类有一个内部类Stub,要实现跨进程通信只要服务端继承iActivityManager.Stub就好;

  • 服务端的AMS继承iActivityManager.Stub后,就可以和客户端的ActivityManager实现跨进程通信

public class ActivityManagerService extends IActivityManager.Stub

SystemServer 进程创建App进程的代理 (SystemServer进程作为客户端)

回忆一下用户进程的入口ActivityThread的main中的初始化做了哪些事情:

    public static void main(String[] args) {
        Looper.prepareMainLooper();
        thread.attach(false, startSeq);
        Looper.loop();
    }

重点关注ActivityThread的attach() :

ActivityThread.attach():
    private void attach(boolean system, long startSeq) {

           final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);

    }

这里调用了AMS在用户进程的代理iActivityManager的attachApplication(),会执行AMS的attachApplication():注意这里传入的参数,是当前ActivityThread的ApplicationThread对象,ApplicationThread是ActivityThread的内部类,继承了IApplicationThread.Stub

public final class ActivityThread extends ClientTransactionHandler {
    final ApplicationThread mAppThread = new ApplicationThread();

    private class ApplicationThread extends IApplicationThread.Stub {  }
}

我们在看看传给AMS后,AMS做了什么事情:

    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        }
    }

   private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
          //  分析1
          app.makeActive(thread, mProcessStats);

                //  分析2
                thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
    }
  • app是一个ProcessRecord对象,ProcessRecord是AMS的一个内部类,是对一个进程的封装,包含了一个用户进程的所有信息;将用户进程传入的ApplicationThread对象通过makeActive()保存到ProcessRecord中,以后当AMS需要向APP发起Binder通信时,就可以直接通过app.thread发起一次Binder通信;

  • 这里就是发起了一次Binder,最终会调用服务端(APP进程)ApplicationThread的bindApplication(); 在这里App进程就知道刚刚发起的attach成功了;

整体梳理:
  • 在AMS构造的时候,会调用ServiceManager.addService()将SystemServer进程的各种需要跨进程的服务保存起来,本质是保存到Binder驱动确定的ServiceManager进程的service_manager表中;
  • Binder通信的本质是通过客户端代理和服务端真实对象实现的,所以AMS进程和APP进程需要实现双工通信,必须同时是客户端和服务端(即有代理对象也有真实对象)
  • App进程作为客户端,AMS作为服务端:首先通过ServiceManager.getService()获取AMS的Binder对象;然后对这个对象封装到ActivityManagerProxy中,这个AMP就是AMS在APP进程本地的代理;
  • App进程作为服务端,AMS作为客户端:在APP的入口main中通过AMS在本地的代理发起一次Binder通信,将本地的Binder(ApplicationThread)发送给AMS进程,AMS接收到了以后将其保存在自己的ProcessRecord中,以后当AMS需要向APP发起跨进程通信时,只需要通过ProcessRecord中保存的APP进程的代理即可;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342