天天看點

簡析 Binder 資料傳輸流程

本文是一篇流程解析,而非源碼解析,雖然文章中包含對源碼的解讀,但源碼中的細節基本已經砍光,僅保留主要部分。

聊到 Android 中的程序間通信,果然還是繞不開 Binder。

前言

何為程序間通信的障礙

在作業系統中,由于虛拟化,不同程序可通路的記憶體區域被隔離,以確定程序間不會互相幹預。因虛拟化記憶體機制的存在,兩個程序的資料傳遞,即程序間通信(IPC,Inter-Process Communication),必須借助相關機制完成。

顯然,程序間通信的相關機制應由作業系統提供,因為使用者程序并沒有足夠的權限去關聯兩個程序。

權限機制

現代化作業系統通常具備兩種處理器模式:使用者模式(User Mode)與核心模式(Kernel Mode)。作業系統(或指作業系統核心)位于核心模式,可管理計算機的全部資源,而處于使用者模式的使用者程序若需要執行如通路磁盤等特權操作,就需要通過系統調用(System Call)交由核心模式的作業系統執行。

原因很簡單,銀行是不會讓取款的客戶進金庫自己數錢的。

Android 底層的 Linux Kernel 自然采用了這一權限機制,核心與相關服務運作的記憶體空間稱為核心空間(Kernel Space),而使用者程序所運作的記憶體空間稱為使用者空間(User Space)。當使用者程序通過系統調用進行特權操作時,稱此時程序為核心态(Kernel Mode,需注意根據上下文與核心模式進行區分)與使用者态(User Mode)。

為什麼是 Binder

Linux Kernel 并非沒有提供原生的 IPC 機制,隻不過它們無法滿足 Android IPC 所需的條件:

  • 可靠性。
  • 安全性,作業系統必須保證兩個程序連接配接與互動的合法性,避免惡意程序幹擾其它程序的正常進行。如共享記憶體,無法保證其它程序的操作合法。
  • 高效性,機制必須保證高傳輸速度。以傳統 IPC 機制 Socket 為例,一般常用于網絡間傳輸,因為需要確定複雜網絡環境中通信的可靠性,需要額外花費一定的資源,是以無法滿足條件。

為了滿足自己可靠、安全、高效的要求的 IPC 機制,Android 自行建立了一套 IPC 機制,就是 Binder。

通信模型

在 Binder 中,程序雙方的通信方式是一個典型的 C/S 模型。發起通信請求的程序為 Client 程序,請求目标程序為 Server 程序。每個采用 Binder 的程序會有一個或多個用于處理接收資料的線程,這些線程在一個線程池中,稱為 Binder 線程池。

Client 不是直接向 Server 發送請求的,正如你無法打電話給一個你不知道電話号碼的人。是以,Client 需要借助第三方以擷取到 Server 的相關資訊,便是 Binder 機制中的一名重要角色 —— Service Manager。

當 Server 将允許被其它線程通路的 Binder 對象,與其對應的名稱,注冊到 Service Manager 後,滿足 Server 通路權限的 Client 便可以通過 Binder 名稱,從 Service Manager 擷取到 Server 端 Binder 對象的代理。

注意,除非 Client 與 Server 為同一個程序(這一情況擷取到的是 Binder 對象本身,本文暫不讨論這一特殊情況),否則因為程序隔離,Client 程序拿到的實際是 Binder 對象的代理對象(後簡稱 Binder 代理),其對代理對象的互動會作用到 Server 程序中的 Binder 對象上。對于一臉天真的 Client 而言,它并沒有意識到自己拿到的是一個代理對象。

哲學問題

但是問題來了,Client 程序與 Server 程序怎麼聯系 Service Manager 程序的?其實其它程序與 Service Manager 的通信同樣采用 Binder 機制進行。

于是這又變成了一個雞生蛋蛋生雞的問題。

Binder 的實作方法是,預設每一個載入 Binder 機制的程序都保留了零号引用,用于擷取 Service Manager 的 Binder 代理,無需再通過其它方式擷取。

