從AILD與bindService談Binder進程間通信原理(下)

從AILD與bindService談Binder進程間通信原理(下)


上文回顧

從AILD與bindService談Binder進程間通信原理(上)

上一篇文章主要講述了,在Client進程向AMS所在進程發起bindService操作的服務請求的過程中,Binder是如何跨進程通信的。實際上,Java層的Binder進程間通信都是同樣的流程。主要有以下2點:
1,A進程持有B進程的IBinder對象(BinderProxy對象,對應原生層的BpBinder對象),通過這個Binder代理A進程即可與B進程進程間通信。
2,Binder代理的獲取有兩種方式,一種是通過查詢ServiceManager獲取,一種是通過一箇中間進程(例如AMS進程)互相交換各自binder代理(實質上ServiceManager也是一箇中間進程,不過他把服務進程的binder代理給保存下來)。

正文

接下來,我們將分析剩餘的bindService所涉及的操作步驟:

這裏寫圖片描述

上篇文章講述到在第22-27步中, 在AMS的Binder對象ActivityManagerNative中解包Parcel,獲取Client進程傳遞過來的參數,主要包括Client進程的2個BinderProxy,Client創建的用於bindeService操作的ServiceConnection對象的BinderProxy,以及Intent對象。最後調用ActivityManagerService類中的bindeService方法。

28,AMS會執行自身的成員變量mServices的bindServiceLocked方法。mServices是一個ActiveServices對象。ActiveServices纔是AMS中真正管理Service的實現類。

  int bindServiceLocked(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) {

        …………………………………………………………………………………………………………

        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType,
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);


        ServiceRecord s = res.record;

        …………………………………………………………………………………………………………

        try {

            …………………………………………………………………………………………………………

            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);//yyt

            IBinder binder = connection.asBinder();
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);
            }
            clist.add(c);//yyt
            b.connections.add(c);
            if (activity != null) {
                if (activity.connections == null) {
                    activity.connections = new HashSet<ConnectionRecord>();
                }
                activity.connections.add(c);
            }
            b.client.connections.add(c);
            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                b.client.hasAboveClient = true;
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app, c, true);
            }
            clist = mServiceConnections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                mServiceConnections.put(binder, clist);
            }
            clist.add(c);

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                    return 0;
                }
            }
            ………………………………………………………………………………………………………………………………………………………………

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }

在bindServiceLocked方法中,首先調用retrieveServiceLocked方法檢索將要啓動的Service所在,是否是在本應用中的Service,通過Service的name和filter來進行檢索,找到目標Service後,查看權限(permission是否滿足),如果該Service是第一次被檢索,則會創建一個新的ServiceRecord對象保存這個Service的信息,這個ServiceRecord會在bringUpServiceLocked方法中被保存到一個mPendingServices數組集合中。

然後會把剛纔的ServiceConnection對象封裝成一個ConnectionRecord對象,並存入ServiceRecord的相關成員列表中。接着再調用bringUpServiceLocked方法

29,在bringUpServiceLocked方法中,判斷目標Service所在的app進程是否已經啓動,如果已經啓動則直接調用realStartServiceLocked方法啓動Service;如果app進程沒啓動,即爲null,則調用startProcessLocked啓動目標Service所在的目標app進程。


    private final String bringUpServiceLocked(ServiceRecord r,
            int intentFlags, boolean execInFg, boolean whileRestarting) {

    …………………………………………………………………………………………

        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        final String procName = r.processName;
        ProcessRecord app;

        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            }
        } else {
            // If this service runs in an isolated process, then each time
            // we call startProcessLocked() we will get a new isolated
            // process, starting another process if we are currently waiting
            // for a previous process to come up.  To deal with this, we store
            // in the service any current isolated process it is running in or
            // waiting to have come up.
            app = r.isolatedProc;
        }

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }

        if (!mPendingServices.contains(r)) {
            mPendingServices.add(r);
        }

       ………………………………………………………………………………………………………………

        return null;
    }

