###1. 单用户时代的 app 与 linux uid/gid

android 从诞生之初, 即将 linux uid/gid 体系用于 android package 的管理, 即为不同的 package 分配不同的 uid/gid (当然, 多个package也可以共享gid/uid, 另外gid通常 与 uid相同)

android.os.Process 中定义了 uid 的分配

public class Process {
    ......
    public static final int SYSTEM_UID = 1000;
    public static final int PHONE_UID = 1001;
    ......
}

对于系统中的package, 可以使用如下的方法来查看其uid

  • 运行该package对应的 app, 使用adb shell ps | grep <package name> 查看输出结果
  • 查看 “/data/system/packages.xml” 文件, 找到该 package 信息对应的 section, 其中, “userId=xxx” 即为该package的 uid
  • 执行 adb shell pm dump <package name> 命令, 其中 “userId=xxx” 即为该package的 uid

###2. android 多用户支持

Android 4.2开始支持多用户, UserManager 的 getMaxSupportedUsers()方法可以获取当前设备支持的最大用户数目的上限

public static int getMaxSupportedUsers() {
    ......
    // 小内存设备, 只支持 1个用户
    if (ActivityManager.isLowRamDeviceStatic()) return 1; 

    int multiuserMaximumUsers = 1;
    try {
        switch(ramSizeLevel()) {
            case 1:
                multiuserMaximumUsers = Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers_1G);
                break;
            case 2:
                multiuserMaximumUsers = Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers);
                break;
            case 3:
                multiuserMaximumUsers = Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers_3G);
                break;
            case 4:
                multiuserMaximumUsers = Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers_4G);
                break;
            default:
                multiuserMaximumUsers = Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers);
                break;
        }
    } catch (Resources.NotFoundException rnfe) {
        multiuserMaximumUsers = 1;
    }

    return SystemProperties.getInt("fw.max_users", multiuserMaximumUsers);
}

可以按照设备的内存大小, 修改对应的config值, 限定用户数目

###3. 多用户与 userid

每一个android user都有一个 userid 来表示, android.os.UserHandle 中定义了一些特殊的 userid

public final class UserHandle implements Parcelable {

    //代表所有的用户            
    public static final int USER_ALL = -1;
    
    //代表当前的用户
    public static final int USER_CURRENT = -2;
    
    //代表当前用户或者调用者用户
    public static final int USER_CURRENT_OR_SELF = -3;
    
    //代表用户不存在
    public static final int USER_NULL = -10000;
    
    //代表主用户
    public static final int USER_OWNER = 0;

    ......
}

###4. 多用户与 uid

为了支持多用户, 将 linux 的 uid 划分为不同的区间, 分配给不同的 android user, android.os.UserHandle 中有如下的定义

public final class UserHandle implements Parcelable {
    ......
    public static final int PER_USER_RANGE = 100000;
    ......
}

例如 user 0 的 uid 区间为 [0, 99999), user 1 的 uid 区间为 [100000, 199999)

###5. 多用户与 appid

在单用户时代, 每个package单独分配一个uid/gid来进行管理(共享uid/gid的除外), 而在支持多用户之后, 因为 uid 按区间分配给不同的 android user, 同一个 app, 在不同的 android user 下运行时, 其 uid 是不同的, 因此, 无法使用唯一的 uid 来标识一个 android package, 因此, 使用 appid 来标识一个 android package, appid 在安装时确定, 与运行状态无关, 因此, 在不同的 android user 下, 同一个 package 的 appid 是不变的

在支持多用户之后, 如下的值保存的都是 package 的 appid

  • android.content.pm.ApplicationInfo.uid
  • /data/system/packages.xml 中对应的pcakage的信息中的 “userId=xxx” 中的值
  • 执行 adb shell pm dump <package name> 命令, 其中 “userId=xxx” 中的值

例如, 在一台有id分别为 0 和 10 的 2个 user 的 android 设备上,

在一个有两个用户(用户id分别为0和10)的安卓设备上,在这2个用户下, 安装同一个app

  • 在用户 0 下:
    • 查看packages.xml,看到uid没有变化10078
    • Process.myUid()得到uid为”10078”
    • Process.myUserHandle()得到”userHandle{0}”
    • 运行该 app, 使用 ps 命令查看 uid 为 “u0_a78”
  • 在用户 10 下:
    • 从packages.xml查看此应用的uid:userId=”10078”
    • Process.myUid()得到uid为”1010078”
    • Process.myUserHandle()得到”userHandle{10}”
    • 运行该 app, 使用 ps 命令查看 uid 为 “u10_a78”

###6. userid uid 与 appid 的关系

在 android.os.UserHandle 中, 提供了转换这3者的方法

public static final int getUid(int userId, int appId) {
    if (MU_ENABLED) {
        return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
    } else {
        return appId;
    }
}

public static final int getAppId(int uid) {
    return uid % PER_USER_RANGE;
}

public static final int getUserId(int uid) {
    if (MU_ENABLED) {
        return uid / PER_USER_RANGE;
    } else {
        return 0;
    }
}

例如, 对于一台 android 设备上 user id 为 10 的一个 app

  • 从packages.xml查看此应用的uid:userId=”10078”
  • Process.myUid()得到uid为”1010078”
  • Process.myUserHandle()得到”userHandle{10}”

其中, appid 为 10078, uid 为 1010078, userid 为10, 按照它们的转换关系

uid = userid * PER_USER_RANGE + appid
1010078 = 10 * 100000 + 10078;

###7. uid 的格式化输出

很多时候, uid 并不是直接使用数字来显示, 比如, 对于 uid 1010078 , 以类似于 “u0a78” 这样的形式来显示, 那么它们之间是怎样的转换关系呢

首先, 在 android.os.Process 中, 有如下的定义

public class Process {    
    ......
    // 分配给 app 的 uid 的范围, 在支持多用户时, 这一值并不是绝对的 uid 值
    // 而是在 对应的 android user 的 uid 范围内的相对值
    public static final int FIRST_APPLICATION_UID = 10000;
    public static final int LAST_APPLICATION_UID = 19999;
    
    // 分配给被隔离的沙箱进程的 uid 的范围, 在支持多用户时, 这一值并不是绝对的 uid 值
    // 而是在 对应的 android user 的 uid 范围内的相对值
    public static final int FIRST_ISOLATED_UID = 99000;
    public static final int LAST_ISOLATED_UID = 99999;
    ......
}

以 android.os.UserHandle 的 formatUid 的实现为例:

public static void formatUid(StringBuilder sb, int uid) {
    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);
        }
    }
}

因此, 以 u10a78 和 uid 1010078 为例

u*axxx = u{uid / PER_USER_RANGE}a{uid % PER_USER_RANGE - FIRST_ISOLATED_UID}
u10a78 = u{1010078 / 100000}a{1010078 % 100000 - 10000}