Android應用程式與SurfaceFlinger服務之間的共用UI中繼資料(SharedClient)的建立過程分析

來源:互聯網
上載者:User

        在前面一篇文章中,我們分析了Android應用程式與SurfaceFlinger服務的串連過程。Android應用程式成功串連上SurfaceFlinger服務之後,還需要一塊匿名共用記憶體來和SurfaceFlinger服務共用它的UI中繼資料,以便使得SurfaceFlinger服務可以正確地為它建立以及渲染Surface。在本文中,我們將詳細地分析這塊用來儲存UI中繼資料的匿名共用記憶體的建立過程。

        在Android應用程式與SurfaceFlinger服務的關係概述和學習計劃一文中提到,用來儲存Android應用程式的UI中繼資料的匿名共用記憶體最終是被結構化為一個SharedClient對象來訪問的。每一個與UI有關的Android應用程式進程有且僅有一個SharedClient對象,而且這些SharedClient對象是由Android應用程式請求SurfaceFlinger服務建立的:Android應用程式首先獲得SurfaceFlinger服務的一個Binder代理介面,然後再通過這個代理介面得到另外一個類型為UserClient的Binder代理介面,最後就可以通過後一個Binder代理介面來獲得一個SharedClient對象。

        由於每一個與UI有關的Android應用程式進程有且僅有一個SharedClient對象,因此,Android系統就通過一個單例模式的類來專負責建立和管理這個SharedClient對象。這個類的名稱為SurfaceClient,定義在frameworks/base/libs/surfaceflinger_client/Surface.cpp檔案中,如下所示:

class SurfaceClient : public Singleton<SurfaceClient>{    // all these attributes are constants    sp<ISurfaceComposer> mComposerService;    sp<ISurfaceComposerClient> mClient;    status_t mStatus;    SharedClient* mControl;    sp<IMemoryHeap> mControlMemory;    SurfaceClient()        : Singleton<SurfaceClient>(), mStatus(NO_INIT)    {        sp<ISurfaceComposer> sf(ComposerService::getComposerService());        mComposerService = sf;        mClient = sf->createClientConnection();        if (mClient != NULL) {            mControlMemory = mClient->getControlBlock();            if (mControlMemory != NULL) {                mControl = static_cast<SharedClient *>(                        mControlMemory->getBase());                if (mControl) {                    mStatus = NO_ERROR;                }            }        }    }    friend class Singleton<SurfaceClient>;public:    status_t initCheck() const {        return mStatus;    }    SharedClient* getSharedClient() const {        return mControl;    }    ssize_t getTokenForSurface(const sp<ISurface>& sur) const {        // TODO: we could cache a few tokens here to avoid an IPC        return mClient->getTokenForSurface(sur);    }    void signalServer() const {        mComposerService->signal();    }};

       當SurfaceClient類的靜態成員函數getInstance第一次被調用的時候,系統就會在對應的應用程式進程中建立一個SurfaceClient對象,即會調用SurfaceClient類的建構函式。SurfaceClient類的建構函式首先會調用ComposerService類的靜態成員函數getComposerService來獲得一個SurfaceFlinger服務的代理介面,並且儲存在SurfaceClient類的成員變數mComposerService中,以便以後可以使用。ComposerService類的靜態成員函數getComposerService在前面Android應用程式與SurfaceFlinger服務的串連過程分析一文中已經分析過了,這裡不再詳述。有了SurfaceFlinger服務的代理介面sf之後,SurfaceClient類的建構函式接著就可以調用它的成員函數createClientConnection來獲得一個類型為UserClient的Binder代理介面,這個Binder代理介面實現了ISurfaceComposerClient介面,因此,我們可以將它儲存在SurfaceClient類的成員變數mClient中。最後,SurfaceClient類的建構函式就調用前面獲得的類型為ISurfaceComposerClient的Binder代理介面mClient的成員函數getControlBlock來獲得一塊用來描述應用程式UI中繼資料的匿名共用記憶體mControlMemory ,並且將這些匿名共用記憶體強制轉化為一個SharedClient對象mControl,以便後面可以方便地訪問UI中繼資料。