秘密通道

當然,不是所有 Server 程序都希望将自己的 Binder 資訊注冊到 Service Manager,進而對外公開的。Binder 代理自身是可以進行程序間傳遞的(後文詳細說明),是以 Client 與 Server 便可借助已注冊到 Service Manager 的 Binder 建構另一條私有連接配接,這便是匿名 Binder。反過來,注冊到 Service Manager,通過名稱進行擷取的,稱為實名 Binder。

類似 HTTPS 協定,匿名 Binder 提高了安全性,在 Client 與 Server 雙方不洩漏 Binder 的情況下,其它程序便無法通過窮舉等方式擷取 Binder 和 Server 通信。

核心科技

上文所提及的 Client、Server 與 Service Manager 都是運作在使用者空間中的,實際資料傳輸需要在核心空間進行。為 Binder 機制提供底層支援的是 Binder 驅動(Binder Driver)。

Linux Kernel 提供了可加載核心子產品(LKM,Loadable Kernel Module)機制,作業系統可根據需求動态加載核心,防止未使用功能的加載浪費記憶體空間,也避免了核心重新編譯。借助這一機制,Android 将 Binder 的底層支援作為核心子產品加載到 Linux Kernel 中。

雖然被稱為 Binder 驅動,但它其實和硬體裝置并沒有什麼關系。LKM 一般用于設計硬體裝置的驅動程式。與 Binder 驅動的互動采用 ioctl() 等系統調用進行,如同通過驅動程式操作硬體裝置。換句話而言,Binder 構造了一個虛拟的裝置,用于支援程序之間的互相通信。

上文提及 Service Manager,其程序在 init 程序解析腳本啟動後,會向 Binder 驅動發送 BINDER_SET_CONTEXT_MGR 指令,将自己注冊為這一特殊的存在。因為系統僅允許一個 Service Manager 存在,是以在目前 Service Manager 程序沒有關閉之前,Binder 驅動不再允許其它程序注冊為 Service Manager。

更高效的記憶體拷貝

在傳統的 IPC 機制中,因為程序無法互相通路對方記憶體,是以資料傳輸需要借由核心進行:通過 copy_from_user() 将資料從發送方的使用者空間拷貝到核心空間中,再通過 copy_to_user() 将核心空間中的資料拷貝到接收方的使用者空間。在這一過程中,經曆了兩次資料拷貝,不僅拷貝自身耗費資源,使用者态與核心态的頻繁切換也會拖慢性能。

Binder 則通過記憶體映射的方式,将接收方使用者空間的操作記憶體與核心空間的操作記憶體映射到同一塊實體記憶體中,避免了 copy_to_user() 操作,以提高傳輸效率。

啥?為什麼不将三者的操作記憶體映射到同一塊實體記憶體中?那不就是共享記憶體麼?

AIDL

AIDL 通信是基于 Binder 的,從 AIDL 入手分析 Binder 機制是個不錯的選擇。

相關類型

不過在此之前,先來看看一些類型的含義。

IBinder(Java)

/**
 * Base interface for a remotable object, the core part of a lightweight
 * remote procedure call mechanism designed for high performance when
 * performing in-process and cross-process calls.  This
 * interface describes the abstract protocol for interacting with a
 * remotable object.  Do not implement this interface directly, instead
 * extend from {@link Binder}.
 *
 * ...
 */
      

IBinder 被 Binder 與 Binder 代理所實作,實作該接口的類會實作與 Binder 驅動進行資料互動的方法。

IInterface

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
複制代碼IInterface 用于定義 Server 可被操作的合法方法,根據 AIDL 生成的接口會繼承此接口。
Binder(Java)
/**
 * Base class for a remotable object, the core part of a lightweight
 * remote procedure call mechanism defined by {@link IBinder}.
 * This class is an implementation of IBinder that provides
 * standard local implementation of such an object.
 *
 * ...
 */
      

Binder 代表了 Server 程序中的 Binder 本地對象,實作了 IBinder 接口。

