zoukankan      html  css  js  c++  java
  • Android 多用户模式原理和实现介绍

           我们可以感受到,在Android 4.2中的一个比较显著的改变就是加入了多用户的支持。因多用户手机专利早已被Symbian雇员注册,故 android官方的多用户切换目前仅支持平板设备。

    多用户模式的启用

    系统判断当前设备是否支持多用户模式的依据是配置文件config.xml中的config_multiuserMaximumUsers配置项。 其取值为整型,决定着当前设备支持的最大用户上限。默认值为1,即不支持多用户。如需启用多用户,则设置此值 为大于1的值。在Nexus 7中,此值为8。

    具体代码的判断位置在UserManager.java:

    [java] view plaincopy
    1. public static int getMaxSupportedUsers() {  
    2.         // Don't allow multiple users on certain builds  
    3.         if (android.os.Build.ID.startsWith("JVP")) return 1;  
    4.         return SystemProperties.getInt("fw.max_users",  
    5.                 Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));  
    6.     }  


    多用户相关操作流程

    对用户的操作目前未对普通应用开放,其相关API都有hide注解,并需要system权限。此外,用户的添加和移除还 要需android.Manifest.permission.MANAGE_USERS权限。

    用户添加流程

    用户添加是通过调用UserManager的public UserInfo createUser(String name, int flags)方法进行的。其具体实现在UserManagerService的同名方法中。

    在调用时,系统进行如下操作:

    1. 检查调用者是否具有所需权限。
    2. 对安装和软件包流程加锁,保证线程安全
    3. 检查多用户环境是否到达用户数量限制。如果没有,创建用户实例
    4. 为新用户创建相关目录
    5. 序列化用户列表
    6. 发送用户建立广播,MountService在收到此广播后,调用createEmulatedVolumeForUserLocked方法为用户建 立相应的数据目录

    用户的保存

    用户创建后,会首先在/data/system/users/userlist.xml文件中保存新增加用户的id,创建/data/system/users/ 用户id 目录,并将用户信息保存至其下的用户id.xml 文件中。其内容包括一些基本的用户信息。

    用户切换流程

    用户切换是通过调用ActivityManager的public boolean switchUser(int userId)方法进行。一般通过 ActivityManagerNative.getDefault().switchUser(int userId)进行调用。

    在调用时,系统进行以下操作

    1. 检查调用者是否具有所需权限。
    2. 获取切换目标用户信息,并设定当前用户为目标用户
    3. WindowsManagerService设置当前用户,锁定屏幕
    1. 切换目标用户状态至启动
    2. 广播REPORT_USER_SWITCH_MSG和USER_SWITCH_TIMEOUT_MSG消息,设定用户切换和切换超时时间(2秒),此超 时时间用于限定REPORT_USER_SWITCH_MSG广播全程时间。
    3. 切换Activity堆栈至当前用户
    4. 广播ACTION_USER_SWITCHED消息,各接收组件进行相应操作。 大部分具体操作,比如存储目录切换、安全设置 切换等,都在此广播后进行。

    用户移除流程

    用户移除是通过调用UserManager的 public boolean removeUser(int userHandle) 方法进行的。其具体实现同样 是在UserManagerService的同名方法中。

    在调用时,系统进行如下操作:

    1. 检查调用者是否具有所需权限。
    2. 对软件包变化加锁
    3. 将用户id加入待移除用户列表,将用户状态设为partial,这样,在下次系统启动时,会清除此用户。
    4. 停止用户,杀掉用户相关进程。
    5. 发送用户移除的广播。广播成功后,删除用户描述文件和数据文件。
    6. 序列化用户列表

    此外UserManager还提供了 public void wipeUser(int userHandle) 方法,用于删除单个用户的所有数据,但保留 用户账号。 此方法目前对应的底层实现尚未完成。

    多用户模式的API接口UserManager->UserManagerService

    大致结构

    与其它系统服务的实现类似,用户管理也采用了经由Binder调用的远程服务机制。UserManager为暴露给用户的接 口,UserManagerService为接口的底层实现。其类图如下所示:

    android.os.UserManager

    UserManager是暴露出来的应用程序接口。对于普通应用程序,提供用户数查询,用户状态判断和用户序列号查询 等基本功能。 普通应用没有用户操作权限。

    对于系统应用,UserManager提供了创建/删除/擦除用户、用户信息获取、用户句柄获取等用户操作的接口。均由远 程调用UserManagerService服务的对应方法实现。

    isUserAGoat()

    UserManager中提供了一个名为isUserAGoat()的方法。源码中此方法直接返回了false。此方法的加入纯粹是为了给 枯燥的编程生活带来一丝乐趣,以便写出:

    [java] view plaincopy
    1. while(!isUserAGoat()){  
    2.    
    3. }  


    这样的语句。

    com.android.server.pm.UserManagerService

    与其它大部分Service一样,UserManagerService的实现采用了 单例模式。在服务中,由组成为UserInfo类的散列 表mUsers维护所有的用户状态。

    mUsers在系统启动时由/data/system/users/userlist.xml读取生成,并在运行期间动态改变。所有用户的添加、删 除操作,都最终序列化回此文件中。

    com.android.server.am.ActivityManagerService

    ActivityManagerService目前加入了多用户支持。负责维护设备中存在的所有用户状态。服务以下述变量来记录当 前处于“启动”状态的用户。

    [java] view plaincopy
    1. /** 
    2.      * Which uses have been started, so are allowed to run code. 
    3.      */  
    4.     final SparseArray mStartedUsers = new SparseArray();  
    5.    
    6.     /** 
    7.      * LRU list of history of current users.  Most recently current is at the end. 
    8.      */  
    9.     final ArrayList mUserLru = new ArrayList();  
    10.    
    11.     /** 
    12.      * Constant array of the users that are currently started. 
    13.      */  
    14.     int[] mStartedUserArray = new int[] { 0 };  


    用户的启动状态对象为com.android.server.am.UserStartedState。其中指定的用户状态有四种:

    • public final static int STATE_BOOTING = 0; //用户启动
    • public final static int STATE_RUNNING = 1; //运行中
    • public final static int STATE_STOPPING = 2; //停止中
    • public final static int STATE_SHUTDOWN = 3; //用户关闭状态

    完整的用户生命周期为:
    BOOTING->RUNNING->STOPPING->SHUTDOWN

    用户必须处于RUNNING状态时,才能作为切换的目标用户。所以在用户切换流程中,首先要判断当前用户的状态, 并启动STOPPING/SHUTDOWN状态的用户。

    多用户模式的牵涉面

    锁屏界面

    用户最先体验到多用户的入口位置即为锁屏界面。锁屏界面中加入了用户切换组件: KeyguardMultiUserSelectorView类。

    该类在设备允许多用户存在的情况下,显示当前所有用户的列表。并在用户进行选择后,调用 ActivityManagerNative.getDefault().switchUser(int userId)方法进行用户切换。

    外部存储

    对于每个用户,Android都为其分配了单独的存储空间。标准的支持多用户的外部存储空间是由init.rc定义的环境 变量所指定:

    [java] view plaincopy
    1. # See storage config details at <a href="http://source.android.com/tech/storage/">http://source.android.com/tech/storage/</a>  
    2. mkdir /mnt/shell/emulated 0700 shell shell  
    3. mkdir /storage/emulated 0555 root root  
    4.    
    5. export EXTERNAL_STORAGE /storage/emulated/legacy  
    6. export EMULATED_STORAGE_SOURCE /mnt/shell/emulated  
    7. export EMULATED_STORAGE_TARGET /storage/emulated  
    8.    
    9. # Support legacy paths  
    10. symlink /storage/emulated/legacy /sdcard  
    11. symlink /storage/emulated/legacy /mnt/sdcard  
    12. symlink /storage/emulated/legacy /storage/sdcard0  
    13. symlink /mnt/shell/emulated/0 /storage/emulated/legacy  


    在Dalvik虚拟机初始化的过程中,会以dalvik_system_Zygote.cpp中的mountEmulatedStorage函数,使用带有 MS_BIND参数的mount命令, 将用户对应的外部存储卡目录mount到上述定义的TARGET目录下。其判断应用userid的 方式为: 以当前应用的uid/100000,获得对应的userid,这段逻辑位于system/core/libcutils/multiuser.c中。

    而Environment类中相应的获取外部存储目录的方法,也是由上述环境变量所获得。对于每个用户,其标准外部存 储路径为:

    EMULATED_STORAGE_TARGET/userid/

    比如:

    /storage/emulated/0 为主用户的外部存储路径。

    包管理(PackageManagerService)

    在多用户环境下,所有用户安装的应用仍然同以前一样,放置于/data/app目录下。但原先/data/data的数据存储位 置目前仅对主用户有效,其余用户的数据存储目录则位于/data/user/用户id/目录下。 此目录的创建是在创建用户 时由前述的MountService完成的。

    对于每个用户,系统都会以PackageuserState类来维护其安装的软件状态。此列表以散列表的形式存在,由 PackageSettingBase类维护。所有的包——用户关系和状态最终仍然序列化至/data/system/package.xml中,并保留 /data/system/package-backup.xml作为备份。

    可能的发展

    Guest用户的实现

    目前代码中已经存在诸如 isGuestEnabled() 之类的方法。但没有对开发公开,可以预计今后会加入Guest用户, 实现“随手玩玩”模式

  • 相关阅读:
    第4月第1天 makefile automake
    第3月30天 UIImage imageWithContentsOfFile卡顿 Can't add self as subview MPMoviePlayerControlle rcrash
    第3月第27天 uitableviewcell复用
    learning uboot fstype command
    learning uboot part command
    linux command dialog
    linux command curl and sha256sum implement download verification package
    learning shell script prompt to run with superuser privileges (4)
    learning shell get script absolute path (3)
    learning shell args handing key=value example (2)
  • 原文地址:https://www.cnblogs.com/lechance/p/4373234.html
Copyright © 2011-2022 走看看