        以上就是Android應用程式與SurfaceFlinger服務之間的共用UI中繼資料(SharedClient)的建立過程的總體描述,接下來我們再詳細分析每一步的實現。現在,我們繼續分析一下SurfaceClient類的其餘成員函數的實現:

        1. 成員函數initCheck用來檢查一個Android應用程式進程是否已經成功地請求SurfaceFlinger服務建立了一塊用來描述UI中繼資料的SharedClient對象了。

        2. 成員函數getSharedClient用來返回用來描述UI中繼資料的SharedClient對象mControl。

        3. 成員函數getTokenForSurface用來返回由參數sur所描述的一個Surface的Token值。這個Token值由SurfaceFlinger服務來建立和管理,並且可以通過前面所獲得的類型為UserClient的Binder代理介面mClient的成員函數getTokenSurface來獲得。

        4. 成員函數signalServer用來通知SurfaceFlinger服務更新Android應用程式UI,這是通過調用SurfaceFlinger服務的代理介面mComposerService的成員函數signal來實現的,實際上就是向SurfaceFlinger服務發送一個訊號,以便可以將它喚醒起來更新UI。

        介紹完成SurfaceClient類的實現之後,我們還需要瞭解一下兩個類的實現,即UserClient類和SharedClient類的實現,以便可以協助我們瞭解用來儲存Android應用程式的UI中繼資料的匿名共用記憶體的建立過程,以及協助後面兩篇文章對Surface的建立和渲染過程的分析。

        接下來,我們就首先分析UserClient類的實現,接著再分析SharedClient類的實現。

        在Android應用程式與SurfaceFlinger服務的串連過程分析一文的圖2中,我們介紹了用來串連Android應用程式和SurfaceFlinger服務的Client類,而UserClient類和Client類是類似的,它們都實現了相同的介面,只不過是側重點有所不同。Android應用程式與SurfaceFlinger服務的串連過程分析一文的圖2中的Client類替換成UserClient類,就可以得到UserClient類的實現結構圖,1所示:

圖1 UserClient類的實現結構圖

       UserClient類與Client類最重要的區別是,前者實現了ISurfaceComposerClient介面的成員函數getControlBlock,而後者實現了ISurfaceComposerClient介面的成員函數createSurface。後面我們就會分析UserClient類是如何?ISurfaceComposerClient介面的成員函數getControlBlock的。

       UserClient類的實現暫時就介紹到這裡,接下來我們來看SharedClient類的實現。為了方便描述,我們把Android應用程式與SurfaceFlinger服務的關係概述和學習計劃一文的圖4和圖5貼出來,如以2和圖3所示:

圖2 用來描述Android應用程式的UI中繼資料的SharedClient

 

圖3 SharedBufferStack的結構

        每一個SharedClient對象包含了至多31個SharedBufferStack,而每一個SharedBufferStack都對應一個Android應用程式進程中的一個Surface。

        SharedClient類定義在檔案frameworks/base/include/private/surfaceflinger/SharedBufferStack.h 檔案中,如下所示:

class SharedClient{public:    SharedClient();    ~SharedClient();    ......private:    ......    SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ];};

        它有一個大小為SharedBufferStack::NUM_LAYERS_MAX的SharedBufferStack數組。SharedBufferStack::NUM_LAYERS_MAX的值等於31,定義在SharedBufferStack類中。

        SharedBufferStack類同樣是定義在檔案frameworks/base/include/private/surfaceflinger/SharedBufferStack.h 檔案中,如下所示:

class SharedBufferStack{    ......public:    // When changing these values, the COMPILE_TIME_ASSERT at the end of this    // file need to be updated.    static const unsigned int NUM_LAYERS_MAX  = 31;    static const unsigned int NUM_BUFFER_MAX  = 16;    static const unsigned int NUM_BUFFER_MIN  = 2;    static const unsigned int NUM_DISPLAY_MAX = 4;    ......    struct SmallRect {        uint16_t l, t, r, b;    };   struct FlatRegion {        static const unsigned int NUM_RECT_MAX = 5;        uint32_t    count;        SmallRect   rects[NUM_RECT_MAX];    };    struct BufferData {        FlatRegion dirtyRegion;        SmallRect  crop;        uint8_t transform;        uint8_t reserved[3];    };    SharedBufferStack();    ......    status_t setDirtyRegion(int buffer, const Region& reg);    status_t setCrop(int buffer, const Rect& reg);    status_t setTransform(int buffer, uint8_t transform);    Region getDirtyRegion(int buffer) const;    Rect getCrop(int buffer) const;    uint32_t getTransform(int buffer) const;    // these attributes are part of the conditions/updates    volatile int32_t head;      // server's current front buffer    volatile int32_t available; // number of dequeue-able buffers    volatile int32_t queued;    // number of buffers waiting for post    ......    // not part of the conditions    ......    volatile int8_t index[NUM_BUFFER_MAX];    ......     int8_t      headBuf;        // last retired buffer    ......    BufferData  buffers[NUM_BUFFER_MAX];   };

