1. 概述

当前版本(Android 5.1)下,多用户管理适用于平板模式(手机版本可能受限于版本等原因)。
多用户模式下,不同用户运行在不同的用户空间,共享具体的应用实例(即不同的用户下,其应用版本是一致的),但拥有各自不同的配置。

本文不会对具体的代码进行解读,只会对相关概念、关联性较强的部分加以说明,目的在于对Android下多用户管理进行整体描述。

主要相关的代码路径如下:

frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
frameworks/base/core/java/android/os/UserManager.java
frameworks/base/core/java/android/content/pm/UserInfo.java
frameworks/base/core/java/android/os/UserHandle.java
frameworks/base/core/java/android/os/Process.java
frameworks/base/core/java/com/android/server/pm/PackageManagerService.java

2. 用户数据目录分析

/data/system/users 目录下,可以看到如下片段:

 drwx------ system   system            2017-12-22 09:15 0-rw------- system   system        193 2017-12-16 15:20 0.xml-rw------- system   system        128 2017-12-16 15:20 userlist.xml

其中 0.xml 记录了 user-0(即root用户,在Process.java中定义了ROOT_UID=0)的相关信息(相关属性字段在UserInfo.java中可以找到):

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<user id="0" serialNumber="0" flags="19" created="0" lastLoggedIn="1513408822281"><name>机主</name><restrictions />
</user>

其中的serialNumber会递增存储,即不会重复使用(在UserManagerService.java的private UserInfo createUserInternal(String name, int flags, int parentId) 方法中,有如下字样的代码:

userInfo.serialNumber = mNextSerialNumber++;

而对应的用户id,每次新建用户时,会通过如下方法获得可用的id:

/*** Returns the next available user id, filling in any holes in the ids.* TODO: May not be a good idea to recycle ids, in case it results in confusion* for data and battery stats collection, or unexpected cross-talk.* @return*/
private int getNextAvailableIdLocked() {synchronized (mPackagesLock) {int i = MIN_USER_ID;while (i < Integer.MAX_VALUE) {if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {break;}i++;}return i;}
}

而在 0 目录下,对应存放了一些系统配置信息:

 accounts.dbaccounts.db-journalappwidgets.xmlpackage-restrictions.xmlusb_device_manager.xmlwallpaperwallpaper_info.xml

而在userlist.xml中,对应存放了所有用户信息:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<users nextSerialNumber="10" version="4"><user id="0" />
</users>

在上述配置文件中,可以看出,系统在/data/system/users目录下管理不同用户的配置信息,android版本的多用户管理是在Linux版本之上的简化、封装,譬如Linux系统下,除了user-id以外,还有group-id、文件权限等(不是本文重点,不做赘述)。

而系统是否支持多用户,在UserManager.java中有明确的函数实现:

    public static boolean supportsMultipleUsers() {return getMaxSupportedUsers() > 1&& SystemProperties.getBoolean("fw.show_multiuserui",Resources.getSystem().getBoolean(R.bool.config_enableMultiUserUI));}

3. 用户权限

UserManager中定义了相关的用户操作权限,

DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
DISALLOW_CONFIG_WIFI = "no_config_wifi";
DISALLOW_INSTALL_APPS = "no_install_apps";
DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
DISALLOW_SHARE_LOCATION = "no_share_location";
DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
DISALLOW_REMOVE_USER = "no_remove_user";
DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
DISALLOW_CONFIG_VPN = "no_config_vpn";
DISALLOW_CONFIG_TETHERING = "no_config_tethering";
DISALLOW_FACTORY_RESET = "no_factory_reset";
DISALLOW_ADD_USER = "no_add_user";
ENSURE_VERIFY_APPS = "ensure_verify_apps";
DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
DISALLOW_APPS_CONTROL = "no_control_apps";
DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
DISALLOW_SMS = "no_sms";
DISALLOW_CREATE_WINDOWS = "no_create_windows";
DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";

此类权限主要用于一些系统功能的控制,也包含了用户的添加、删除等。

4. UserHandle 用户处理

UserHandle.java中,其内部值通过 final int mHandle 变量保存,用于保存对应的userId,UserHandle中定义了几个特殊的userId:

userId 注释
USER_OWNER 0 the “owner” user of the device (机主用户)
USER_ALL -1 all users on the device (设备上所有用户)
USER_CURRENT -2 the currently active user (当前用户)
USER_CURRENT_OR_SELF -3 A user handle to indicate that we would like to send to the current user, but if this is calling from a user process then we will send it to the caller’s user instead of failing with a security exception (1、当前用户 2、调用者所在用户非当前用户时返回对应用户)
USER_NULL -1000 An undefined user id (未定义用户)

4.1 uid、appId、userId、userGid、sharedAppId

  • userId: 用户id,即上文中所说的多用户id,UserHandle中定义了几个特殊值(0、-1、2、-3、-1000),其中0值作为默认用户id,无论是否存在多用户状态,均为此值,其余userId根据添加时缓存情况变化,上文稍有提及

  • appId(base uid): 与用户id无关的应用程序id,取值范围为 [0, 100000),UserHandle中定义了PER_USER_RANGE = 100000,即每个user下允许的uid个数,根据对应的方法和说明可以知道其对应范围。所有的系统应用、用户应用均在此范畴,下文会对Process中的对应uid进行说明

  • uid: userId与appId结合下的id,其对应的获取可以从UserHandle.getUid方法中找到全貌:

     public static final int getUid(int userId, int appId) {if (MU_ENABLED) {return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);} else {return appId;}}
  • userGid: 获取某个user下的group id。通过getUid(userId, Process.SHARED_USER_GID)获取,即上述getUid方法中的参数appId使用Process.SHARED_USER_GID值获取,从变量描述可知,同一用户下的所有应用共享一个userGid,且此值是唯一的,可见android中的用户组管理直接与user关联,简化了对应管理。

  • sharedAppId:共享的appId。可以根据对应的uid或appId获取,从下述方法可知,该值其实也是唯一的,只有输入参数id一个变量,其值为 40000+id%100000,应该也只会在某些特殊场景使用到,但是是与userId无关的,粗略看过代码后发现,应该是和某个apk被拆分成多个包的实现有关,譬如abc.apk被拆分成abc-1.apk与abc-2.apk,那么二者需要共享一个appId。

    public static final int getSharedAppGid(int id) {return Process.FIRST_SHARED_APPLICATION_GID + (id % PER_USER_RANGE)- Process.FIRST_APPLICATION_UID;}