BinderProxy

/**
 * Java proxy for a native IBinder object.
 * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
 * directly from Java code.
 *
 * @hide
 */
      

BinderProxy 代表了 Client 程序中的 Binder 代理對象,實作了 IBinder 接口。

生成代碼解析

這裡以 Android Studio 預設建立的 AIDL 接口模版為例,通過閱讀生成代碼來解析 Binder。

// ISampleInterface.aidl
package moe.aoramd.binder;

// Declare any non-default types here with import statements

interface ISampleInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

}
      

點選 Build 後會生成如下 Java 代碼:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package moe.aoramd.binder;
// Declare any non-default types here with import statements

public interface ISampleInterface extends android.os.IInterface {
    /**
     * Default implementation for ISampleInterface.
     */
    public static class Default implements moe.aoramd.binder.ISampleInterface {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
        }

        @Override
        public android.os.IBinder asBinder() {
            return null;
        }
    }

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements moe.aoramd.binder.ISampleInterface {
        private static final java.lang.String DESCRIPTOR = "moe.aoramd.binder.ISampleInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an moe.aoramd.binder.ISampleInterface interface,
         * generating a proxy if needed.
         */
        public static moe.aoramd.binder.ISampleInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof moe.aoramd.binder.ISampleInterface))) {
                return ((moe.aoramd.binder.ISampleInterface) iin);
            }
            return new moe.aoramd.binder.ISampleInterface.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements moe.aoramd.binder.ISampleInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            public static moe.aoramd.binder.ISampleInterface sDefaultImpl;
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        public static boolean setDefaultImpl(moe.aoramd.binder.ISampleInterface impl) {
            if (Stub.Proxy.sDefaultImpl == null && impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static moe.aoramd.binder.ISampleInterface getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
      

可見編譯器幫我們生成了三個實作了 ISampleInterface 的靜态内部類。

  • Stub,繼承于 Binder,表明這是 Binder 本地對象。在使用 AIDL 時我們會在 Server 代碼繼承該類。其中 onTransact() 方法會把從 Client 傳來的資料作解析(類似于通信中的解調),然後調用指定的方法。
  • Stub.Proxy,Client 持有的 Binder 代理所屬類,在 Stub.asInterface() 中被構造。當接口方法被調用時會對相關資料進行處理(類似通信中的調制),之後委托給構造方法中傳入的 Binder 代理。注意!Stub.Proxy 并不是 Binder 代理的實作,而是 Binder 代理的包裝,它将操作委托給了 Binder 代理。
  • Default,顧名思義,這是接口預設實作的預設實作(是不是很繞口)。Stub 提供了靜态的 getter/setter 用于設定接口的全局預設實作,當 Server 無法成功處理調用時會使用預設實作。

講完這三個類的功能之後,回頭看 AIDL 生成的代碼,會發現該講的已經講完一大半了:Client 端代碼調用 Proxy.basicType() 時,參數被設定到 Parcel 對象後委托給 BinderProxy.transact() 傳遞到 Server,Server 在 onTransact() 擷取到資料後,通過對資料進行解析,得知 Client 調用的是哪一個方法、以及方法的參數,随後執行方法。

是以,若要進一步了解 Binder 是如何傳遞資料的,我們的重心自然應放在 transact() 與 onTransact() 上。

Binder 線程池注冊

雖然如此,但現在我們先把 transact() 和 onTransact() 放一放,回頭從另一個方向走一走看看。

前文提到,每個采用 Binder 的程序會有一個或多個用于處理接收資料的線程,位于 Binder 線程池。采用 Binder 機制的程序最典型的就是應用程式程序了。那應用程式程序的 Binder 線程池是在什麼時候啟動的呢?

應用程式程序由 Zygote 程序 fork 所得,若從 ZygoteInit.zygoteInit() 開始跟蹤 Binder 線程池啟動的話,忽略掉過程中的調用鍊,最終會定位到如下代碼:

調用鍊:ZygoteInit.zygoteInit() --> ZygoteInit.nativeZygoteInit() ··JNI··> com_android_internal_os_ZygoteInit_nativeZygoteInit() --> AppRuntime.onZygoteInit()
virtual void onZygoteInit()
{
    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool();
}
      

ProcessState 是 Binder 機制核心之一(敲黑闆,重點,要記),它是 Binder 通信的基礎,負責與 Binder 驅動的互動與 Binder 線程池的管理。它實作了單例模式,通過 self() 函數擷取執行個體。在這裡,函數擷取了 ProcessState 執行個體後調用其 startThreadPool() 函數,以啟動程序的 Binder 線程池。

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}
      

mThreadPoolStarted 用于辨別線程池是否已經啟動過,以確定 Binder 線程池僅初始化一次。spawnPooledThread() 函數啟動了一個 Binder 線程,類型為 PoolThread,函數參數表示這是 Binder 線程池中的第一線程。

class PoolThread : public Thread
{
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain)
    {
    }

protected:
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }

    const bool mIsMain;
};
      

