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.db accounts.db-journal appwidgets.xml package-restrictions.xml usb_device_manager.xml wallpaper wallpaper_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_";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相关源码分析

更多相关文章

  1. 说说Android桌面(Launcher应用)背后的故事(六)——研究Launcher
  2. android webview js交互之自定义错误加载界面(重新刷新)
  3. 牛B的Android(安卓)UI--第18章 与用户交互相关的设计模式 (上)
  4. 东拼西凑写的android 相机例子,包含一些遇到的坑
  5. Android(安卓)值得深入思考的几个面试问答分享
  6. Android(安卓)数据持久化(SQLite数据存储)
  7. Android线程与并行,AsyncTask(AsyncTask回调方法、AsyncTask泛型参
  8. [置顶] Android(安卓)Binder通信机制学习
  9. Android接入多盟广告SDK--让你的APP通过广告平台赚钱

随机推荐

  1. Android多线程问题
  2. AndroidADB工具使用
  3. Android 不要标题和全屏的设置方式
  4. SMS Messaging in Android(1)
  5. Android实现可播放GIF动画的ImageView
  6. 就在10/19!Android 4.0 冰淇淋三明治正式
  7. [置顶] android利用jni调用第三方库——
  8. 伦敦开发者构建了一个基于 Android 的坦
  9. Android监控来电/通话中的状态
  10. android下自定义字体的使用方法