1. 程式人生 > 實用技巧 >位元組跳動面試官:Android原始碼的Binder許可權是如何控制?

位元組跳動面試官:Android原始碼的Binder許可權是如何控制?

想要成為一名優秀的Android開發,你需要一份完備的知識體系,在這裡,讓我們一起成長為自己所想的那樣~。

一、原始碼分析

(1)clearCallingIdentity方法,最終呼叫如下:

int64_t IPCThreadState::clearCallingIdentity()
{
    int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
    clearCaller();
    return token;
}

void IPCThreadState::clearCaller()
{
    mCallingPid = getpid(); //當前程序pid賦值給mCallingPid
    mCallingUid = getuid(); //當前程序uid賦值給mCallingUid
}
  • mCallingUid(記為UID),儲存Binder IPC通訊的呼叫方程序的Uid;
  • mCallingPid(記為PID),儲存Binder IPC通訊的呼叫方程序的Pid;

UID和PID是IPCThreadState的成員變數, 都是32位的int型資料,通過移位操作,將UID和PID的資訊儲存到token,其中高32位儲存UID,低32位儲存PID。然後呼叫clearCaller()方法將當前本地程序pid和uid分別賦值給PID和UID,最後返回token。

一句話總結:clearCallingIdentity作用是清空遠端呼叫端的uid和pid,用當前本地程序的uid和pid替代;

(2)restoreCallingIdentity方法,最終呼叫如下:

void IPCThreadState::restoreCallingIdentity(int64_t token)
{
    mCallingUid = (int)(token>>32);
    mCallingPid = (int)token;
}

從token中解析出PID和UID,並賦值給相應的變數。該方法正好是clearCallingIdentity的反過程。

一句話總結:restoreCallingIdentity作用是恢復遠端呼叫端的uid和pid資訊,正好是clearCallingIdentity

的反過程;

到此,應該明白了從程式碼角度是如何實現的。

(3) 原始碼示例

上述過程主要在system_server程序的各個執行緒中比較常見(普通的app應用很少出現),比如system_server程序中的ActivityManagerService子執行緒,程式碼如下:

[–>ActivityManagerService.java]

@Override
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        //獲取遠端Binder呼叫端的pid
        int callingPid = Binder.getCallingPid();
        //清除遠端Binder呼叫端uid和pid資訊,並儲存到origId變數
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        //通過origId變數,還原遠端Binder呼叫端的uid和pid資訊
        Binder.restoreCallingIdentity(origId);
    }
}

文章startService中有講到attachApplication()的呼叫。該方法一般是system_server程序的子執行緒呼叫遠端程序時使用,而attachApplicationLocked方法則是在同一個執行緒中,故需要在呼叫該方法前清空遠端呼叫者的uid和pid,呼叫結束後恢復遠端呼叫者的uid和pid。

二、場景分析

場景1:首先執行緒A通過Binder遠端呼叫執行緒B,然後執行緒B通過Binder呼叫當前執行緒的另一個service或者activity之類的元件

分析:

  1. 執行緒A通過Binder遠端呼叫執行緒B:則執行緒B的IPCThreadState中的mCallingUid和mCallingPid儲存的就是執行緒A的UID和PID。這時線上程B中呼叫Binder.getCallingPid()和Binder.getCallingUid()方法便可獲取執行緒A的UID和PID,然後利用UID和PID進行許可權比對,判斷執行緒A是否有許可權呼叫執行緒B的某個方法。
  2. 執行緒B通過Binder呼叫當前執行緒的某個元件:此時執行緒B是執行緒B某個元件的呼叫端,則mCallingUid和mCallingPid應該儲存當前執行緒B的PID和UID,故需要呼叫clearCallingIdentity()方法完成這個功能。當執行緒B呼叫完某個元件,由於執行緒B仍然處於執行緒A的被呼叫端,因此mCallingUid和mCallingPid需要恢復成執行緒A的UID和PID,這是呼叫restoreCallingIdentity()即可完成。

一句話:圖中過程2(呼叫元件2開始之前)執行clearCallingIdentity(),過程3(呼叫元件2結束之後)執行restoreCallingIdentity()。

二、類比分析

看完場景分析,估計還有不少朋友感到迷惑,為何需要這兩個方法來多此一舉,直接檢測最初呼叫端的許可權不就行了嗎?為了更加形象明瞭地說明其用途,下面用一個生活中的場景來類比說明。

場景:假如你的朋友請你幫忙,給她(他)到你的公司以內部價購買公司的某個產品。

**分析:**這個過程分為兩個階段

  • 第一階段:你的朋友請你幫忙的過程,這個過程並不一定所有朋友都會幫的,這時就需要一個許可權檢測,那麼在你的朋友”遠端呼叫”你執行任務時,你會記錄他的”Identity”資訊(比如是性別),有了資訊那麼就可以許可權檢測,不妨令許可權規則是如果這個朋友是女性則答應幫忙,否則就認定許可權不夠拒絕執行(可能黑客會想到先去一趟泰國,許可權控制可能相應需要打補丁了),若答應幫忙則進入第二階段,否則直接返回。
  • 第二階段:你向自己所在公司的相關部門內購產品的過程,這個過程也並不是所有人都能許可權能夠內購的,只有自己公司的員工才行,否則你的朋友也不會找你幫忙了。 這個過程同樣需要許可權檢測,但是”Identity”儲存的是性別女的資訊,公司內購產品如果也以性別來判斷,那豈不是公司的所有男員工沒有許可權內購,那這公司就有點太坑了,這明顯不符合實情。 clearCallingIdentity()是時候該登場了,在第二階段開始之前,先執行clearCallingIdentity()過程,也就是把”Identity”資訊清空,替換為你的資訊(比如員工編碼ITCode之類的),那公司相關部門通過ITCode就可以直接判斷是否允許內購某產品。當第二階段完成後,也就是你已經購買到了公司產品,這時你需要將產品交付給你的朋友,需要restoreCallingIdentity,恢復”Identity”為女的資訊,這樣就該順便交付給你的女朋友。如果不恢復資訊,還是原來的ITCode,你交付的朋友可能是男的,另有其人,這樣就不科學了。

相信到此,大都能明白這兩個方法的作用,缺一不可,而且要成對出現。

本文在開源專案:https://github.com/Android-Alvin/Android-LearningNotes中已收錄,裡面包含不同方向的自學程式設計路線、面試題集合/面經、及系列技術文章等,資源持續更新中…