android源碼探索----多用戶下phone進程問題

 android4.2增長了多用戶功能,終於在遲遲以後與linux保持了一致。可是手機上的多用戶實際上是至關雞肋的,試想手機這種移動設備基本 上就是每個人的惟一id,因此基本上不存在多用戶共用設備的狀況。也正由於此以及專利的緣由,因此電話上的多用戶功能是關閉的,只有平板上的多用戶是打 開的。但仍是要感謝谷歌開發人員引入多用戶機制,這樣能夠幫助開發一些安全系統有極大幫助。java

    但打開多用戶以後,有一個比較蛋疼的地方是沒法在多用戶中打電話發短信。查看源碼的知,這是android對於電話通訊這一塊根本沒有作多用戶兼容性適 配。只是在PhoneApp中簡單粗暴的作了一個單用戶判斷if (UserHandle.myUserId() == UserInfo.ROOT_USER_ID ),在其它用戶中根本沒法使用電話和短信。而平板上又無這種通訊需求,因此谷歌開發人員根本沒在這塊作代碼適配。唉,無奈咱們公司項目須要這方面的功能, 即在多用戶下也要能打電話發短信。因此只能硬這頭皮上了,去作谷歌人員未竟的工做。這個過程是痛苦的前期後後後找了好幾個phone方面比較熟悉的兄弟幫 忙分析代碼,加起來有快10天的工做量,終於初步知足了需求,能打電話發短信了。下面是將這一過程當中遇到的問題,作個摘錄以備忘。linux

   首先理解多用戶原理,就算多用戶究竟是一個什麼東西以及是一個什麼樣的實現機制。多用戶固名思意,就是在同一臺設備上隔離出另外一個用戶空間,這個空間裏 面運行的程序與普通空間運行的程序是隔離的,徹底是在兩個進程中,固然數據的存儲,好比數據庫也徹底是獨立的。最直觀的感覺就是,兩個用戶中裝上同一個應 用,那麼這兩個應用的導航頁是須要走兩遍的。固然這裏面涉及到不少隔離,好比install,鎖屏,某些設置等,因此在看4.2以後的源代碼,會發現基本 上全部的模塊都涉及到多用戶的判斷,這說明android在加多用戶這個功能的時候,谷歌開發人員也作了一個海量的工做啊,這裏表示欽佩和感激。android

    好了,閒話少說,繼續說多用戶下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模塊的初始化。因此這裏作了第一個修改就是拿掉這個判斷,這個很容易,再也不贅述。app

  代碼位置android/packages/services/Telephony/src/com/android/phone/PhoneApp.java中的onCreate()方法socket


二。去掉判斷以後,固然但願能直接打電話了,但事情永遠不會這麼順利,接下來下面這個異常擋住了前路:ui

  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 morethis

  分析異常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人員這一塊也是簡單粗暴的沒有處理。但萬幸最終在同僚幫助下解決了。

感謝同僚!

相關文章
相關標籤/搜索