標籤:
android4.2增加了多使用者功能,終於在遲遲之後與linux保持了一致。但是手機上的多使用者其實是相當雞肋的,試想手機這種行動裝置基本 上就是每一個人的唯一id,所以基本上不存在多使用者共用裝置的情況。也正因為此以及專利的原因,所以電話上的多使用者功能是關閉的,只有平板上的多使用者是打 開的。但還是要感謝Google開發人員引入多使用者機制,這樣可以協助開發一些安全系統有極大協助。
但開啟多使用者之後,有一個比較蛋疼的地方是無法在多使用者中打電話發簡訊。查看源碼的知,這是android對於電話通訊這一塊根本沒有做多使用者相容性適 配。只是在PhoneApp中簡單粗暴的做了一個單使用者判斷if (UserHandle.myUserId() == UserInfo.ROOT_USER_ID ),在其它使用者中根本無法使用電話和簡訊。而平板上又無這種通訊需求,所以Google開發人員根本沒在這塊做代碼適配。唉,無奈我們公司項目需要這方面的功能, 即在多使用者下也要能打電話發簡訊。所以只能硬這頭皮上了,去做Google人員未竟的工作。這個過程是痛苦的前期後後後找了好幾個phone方面比較熟悉的兄弟幫 忙分析代碼,加起來有快10天的工作量,終於初步滿足了需求,能打電話發簡訊了。下面是將這一過程中遇到的問題,做個摘錄以備忘。
首先理解多使用者原理,就算多使用者到底是一個什麼東西以及是一個什麼樣的實現機制。多使用者固名思意,就是在同一台裝置上隔離出另一個使用者空間,這個空間裡 面啟動並執行程式與普通空間啟動並執行程式是隔離的,完全是在兩個進程中,當然資料的儲存,比如資料庫也完全是獨立的。最直觀的感受就是,兩個使用者中裝上同一個應 用,那麼這兩個應用的導航頁是需要走兩遍的。當然這裡面涉及到很多隔離,比如install,鎖屏,某些設定等,所以在看4.2之後的原始碼,會發現基本 上所有的模組都涉及到多使用者的判斷,這說明android在加多使用者這個功能的時候,Google開發人員也做了一個海量的工作啊,這裡表示欽佩和感激。
好了,閑話少說,繼續說多使用者下phone進程問題。
一。首先建立多使用者之後,該使用者下會有一個com.android.phone進程,但是該進程是完全沒有東西的,因為在進程入口PhoneApp中已經做了判斷:
public void onCreate() {
if (UserHandle.myUserId() == 0) {
// We are running as the primary user, so should bring up the
// global phone state.
if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
mPhoneGlobals = new MSimPhoneGlobals(this);
} else {
mPhoneGlobals = new PhoneGlobals(this);
}
mPhoneGlobals.onCreate();
}
}
看到了吧,只有在0使用者(根使用者)下,才能進行phone模組的初始化。所以這裡做了第一個修改就是拿掉這個判斷,這個很容易,不再贅述。
代碼位置android/packages/services/Telephony/src/com/android/phone/PhoneApp.java中的onCreate()方法
二。去掉判斷之後,當然希望能直接打電話了,但事情永遠不會這麼順利,接下來下面這個異常擋住了前路:
E/AndroidRuntime( 3452): Caused by: java.lang.SecurityException
E/AndroidRuntime( 3452): at android.os.BinderProxy.transact(Native Method)
E/AndroidRuntime( 3452): at android.os.ServiceManagerProxy.addService(ServiceManagerNative.java:150)
E/AndroidRuntime( 3452): at android.os.ServiceManager.addService(ServiceManager.java:72)
E/AndroidRuntime( 3452): at com.android.internal.telephony.IccSmsInterfaceManager.<init>(IccSmsInterfaceManager.java:128)
E/AndroidRuntime( 3452): at com.android.internal.telephony.PhoneProxy.init(PhoneProxy.java:95)
E/AndroidRuntime( 3452): at com.android.internal.telephony.PhoneProxy.<init>(PhoneProxy.java:84)
E/AndroidRuntime( 3452): at com.android.internal.telephony.PhoneFactory.makeDefaultPhone(PhoneFactory.java:134)
E/AndroidRuntime( 3452): at com.android.internal.telephony.PhoneFactory.makeDefaultPhones(PhoneFactory.java:59)
E/AndroidRuntime( 3452): at com.android.phone.PhoneGlobals.onCreate(PhoneGlobals.java:416)
E/AndroidRuntime( 3452): at com.android.phone.PhoneApp.onCreate(PhoneApp.java:45)
E/AndroidRuntime( 3452): at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1009)
E/AndroidRuntime( 3452): at android.app.LoadedApk.makeApplication(LoadedApk.java:526)
E/AndroidRuntime( 3452): ... 12 more
分析異常log,發現是在添加一個與簡訊相關的服務isms時失敗,報了一個 SecurityException。但是在普通使用者下這個服務是添加成功 了的,所以在這裡有一個初步的猜測,就是添加系統服務這個動作並不是任何進程都能做的,因為在普通使用者下phone進程uid=1001,而在多使用者下 phone的uid=901001(9使用者),所以猜測是底層c代碼中對addservice的調用者做了許可權控制。果不其然,跟蹤addservice 的源碼最後跟到c層,在service_manager.c中有一個do_add_service()方法,這個方法就是ServiceManager的 addService方法的最終實現,在該方法中對調用者的uid做了一個判斷:
int svc_can_register(unsigned uid, uint16_t *name)
{
if ((uid == 0) || (uid == AID_SYSTEM))
return 1;
for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++)
if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name))
return 1;
return 0;
}
可以發現,只有root,system,以及系統基礎的phone,簡訊等進程才能通過判斷,而9使用者中的phone進程的uid==901001(多用 戶進程uid==userId*100000+uid),顯然不在這個範圍之內,當然添加系統服務失敗。修改方法是在這裡面加上一句多使用者相容性適配 uid = uid % 100000。這樣多使用者下相關進程也是可以添加系統服務了。
代碼位置:android/frameworks/native/cmds/servicemanager/service_manager.c的svc_can_register()方法.
三。服務添加完了,事情還遠遠沒有結束。接下來,在多使用者中,打電話時提示“無法訪問移動網路”.
顯然手機沒有連上訊號,查看log
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
I/RILJ ( 3839): Couldn‘t find ‘rild‘ socket; retrying after timeout
E/RILJ ( 3839): Couldn‘t find ‘rild‘ socket after 8 times, continuing to retry silently
這些log是在RIL.java中報出來的。這是java層與ril層通訊的類,而這個異常就是在與ril連結的時候報出來的,說明在多使用者下是連結不上ril的。
事情到這裡就很不好搞了,本人對於ril通訊這一塊完全沒接觸過啊。但是項目工期緊急,只能去求助公司的phone這塊研發人員。最終,黃天不負苦 心人,經過與phone這塊人員的討論之後,找到了一個解決方案:就是在使用者切換的時候,對ril進行一個斷開重連的過程,因為ril連結同時只能被一個 用戶端連結,而多使用者裡是會有多個phone進程的。所以在這裡,必須將之前普通使用者下的ril連結斷開,在新使用者裡重新連結ril。當然,這裡肯定需要 一個對phone很熟悉的人員找到這樣一個切換連結的地方,而我們公司正好各方面的人才都有,實現這樣一個方案不是太困難。
同時,為了連上ril還要在ActivityManagerService的startProcessLocked()方法裡開啟phone進程的時候,將進程的gid=1001.這裡是一個許可權判斷,對於gid!=1001的進程是不能連上ril服務的。
四。使用者切換時要替換兩個系統服務
在PhoneInterfaceManager裡切換phone的service(打電話相關)IccSmsInterfaceManager裡切換isms(發簡訊相關)
這兩個修改主要是保證來回切換使用者時,其它進程通過這兩個服務擷取電話簡訊相關狀態操作時能正確擷取。
好了,整體上主要是做了這四個修改。雖然這裡說的很簡單,但整個過程其實是很漫長糾結的,一度以為解決不了了,畢竟google人員這一塊也是簡單粗暴的沒有處理。但萬幸最終在同僚協助下解決了。
感謝同僚!
android源碼探索----多使用者下phone進程問題