主頁 > 移動端開發 > Android Binder驅動分析四 addservice原始碼分析

Android Binder驅動分析四 addservice原始碼分析

2021-04-05 10:32:32 移動端開發

Android Binder驅動分析三 addservice原始碼分析

接著上網我們分析了如下的代碼:

  1. 將事務添加到servicemanager,然后將其喚醒
  2. 添加一個binder_work事務給mediaplayserice

我們這里分析mediaplayservice提交事務后會做什么

//drivers/staging/android/binder.c


static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
	struct binder_write_read bwr;

    //從用戶空間的arg的地址處拷貝到內核
    //ubuf = arg
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
    //因為有資料傳入所以不為0
	if (bwr.write_size > 0) {
	
	//	1. 將事務添加到`servicemanager`,然后將其喚醒
	//	2. 添加一個`binder_work`事務給`mediaplayserice` 
		ret = binder_thread_write(proc, thread,
					  bwr.write_buffer,
					  bwr.write_size,
					  &bwr.write_consumed);
	
	}
	
	//這里不為空,表示想接收binder驅動的資訊
	if (bwr.read_size > 0) {
			ret = binder_thread_read(proc, thread, bwr.read_buffer,
					 bwr.read_size,
					 &bwr.read_consumed,
					 filp->f_flags & O_NONBLOCK);
		
		//如果還有未處理的任務喚醒執行緒,但是這里沒有沒有其他事務所以不會進入下面的代碼
		if (!binder_worklist_empty_ilocked(&proc->todo))
			binder_wakeup_proc_ilocked(proc);
	
			goto out;
		}
	}
	//binder驅動將修改過后的bwr傳回用戶空間(mediaplayservice)
	//可見記憶體共享僅針對于服務端來說,客戶端的讀寫還是要拷貝滴
	if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
out:
	return ret;
}





//ret = binder_thread_read(proc, thread, bwr.read_buffer,
//				 bwr.read_size,
//				 &bwr.read_consumed,
//				 filp->f_flags & O_NONBLOCK);

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;
	//這里是0所以放入BR_NOOP
	if (*consumed == 0) {
		if (put_user(BR_NOOP, (uint32_t __user *)ptr))
			//失敗處理
			return -EFAULT;
		//指標偏移
		ptr += sizeof(uint32_t);
	}



	while (1) {
		
		uint32_t cmd;
		struct binder_transaction_data_secctx tr;
		struct binder_transaction_data *trd = &tr.transaction_data;
		struct binder_work *w = NULL;
		struct list_head *list = NULL;
		struct binder_transaction *t = NULL;
		struct binder_thread *t_from;
		size_t trsize = sizeof(*trd);


		//一個程式的待處理任務會在執行緒或行程todo串列中
		//這里是取出串列物件
		if (!binder_worklist_empty_ilocked(&thread->todo))
			list = &thread->todo;
		else if (!binder_worklist_empty_ilocked(&proc->todo) &&
			   wait_for_proc_work)
			list = &proc->todo;
		//是否還有資料要讀取
		if (end - ptr < sizeof(tr) + 4) {
			break;
		}

		//取出串列物件,這里就是上篇文章的的內容tcomplete    
		w = binder_dequeue_work_head_ilocked(list);

		switch (w->type) {
	
			case BINDER_WORK_TRANSACTION_COMPLETE: {
				cmd = BR_TRANSACTION_COMPLETE;
				//放入一個命令BR_TRANSACTION_COMPLETE
				if (put_user(cmd, (uint32_t __user *)ptr))
				
					return -EFAULT;
				//指標后移
				ptr += sizeof(uint32_t);

				
				kfree(w);
				
			} break;
		
		}

		

		
	}

done:
	//注意consume變成寫入的數值大小
	//比如buffer是100      寫入64 
	//ptr初始值=100   寫入64為164.和原始地址先減去可以到寫入的位元組大小
	*consumed = ptr - buffer;
	
	//這里用于讓用戶空間新生成一個執行緒,這里我們先無視,就當沒有寫入
	if (proc->requested_threads == 0 &&
	    list_empty(&thread->proc->waiting_threads) &&
	    proc->requested_threads_started < proc->max_threads &&
	    (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
	     BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
	     /*spawn a new thread if we leave this out */) {
		proc->requested_threads++;
	
	
		if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
			return -EFAULT;
		binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
	}
		 
	return 0;
}


可見binder驅動給mediaplayservice提交了兩個主要命令BR_NOOPBR_TRANSACTION_COMPLETE 就回傳用戶空間

也就是以下代碼:

// frameworks\native\libs\binder\IPCThreadState.cpp