UserHandle中其余静态方法解释:

getAppId(int uid):获取uid的appId,直接通过 uid % PER_USER_RANGE 公式获得,可以从 getUid方法的实现中得知,此处其实是其对应的逆解。

getUserGid(int userId):获取userId对应的gid,上文有提及,内部直接调用的getUid(userId, Process.SHARED_USER_GID)

getUserId(int uid):获取uid对应的userId,代码中有对MU_ENABLED进行特殊处理,其实 uid / PER_USER_RANGE 公式已经可以满足,因为单用户情况下,uid通常为0. 同样,也是对getUid方法的逆解。

isApp(int uid):是否是应用进程。 uid <=0 时必然为false,对应UserHandle中定义的几种特殊uid;uid>0时,获得对应的appId,然后进行范围判断 return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID

isIsolated(int uid):是否是完全隔绝的沙箱进程,完全隔绝的沙箱进程每次启动都是独立的,不能复用已有的进程信息。这个进程与系统其他进程分开且没有自己的权限。获得对应的appId后,对其范围进行判断 return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID

isSameUser(int uid1, int uid2):比较两个uid的userId是否相同,即它们是否属于同一个用户 return getUserId(uid1) == getUserId(uid2)

isSameApp(int uid1, int uid2):比较两个uid的appId是否相同 getAppId(uid1) == getAppId(uid2)

myUserId():获取当前进程的userId => getUserId(Process.myUid())

