[SystemServer] Android 多用户管理

前言

Android支持多用户,所以多用户管理作为一个课题是研究Android frameworks逃不开的一个模块。从名字上来看UserManagerService看起来是管理用户的主要service,这个service位于
com.android.server.pm包下。Android并没有为用户管理新建一个package而是将其放在pm包下,我们都知道pm是对主要对一个个应用包做管理,也就是目标对象是一个个应用程序,那对用户的管理与对应用程序的管理同级存在是否别有一番意义呢?

首先回顾下Linux的基础知识点,Linux中是支持多用户体系的: 使用uid/gid来管理用户,uid代表用户Id,gid代表组id。而在不支持多用户Android早期,这种体系被应用到了应用管理上,这种感觉就是应用本身就Android的一个个独立用户。但是这种“用户”概念毕竟不是现实生活中真正使用手机的不同用户,所以在API 17,Android开始支持多用户后,引入了UserHandle的概念以此来区别真正操作手机的用户。除了这样微妙的联系外,PMS和UMS还有更加直接的联系,下文会介绍到。

静态结构

个人习惯先看一个模块的静态结构,所以这里先放上一张静态图:


UserManagerService 结构类图

从上面简便的结构图及类图中我们可以看到,对应UMS的Bp端服务UserManager位于base/core下的
android.os包中,而Bn端的UserManagerService服务却位于base/services/core下的
com.android.server.pm包中,从这样的位置我们不难看出Google的想法:面对上层用户管理(UserManager)理应是系统级的服务和系统息息相关,所以放在android.os包下,而在对应services中的用户管理和pm关系密切,所以放在com.android.server.pm包下。

除此之外,我们看到UMS和AMS有交互,这种交互主要是通过AMS中含有的UserController类来完成。PMS和UMS的交互则更加直接,PMS直接通过含有的UMS实例来完成对用户的管理亦或是对用户信息的获取。

启动

接下来我们动态的来看整个模块,首先是启动:

启动

这张时序图描绘了启动时AMS,PMS和UMS关于用户管理的一些操作。虽然时序图的重点本身就是时间和顺序,但是这里还是要单独提下几个service的启动顺序。

首先是AMS的启动,AMS启动后一直在做自己的事情,前期并没有做关于用户管理的操作。然后就是PMS的启动,这里注意PMS的启动并不是像大多数的其他Service那样使用内部类LifeCycle来,他是直接调用了PackManagerService的main方法:

        traceBeginAndSlog("StartPackageManagerService");
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();

而main方法中:

    public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();
        // 这里直接构建了PackageManagerService的创造方法
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }

而在PMS的构造函数中:

 `public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
//...
// 在这里直接构建了UserManagerService的实例
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
//...` 

以上的代码对应时序图中的第6、7步,可以直观的看出在SystemServer启动UMS之前,UMS的实例已经被创建了。而在正式start UMS时,如下面代码所示:

    public static class LifeCycle extends SystemService {

        private UserManagerService mUms;

        /** * @param context */
        public LifeCycle(Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            // UMS实例已经被创建,所以这里直接publish了已存在的实例
            mUms = UserManagerService.getInstance();
            publishBinderService(Context.USER_SERVICE, mUms);
        }

        @Override
        public void onBootPhase(int phase) {
            // 在如下阶段的时候,UMS做的第一件事情是去clean up
            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                mUms.cleanupPartialUsers();
            }
        }
    }

上面UMS真正工作前依赖了PHASE_ACTIVITY_MANAGER_READY的启动阶段,这个阶段由AMS释放,显然目前的阶段不符合,所以UMS被start了之后还未开始工作。
接着PackageManagerService 开始标记ready了对应12步,然后开始调用UMS中的方法整理用户信息。对应13步和15步。

接着AMS开始ready并且标记了PHASE_ACTIVITY_MANAGER_READY,UMS开始主动做clean up user的工作

最后当ActivityStackSupervisor触发finish boot时,AMS会调用UserController中的方法开始
start user。这里的start user其实就是记录start的user state保存在一个列表中,并且记
录了这个user的状态最终为启动状态。至此启动阶段的工作完成。

增删及切换用户

说到多用户管理,自然离不开启动、增加和删除用户。

1、启动用户

首先是启动用户


启动

这里有个参数需要了解一下:forground,如其表面含义一样,这个参数其实代表是否是前台启动user还是后台启动,前台启动是什么意思?其实是比如像切换用户这种,可见的启动另一个用户。从上面的时序图我们
可以看出启动用户主要是以下几个工作:
1、如果是新用户启动的话,创建对应的UserState实例,并保存(对应图中2、3)
2、如果是前台启动用户,会调用WMS中的相关方法(对应4、5、6)
3、准备相应用户数据(对应7、8、9)
4、如果是前台启动就是发送相应广播,并且使用ActivityStackSupervisor将相应activity切到前台。(对应图中10-12步)
5、如果是后台启动则标记user state并且做些善后工作将finish user boot广播出去。

2、增加用户

我们从面向上层的UserManager开始出发:

增加

创建用户的流程从图中可以看出主要分为以下步骤:
1、获取可用的user id,然后封装UserData信息,保存在UMS的列表中(对应图中5、6、7)
2、将用户数据写入本地持久化保存,注意这里partial字段标注了此时的用户因为还未创建完全所以是不完整的用户(对应图中8、9)
3、通过工具类准备用户目录和数据,在准备的过程中会做相应的校验,校验不通过会清空用户目录(对应图中10、11、12、13、14,在这个过程中会用到Installer(installd)来创建用户数据,省略在这几步中)
4、通过PMS来为新创建的用户安装系统app,准备系统app数据(对应图中15、16)
5、在数据准备完成之后,标记用户为完整用户并且更新本地用户数据,同时在pms中约束相关权限(对应图中12、18、19、20)

创建用户的最终产物是在UMS(内存)中创建了一份用户信息,同时磁盘上增加了此用户的用户目录数据及配套的系统app。

总的来说过程相对明朗,细节不再过多跟踪,下面来看下删除用户的流程。

3、删除用户

同样从UserManager出发:

删除

从图中我们可以看出,删除用户的主要步骤为:
1、获取当前用户,check是否和要删除的一致,一致则不删除(对应图中4、5)
2、将要删除的用户数据记录到一个列表中,并置partial为true,同时更新磁盘上的用户数据(对应图中6、7)
3、通过AMS stop user并且改变对应user state,之后发送广播通知user_removed(对应图中8、9、10)
4、开始实际清除用户数据,首先删除了该用户下的app数据,然后利用工具类删除用户数据(对应图中11、12、13、14、15)
5、最后清除内存中的数据,同时删除磁盘上记录用户的xml文件

总的来说这个过程和创建用户对应,大致可以看作是逆过程,值得注意的是:在删除用户的时候同样先将用户的partial置为true,但是最后没有置回false是因为用户数据如果顺利被删除的话最后本身就已经不存在了,所以自然也没了将其置回false的过程。如果在删除的过程中手机重启了,带有标记位partial的数据,如之前所述第一时间会被继续清除掉。

总结

这里主要讲述了Android framework中对多用户的管理和支持,省略了storage 和 installd对于相关
存储和数据的操作。如对pms熟悉者读起来效果最佳。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容