Android UID

250 阅读5分钟

1. Android UID 概述

在 Android 操作系统中,UID (User ID) 是用于标识一个应用程序的核心标识符。

  1. 分配机制:UID 在应用安装时由系统(PackageManagerService)分配。
  2. 生命周期:在应用存在于设备期间,其 UID 保持不变。只有当应用被卸载后,该 UID 才可能被回收并分配给新的应用。
  3. 唯一性原则:通常情况下,一个应用程序对应一个 UID。
  4. 共享机制:多个应用可以通过 sharedUserId 方式共享同一个 UID,但前提是这些应用必须使用相同的签名

UID 是 Android 安全沙箱(Sandbox)机制的基础,它决定了应用的文件访问权限、资源使用权限以及进程间通信(IPC)的身份验证。

2. UID 的格式与计算

在 Android log 中,我们经常看到类似 u0_a36 的标识,这就是 UID 的一种表现形式。

image.png

image.png

u0_a199 为例:

  1. u0: 代表 User 0,即 Android 系统的主用户(Owner)。Android 支持多用户,其他用户可能是 u10 等。
  2. a199: 代表该应用在该用户下的 ID 偏移量,这里是 199。

Android 普通应用程序的 UID 起始值定义为 10000 (FIRST_APPLICATION_UID)。

真实数值计算公式:

UID=Base+Offset\text{UID} = \text{Base} + \text{Offset}

针对 u0_a1999

10000(基础值)+199(偏移量)=1019910000 (\text{基础值}) + 199 (\text{偏移量}) = 10199

因此,系统底层实际使用的 int 类型 UID 为 10036

3. UID 的分配范围

根据 frameworks\base\core\java\android\os\Process.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:

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:

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 反查包名。

String packagename = getPackageManager().getNameForUid(uid); 

4.3 声明 Shared UID (系统级应用开发)

如果拥有系统签名,或者需要让两个自己的应用运行在同一进程、共享数据,可以在 AndroidManifest.xml 中声明 sharedUserId

示例:设置应用为系统进程 UID

<?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 类型:

  1. android.uid.system (系统权限)
  2. android.uid.shared
  3. android.uid.media

仅仅配置 android:sharedUserId="android.uid.system"(即 UID 1000)并不代表能无条件互相访问,实际情况取决于三层机制:

  1. Linux 权限层(允许): 如果两个应用 UID 相同且签名一致,Linux 内核视它们为同一个用户,默认打破沙箱,可以互相读写数据。
  2. SELinux 层(可能拦截): 即使 UID 相同,如果两个进程属于不同的 SELinux 安全域(system_server,system_app),系统仍会强制拦截访问。
  3. 多用户层(物理隔离): 不同 Android 用户(如主用户 vs 工作资料)下的应用,实际运行时的 UID 是不同的(如 1000 vs 1001000),因此无法互通

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。

  1. 权限检查示例:当 App 请求打开相机时,系统检查的是 Binder.getCallingUid() 是否拥有相机权限。
  2. 文件安全/data/data/com.example.app/ 目录的所有者是该应用的 UID,Linux 内核通过 UID 确保其他应用无法访问该目录。

尝试以 Shell 用户(UID 2000)查看目录 /data/data/com.example.study:

$ 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)

  1. UID 范围:99000 - 99999
  2. 特点:没有网络访问权限、无法访问外部存储、只能访问极少数系统服务。
  3. 用途:用于解析不信任的数据(如 Chrome 的渲染进程),即使进程被攻破,攻击者也无法获取用户隐私数据。

6.2 SDK 沙箱 (SDK Sandbox)

  1. UID 范围:20000 - 29999
  2. 背景:Android 13 引入。
  3. 用途:将第三方广告 SDK 等运行在独立的沙箱进程中,使其无法直接访问主应用的敏感数据,增强隐私保护。