1. 程式人生 > >MediaPlayerService分析

MediaPlayerService分析

一.MediaPlayerService簡介

1.Media Service的啟動

Media程序定義:

service media /system/bin/mediaserver

    class main

    user media

    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc

    ioprio rt 4

servicemanager 程序定義:

service servicemanager /system/bin/servicemanager

    class core

    user system

  group system

Media中有很多Native ServiceAudioFlinger MediaPlayerService CameraService AudioPolicyService等),整個Androidnative或者framework)的Service都需要加入servicemanager中進行統一管理。  那麼Media Process ServiceManager Process是如何進行通訊的呢——Binder.通過Media Process中使用binder進行完成IPC過程,學習Binder的概念和使用方法

2.MediaPlayer的檔案

MediaPlayer是從MediaPlayer.java開始的,應用如果想播放音樂,先要new 一個MediaPlayer,並設定其相關的引數。先列出主要的程式碼檔案,他們都在frameworks裡面:

Java層程式碼:base/media/java/android/media/MediaPlayer.java

Jni層程式碼:

base/media/jni/android_media_MediaPlayer.cpp

原生代碼:

av/media/libmedia/mediaplayer.cpp

./av/media/libmedia/IMediaPlayer.cpp

MediaPlayerService

av/media/libmediaplayerservice/MediaPlayerService.cpp

二.分析MediaPlayer的Binder機制

\frameworks\av\media\mediaserver\ main_mediaserver.cpp

1 .   ProcessState物件建立

當前程序的狀態屬性,物件建立:

sp<ProcessState> ProcessState::self()

{

    Mutex::Autolock _l(gProcessMutex);

    if (gProcess != NULL) {

        return gProcess;

    }

    gProcess = new ProcessState;

    return gProcess;

}

ProcessState建構函式:

ProcessState::ProcessState()

    : mDriverFD(open_driver())    //開啟binder驅動裝置

    , mVMStart(MAP_FAILED)

    , mManagesContexts(false)

    , mBinderContextCheckFunc(NULL)

    , mBinderContextUserData(NULL)

    , mThreadPoolStarted(false)

    , mThreadPoolSeq(1)

{

    if (mDriverFD >= 0) {

        //binderfd對映到當前程序虛擬空間地址中 binder進行互動

        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ,

        MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

    }

}

開啟binder裝置:

static int open_driver()

{

    //開啟binder裝置驅動

    int fd = open("/dev/binder", O_RDWR);

    if (fd >= 0) {

        //bidner最大支援執行緒數

        size_t maxThreads = 15;

        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);

    }

    return fd;

}

ProcessState物件建立過程所做的事:  開啟/dev/binder裝置,得到binder裝置的fd  將bidner裝置fd對映到當前程序虛擬地址空間建立互動的通道;

2. IServiceManager物件建立   sp<IServiceManager> sm = defaultServiceManager(); 為什麼需要一個IServiceManager物件呢,這個類是個抽象提供介面

class IServiceManager : public IInterface

{

    virtual sp<IBinder> getService( const String16& name) const = 0;

    virtual status_t addService( const String16& name,

                             const sp<IBinder>& service,

                             bool allowIsolated = false) = 0;

    ……                                   

};

  通過IServiceManager派生物件操作將Service加入到ServiceManager.

defaultServiceManager()函式

ProcessState::self()->getContextObject(NULL)得到一個IBinder物件

ProcessState::self()剛才所建立的ProcessState物件。

handle_entry是什麼呢?

  struct handle_entry {

                IBinder* binder;

                RefBase::weakref_type* refs;

            };

  也就是說ProcessState 有一個表Vector<handle_entry>mHandleToObject;

表裡面的每一項儲存了一個binder,每一個binder對應一個handle

  http://pic002.cnblogs.com/images/2012/328668/2012111309111812.jpg

  Handle = 0是什麼,控制代碼?代表誰的控制代碼——ServiceManagerbinder中的資源。ProcessState::self()->getContextObject(NULL)得到一個 IBinder——BpBinder(0);