       下面我們簡要地對SharedBufferStack類進行分析。

        首先,SharedBufferStack類在內部定義了四個常量:

        NUM_LAYERS_MAX -- 表示一個Android應用程式最多可以有NUM_LAYERS_MAX個Layer,可以將一個Layer理解為一個Surface。

        NUM_BUFFER_MAX -- 表示一個SharedBufferStack至多可以有NUM_BUFFER_MAX個UI中繼資料緩衝區。

        NUM_BUFFER_MIN -- 表示一個SharedBufferStack至少要有UM_BUFFER_MIN個UI中繼資料緩衝區。

        NUM_DISPLAY_MAX -- 表示Android系統至多可以支援NUM_DISPLAY_MAX個顯示屏。

        從這些常量就可以看出:

        1. Android系統至多支援4個顯示屏。

        2. 一個Android應用程式至多可以同時建立31個Surface。

        3. 一個Surface可以有2~16個UI中繼資料緩衝區,即可以使用2~16緩衝區技術來渲染Surface。

        其次,SharedBufferStack類在內部定義了三個結構體:

        SmallRect -- 用來描述一個矩形地區,其中,成員變數l、t,、r和b分別表示左上和右下兩個角的位置。

        FlatRegion -- 用來描述一個SmallRect數組rects ,數組的大小為NUM_RECT_MAX,但是實際個數為count。

        BufferData -- 用來描述一個UI中繼資料緩衝區,它有四個成員變數dirtyRegion、crop、transform和reserved,其中,dirtyRegion用來描述一個Surface需要更新的地區,即裁剪地區,crop用來描述一個Surface的紋理座標,transform用來描述一個Surface的旋轉方向,例如,旋轉90度或者上下翻轉等等,而reserved是保留給以後使用的。通過這個UI中繼資料緩衝區,SurfaceFlinger服務就可以正確地把一個Surface的圖形緩衝區所描述的圖形渲染到螢幕來。

        SharedBufferStack類有一個BufferData數組buffers,它的大小為NUM_BUFFER_MAX,即16,就是用來一組UI中繼資料緩衝區的,這些UI中繼資料緩衝區的內容可以分別通過setDirtyRegion、setCrop、setTransform、getDirtyRegion、getCropgetTransform這六個成員函數來訪問。這六個成員函數的第一個參數均為一個int值,用來描述要訪問的是哪一個BufferData的資料。

        SharedBufferStack類還有另外一個類型為int8_t的數組index,它的大小也為NUM_BUFFER_MAX。這個index數組才是一個真正的Stack,它按照一定的規則來訪問。index數組的每一個元素的值均是一個索引值,用來映射到數組buffers中去的。例如,假設index[0]的值等於2,那麼它就對應數組buffers中的第2個元素,即buffers[2]

        SharedBufferStack類的其餘重要成員變數的含義如下所示:

        head -- 用來描述一個SharedBufferStack的頭部,它是一個索引值,是映射到數組index中去的。

        available -- 用來描述一個SharedBufferStack中的空閑UI中繼資料緩衝區的個數。

        queued -- 用來描述一個SharedBufferStack中的已經補使用了的UI中繼資料緩衝區的個數,即那些在排隊等待SurfaceFlinger服務使用的UI中繼資料緩衝區。

        headBuf -- 用來描述一個SharedBufferStack的頭部所對應的UI中繼資料緩衝區的編號,這個編號是映射到數組buffers中去。

        關於SharedBufferStack類的實現,我們就暫時介紹到這裡,在下一篇文章分析Android應用程式的Surface建立過程時,我們再通過SharedBufferServer類和SharedBufferClient類的實現來進一步理解SharedBufferStack類的實現。