status_t IPCThreadState::talkWithDriver(bool doReceive=true)
{

   	//略
    do {
       
		//將我們的內容發送給binder驅動,這時從驅動回傳
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        
       
    } while (err == -EINTR);

   
    //binder驅動會修改bwr
    if (err >= NO_ERROR) {
        //運行到這時候binder驅動會消費我們傳入bwr.write_buffer,具體消費了多個位元組就是write_consumed
        if (bwr.write_consumed > 0) {
        	//將這個重置后繼續下次使用
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        //binder驅動會順帶回傳資訊,回傳的資訊的大小是read_consumed
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
 
        return NO_ERROR;
    }
    
    return err;
}

沒有太特殊繼續向上層函式回傳

以下函式首先讀取BR_NOOP 后再進入回圈呼叫talkWithDriver()后在讀取資訊,等回傳后在處理BR_TRANSACTION_COMPLETE

// frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
    	//回傳到這繼續向下走
        if ((err=talkWithDriver()) < NO_ERROR) break;
        
      	//如果沒有回傳資訊那么繼續回圈讀取binder驅動
        if (mIn.dataAvail() == 0) continue;
        //讀取binder驅動回傳的前4位元組,這四個位元組表示binder驅動是什么訊息型別
        cmd = (uint32_t)mIn.readInt32();
        
		//根據回傳的binder回傳的命令做出操作
		//首先讀取BR_NOOP
        switch (cmd) {
        
	        case BR_TRANSACTION_COMPLETE:
	        //這里reply不為空,acquireResult為NULL
	         //所以!reply && !acquireResult 為false,因此繼續回圈進入talkWithDriver等
	         if (!reply && !acquireResult) goto finish;
            break;
	        case BR_DEAD_REPLY:
			//..略
	        case BR_FAILED_REPLY:
	        //..略          
	        case BR_ACQUIRE_RESULT:
	        //..略  
	        case BR_REPLY:
            //..略
        	default:
        		//BR_NOOP在內部什么都不做,繼續回圈進入talkWithDriver
            	err = executeCommand(cmd);
            	if (err != NO_ERROR) goto finish;
        }
    }
    return err;
}


status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    
    switch (cmd) {
  	//略
    case BR_NOOP:
        break;
   }
    return result;
}


第一次讀取BR_NOOP沒有太多特殊的情況于是乎繼續進入talkWithDriver()


status_t IPCThreadState::talkWithDriver(bool doReceive)
{
 
    
    binder_write_read bwr;
    
    //因為dataPosition還有4位元組沒有讀取,
    //假設dataSize為8位元組,分別存放`BR_NOOP` 和`BR_TRANSACTION_COMPLETE` 
    //讀取`BR_NOOP`后dataPosition為4
    //因此這里為false
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    //outAvail 自然為0
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();
	//needRead false
	//doReceive  true
	//這里走到false
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }


    
    // bwr.write_size == 0為true
    //  bwr.read_size == 0 為true
    // 那么整個運算式為true,因此直接回傳
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
 

        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;


        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
      
    } while (err == -EINTR);

  

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
       
        return NO_ERROR;
    }
    
    return err;
}

talkWithDriver()回傳繼續讀取下一個命令BR_TRANSACTION_COMPLETE 這里由于這個命令也沒有特殊的地方,所以會重新再次進入talkWithDriver


status_t IPCThreadState::talkWithDriver(bool doReceive)
{
 
    
    binder_write_read bwr;
    
    //資料讀取完`BR_TRANSACTION_COMPLETE`  dataPosition會等于dataSize
  
    //因此這里為true
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    //outAvail dataSize
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();
	//needRead true
	//doReceive  true
	//這里走到true
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }


    
 	//這里直接為false
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
 
		//這里在進入binder驅動中
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;


        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
      
    } while (err == -EINTR);

  

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
       
        return NO_ERROR;
    }
    
    return err;
}

最后再次進入binder驅動的binder_thread_read函式.


static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;

	if (*consumed == 0) {
		if (put_user(BR_NOOP, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
	}

retry:
	//這里回傳false.   binder_available_for_proc_work_ilocked如果有事務就回傳true,沒有就回傳false
	wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);


	
	//這里為
	if (non_block) {
		//;略
	} else {
		//阻塞由于wait_for_proc_work為fakse
		//所以mediaplayservice被阻塞在這
		ret = binder_wait_for_work(thread, wait_for_proc_work);
	}

	//略
	return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/272516.html

標籤:其他

上一篇:QT的UDP通信

下一篇:2021年度訓練聯盟熱身訓練賽第二場 F Interstellar Love DFS 并查集

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more