編輯:關於Android編程
基於Android 6.0的源碼剖析, 分析Binder IPC通信的權限控制方法clearCallingIdentity和restoreCallingIdentity的原理和用途。
/frameworks/base/core/java/android/os/Binder.java
/frameworks/base/core/jni/android_util_Binder.cpp
/frameworks/native/libs/binder/IPCThreadState.cpp
在Binder系列中通過十篇文章,深入探討了Android M的Binder IPC機制。看過Android系統源代碼的朋友,一定看到過Binder.clearCallingIdentity()
和Binder.restoreCallingIdentity()
這兩個方法,其定義在Binder.java
文件:
//作用是清空遠程調用端的uid和pid,用當前本地進程的uid和pid替代;
public static final native long clearCallingIdentity();
//作用是恢復遠程調用端的uid和pid信息,正好是`clearCallingIdentity`的反過程;
public static final native void restoreCallingIdentity(long token);
這兩個方法涉及的uid和pid,每個線程都有自己獨一無二的IPCThreadState
對象,記錄當前線程的pid和uid,可通過方法Binder.getCallingPid()
和Binder.getCallingUid()
獲取相應的pid和uid。
clearCallingIdentity(), restoreCallingIdentity()這兩個方法使用過程都是成對使用的,這兩個方法配合使用,用於權限控制檢測功能。
從定義這兩個方法是native方法,通過Binder的JNI調用,在android_util_Binder.cpp
文件中定義了native方法所對應的jni方法。
[–>android_util_Binder.cpp]
static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
{
//調用IPCThreadState類的方法執行
return IPCThreadState::self()->clearCallingIdentity();
}
[–>IPCThreadState.cpp]
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
}
UID和PID是IPCThreadState的成員變量, 都是32位的int型數據,通過移位操作,將UID和PID的信息保存到token
,其中高32位保存UID,低32位保存PID。然後調用clearCaller()方法將當前本地進程pid和uid分別賦值給PID和UID,最後返回token
。
[–>android_util_Binder.cpp]
static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
{
//token記錄著uid信息,將其右移32位得到的是uid
int uid = (int)(token>>32);
if (uid > 0 && uid < 999) {
//目前Android中不存在小於999的uid,當uid<999則拋出異常。
char buf[128];
jniThrowException(env, "java/lang/IllegalStateException", buf);
return;
}
//調用IPCThreadState類的方法執行
IPCThreadState::self()->restoreCallingIdentity(token);
}
[–>IPCThreadState.cpp]
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
mCallingPid = (int)token;
}
從token
中解析出PID和UID,並賦值給相應的變量。該方法正好是clearCallingIdentity
的反過程。
場景:首先線程A通過Binder遠程調用線程B,然後線程B通過Binder調用當前線程的另一個service或者activity之類的組件。
分析:
mCallingUid
和mCallingPid
保存的就是線程A的UID和PID。這時在線程B中調用Binder.getCallingPid()
和Binder.getCallingUid()
方法便可獲取線程A的UID和PID,然後利用UID和PID進行權限比對,判斷線程A是否有權限調用線程B的某個方法。mCallingUid
和mCallingPid
應該保存當前線程B的PID和UID,故需要調用clearCallingIdentity()
方法完成這個功能。當線程B調用完某個組件,由於線程B仍然處於線程A的被調用端,因此mCallingUid
和mCallingPid
需要恢復成線程A的UID和PID,這是調用restoreCallingIdentity()
即可完成。一句話:圖中過程2(調用組件2開始之前)執行clearCallingIdentity()
,過程3(調用組件2結束之後)執行restoreCallingIdentity()
。
看完場景分析,估計還有不少朋友感到迷惑,為何需要這兩個方法來多此一舉,直接檢測最初調用端的權限不就行了嗎?為了更加形象明了地說明其用途,下面用一個生活中的場景來類比說明。
場景:假如你的朋友請你幫忙,給她(他)到你的公司以內部價購買公司的某個產品。
分析:這個過程分為兩個階段
clearCallingIdentity()
是時候該登場了,在第二階段開始之前,先執行clearCallingIdentity()
過程,也就是把”Identity”信息清空,替換為你的信息(比如員工編碼ITCode之類的),那公司相關部門通過ITCode就可以直接判斷是否允許內購某產品。當第二階段完成後,也就是你已經購買到了公司產品,這時你需要將產品交付給你的朋友,需要restoreCallingIdentity
,恢復”Identity”為女的信息,這樣就嗯呢該順便交付給你的女朋友。如果不恢復信息,還是原來的ITCode,你交付的朋友可能是男的,另有其人,這樣就不科學了。相信到此,大家應該都能明白這兩個方法的作用,缺一不可,而且要成對出現。
上述過程主要在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。
為什麼要使用Android StudioAndroid Studio是谷歌推出了新的Android開發環境,其重要性可想而知!1. 集成了Gradle 打包工具2. 所見
接著上篇文章,現在在通過Android實際開發和源碼再探觀察者模式,listview是我們日常開發中必用的控件,雖然之前就有listview的替代品(recyclervi
eMMC主要是針對手機和平板電腦等產品的內嵌式存儲器,由於其在封裝中集成了一個控制器,且提供標准接口並管理閃存等優勢,越來越受到Android手機廠商的青睐,以eMMC為
surface,這個單詞的意思是浮在表面的,那麼surfaceview就是浮在表面的view了。如果真的這樣解釋,估計有人要拍磚了。然而,話雖不能這麼說,取這個名兒,多少