码迷,mamicode.com
首页 > 移动开发 > 详细

android源码探索----多用户下phone进程问题

时间:2014-12-26 16:57:31      阅读:690      评论:0      收藏:0      [点我收藏+]

标签:

 android4.2增加了多用户功能,终于在迟迟之后与linux保持了一致。但是手机上的多用户其实是相当鸡肋的,试想手机这种移动设备基本 上就是每一个人的唯一id,所以基本上不存在多用户共用设备的情况。也正因为此以及专利的原因,所以电话上的多用户功能是关闭的,只有平板上的多用户是打 开的。但还是要感谢谷歌开发人员引入多用户机制,这样可以帮助开发一些安全系统有极大帮助。

    但打开多用户之后,有一个比较蛋疼的地方是无法在多用户中打电话发短信。查看源码的知,这是android对于电话通信这一块根本没有做多用户兼容性适 配。只是在PhoneApp中简单粗暴的做了一个单用户判断if (UserHandle.myUserId() == UserInfo.ROOT_USER_ID ),在其它用户中根本无法使用电话和短信。而平板上又无这种通信需求,所以谷歌开发人员根本没在这块做代码适配。唉,无奈我们公司项目需要这方面的功能, 即在多用户下也要能打电话发短信。所以只能硬这头皮上了,去做谷歌人员未竟的工作。这个过程是痛苦的前期后后后找了好几个phone方面比较熟悉的兄弟帮 忙分析代码,加起来有快10天的工作量,终于初步满足了需求,能打电话发短信了。下面是将这一过程中遇到的问题,做个摘录以备忘。

   首先理解多用户原理,就算多用户到底是一个什么东西以及是一个什么样的实现机制。多用户固名思意,就是在同一台设备上隔离出另一个用户空间,这个空间里 面运行的程序与普通空间运行的程序是隔离的,完全是在两个进程中,当然数据的存储,比如数据库也完全是独立的。最直观的感受就是,两个用户中装上同一个应 用,那么这两个应用的导航页是需要走两遍的。当然这里面涉及到很多隔离,比如install,锁屏,某些设置等,所以在看4.2之后的源代码,会发现基本 上所有的模块都涉及到多用户的判断,这说明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模块的初始化。所以这里做了第一个修改就是拿掉这个判断,这个很容易,不再赘述。

  代码位置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进程问题

标签:

原文地址:http://my.oschina.net/u/994235/blog/360884

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!