PoolThread 繼承自 Thread,在運作時通過 IPCThreadState 将自身線程注冊到 Binder 驅動中。IPCThreadState 同樣是 Binder 機制的核心之一,它用于管理與 Binder 通信相關線程的狀态,每個 Binder 線程都會通過此将自己

注冊到 Binder 驅動。

self() 函數是一個工廠函數,用于擷取 IPCThreadState 執行個體。self() 根據 pthread_getspecific() 管理每個參與 Binder 通信線程的執行個體,類似于 Handler 中所用的 ThreadLocalMap,每個參與 Binder 通信的線程其 IPCThreadState 對象都是互相獨立的,保證了後續操作的線程安全。

與 Binder 驅動進行通信

之是以在聊 transact() 和 onTransact() 之前扯 Binder 線程池的初始化,是因為兩者都與 ProcessState 和 IPCThreadState 密不可分。

在閱讀源碼前,與前文一樣,先來看看一些類型的含義。

IBinder(C++)

/**
 * Base class and low-level protocol for a remotable object.
 * You can derive from this class to create an object for which other
 * processes can hold references to it.  Communication between processes
 * (method calls, property get and set) is down through a low-level
 * protocol implemented on top of the transact() API.
 */
      

與 Java 層的 IBinder 類似,是跨程序資料互動類型的基類。Native的 Binder 與 Binder 代理均繼承此類。

BBinder

即 Binder Native,代表 Binder 本地對象,是 Binder 代理互動的目的端,等同 Binder 在 Native 層功能的角色。如果根據 Framework 中的代碼風格,更合适的名字為 BnBinder。

BpBinder

即 Binder Proxy,代表 Binder 代理對象,等同 BinderProxy 在 Native 層功能的角色。

Transaction

如果跟蹤 transact(),會發現之後調用了 Native 層 BpBinder 對象的 transact() 函數。

調用鍊:BinderProxy.transact() --> BinderProxy.transactNative ··JNI··> android_os_BinderProxy_transact() --> BpBinder.transact()
// NOLINTNEXTLINE(google-default-arguments)
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {

        /* ... */

        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;

        return status;
    }

    return DEAD_OBJECT;
}
      

IPCThreadState,熟悉的面孔。在調用鍊中的各個函數對參數進行預處理之後,資料最終将通過 IPCThreadState.transact() 解析發送給 Binder 驅動。其實作如下:

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err;

    /* ... */

    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ( /* ... */ ) {

        /* ... */

        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }

        /* ... */

    } else {
        err = waitForResponse(nullptr, nullptr);
    }

    return err;
}
      

核心調用為 writeTransactionData() 與 waitForResponse(),兩者實作如下:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;

    /* ... */

    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;

        cmd = (uint32_t)mIn.readInt32();

        /* ... */

        switch (cmd) { /* ... */ }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }

    return err;
}
      

mIn 與 mOut 均為 IPCThreadState 中 Parcel 類型的成員變量,作為與 Binder 驅動通信的資料接收、發送視窗。在 waitForResponse() 中調用了 talkWithDriver(),從函數名可以看出它的功能了。

