做Android多年總是有種雲山霧裏的感覺,說懂,其實你不懂,也知道自己還沒懂。說不懂,你又能支支吾吾說個三四,生活中我們也有很多這種一知半解的情況。因爲自己沒有去深入鑽研,在工作中大多都是拿來主義,過後又沒有去深入瞭解個所以然,久而久之你知道的永遠都只是一些皮毛,也就談不上進步。
不積跬步,無以至千里,養成總結記錄的好習慣,有時間多看看Android的源碼,逐段代碼的分析理解,相信不久後就能對別人說我是真的懂Android。
後續我將一點點記錄自己看源碼的過程,做個記錄,增強理解,一起學習,共同進步。
我給自己定的路子是先花時間深入研究一下Android的整個權限機制,也是Android最讓人頭疼的安全性。
相信很多人都看到過下面這段的代碼,那這段代碼到底有什麼作用呢?。。。往後看你就會知道這處的代碼在整個Android中有多重要了
Java代碼
final long token = Binder.clearCallingIdentity();
try {
...//邏輯代碼
} finally {
Binder.restoreCallingIdentity(token);
}
C++代碼
//獲取遠程Binder調用端的pid
pid_t IPCThreadState::getCallingPid() const
{
return mCallingPid;
}
//獲取遠程Binder調用端的uid
uid_t IPCThreadState::getCallingUid() const
{
return mCallingUid;
}
//清空遠程端Binder調用的uid和pid,賦值爲本地端的pid和uid
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
}
//恢復遠程端Binder調用的uid和pid
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
mCallingPid = (int)token;
}
在執行代碼邏輯之前調用clearCallingIdentity設置爲本地uid和pid,並且保存遠端調用的uid和pid,返回token,高32位存儲uid,低32位存儲pid,代碼執行完畢後又調用restoreCallingIdentity恢復遠端的值,token逆向位移運算。
一句話總結:restoreCallingIdentity作用是恢復遠程調用端的uid和pid信息,正好是`clearCallingIdentity`的反過程;
說了這麼多,那麼他到底爲什麼重要呢,舉個例子來說明,就會明白了
場景分析:
- 線程A通過Binder遠程調用線程B:則線程B的IPCThreadState中的mCallingUid和mCallingPid保存的就是線程A的UID和PID。這時在線程B中調用Binder.getCallingPid()和Binder.getCallingUid()方法便可獲取線程A的UID和PID,然後利用UID和PID進行權限比對,判斷線程A是否有權限調用線程B的某個方法。
- 線程B通過Binder調用當前線程的某個組件:此時線程B是線程B某個組件的調用端,則mCallingUid和mCallingPid應該保存當前線程B的PID和UID,故需要調用clearCallingIdentity()方法完成這個功能。當線程B調用完某個組件,由於線程B仍然處於線程A的被調用端,因此mCallingUid和mCallingPid需要恢復成線程A的UID和PID,這是調用restoreCallingIdentity()即可完成。
一句話總結:每個線程都可以是別人的中間者,調用你的人改變了你的角色賦予你某些權力,去實現某個目的,目的達到後你要恢復自己本來角色(這是你的義務),否者你將失去自我,給整個系統帶來安全隱患。