最近遇到这么个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代表以下内容:
- UID(User ID):每个应用程序都有一个唯一的UID,用于标识该应用程序的所有进程和数据。UID是一个整数,通常在安装应用程序时分配,并且在应用程序的整个生命周期中保持不变。UID用于实现应用程序之间的隔离和安全性,以及控制应用程序对系统资源的访问权限。
- 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
(主用户)下面的应用,userid
为0
,id是94
,普通应用程序的UID都是从10000开始的,所以 最终计算出的 appid
是 10094
,uid
为0 * 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 双开应用