在這裡,Binder 驅動與 mIn、mOut 的資料互動開始了。

繞個圈,回過頭看 Binder 線程的注冊過程,同樣使用到了 talkWithDriver() 與 Binder 驅動進行通信。

void IPCThreadState::joinThreadPool(bool isMain)
{
    /* ... */

    talkWithDriver(false);
}
      

** 資料互動**

​IPCThreadState.talkWithDrive()​

​ 的函數簽名位于 IPCThreadState.h,參數用于确定是否接收資訊,其預設值為 true。

class IPCThreadState
{
/* ... */

private:
            /* ... */

            status_t            talkWithDriver(bool doReceive=true);

            /* ... */
}
      

函數實作如下:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD < 0) {
        return -EBADF;
    }

    binder_write_read bwr;

    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }

    /* logcat ... */

    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        /* logcat ... */
#if defined(__ANDROID__)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD < 0) {
            err = -EBADF;
        }
        /* logcat ... */
    } while (err == -EINTR);

    /* logcat ... */

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                /* logcat ... */
            else {
                mOut.setDataSize(0);
                processPostWriteDerefs();
            }
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        /* logcat ... */
        return NO_ERROR;
    }

    return err;
}
      

有意思的是,函數名為 ​

​talkWithDriver()​

​,而不是 ​

​talkToDriver()​

​,其實已經表明在函數中與 Binder 驅動的資料互動是一個雙向的過程。

​mOut​

​ 資料視窗中的資料、​

​mIn​

​ 和 ​

​mOut​

​ 視窗的記憶體位址,在經過 ​

​binder_write_read​

​ 結構體封裝後,通過 ​

​ioctl()​

​ 系統調用發送到 Binder 驅動。​

​mProcess​

​ 即程序中的 ​

​ProcessState​

​ 對象,在 ​

​IPCThreadState​

​ 執行個體構造時便已初始化。​

​ioctl()​

​ 通過 ​

​ProcessState​

​ 初始化時指定的 Binder 驅動檔案描述符,将資料發送到 Binder 域 ​

​/dev/binder​

​ 的 Binder 驅動中。

Android 并不隻有 ​

​/dev/binder​

​ 一個 Binder 驅動。

在 Android 8 采用 Project Treble 後,将 Android Framework 與供應商實作分離。Android 8 即更高版本的系統總共擁有三個 Binder 域,提供給硬體供應商實作 IPC。其中:

  • ​/dev/binder​

    ​,用于 Android Framework 與應用程式程序的 IPC,使用 AIDL。
  • ​/dev/hwbinder​

    ​,用于 Android Framework 與供應商程序或供應商程序之間的 IPC,使用 HIDL。
  • ​/dev/vndbinder​

    ​,用于供應商程序之間的 IPC,使用 AIDL。
詳情可見 ​​Android 開源項目 - 使用 Binder IPC​​

小結

雖然僅僅是對 Client 發送資料流程的一次走馬觀花,但還是可以得出一些有價值的資訊,在後面的分析過程中少走一些彎路:

  • IPCThreadState

    是線程局部對象,每個參與 Binder 通信的程序通過

    IPCThreadState.self()

    擷取到的執行個體是互相獨立的。
  • 從 Client 調用 Binder 代理方法到擷取傳回值,是一個阻塞的過程。

另一個世界

看完了 Client 發送資料的流程,接下來看看 Server 接收資料的流程。

Register

要聊 Server,就應該從 Server 到 Service Manager 上戶口講起。

Server 端的代碼會通過 ​

​defaultServiceManager()​

​ 函數擷取 Service Manager 的 Binder 代理(此時的 Server 相對于 Service Manager 是 Client)。

sp<IServiceManager> defaultServiceManager()
{
    std::call_once(gSmOnce, []() {
        sp<AidlServiceManager> sm = nullptr;
        while (sm == nullptr) {
            sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
            if (sm == nullptr) {
                ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
                sleep(1);
            }
        }

        gDefaultServiceManager = new ServiceManagerShim(sm);
    });

    return gDefaultServiceManager;
}
      