在啓動完目標進程後,會把剛纔28中創建的ServiceRecord對象添加到mPendingServices數組集合中去,mPendingServices保存的是將要啓動的Service記錄。因此,由28可知,Client進程傳入的ServiceConnection對象的binder代理實際就是保存在這裏面。

   if (!mPendingServices.contains(r)) {
            mPendingServices.add(r);
        }

30,startProcessLocked方法是在AMS中實現的,最終將調用Process.start方法啓動目標進程。接着會調用startViaZygote,最後調用zygoteSendArgsAndGetResult,發送Socket請求給ZygoteInit進程。

31,在ZygoteInit的runSelectLoop方法中,獲取Socket傳來的消息,並執行,最終通過 Zygote.forkAndSpecialize方法fork一個新的進程。

32,新ActivityThread進程創建,調用其入口方法main。ActivityThread即應用的主線程,也叫做UI線程。這個main方法就是所有應用進程的入口點,

  • 創建了Looper對象,並開啓Looper.loop()循環
  • 調用自身attach方法,會調用AMS的attachApplication方法,最終會執行自己的bindApplication方法
    • 待補充
  • 啓動AsyncTask的init,這就是爲什麼主線程才能使用AsyncTask。
//ActivityThread.java
  public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new AndroidKeyStoreProvider());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        AsyncTask.init();

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

33-34,在attach方法中,又獲取了ActivityManagerNative的binder代理mgr,然後調用mgr.attachApplication(mAppThread)方法,即又通過AMS的Binder代理與AMS遠程通信,實際執行AMS中的attachApplication方法。

            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }

主意這裏的參數是mAppThread,是ActivityThread創建時一同創建的,是一個Binder對象,主要就是用來與AMS進行進程間通信的,這個Binder會傳給AMS,然後AMS就是通過這個Binder對應的BinderProxy與對應的應用進程通信。

final ApplicationThread mAppThread = new ApplicationThread();

35,這裏省略了進程間通信的詳細過程(如3-27步驟所述),這裏進程間通信的參數就只有一個剛纔傳入的mAppThread對象對應的BinderProxy對象。

36,最終attachApplication會調用AMS中的attachApplicationLocked方法。在四大組件的啓動過程中,如果對應的進程沒有創建,那麼都會最終調用到這個方法。在這個方法中會啓動待啓動的四大組件。

    private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {

        ProcessRecord app;
        …………………………………………………………………………

        app.makeActive(thread, mProcessStats);//把thread對象保存到app中,thread即剛纔所述的服務器進程的BinderProxy對象。

        ………………………………………………………………

        thread.bindApplication(processName, appInfo, providers,app.instrumentationClass,profilerInfo, app.instrumentationArguments,app.instrumentationWatcher,app.instrumentationUiAutomationConnection,testMode,enableOpenGlTrace,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(mConfiguration), app.compat,getCommonServicesLocked(),mCoreSettingsObserver.getCoreSettingsLocked());//通過thread與服務器進程通信,調用ActivityThread中的bindApplication方法,進程的ContentProvider組件在其中啓動

        …………………………………………………………………

        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                //啓動待啓動的Activity
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }

        // Find any services that should be running in this process...
        if (!badApp) {
            try {
                //啓動待啓動的Service
                didSomething |= mServices.attachApplicationLocked(app, processName);
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
                badApp = true;
            }
        }

        // Check if a next-broadcast receiver is in this process...
        if (!badApp && isPendingBroadcastProcessLocked(pid)) {
            try {
                //啓動待啓動的廣播
                didSomething |= sendPendingBroadcastsLocked(app);
            } catch (Exception e) {
                // If the app died trying to launch the receiver we declare it 'bad'
                Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
                badApp = true;
            }
        }
        ………………………………………………………………………………
    }

在該方法中主要把服務端的BinderProxy保存到app變量中,然後通過該BinderProxy遠程調用服務器進程的bindApplication方法;最後準備就緒,正式啓動組件,因爲我們是通過bindSerivice方法調用的,因此會調用mServices.attachApplicationLocked(app, processName)啓動一個Service。如果是startActivity方法那麼通過mStackSupervisor.attachApplicationLocked(app)啓動Activity。