        現在,我們就開始詳細分析Android應用程式與SurfaceFlinger服務之間的共用UI中繼資料的建立過程,4所示:

圖 4 Android應用程式的共用UI中繼資料的建立過程

        接下來我們就詳細分析每一個步驟。

        Step 1. SurfaceFlinger::createClientConnection

sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection(){    sp<ISurfaceComposerClient> bclient;    sp<UserClient> client(new UserClient(this));    status_t err = client->initCheck();    if (err == NO_ERROR) {        bclient = client;    }    return bclient;}

        SurfaceFlinger類的成員函數createClientConnection實現在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,它的實現很簡單,只是建立了一個類型為UserClient的Binder對象client,並且獲得它的一個ISurfaceComposerClient介面,最後將這個ISurfaceComposerClient介面,即一個UserClient代理對象,返回給Android應用程式進程。

        接下來,我們再繼續分析UserClient對象的建立過程,,即UserClient類的建構函式的實現。

         Step 2. new UserClient

UserClient::UserClient(const sp<SurfaceFlinger>& flinger)    : ctrlblk(0), mBitmap(0), mFlinger(flinger){    const int pgsize = getpagesize();    const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));    mCblkHeap = new MemoryHeapBase(cblksize, 0,            "SurfaceFlinger Client control-block");    ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());    if (ctrlblk) { // construct the shared structure in-place.        new(ctrlblk) SharedClient;    }}

        UserClient類的成員變數mFlinger是一個類型為SurfaceFlinger的強指標,它指向了SurfaceFlinger服務, UserClient類的另外一個成員變數mBitmap是一個int32_t值,它是用來為Android應用程式的Surface分配Token值的,即如果它的第n位等於1,那麼就表示值等於n的Token已經被分配出去使用了。

        UserClient類的建構函式首先得到一個SharedClient對象的大小,接著再將這個大小對齊到頁面邊界,於是就得到了接下來要建立的匿名共用塊的大小cblksize。這塊匿名共用記憶體是一個MemoryHeapBase對象描述的,並且儲存在UserClient類的成員變數mCblkHeap。MemoryHeapBase類是用來建立匿名共用記憶體的一個C++介面,它的實現原理可以參考Android系統匿名共用記憶體(Anonymous Shared Memory)C++調用介面分析一文。

        UserClient類的建構函式得到了一塊匿名共用記憶體之後,緊接著就在這塊匿名共用記憶體上建立了一個SharedClient對象,並且儲存在UserClient類的成員變數ctrlblk中,以便後面可以通過它來訪問Android應用程式的UI中繼資料。

        回到SurfaceFlinger類的成員函數createClientConnection中,它將一個指向了一個UserClient對象的ISurfaceComposerClient介面返回到Android應用程式進程之後,Android應用程式進程就可以將它封裝成一個類型為BpSurfaceComposerClient的Binder代理對象。

        Step 3. return BpSurfaceComposerClient

        將一個Binder代理對象封裝成一個BpSurfaceComposerClient的過程可以參考前面Android應用程式與SurfaceFlinger服務的串連過程分析一文中的Step 4。

        Step 4. UserClient::getControlBlock

sp<IMemoryHeap> UserClient::getControlBlock() const {    return mCblkHeap;}

        從前面的Step 2可以知道,UserClient類的成員變數mCblkHeap指向了一塊匿名共用記憶體,UserClient類將這塊匿名共用記憶體返回給Android應用程式之後,Android應用程式就會將它結構化成一個SharedClient對象來訪問,並且儲存在SurfaceClient類的成員變數mControl中,這個結構化過程就可以參考前面所描述的SurfaceClient類的建構函式了。

        至此,用來描述Android應用程式的UI中繼資料的一個SharedClient對象的建立過程就分析完了。以後當Android應用程式請求SurfaceFlinger服務建立一個Surface的時候,SurfaceFlinger服務就會從這個SharedClient對象中取出一個SharedBufferStack出來,以便可以用作這個Surface的UI中繼資料緩衝區。在接下來的一篇文章中,我們將詳細描述Android應用程式請求SurfaceFlinger服務建立Surface的過程,敬請期待!

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.