​ServiceManagerShim()​

​ 構造函數傳入的是一個 ​

​AidlServiceManager​

​ 對象,即 ​

​IServiceManager​

​ 對象。而在這裡調用 ​

​ProcessState.getContextObject()​

​ 所得到的,正是零号引用 —— Service Manager 的 Binder 代理。

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    sp<IBinder> context = getStrongProxyForHandle(0);

    /* ... */

    return context;
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != nullptr) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  The
        // attemptIncWeak() is safe because we know the BpBinder destructor will always
        // call expungeHandle(), which acquires the same lock we are holding now.
        // We need to do this because there is a race condition between someone
        // releasing a reference on this BpBinder, and a new reference on its handle
        // arriving from the driver.
        IBinder* b = e->binder;
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, nullptr, 0);
                if (status == DEAD_OBJECT)
                   return nullptr;
            }

            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}
      

零号引用的 ​

​BpBinder​

​ 對象傳入 ​

​interface_cast()​

​ 模版函數,會最終通過 ​

​IMPLEMENT_META_INTERFACE()​

​ 宏,生成 ​

​BpServiceManager​

​ 對象。該對象被傳入 ​

​ServiceManagerShim​

​ 的構造函數中,成為其成員變量 ​

​mTheRealServiceManager​

​。

/**
 * If this is a local object and the descriptor matches, this will return the
 * actual local object which is implementing the interface. Otherwise, this will
 * return a proxy to the interface without checking the interface descriptor.
 * This means that subsequent calls may fail with BAD_TYPE.
 */
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

#ifndef DO_NOT_CHECK_MANUAL_BINDER_INTERFACES

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    static_assert(internal::allowedManualInterface(NAME),               \
                  "b/64223827: Manually written binder interfaces are " \
                  "considered error prone and frequently have bugs. "   \
                  "The preferred way to add interfaces is to define "   \
                  "an .aidl file to auto-generate the interface. If "   \
                  "an interface must be manually written, add its "     \
                  "name to the whitelist.");                            \
    DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)    \

#else

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)    \

#endif

#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
    /* ... */                                                           \
    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        ::android::sp<I##INTERFACE> intr;                               \
        if (obj != nullptr) {                                           \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == nullptr) {                                      \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    /* ... */
      

調用 ​

​ServiceManagerShim.addService()​

​ 函數以将 Binder 注冊到 Service Manager 中。注冊的過程同樣也是一次 Binder 傳輸,最後被注冊的 Binder 到達 Service Manager 程序。

Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {

    /* ... */

    auto entry = mNameToService.emplace(name, Service {
        .binder = binder,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
        .debugPid = ctx.debugPid,
    });

    /* ... */
}
      

​mNameToService​

​ 類型為 ​

​std::map<std::string, Service>​

​。至此,Server 的 Binder 已被注冊到 Service Manager 中。

看過 Android 10 及之前相關源碼的同學應該會對 ​

​BpServiceManager​

​ 這個類比較熟悉,它是 Service Manager 在其它 Binder 線程的代理。不過這一部分代碼現在已經被删除了。
80e1e6d   [email protected]   2019-07-09 09:54 +08:00

servicemanager: use libbinder

Bug: 135768100
Test: boot
Test: servicemanager_test

Change-Id: I9d657b6c0d0be0f763b6d54e0e6c6bc1c1e3fc7a
(cherry picked from commit 3e092daa14c63831d76d3ad6e56b2919a0523536)
複制代碼
      
在此之後,​

​BpServiceManager​

​ 不再通過手動實作,而是采用 AIDL(檔案為 ​

​IServiceManager.aidl​

​),生成 ​

​IServiceManager​

​、​

​BnServiceManager​

​BpServiceManager​

​ 的頭檔案及具體實作。

關于通過 AIDL 生成 C++ 代碼,詳見 ​​Generating C++ Binder Interfaces with aidl-cpp​​

Listen

Binder 線程用于在 Server 中接收處理從 Binder 驅動發送來的資料。一個線程通過前文提及的函數 ​

​IPCThreadState.joinThreadPool()​

​ 将自己注冊到 Binder 線程池,等待接收資料。

不過關于 ​

​joinThreadPool()​

​ 的實作,這次的重點不再是 ​

​talkWithDriver()​

​,而是循環與 ​

​getAndExecuteCommand()​

​ 函數。

void IPCThreadState::joinThreadPool(bool isMain)
{
    /* ... */

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    status_t result;
    do {
        processPendingDerefs();
        // now get the next command to be processed, waiting if necessary
        result = getAndExecuteCommand();

        /* ... */

        // Let this thread exit the thread pool if it is no longer
        // needed and it is not the main process thread.
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);

    /* ... */

    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}
      

​getAndExecuteCommand()​

​ 用于擷取從 Binder 驅動傳來的資料,并執行資料中所包含的指令。在資料發送的小結中提過一點,這裡的情況也與其相似:調用過程中是阻塞的,是以在未處理完指令之前,Binder 線程無法執行下一道指令。

status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;

    result = talkWithDriver();
    if (result >= NO_ERROR) {
        size_t IN = mIn.dataAvail();
        if (IN < sizeof(int32_t)) return result;
        cmd = mIn.readInt32();

        /* ... */

        result = executeCommand(cmd);

        /* ... */
    }

    return result;
}
      

