最近遇到这么个Bug,关于UserId的,代码是这样的:

// we don't need to show button for secondary user
if (UserHandle.USER_OWNER != UserHandle.myUserId()) {
	return;
}

经过一顿Google之后,发现代码的意思是:如果当前进程的用户(UserHandle.myUserId())不是默认用户的话,直接return。

Linux UID

Linux会为每个用户分配一个uid,可以用id命令查看:

mi@zsy:~$ id
uid=1000(mi) gid=1000(mi) 组=1000(mi)···

root用户uid=0

root@zsy:/home/mi# id
uid=0(root) gid=0(root) 组=0(root)

Android UID

GPT给的答案
问:Android的uid和userid分别代表什么?
答:在Android系统中,UID和UserID代表以下内容:

  1. UID(User ID):每个应用程序都有一个唯一的UID,用于标识该应用程序的所有进程和数据。UID是一个整数,通常在安装应用程序时分配,并且在应用程序的整个生命周期中保持不变。UID用于实现应用程序之间的隔离和安全性,以及控制应用程序对系统资源的访问权限。
  2. UserID(User Identifier):每个Android用户都有一个唯一的UserID,用于标识该用户的所有应用程序和数据。UserID也是一个整数,通常在创建用户时分配,并且在用户的整个生命周期中保持不变。UserID用于实现多用户环境下的隔离和安全性,以及控制用户对系统资源的访问权限。

总之,UID和UserID都是用于实现Android系统中应用程序和用户之间的隔离和安全性的重要标识符。

下面详细说下:

在Android系统中,uid在应用安装时被分配,范围[10000, 19999],称之为appid,并且在应用存在于手机上期间,都不会改变,每个进程只能访问uid的权限范围内的文件。

通过在manifest文件中指定android:sharedUserId=""可以使多个应用共享同一个uid,使用相同sharedUserId的应用需要使用相同的签名,应用之间可以直接访问数据, 甚至在同一进程中运行。

UID 的分配

app 的 UID 是安装的时候就确认的, 关键的代码如下:
PMS中:

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
                                                 final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user)
        throws PackageManagerException {
    if (DEBUG_PACKAGE_SCANNING) {
        if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
            Log.d(TAG, "Scanning package " + pkg.packageName);
    }
    
    applyPolicy(pkg, policyFlags);
    .....................................

        if (pkgSetting == null) {
            .......................
            // SIDE EFFECTS; updates system state; move elsewhere
            if (origPackage != null) {
                mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
            }
            //主要是这句, PMS 维护着 mSettings 这个数据结构,里面存储着所有应用的安装信息
            mSettings.addUserToSettingLPw(pkgSetting);
        } else {
            // REMOVE SharedUserSetting from method; update in a separate call.
            //
            // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
            // secondaryCpuAbi are not known at this point so we always update them
            // to null here, only to reset them at a later point.
            Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, suid, destCodeFile,
                    pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,
                    pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,
                    pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),
                    UserManagerService.getInstance(), usesStaticLibraries,
                    pkg.usesStaticLibrariesVersions);
        }
        ....................
    return pkg;
}

frameworks\base\services\core\java\com\android\server\pm\Settings.java:

/**
 * Registers a user ID with the system. Potentially allocates a new user ID.
 * @throws PackageManagerException If a user ID could not be allocated.
 */
void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
    if (p.appId == 0) {
        // Assign new user ID
        p.appId = newUserIdLPw(p);
    } else {
        // Add new setting to list of user IDs
        addUserIdLPw(p.appId, p, p.name);
    }
    if (p.appId < 0) {
        PackageManagerService.reportSettingsProblem(Log.WARN,
                "Package " + p.name + " could not be assigned a valid UID");
        throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                "Package " + p.name + " could not be assigned a valid UID");
    }
}
// uid 的分配
    // Returns -1 if we could not find an available UserId to assign
    private int newUserIdLPw(Object obj) {
        // Let's be stupidly inefficient for now...
        final int N = mUserIds.size();
        //从0开始,找到第一个未使用的ID,此处对应之前有应用被移除的情况,复用之前的ID
        for (int i = mFirstAvailableUid; i < N; i++) {
            if (mUserIds.get(i) == null) {
                mUserIds.set(i, obj);
                return Process.FIRST_APPLICATION_UID + i;
            }
        }

        //最多只能安装 9999 个应用
        // None left?
        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
            return -1;
        }

        mUserIds.add(obj);
        // 可以解释为什么普通应用的UID 都是从 10000开始的
        return Process.FIRST_APPLICATION_UID + N;
    }

支持多用户后,Android的uid由两部分组成:

uid = userid * 100,000 + appid

使用ps查看进程, 可以看到用户名形如u0_a94

$ ps -ef  
UID            PID  PPID C STIME TTY          TIME CMD
u0_a94       21465  1160 0 24:06 ?        00:00:00 com.android.htmlviewer
u0_a133       1493  1160 0 32:03 ?        00:00:05 com.android.camera
u10_nfc       2750  1160 0 05:30 ?        00:00:00 com.android.nfc
u999_a225    22467  1160 0 24:30 ?        00:00:01 com.android.permissioncontroller

这个u0_a94就表示该应用是 user 0(主用户)下面的应用,userid0,id是94,普通应用程序的UID都是从10000开始的,所以 最终计算出的 appid10094uid0 * 100,000 + 10094 = 10094

编程中使用android.os.Process.myUid()可以获取当前uid

UserHandle

UserHandle为对userid的封装。

使用android.os.Process.myUserHandle()可以获取当前UserHandle

UserHandle中包含一些对uid进行转换的方法:

public static final int PER_USER_RANGE = 100000;

// uid2userId
public static @UserIdInt int getUserId(int uid) {
	// 是否支持多用户
	if (MU_ENABLED) { 
        return uid / PER_USER_RANGE;
    } else {
        return UserHandle.USER_SYSTEM; //USER_SYSTEM = 0;
    }
}

// uid2appId
public static @AppIdInt int getAppId(int uid) {
	return uid % PER_USER_RANGE;
}

// (userId + appId)2uid
public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
	if (MU_ENABLED) {
	    return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
	} else {
	    return appId;
	}
}

UserHandle中包含一些预定义的userid:

public static final @UserIdInt int USER_ALL = -1;  // 全部用户

public static final @UserIdInt int USER_CURRENT = -2;  // 当前用户

public static final @UserIdInt int USER_CURRENT_OR_SELF = -3;  // 当前用户或当前应用用户

public static final @UserIdInt int USER_NULL = -10000;  // 未定义

public static final @UserIdInt int USER_OWNER = 0; // 系统默认用户, 已弃用

public static final @UserIdInt int USER_SYSTEM = 0;  // 系统默认用户

UID System (uid=1000)

使用ps查看进程时,可以看到名为system的用户:

$ ps -ef  
UID            PID  PPID C STIME TTY          TIME CMD
system       32342  1160 0 05:19 ?        00:00:00 com.miui.securitycore.remote

该用户uid为1000, 开发系统应用时, 指定android:sharedUserId="android.uid.system",并使用系统签名, 可以直接访问系统应用数据。

小米手机中的UserInfo:

mi@zsy:~$ adb shell pm list users
Users:
	UserInfo{0:机主:c13} running
	UserInfo{10:security space:413} running
	UserInfo{999:XSpace:801010} running
	
0 :系统默认用户
10:security space 手机分身
999:XSpace 双开应用