37-38,先說AMS通過服務進程的binder代理遠程調用服務器進程的bindApplication方法,該方法會把參數打包成Message,然後提交給ActivityThread中的Handler對象進行處理,最終會調用handleBindApplication方法。


    private void handleBindApplication(AppBindData data) {

        …………………………………………………………………………

        …………………………………………………………………………

        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                List<ProviderInfo> providers = data.providers;
                if (providers != null) {
                    installContentProviders(app, providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

            try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        } finally {
            StrictMode.setThreadPolicy(savedPolicy);
        }
    }

該方法做了一系列的初始化,創建了進程包含的Application對象,然後如果該服務攜帶ContentProvider對象,則初始化該ContentProvider,並調用其onCreate方法,最後調用Application的onCreate方法。

39,在36步驟中,AMS的attachApplicationLocked方法會調用mServices.attachApplicationLocked,即調用ActiveServices中的的attachApplicationLocked,之前提到過ActiveServices是AMS真正處理Service的類。

//ActiveServices.java

    boolean attachApplicationLocked(ProcessRecord proc, String processName)
            throws RemoteException {
        boolean didSomething = false;
        // Collect any services that are waiting for this process to come up.
        if (mPendingServices.size() > 0) {
            ServiceRecord sr = null;
            try {
                for (int i=0; i<mPendingServices.size(); i++) {
                    sr = mPendingServices.get(i);
                    if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
                            || !processName.equals(sr.processName))) {
                        continue;
                    }

                    mPendingServices.remove(i);
                    i--;
                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
                            mAm.mProcessStats);
                    realStartServiceLocked(sr, proc, sr.createdFromFg);//啓動該服務
                    didSomething = true;
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception in new application when starting service "
                        + sr.shortName, e);
                throw e;
            }
        }
     ……………………………………
        return didSomething;
    }

之前28,29步驟中提到ServiceRecord被存入mPendingServices中,代表其等待被啓動。因此,現在服務所在的進程已經啓動,是時候從mPendingServices中取出待啓動的ServiceRecord,並通過realStartServiceLocked方法啓動該服務。

40,在realStartServiceLocked方法中,也是通過剛纔36中保存的服務端的BinderProxy對象來遠程調用服務進程中的scheduleCreateService方法和scheduleBindService方法,先是Create服務,然後是Bind服務。

//ActiveServices.java
private final void realStartServiceLocked(ServiceRecord r,ProcessRecord         app, boolean execInFg) throws RemoteException {

        …………………………………………………………………………    app.thread.scheduleCreateService(r,r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);      
 requestServiceBindingsLocked(r, execInFg);//該方法中調用scheduleBindService

        …………………………………………………………………………
    }

41-42,我們只關注bind方法,scheduleBindService方法最終會如38中一樣通過Handler處理請求,調用ActivityThread中的handleBindService方法。

//ActivityThread.java
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().
                        publishService(data.token, data.intent, binder);

該方法首先調用Service的onBind方法,返回需要被綁定的IBinder對象,即AIDL通信過程中創建的Stub對象。
然後把onBind方法返回的Binder對象作爲參數,調用AMS的Binder代理,發送給AMS進程,並遠程調用AMS的publishService方法。

43,AMS中的publishService方法也是直接調用ActiveServices中的publishServiceLocked方法。

//ActivityManagerService.java
    public void publishService(IBinder token, Intent intent, IBinder service) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        }
    }

觀察這裏參數有三個,第一個是一個ServiceRecord對象,我們追溯這個ServiceRecord對象發現其實就是在39中所述的mPendingServices取出的待啓動的Service記錄ServiceRecord。因此,我們可以猜測,所謂的token其實都是應用進程保存在AMS中的Binder記錄;第二個參數是一個intent對象,攜帶數據;第三個參數service就是剛纔Service服務onBind方法返回的AIDL用於通信的BinderProxy對象。

44,在ActiveServices的publishServiceLocked方法中,主要就是獲取之前保存的Client進程創建的ServiceConnection對象的BinderProxy對象。通過該對象與Client中的ServiceConnection對象進行交互,然後把Service服務端onBind方法返回的AIDL用於通信的BinderProxy對象遠程傳遞給Client進程。當Client進程擁有Service服務進程用於進程間通信的Binder代理時,即可進行進程間通信。