​talkWithDriver()​

​ 從 ​

​mIn​

​ 視窗解析出需要執行的指令後,執行 ​

​executeCommand()​

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    /* ... */

    switch ((uint32_t)cmd) {

    /* ... */

    case BR_TRANSACTION:
        {
            /* ... */

            if (tr.target.ptr) {
                // We only have a weak reference on the target object, so we must first try to
                // safely acquire a strong reference before doing anything else with it.
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } else {
                    error = UNKNOWN_TRANSACTION;
                }

            } else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }

            /* ... */
        }
        break;

    /* ... */

    case BR_SPAWN_LOOPER:
        mProcess->spawnPooledThread(false);
        break;

    /* ... */
}
      

其中 ​

​the_context_object​

​ 為 ​

​BBinder​

​ 對象,也就是 Server 的 Binder 本體。​

​BBinder.transact()​

​ 會再調用 ​

​BBinder.onTransact()​

​ 函數,實作 Server 程序 Binder 的調用。

這裡稍微跑題,額外提一個 switch 分支:​

​BR_SPAWN_LOOPER​

​。可以看出這裡與應用程序啟動 Binder 線程池時類似,但在這裡傳入的參數為 false,表明不是第一線程。這代表着,Binder 驅動可以根據目前程序處理 Binder 資料的繁忙程度來決定是否開啟更多的 Binder 線程。

如果是在 C++ 中采用 Binder,Server 接收資料的流程便已經到頭了。而對于使用 Java 的 Binder,​

​JavaBBinder​

​ 是 ​

​BBinder​

​ 的一個子類,​

​JavaBBinder.onTransact()​

​ 将會通過 JNI 調用 Java 層 ​

​Binder.onTransact()​

class JavaBBinder : public BBinder
{
/* ... */

protected:
    /* ... */

    status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
    {
        JNIEnv* env = javavm_to_jnienv(mVM);

        /* ... */

        IPCThreadState* thread_state = IPCThreadState::self();

        /* ... */

        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

        /* ... */
    }

    /* ... */

private:
    JavaVM* const   mVM;
    jobject const   mObject;  // GlobalRef to Java Binder

    /* ... */
};
      

啟動 Service Manager

80e1e6d 送出同樣變更了 Service Manager 的實作方式,現在的實作與 Hardware Service Manager 的代碼有些相似。

Service Manager 是一個系統服務程序。與 Zygote 程序相似,我們可以在 init.rc 中找到其啟動的相關資訊。

