1. Android UID 概述
在 Android 操作系统中,UID (User ID) 是用于标识一个应用程序的核心标识符。
- 分配机制:UID 在应用安装时由系统(PackageManagerService)分配。
- 生命周期 :在应用存在于设备期间,其 UID 保持不变。只有当应用被卸载后,该 UID 才可能被回收并分配给新的应用。
- 唯一性原则:通常情况下,一个应用程序对应一个 UID。
- 共享机制 :多个应用可以通过
sharedUserId方式共享同一个 UID,但前提是这些应用必须使用相同的签名。
UID 是 Android 安全沙箱(Sandbox)机制的基础,它决定了应用的文件访问权限、资源使用权限以及进程间通信(IPC)的身份验证。
2. UID 的格式与计算
在 Android log 中,我们经常看到类似 u0_a36 的标识,这就是 UID 的一种表现形式。


以 u0_a199 为例:
- u0 : 代表 User 0 ,即 Android 系统的主用户(Owner)。Android 支持多用户,其他用户可能是
u10等。 - a199: 代表该应用在该用户下的 ID 偏移量,这里是 199。
Android 普通应用程序的 UID 起始值定义为 10000 (FIRST_APPLICATION_UID)。
真实数值计算公式::
<math xmlns="http://www.w3.org/1998/Math/MathML"> UID = Base + Offset \text{UID} = \text{Base} + \text{Offset} </math>UID=Base+Offset
针对 u0_a1999:
<math xmlns="http://www.w3.org/1998/Math/MathML"> 10000 ( 基础值 ) + 199 ( 偏移量 ) = 10199 10000 (\text{基础值}) + 199 (\text{偏移量}) = 10199 </math>10000(基础值)+199(偏移量)=10199
因此,系统底层实际使用的 int 类型 UID 为 10036。
3. UID 的分配范围
根据 frameworks\base\core\java\android\os\Process.java 中的定义:
java
// 应用程序 UID 范围
public static final int FIRST_APPLICATION_UID = 10000;
public static final int LAST_APPLICATION_UID = 19999;
// SDK 沙箱进程 UID 范围
public static final int FIRST_SDK_SANDBOX_UID = 20000;
public static final int LAST_SDK_SANDBOX_UID = 29999;
// 隔离进程 UID 范围
public static final int FIRST_ISOLATED_UID = 99000;
public static final int LAST_ISOLATED_UID = 99999;
// 应用 Zygote 隔离进程 UID 范围
public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
系统预定义的特殊 UID:
java
public static final int SYSTEM_UID = 1000; // 系统进程
public static final int PHONE_UID = 1001; // 电话服务
public static final int SHELL_UID = 2000; // Shell 进程
public static final int LOG_UID = 1007; // 日志服务
public static final int WIFI_UID = 1010; // WiFi 服务
public static final int MEDIA_UID = 1013; // 媒体服务
public static final int DRM_UID = 1019; // DRM 服务
public static final int VPN_UID = 1016; // VPN 服务
public static final int NFC_UID = 1027; // NFC 服务
public static final int BLUETOOTH_UID = 1002; // 蓝牙服务
4. 应用
4.1 通过包名获取 UID
在应用开发中,可以通过 PackageManager 获取任意应用的 UID:
ini
PackageManager mPm = getPackageManager();
try {
// 0 表示不需要特殊的 flag
ApplicationInfo applicationInfo = mPm.getApplicationInfo("com.tencent.mm", 0);
int uid = applicationInfo.uid;
// 显示 UID
Toast.makeText(MainActivity.this, "UID: " + uid, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
4.2 通过 UID 获取包名
反之,也可以通过 UID 反查包名。
ini
String packagename = getPackageManager().getNameForUid(uid);
4.3 声明 Shared UID (系统级应用开发)
如果拥有系统签名,或者需要让两个自己的应用运行在同一进程、共享数据,可以在 AndroidManifest.xml 中声明 sharedUserId。
示例:设置应用为系统进程 UID
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
package="cn.izis.chessdeskrobot"
android:sharedUserId="android.uid.system">
<!-- 注意:必须使用与系统相同的签名文件对 APK 进行签名,否则安装会失败 -->
...
</manifest>
常见的共享 UID 类型:
android.uid.system(系统权限)android.uid.sharedandroid.uid.media
仅仅配置 android:sharedUserId="android.uid.system"(即 UID 1000)并不代表能无条件互相访问,实际情况取决于三层机制:
- Linux 权限层(允许): 如果两个应用 UID 相同且签名一致 ,Linux 内核视它们为同一个用户,默认打破沙箱,可以互相读写数据。
- SELinux 层(可能拦截): 即使 UID 相同,如果两个进程属于不同的 SELinux 安全域(system_server,system_app),系统仍会强制拦截访问。
- 多用户层(物理隔离): 不同 Android 用户(如主用户 vs 工作资料)下的应用,实际运行时的 UID 是不同的(如
1000vs1001000),因此无法互通。
5. UID vs PID
5.1 对比分析
| 特性 | UID (User ID) | PID (Process ID) |
|---|---|---|
| 定义 | 用户身份标识 | 进程实例标识 |
| 层级 | 应用级 (Application Scope) | 进程级 (Process Scope) |
| 分配时机 | 应用安装时 | 进程启动时 |
| 生命周期 | 极长(应用安装到卸载) | 短暂(进程启动到被杀/退出) |
| 唯一性 | 可共享 (sharedUserId) |
系统运行时唯一 |
| 主要作用 | 权限控制、沙箱隔离、文件归属 | 进程调度、内存管理、信号发送 |
5.2 为什么需要 UID?
PID 是临时的、易变的。应用重启后 PID 会改变,但 UID 不会。Android 权限系统(如是否允许访问相机、文件)是基于 UID 进行持久化记录和检查的,而不是 PID。
- 权限检查示例 :当 App 请求打开相机时,系统检查的是
Binder.getCallingUid()是否拥有相机权限。 - 文件安全 :
/data/data/com.example.app/目录的所有者是该应用的 UID,Linux 内核通过 UID 确保其他应用无法访问该目录。
尝试以 Shell 用户(UID 2000)查看目录 /data/data/com.example.study:
bash
$ adb shell dumpsys package com.android.shell | grep uid
uid=2000 gids=[] type=0 prot=signature
sharedUser=SharedUserSetting{83df8ed android.uid.shell/2000}
SharedUser [android.uid.shell] (83df8ed):
$ adb shell ls -l //data/data/com.example.study
ls: //data/data/com.example.study: Permission denied
$ adb shell run-as com.example.study ls -l //data/data/com.example.study
total 40
drwxrws--x 2 u0_a199 u0_a199_cache 4096 2025-12-16 13:35 cache
drwxrws--x 5 u0_a199 u0_a199_cache 4096 2025-12-16 13:35 code_cache
drwxrwx--x 2 u0_a199 u0_a199 4096 2025-12-16 13:35 databases
drwxrwx--x 3 u0_a199 u0_a199 4096 2025-12-16 13:35 files
drwxrwx--x 2 u0_a199 u0_a199 4096 2025-12-16 13:35 shared_prefs
$ adb shell pm list packages --uid 1000
package:com.android.inputdevices uid:1000
package:com.android.location.fused uid:1000
package:com.android.settings uid:1000
package:com.android.localtransport uid:1000
package:android uid:1000
package:com.android.emulator.multidisplay uid:1000
package:com.android.dynsystem uid:1000
package:com.android.keychain uid:1000
package:com.android.server.telecom uid:1000
package:com.android.providers.settings uid:1000
package:com.android.wallpaperbackup uid:1000
5.3 线程与进程的关系
在 Linux/Android 内核中:
- 主线程 ID (TID) == 进程 ID (PID) 。
- 可以通过
Process.myPid() == Process.myTid()来判断当前代码是否运行在主线程。
6. 特殊进程机制
6.1 隔离进程 (Isolated Process)
- UID 范围:99000 - 99999
- 特点:没有网络访问权限、无法访问外部存储、只能访问极少数系统服务。
- 用途:用于解析不信任的数据(如 Chrome 的渲染进程),即使进程被攻破,攻击者也无法获取用户隐私数据。
6.2 SDK 沙箱 (SDK Sandbox)
- UID 范围:20000 - 29999
- 背景:Android 13 引入。
- 用途:将第三方广告 SDK 等运行在独立的沙箱进程中,使其无法直接访问主应用的敏感数据,增强隐私保护。