//ActiveServices.java
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
                    + " " + intent + ": " + service);
            if (r != null) {
                Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
                IntentBindRecord b = r.bindings.get(filter);
                if (b != null && !b.received) {
                    b.binder = service;
                    b.requested = true;
                    b.received = true;
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) {
                            ConnectionRecord c = clist.get(i);
                            if (!filter.equals(c.binding.intent.intent)) {
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG, "Not publishing to: " + c);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG, "Bound intent: " + c.binding.intent.intent);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG, "Published intent: " + intent);
                                continue;
                            }
                            if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
                            try {
                                c.conn.connected(r.name, service);//關鍵語句
                            } catch (Exception e) {
                                Slog.w(TAG, "Failure sending service " + r.name +
                                      " to connection " + c.conn.asBinder() +
                                      " (in " + c.binding.client.processName + ")", e);
                            }
                        }
                    }
                }

                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

如28步驟中所述,Client進程的ServiceConnection對象的Binder代理就保存在ServiceRecord的ConnectionRecord中,最終調用 c.conn.connected(r.name, service)方法。

45,其中conn是一個InnerConnection對象,該對象是LoadedApk.java文件中的LoadedApk類的ServiceDispatcher子類的一個子類。

//LoadedApk.java
        private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service);
                }
            }
        }

最終調用ServiceDispatcher的doConnected方法

        public void doConnected(ComponentName name, IBinder service) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) {
                if (mForgotten) {
                    // We unbound before receiving the connection; ignore
                    // any connection received.
                    return;
                }
                old = mActiveConnections.get(name);
                if (old != null && old.binder == service) {
                    // Huh, already have this one.  Oh well!
                    return;
                }

                if (service != null) {
                    // A new service is being connected... set it all up.
                    mDied = false;
                    info = new ConnectionInfo();
                    info.binder = service;
                    info.deathMonitor = new DeathMonitor(name, service);
                    try {
                        service.linkToDeath(info.deathMonitor, 0);
                        mActiveConnections.put(name, info);
                    } catch (RemoteException e) {
                        // This service was dead before we got it...  just
                        // don't do anything with it.
                        mActiveConnections.remove(name);
                        return;
                    }

                } else {
                    // The named service is being disconnected... clean up.
                    mActiveConnections.remove(name);
                }

                if (old != null) {
                    old.binder.unlinkToDeath(old.deathMonitor, 0);
                }
            }

            // If there was an old service, it is not disconnected.
            if (old != null) {
                mConnection.onServiceDisconnected(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }

最終回調了接口方法onServiceConnected,該方法中把service(Server進程的BinderProxy)交給了Client進程。Client進程就通過該BinderProxy與Server進程進程間通信。

至此AIDL的實現,Binder進程間機制與bindService方法的全過程都已經一一分析完畢了。

下一篇文章因該會在通過幾個實例,在鞏固一下安卓進程間通信。

參考資料:(可能會有遺漏,盡請諒解)

進程啓動過程
http://blog.csdn.net/luoshengyang/article/details/6747696

Android系統進程間通信(IPC)機制Binder中的Server啓動過程源代碼分析
http://blog.csdn.net/luoshengyang/article/details/6629298

開機SystemServer到ActivityManagerService啓動過程分析
http://www.cnblogs.com/bastard/p/5770573.html

Android應用程序綁定服務(bindService)的過程源代碼分析
http://blog.csdn.net/luoshengyang/article/details/6745181

Android:學習AIDL,這一篇文章就夠了(上)
http://blog.csdn.net/luoyanglizi/article/details/51980630

Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
http://blog.csdn.net/luoshengyang/article/details/6642463

Binder詳解
http://blog.csdn.net/yangzhiloveyou/article/details/14043801

IBinder對象在進程間傳遞的形式(二)
http://blog.csdn.net/windskier/article/details/6913698

IBinder對象傳遞形式
http://www.cnblogs.com/zhangxinyan/p/3487866.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章