on init

    # ... #

    # Start essential services.
    start servicemanager
    start hwservicemanager
    start vndservicemanager
      

在 init 觸發器觸發後,servicemanager 服務将被啟動。servicemanager 服務的定義位于 servicemanager.rc 中。

service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    onrestart restart apexd
    onrestart restart audioserver
    onrestart restart gatekeeperd
    onrestart class_restart main
    onrestart class_restart hal
    onrestart class_restart early_hal
    writepid /dev/cpuset/system-background/tasks
    shutdown critical
      

服務運作程式為 /system/bin/servicemanager。這裡的一個細節是修飾符 critical,文檔中的說明如下,表明 servicemanager 是一個系統關鍵程序,如果出現問題,Android 系統是無法正常運作的:

This is a device-critical service. If it exits more than four times in four minutes or before boot completes, the device will reboot into bootloader.

與 systemservice 相關的代碼位于 /frameworks/native/cmds/servicemanager。程式的入口在 main.cpp。

int main(int argc, char** argv) {
    if (argc > 2) {
        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
    }

    const char* driver = argc == 2 ? argv[1] : "/dev/binder";

    sp<ProcessState> ps = ProcessState::initWithDriver(driver);
    ps->setThreadPoolMaxThreadCount(0);
    ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);

    sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
    if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
        LOG(ERROR) << "Could not self register servicemanager";
    }

    IPCThreadState::self()->setTheContextObject(manager);
    ps->becomeContextManager(nullptr, nullptr);

    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);

    BinderCallback::setupTo(looper);
    ClientCallbackCallback::setupTo(looper, manager);

    while(true) {
        looper->pollAll(-1);
    }

    // should not be reached
    return EXIT_FAILURE;
}
      

代碼邏輯可以拆分成兩部分進行解析。

Service Manager 需要将自己注冊到 Binder 驅動中。第一步操作是設定 ProcessState 的 Binder 域。

const char* driver = argc == 2 ? argv[1] : "/dev/binder";

sp<ProcessState> ps = ProcessState::initWithDriver(driver);
      

然後建立類型為 ServiceManager 的 Binder 本地對象,并将自己注冊到自己的 Binder 清單中,提供給其它程序擷取。

sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
    LOG(ERROR) << "Could not self register servicemanager";
}
      

在目前線程的 IPCThreadState 對象中注冊 the_context_object,以指定 Binder 線程對應的 Binder 對象。

IPCThreadState::self()->setTheContextObject(manager);
      

最後,向 Binder 驅動發送 BINDER_SET_CONTEXT_MGR,将自己注冊為唯一的 Service Manager,完成注冊過程。

ps->becomeContextManager(nullptr, nullptr);
      

ProcessState.becomeContextManager() 實作如下:

bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
{
    /* ... */

    int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);

    // fallback to original method
    if (result != 0) {

        /* ... */

        result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
    }

    /* ... */

    return result == 0;
}
      

以前 Service Manager 通過無限循環處理消息,現在的實作采用了 Looper。

在這裡,程序通過 Looper 處理事件。BinderCallback 的 setupTo() 調用了 Looper.addFd() 向 Looper 注冊檔案描述符,監聽 Looper.POLL_CALLBACK 資訊,其中 BinderCallback 監聽的檔案描述符為 -1。

BinderCallback::setupTo(looper);
      

在循環中調用 Looper.pollAll(),執行檔案描述符相關的 Callback。

while(true) {
    looper->pollAll(-1);
}
      

BinderCallback 是 Looper 接收事件時的回調。實際實作等待 Binder 驅動資料的還是那個函數:IPCThreadState.getAndExecuteCommand()。

調用鍊:BinderCallback.handleEvent() --> IPCThreadState.handlePolledCommands() --> IPCThreadState.getAndExecuteCommand()
status_t IPCThreadState::handlePolledCommands()
{
    status_t result;

    do {
        result = getAndExecuteCommand();
    } while (mIn.dataPosition() < mIn.dataSize());

    processPendingDerefs();
    flushCommands();
    return result;
}