於是得到:   gDefaultServiceManager = interface_cast<IServiceManager>(BpBinder(0))

使用interface_castIBinder例項轉化成IServiceManager例項。

翻譯為:

inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)

{

    return IServiceManager::asInterface(obj);

}

替換成IServiceManager:

android::sp<IServiceManager> IServiceManager::asInterface(               

            const android::sp<android::IBinder>& obj)                  

{  

        //obj BpBinder例項                                                               

        android::sp<IServiceManager> intr;                               

        if (obj != NULL) {

            //返回NULL                                       

            intr = static_cast<IServiceManager*>(                         

                obj->queryLocalInterface(                               

                        IServiceManager::descriptor).get());             

            if (intr == NULL) {                                        

                intr = new BpServiceManager(obj);                         

            }                                                           

        }                                                              

        return intr;                                                   

}

這裡得到IServiceManager 例項:

BpServiceManagernew BpServiceManagernew BpBinder0));

關注BpServiceManager繼承於模板類BpInterface

BpInterface建構函式:

BpRefBase建構函式:

  gDefaultServiceManager = interface_cast<IServiceManager>(BpBinder(0))實際為:   gDefaultServiceManager = new BpServiceManagernew BpBinder0));Bn代表Binder Native Bp代表Binder ProxyBpServiceManager代理的BpBinder例項 BpBinder代理的handle0

這個關係有些複雜,看一下類繼承結構圖:

http://pic002.cnblogs.com/images/2012/328668/2012111309274546.jpg

上面這個結構看起來感覺很熟悉——Bridge模式。將Binder資料互動和功能處理橋接起來。

Media Process main函式中通過:

  sp<IServiceManager> sm = defaultServiceManager();  我們得到了sm:是BpServiceManager物件。

3.    MediaPlayerService加入到ServiceManager中

回到main函式中:

MediaPlayerService::instantiate();

BpServiceManager新增Service

virtual status_t addService(const String16& name, const sp<IBinder>& service,

            bool allowIsolated)

{

        //生成資料包Parcel

        Parcel data, reply;

        // Write RPC headers 寫入Interface名字 得到android.os.IServiceManager

        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

        //寫入Service名字 media.player

        data.writeString16(name);

        //寫入服務

        data.writeStrongBinder(service);

        data.writeInt32(allowIsolated ? 1 : 0);

        // remote()返回BpBinder物件

        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

        return err == NO_ERROR ? reply.readExceptionCode() : err;

}

remote()->transact BpBinder

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

if (mAlive) {

        //到當前程序IPCThreadState mHandle=0

        status_t status = IPCThreadState::self()->transact(

            mHandle, code, data, reply, flags);

        if (status == DEAD_OBJECT) mAlive = 0;

        return status;

    }

    return DEAD_OBJECT;

}

IPCThreadState中寫入資料到Binder裝置過程

IPCThreadState::self()->transact過程:

status_t IPCThreadState::transact(int32_t handle,

                            uint32_t code, const Parcel& data,

                            Parcel* reply, uint32_t flags)

{

    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;

    //將資料轉化成binder_transaction_data 寫入到Parcel例項mOut

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

    //寫入資料

    err = waitForResponse(reply);

    return err;

}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

    while (1) {

        //將資料寫入到Binder裝置中

        talkWithDriver();

        ……

    }

    return err;

}

status_t IPCThreadState::talkWithDriver(bool doReceive)

{

    //將資料封裝成binder_write_read結構

    binder_write_read bwr;

    do {

        //將資料寫入到所開啟的Binder裝置中

        ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

        ……

    } while (err == -EINTR);

    return NO_ERROR;

}

MediaPlayerService加入到ServiceManager中,

這裡就通過BpServiceManagerAddService將資料寫入到Binder裝置傳遞給ServiceManager

繼續Media Process過程

4.    Media Process訊息循

int main(int argc, char** argv)

