从源码角度分析Android中UID与GID的分配
概述
UID一般理解为User Identifier,在linux中就是用户的ID,表明是哪个用户运行了这个程序,GID则表明了这个用户属于哪个组。它们主要用于权限的管理。
而在Android中,部分权限的管理是依赖底层的linux的,所以了解Android的UID/GID十分必要。
网上有下面的一段话:
而在Android 中又有所不同,因为Android为单用户系统,这时UID 便被赋予了新的使命,android为每个应用几乎都分配了不同的UID,不像传统的linux,每个用户相同就为之分配相同的UID。(当然这也就表明了一个问题,android只能时单用户系统,在设计之初就被他们的工程师给阉割了多用户),使之成了数据共享的工具。
这段话有对有错,这篇文章将结合源码来分析UID与GID的分配,使我们有个最清晰的理解。源码版本为4.3。
首先需要明确的一点是,App的UID和GID是安装的时候确认的。而与安装相关的源码目录是:
frameworks\base\services\java\com\android\server\pm
PackageManagerService.java的scanPackageLI方法
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime) { ............ //获取一系列属性 pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.flags, true, false); ............ //UID赋值 pkg.applicationInfo.uid = pkgSetting.appId; ............
可以看到在getPackageLPw方法中,获取了UID,那我们打开这个函数看看:
Settings.java的getPackageLPw方法
private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) { ......... s.userId = newUserIdLPw(s); ......... }
可以看到UID是newUserIdLPw()指定的,那再打开这个看看:
依然是Settings文件
private int newUserIdLPw(Object obj) { // Let's be stupidly inefficient for now... final int N = mUserIds.size(); for (int i = 0; i < N; i++) { if (mUserIds.get(i) == null) { mUserIds.set(i, obj); return Process.FIRST_APPLICATION_UID + i; } } // None left? if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) { return -1; } mUserIds.add(obj); return Process.FIRST_APPLICATION_UID + N; }
至此,UID就算是分配下来了。
可以看到for循环和if (mUserIds.get(i) == null)语句限定了一个APP只有一个UID,而这个Process.FIRST_APPLICATION_UID是在frameworks/base/core/java/android/os/Process.java中定义的,其值为10000,这就是为什么Android的UID都是从10000开始的。
再回到PackageManagerService.java的scanPackageLI代码:
//invoke installer to do the actual installation //第二个uid就是GID int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } // Create data directories for all users // 指定工作目录 sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { Slog.w(TAG, "Unable to create data directory: " + dataPath); pkg.applicationInfo.dataDir = null; }
可以看到此函数在运行时,将UID的值直接赋值给了GID,所以通常UID和GID也是相同的。从官方注释这可以看到Android有多用户。事实上,Android某些平板对多用户是支持的,我猜大家常见的是阉割版的吧,所以有误会。
打开UserManager的installPackageForAllUsers方法可以看到:
public void installPackageForAllUsers(String packageName, int uid) { for (int userId : mUserIds) { // Don't do it for the primary user, it will become recursive. if (userId == 0) continue; mInstaller.createUserData(packageName, UserId.getUid(userId, uid), userId); }}
写了这么多,相信大家对UID/GID的分配有了更深刻的理解。欢迎一起讨论哈。
更多相关文章
- Android笔记-MultiThreading in Android(1)-Thread,Looper,Handl
- 最新的数据显示,十分之四的 Android(安卓)用户还在吃煎饼
- Android艺术开发探索第四章——View的工作原理(上)
- kotlin 协程在 Android(安卓)中的使用——Jetpack 中的协程、Ret
- Android、iPhone和Java三个平台一致的加密方法
- android:绘图 (android.graphics包)
- 从Android到iOS开发——(1)、objective-c与java语法对比
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用