formatUid(StringBuilder sb, int uid) / formatUid(PrintWriter pw, int uid) 用于将uid进程显示成对应的格式化字符串,即通过ps指令查询到的进程USER值,将对应用户应用拆分成user/app/isolated等组合,形如u0_a20,而系统uid如1000则直接显示为system等,以StringBuilder中的实现为例:

     if (uid < Process.FIRST_APPLICATION_UID) {sb.append(uid);} else {sb.append('u');sb.append(getUserId(uid));final int appId = getAppId(uid);if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {sb.append('i');sb.append(appId - Process.FIRST_ISOLATED_UID);} else if (appId >= Process.FIRST_APPLICATION_UID) {sb.append('a');sb.append(appId - Process.FIRST_APPLICATION_UID);} else {sb.append('s');sb.append(appId);}}

上述方法本质上都是在相关概念基础上的封装,理解清除各个id的内在含义就会很清楚了。

4.2 特殊的id定义

Process.java中定义了系统应用UID、应用程序应用UID范围、沙盒应用UID范围、共享应用UID范围等:

UID/GID 注释
ROOT_UID 0 rootUid,默认根用户id
SYSTEM_UID 1000 systemUid,系统代码运行的 uid/gid
PHONE_UID 1001 the UID/GID under which the telephony code runs
SHELL_UID 2000 the UID/GID for the user shell
LOG_UID 1007 the UID/GID for the log group
WIFI_UID 1010 the UID/GID for the WIFI supplicant process
MEDIA_UID 1013 the UID/GID for the mediaserver process
DRM_UID 1019 the UID/GID for the DRM process(Digital Rights Management数字版权管理)
VPN_UID 1016 the UID/GID for the group that controls VPN services
NFC_UID 1027 the UID/GID for the NFC service process
BLUETOOTH_UID 1002 the UID/GID for the Bluetooth service process
MEDIA_RW_GID 1023 the GID for the group that allows write access to the internal media storage
PACKAGE_INFO_GID 1032 Access to installed package details
SHARED_RELRO_UID 1037 the UID/GID for the shared RELRO file updater process
SHARED_USER_GID 9997 Defines the gid shared by all applications running under the same profile
FIRST_APPLICATION_UID 10000 Defines the start of a range of UIDs (and GIDs), going from this number to {@link #LAST_APPLICATION_UID} that are reserved for assigning to applications (首个应用程序UID,非系统应用,每个用户的首个安装应用id一般均由此值开始)
LAST_APPLICATION_UID 19999 Last of application-specific UIDs starting at {@link #FIRST_APPLICATION_UID}
FIRST_ISOLATED_UID 99000 First uid used for fully isolated sandboxed processes (with no permissions of their own) (首个完全沙盒进程uid,禁止应用访问)
LAST_ISOLATED_UID 99999 Last uid used for fully isolated sandboxed processes (with no permissions of their own)
FIRST_SHARED_APPLICATION_GID 50000 First gid for applications to share resources. Used when forward-locking is enabled but all UserHandles need to be able to read the resources (首个共享gid,用于应用程序共享资源)
LAST_SHARED_APPLICATION_GID 59999 Last gid for applications to share resources. Used when forward-locking is enabled but all UserHandles need to be able to read the resources

5. UserInfo、UserState、用户操作逻辑等

关于UserInfo、UserState以及对应的用户创建、删除、启动逻辑等,可参考如下两个链接中描述,非本文重点,不加赘述:

多用户管理UserManager
Android UserManager相关源码分析

[Android 5.1] 多用户管理UserManager相关整理相关推荐

  1. 多用户管理UserManager

    多用户管理UserManager http://gityuan.com/2016/11/20/user_manager/ 注意:获取各个用户状态信息的命令: adb shell dumpsys use ...

  2. android 多用户管理UserManager

    一.概述 Android从4.2开始支持多用户模式,不同的用户运行在不同的用户空间,相关的系统设置是各不相同而且不同用户安装的应用和应用数据也是不一样的,但是系统中和硬件相关的设计则是共用的. And ...

  3. python下载包管理器_Python包管理整理:setuptool管理python相关的包

    setuptool管理python相关的包 一.介绍 setuptool管理python相关的包的工具.这些包是zip格式发布,但是后缀一般都是.egg setuptool能解决python包的依赖关 ...

  4. Python包管理整理:setuptool管理python相关的包

    setuptool管理python相关的包 一.介绍 setuptool管理python相关的包的工具.这些包是zip格式发布,但是后缀一般都是.egg setuptool能解决python包的依赖关 ...

  5. 关于Hadoop多用户管理支持客户端远程操作的理论总结

    1.问题 Hadoop客户端如何配置可远程操作Hadoop:Hadoop多用户情况下,是如何管理权限并分配存储空间和计算能力,保证集群的稳定. 2.Hadoop平台 要理解客户端如何通过指定用户远程操 ...

  6. android qq分组展开,Android仿qq分组管理的第三方库

    本文实例为大家分享了Android仿qq分组管理的第三方库,供大家参考,具体内容如下 下面先看效果 我们点击展开与折叠分组的功能在库里面是已经封装好的,只能把它已入到项目中,就可以直接用了,十分的方便 ...

  7. 智能会议系统(34)---Android语音通话实现方案及相关技术介绍

    Android语音通话实现方案及相关技术介绍 Android语音通话实现方案及相关技术介绍 语音通话 Step1语音采集和输出 Step2编解码方式 Step3网络传输 Step4去噪声消回音 语音通 ...

  8. Android 高级面试-2:IPC 相关

    内容 IPC 就是指跨进程通信.IPC 相关的内容,涉及的主要有: 常见的 IPC 通信方式: Binder 相关: 两种序列化方式及其对比: 问题 IPC Android 上的 IPC 跨进程通信时 ...

  9. Android隐藏的权限管理机制:AppOps

    最近整理以前开发中的笔记,发现有点零乱,遂决定将这些笔记整理迁移到 CSDN 上,分享出来与大家一起交流学习.如果有发现不当或有待商榷的地方,欢迎大家拍砖和指正.废话结束,进入本文正题:Android ...

  10. Android Gradle Composing builds 管理三方依赖

    Android Gradle Composing builds 管理三方依赖 Android Gradle Composing builds 管理三方依赖 Gradle Composing build ...

最新文章

  1. Node 抓取非utf-8编码页面
  2. 山东大学和哈工大的教师招聘条件对比,心里要有点数
  3. DataNode启动后自动停止的问题( Incompatible clusterIDs in /xxx/xxx;namenode clusterID = xxxx;datanode clusterI)
  4. 比较全面的MySQL优化参考
  5. Python列表的常用你操
  6. element ui 组件踩坑记录--后台管理系统-最全
  7. tomcat8+idea远程调试
  8. HeroM2连击技能设置和DB完整数据
  9. 交通信息工程 实验三:交通信号机仿真实验
  10. 00-高通msm8953 Android驱动教程
  11. Android源码编译过程及刷机过程详解
  12. helm开发环境部署gitea
  13. Android O 自定义通知实例及一个自定义自动适配缩放图片至特定大小的田字格ImageView
  14. 提交.a文件到svn
  15. 卫星过顶计算matlab,基于SGP4模型的卫星轨道计算.docx
  16. spring学习笔记(spring概述和IOC)
  17. 手机上的计算机黑屏怎么办,手机黑屏是怎么回事,教您苹果手机黑屏怎么办
  18. How to deal with Imbalanced Datasets in PyTorch - Weighted Random Sampler Tutorial
  19. httpclient默认配置导致rt飙高
  20. 计算机学院2022级新生邀请赛(三)

热门文章

  1. java 置换_Java中的置换和组合
  2. [JNI] 开发实例(2) 编译libwebsocket,封装jni函数,搭建IM通信基础服务
  3. Ajax学习笔记-客户端模板引擎-9
  4. 计算机应用计算专业难吗,计算机应用专业好学吗
  5. jqgrid 单元格绑定点击事件_VBA代码解决方案第115讲:点击鼠标实现精准控制触发事件的VBA代码第二方案...
  6. html5 拖放游戏,HTML5拖放API实现拖放排序的实例代码
  7. mysql八大知识点_MySQL索引八大法则之上篇
  8. Java监测他人的消息_Java中的Listener 监听器
  9. 浅谈SpringMVC的概念及执行原理
  10. php编辑jquery弹出窗,jquery实现一个简单好用的弹出框