{

    //啟動程序的執行緒池   

    ProcessState::self()->startThreadPool();     //走到了這裡

    //執行執行緒訊息迴圈

    IPCThreadState::self()->joinThreadPool();

}

  1. 建立工作者執行緒

void ProcessState::startThreadPool()

{

    spawnPooledThread(true);

}

void ProcessState::spawnPooledThread(bool isMain)

{

    //建立PoolThread物件 run ,非執行緒

    sp<Thread> t = new PoolThread(isMain);

  t->run(buf);

}

PoolThread繼承Thread

執行Threadrun函式:

status_t Thread::run(const char* name, int32_t priority, size_t stack)

{

    //建立執行緒mThread _threadLoop

    bool res;

    res = createThreadEtc(_threadLoop,

    this, name, priority, stack, &mThread);

    return NO_ERROR;

}

現在有兩個執行緒:主執行緒和mThread執行緒

mThread執行緒執行:_threadLoop

int Thread::_threadLoop(void* user)

{

    Thread* const self = static_cast<Thread*>(user);

    do {

        //呼叫子類的threadLoop

        result = self->threadLoop();

        ……

    } while(strong != 0);

    return 0;

}

class PoolThread : public Thread

{

protected:

    virtual bool threadLoop()

    {

        IPCThreadState::self()->joinThreadPool(mIsMain);

        return false;

    }

};

2 程序間通訊訊息迴圈過程

訊息迴圈

void IPCThreadState::joinThreadPool(bool isMain)

{

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    status_t result;   

    //訊息迴圈

    do {

        int32_t cmd;

        //binder裝置中讀取命令

        result = talkWithDriver();

        if (result >= NO_ERROR) {

            cmd = mIn.readInt32();

            //執行命令

            result = executeCommand(cmd);

        }

           ……

    } while (result != -ECONNREFUSED && result != -EBADF);

    mOut.writeInt32(BC_EXIT_LOOPER);

    talkWithDriver(false);

}

命令執行

status_t IPCThreadState::executeCommand(int32_t cmd)

{

    BBinder* obj;

    RefBase::weakref_type* refs;

    switch (cmd) {

    case BR_DECREFS:

        break;

    case BR_ATTEMPT_ACQUIRE:

        break;

case BR_TRANSACTION:

  binder_transaction_data tr;

    result = mIn.read(&tr, sizeof(tr));

      if (tr.target.ptr) {

        //將目標物件轉化成BBinder

        sp<BBinder> b((BBinder*)tr.cookie);

        //呼叫BBindertransact 函式

       const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);

      }

        break;

    ……

    default:

    }

    return result;

}

binder_transaction_data.cookietarget object cookie目標物件,這個target object是指那個呢?

Media Process裡面有幾個ServiceAudioFlingerMediaPlayerServiceCameraService等。

這個目標是這其中Service中的一個,假設目標物件為為MediaPlayerService,那為何要轉化成BBinder呢?

3 Service對命令的處

執行緒從binder接收到訊息命令,將命令傳遞給Service處理。將目標物件轉化成BBinder,然後排程此命令;

命令從遠端傳遞到本地端進行處理,每個Service都對應BnXXX物件來處理遠端BpXXX傳來的命令。

sp<BBinder> b((BBinder*)tr.cookie);   const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);  這裡b代表某個Service:假設為MediaPlayerService;弄清楚執行過程,要弄清楚類繼承關係

本地端BnMediaPlayerService訊息處理過程:真正的物件是MediaPlayerService例項。BBinder ->transact開始傳遞

status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

    //onTransact是個virtual函式 派生類BnMediaPlayerService重寫

    err = onTransact(code, data, reply, flags);

    return err;

}

status_t BnMediaPlayerService::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

    switch (code) {

        case CREATE: {

            pid_t pid = data.readInt32();

            sp<IMediaPlayerClient> client =

                interface_cast<IMediaPlayerClient>(data.readStrongBinder());

            //create是個virtual函式 派生類MediaPlayerService重寫

            sp<IMediaPlayer> player = create(pid, client, audioSessionId);

            //建立player寫入到資料包中 傳回

            reply->writeStrongBinder(player->asBinder());

            return NO_ERROR;

        } break;

        ……

        default:

            return BBinder::onTransact(code, data, reply, flags);

    }

}

