文档章节

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

s
 sflfqx
发布于 2014/12/26 15:00
字数 1999
阅读 463
收藏 10

 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人员这一块也是简单粗暴的没有处理。但万幸最终在同僚帮助下解决了。

感谢同僚!


本文转载自:http://blog.csdn.net/stephen8341/article/details/38079679

s
粉丝 42
博文 202
码字总数 0
作品 0
深圳
高级程序员
私信 提问
[Android] adb setuid提权漏洞的分析

原来我看到一篇关于分析RageAgainstTheCage源码的很不错的文章,后来发现原链接失效了,只能从网页的缓存副本中找寻蛛丝马迹。下面就是我找到的内容,保存到自己的博客中,来保证以后能找到。...

亭子happy
2012/09/12
373
0
Android--面试中遇到的问题总结(三)

《Android 开发工程师面试指南 LearningNotes 》,作者是陶程,由梁观全贡献部分。大家可以去知乎关注这两位用心的少年。这份指南包含了大部分Android开发的基础、进阶知识,不仅可以帮助准备...

sealin
2017/02/22
0
0
Android 进阶学习笔记整理

一.android系统架构图及各层介绍 1. 应用层:应用是用java语言编写的运行在虚拟机上的程序,比如通讯录,日历,电话,短信,浏览器等。 2. 应用框架层:这一层是编写Google发布的核心应用时所...

FunCode
2018/07/22
0
0
[学习笔记一]、Android体系与系统架构

1、Android系统架构 2、Linux Linux层,Android最底层最核心的部分。我们打款手机Setting,选择about phone选项,这一选项所显示的内核版本,就是我们所用的Linux内核的版本。 Linux层包含了A...

2tman
2015/11/20
61
0
探索Activity启动流程-实现打开插件中的Activity

通过分析Activity的启动流程,探索Android的插件化,下面通过源码分析实现一个简单的插件化 打开一个 未安装apk中的Activity 开始分析 Activity的启动流程从 startActivity开始 然后通过 In...

liwg
2018/05/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
5
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
6
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
昨天
6
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
7
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部