可以看看Client類繼承關係結構圖

    

  看到這個跟上面的MediaPlayerService繼承關係非常的相似,

  這個結構也非常的熟悉——Adapter模式;將binder訊息互動和命令處理適配到一起。

Client端與Service端交

ClientService進行使用Binder通訊,是得到一個Service BnXXX端物件的代理,在Client BpXXX代理,

然後使用此代理進行相關的操作. 前面在使用ServiceManager就是此種方式進行。

  實際上通訊的基礎是BinderProxy不過是在Binder上進行了一層封裝,封裝了對binder驅動的底層操作,使具有面向物件的特性。

任意兩個程序通過Binder進行通訊,就是先得到另一個程序的binder標識,通過此binder進行資料交換。

1 新增加一個服務 看下面media Palyer類繼承結構。實現Bn端類繼承結構

實現Bn端類繼承結構:

實現Bp端類繼承結構:

    

可以看到

相關推薦

MediaPlayerService分析

一.MediaPlayerService簡介 1.Media Service的啟動 Media程序定義: service media /system/bin/mediaserver     class main     user media     group a

典型用戶和場景分析

水平 用途 環境 大學生 空間 黑板 層次 重要性 可能 1. 名字:韓梅梅 年齡:39 職業:食堂阿姨 代表的用戶在市場上的比例和重要性:5% 較重要 知識層次和能力:可能不太會熟練使用手機APP 使用本軟件的環境:食堂或其他地方 典型場景:食堂阿姨撿到一張飯卡,將此信

Pig系統分析(8)-Pig可擴展性

abstract nike 自己 this script dex java fcm cep 本文是Pig系統分析系列中的最後一篇了,主要討論怎樣擴展Pig功能。不僅介紹Pig本身提供的UDFs擴展機制,還從架構上探討Pig擴展可能性。 補充說明:前些天同事發現twitt

團隊項目——軟件需求分析(NABCD)

找到 越來越大 展示 需求分析 人的 分發 競爭 做到 推廣 一、團隊項目簡介 團隊名稱:SmartCoder 項目名稱:《一起》 二、針對 " 地圖可視化查看發布的內容 " 這一特點進行 NABCD 分析 N(Need需求)   往往用戶通過瀏覽文字信息這個模式

flask框架+pygal+sqlit3搭建圖形化業務數據分析平臺

百度 數據庫 python 技術 兼容性 一. 前言 先說下主要的框架和主要的圖形庫的特點:(個人見解) Django:python開發的一個重量級的web框架,集成了MVC和ORM等技術,設計之初是為了使開發復雜的、數據庫驅動的網站變

典型用戶分析

使用 大學 記錄 困難 能力 排名 沒有 習慣 多少 1、姓名:李嬌嬌 2、年齡:21 3、開銷:每月500-1000,但有自己的一部分收入 4、代表的用戶在市場上的比例和重要性:該用戶代表了絕大多數的大學生,是我們軟件的典型用戶 5、使用這個軟件的典型場景:每天在宿舍裏記

《大型網站技術架構:核心原理與案例分析》-- 讀書筆記 (5) :網購秒殺系統

案例 並發 刷新 隨機 url 對策 -- 技術 動態生成 1. 秒殺活動的技術挑戰及應對策略 1.1 對現有網站業務造成沖擊 秒殺活動具有時間短,並發訪問量大的特點,必然會對現有業務造成沖擊。對策:秒殺系統獨立部署 1.2 高並發下的應用、

js中cookie的使用具體分析

一個 全局變量 加密 下使用 txt 保存 返回 每次 格式 JavaScript中的還有一個機制:cookie,則能夠達到真正全局變量的要求。 cookie是瀏覽器 提供的一種機制,它將document 對象的cookie屬性提供給Jav

MVC模式在Java Web應用程序中的實例分析

rip run writer fault esp 身份驗證 int 網站 table 結合六個基本質量屬性 可用性: 異常 可修改性: 1.維持語義的一致性,高內聚低耦合 2.維持現有的接口,Login依賴LoginIService接口,LoginService依賴ILog

Vertica DBD 分析優化設計

and tomat ron per vertica small resource resources start DBD = Database Designer,是Vertica數據庫優化中最主要的原生工具。 首先運行admintools工具,按下面步驟依次執行: 1.選擇

epoll驚群原因分析

all lec 水平 next log lag 到來 delayed eas 考慮如下情況(實際一般不會做,這裏只是舉個例子): 在主線程中創建一個socket、綁定到本地端口並監聽 在主線程中創建一個epoll實例(epoll_create(2)) 將監聽socket添

VS 解決方案文件結構分析

nbsp 項目文件 需要 管理器 TTT lease new projects build VS2013 解決方案文件結構分析 Visual Studio 的解決方案文件是一個文本文件,其中的內容不是太復雜,有些時候 Visual Studio 會把這個文件搞亂,理解一下

YII框架分析筆記2:組件和事件行為管理

reac 設置 有變 相關 article class ces col cal Yii是一個基於組件、用於開發大型 Web 應用的高性能 PHP 框架。CComponent幾乎是所有類的基類,它控制著組件與事件的管理,其方法與屬性如下,私有變量$_e數據存放事件(evnet

動態規劃分析總結——怎樣設計和實現動態規劃算法

基於 進一步 使用 sdn 能夠 疑惑 樓梯 -1 們的 進行算法設計的時候,時常有這種體會:假設已經知道一道題目能夠用動態規劃求解,那麽非常easy找到對應的動態規劃算法並實現;動態規劃算法的難度不在於實現,而在於分析和設計—— 首先你得知道這道題目須要用動態規劃來求

團隊題目需求分析-NABCD

同學 一定的 問題: 活動 下載 相關 什麽 方便 數據庫 Need: 由本人親身體驗出發,覺得很多同學記不住老師留的作業,或者上課時間記錄了,但是老是忘記到底記錄了什麽,導致沒有半大寫作業,所以準備設計一個東西來幫助同學。 A: 首先,我最先想到的是手機,所以準備用相

用戶場景分析i

ble lpad tab 環境 知識 span 場景 其他人 解決 名字 學生(註重飲食選擇,挑剔) 年齡 20 收入 無 知

軟件開發—錢多多—典型用戶分析

工資 學生 銀行 用戶分析 使用 層次 排名 校園 手機軟件 (1)姓名:陳晨 (2)年齡:20 (3)開銷:每月1500-2000,每月有固定的兼職收入 (4)代表的用戶在市場上的比例和重要性:該用戶代表了絕大多數的大學生,是我們軟件針對的典型用戶 (5)使用這個軟件的典

海量分頁的簡單分析

elk mic als emc usb won tm4 pop iap 此文僅個人理解,不到之處望指出 提出:easyui的datagrid組件有海量分頁的內容,通過查詢數據庫的所有數據在表格進行分頁瀏覽,因為數據量多,也叫海量分頁, 網

用戶場景分析

接受 alt 快遞 png 技術分享 信用 發布任務 背景 .cn 用戶場景分析: 典型用戶: 張三:有諸多事務等抽不開身;李四:比較懶 1、背景 (1)典型用戶:張三李四 (2)用戶需求/迫切需要解決的問題:有事不能抽開身,誰能幫我個忙啊;一些小事不想自己幹,誰可

實戰:MySQL Sending data導致查詢很慢的問題詳細分析(轉)

sql 格式 一段 ace 研究 軟件測試 tar 遊戲 很好 這兩天幫忙定位一個MySQL查詢很慢的問題,定位過程綜合各種方法、理論、工具,很有代表性,分享給大家作為新年禮物:) 【問題現象】 使用sphinx支持倒排索引,但sphinx從mysql查詢源數據的