Android AMS 完全剖析 —— 补充知识之 Android 中的各种 ID

在分析 Activity 的启动过程中会涉及到 Android 中的各种各样的 ID,很多同学不太清楚这些 ID 的作用和区别,本文做一个简单的介绍.

1. Linux 中的 uid 与 gid

Linux 是一个多用户操作系统,系统中可以同时存在有多个用户。

每个用户有一个用户名,也就是我们登录时输入的的用户名。用户名在系统中会对应一个整数值 UID,是用户在系统中的唯一标识,就像现实生活中一个身份证号标识一个具体的人。

在终端中可以通过 whoami 打印当前登录的用户名:

bash 复制代码
whoami
zzh0838

可以通过 id 命令查看 uid

bash 复制代码
id
uid=1000(zzh0838) gid=1000(zzh0838) groups=1000(zzh0838),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare)**

输出的第一组数据 uid=1000(zzh0838) 表示当前用户 zzh0838 的 uid 是 1000。

为了方便管理系统中的多个用户,系统将用户进行了分组,每个用户可以在一个或者多个组中。每个组也有自己的组名和组 id(gid)。一个用户可以同时在多个组中。这个有点类似大学里面的社团,一个社团就是一个用户组,一个学生就是一个用户,一个学生可以同时参加多个社团,就是说一个用户可以同时属于多个用户组。

我们可以通过 id 命令查看当前用户的组信息:

bash 复制代码
id
uid=1000(zzh0838) gid=1000(zzh0838) groups=1000(zzh0838),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare)

后半部分就是当前用户的用户组信息:

gid=1000(zzh0838) groups=1000(zzh0838),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare)

gid=1000(zzh0838) 表示当前用户的初始组是 zzh0838,初始组 zzh0838 的 gid 是 1000。每个用户的初始组只能有一个,通常就是将和此用户的用户名相同的组名作为该用户的初始组。

后面的内容就是当前用户所在的组,包括了组名和组 id。

2. Android 中的 uid

2.1 进程的 uid

Andorid 基于 Linux 内核打造,在 Android 4.2 之前,Android 是不支持多用户,但是借用了 linux 的用户体系和文件权限体系实现了 App 之间的数据隔离,所谓数据隔离就是每个 App 有自己的一个数据文件夹,App 只能访问自己的数据文件夹,不能访问其他的 App 的数据文件夹和其他路径下的文件。

那 Android 是怎么实现的呢?

在 App 的安装过程中,会给每个 App 分配一个 userId,这个 userId 保存在手机的 /data/system/packages.xml 文件中,文件中记录了 App 的包名和 userId 的对应关系。

如果要查看我们的一个 Demo yuandaima.ahao.myactivitytext 应用的 userId,可以在模拟器的 shell 中执行下面的命令:

bash 复制代码
cat /data/system/packages.xml | grep yuandaima.ahao.myactivitytext             
    # ......
<package name="yuandaima.ahao.myactivitytext" codePath="/data/app/yuandaima.ahao.myactivitytext-FicL4FdzvHE8_c4bWCbN9A==" nativeLibraryPath="/data/app/yuandaima.ahao.myactivitytext-FicL4FdzvHE8_c4bWCbN9A==/lib" publicFlags="810073926" privateFlags="0" ft="18cf7636700" it="18cf76369b1" ut="18cf76369b1" version="1" userId="10101">
    # ......

可以看到应用 yuandaima.ahao.myactivitytext 的 userId 是 10101。

这里的 userId 就是上一节说到的 linux 中的 uid。

在 Linux 系统中,每个进程也有个 uid 属性,表示是哪个用户启动了当前进程,传统的 linux 系统中,用户启动的进程的 uid 都是当前登录的 uid。

在 Android 中有一些差异,每个进程的 uid 属性,在启动时会被设置成 data/system/packages.xml 文件中记录的进程的 userId。

比如当我们启动 yuandaima.ahao.myactivitytext 应用时,应用的 uid 会被设置为 10101

查看进程信息:

bash 复制代码
ps -elf | grep yuandaima.ahao.myactivitytext                        
u0_a101       4718  1505 0 13:27:30 ?     00:00:00 yuandaima.ahao.myactivitytext
root          4749  4636 0 13:33:21 pts/0 00:00:00 grep yuandaima.ahao.myactivitytext

可以看到启动 yuandaima.ahao.myactivitytext 进程的用户是 u0_a101。

我们接着使用 id 命令看 u0_a101 用户的相关信息:

bash 复制代码
id u0_a101
uid=10101(u0_a101) gid=10101(u0_a101) groups=10101(u0_a101), context=u:r:su:s0

可以看到 u0_a101 对应的 uid 是 10101,这个 uid 就是来自 data/system/packages.xml 文件中记录的进程的 userId。

2.2 应用的数据

Android 中应用的数据通常保存在 /data/data 目录下:

bash 复制代码
cd /data/data
ls -l
# ......
drwx------ 4 u0_a48         u0_a48         4096 2024-01-11 09:37 com.android.timezone.updater
drwx------ 5 u0_a77         u0_a77         4096 2024-01-11 09:37 com.android.traceur
drwx------ 4 u0_a53         u0_a53         4096 2024-01-11 09:37 com.android.vpndialogs
drwx------ 4 u0_a58         u0_a58         4096 2024-01-11 09:37 com.android.wallpaper.livepicker
drwx------ 4 system         system         4096 2024-01-11 09:37 com.android.wallpaperbackup
drwx------ 4 u0_a83         u0_a83         4096 2024-01-11 09:37 com.android.wallpapercropper
drwxr-x--x 4 u0_a89         u0_a89         4096 2024-01-11 09:37 com.android.wallpaperpicker
drwx------ 4 u0_a92         u0_a92         4096 2024-01-13 09:31 com.android.webview
drwx------ 4 u0_a95         u0_a95         4096 2024-01-11 09:37 org.chromium.webview_shell
drwx------ 4 u0_a101        u0_a101        4096 2024-01-11 15:18 yuandaima.ahao.myactivitytext

这里的文件夹的名字均为应用的包名,文件夹中保存了对应应用的数据。比如 yuandaima.ahao.myactivitytext 文件夹就保存了 yuandaima.ahao.myactivitytext app 的数据。

需要注意的是这里文件夹的权限均为 rwx------(除了少数特例),表示仅文件的所有者可读可写可执行,其他用户均没有读写执行权限。

同时,很重要的一点文件的所属用户和对应 app 进程的所属用户相同。比如 yuandaima.ahao.myactivitytext 文件夹的所属用户是 u0_a101yuandaima.ahao.myactivitytext 进程的所属用户也是 u0_a101

也就是说 yuandaima.ahao.myactivitytext 文件夹中的文件只能又 yuandaima.ahao.myactivitytext 进程来读写执行。

这样就利用 linux 的用户体系和文件权限体系实现了 App 之间的数据隔离。妙~

3. Android 中的多用户

早期的 Android 并不支持多用户,因为 Linux 本来的多用户体系被用来实现 App 之间的数据隔离了。但是用户对于多用户功能的需求还是存在的。在 Android 4.2 的时候,google 再一次对 Linux 进行了魔改,重新开发了一套多用户体系。

国产手机的分身功能一般就是基于这套魔改的多用户体系实现的。

假设我们的 Android 手机上有两个用户,用户 id 是 0 和 10,这里的 id 在 Android 中称为 UserHandle

我们先登录 id 为 10 的用户,安装一个应用,在 data/system/packages.xml 中查看, 此应用的 userId="10068"

我们在应用中调用:

java 复制代码
Process.myUid()   // 返回值为 1010068
Process.myUserHandle()  // 返回值为 userHandle{10}

接着重新登录 id 为 0 的用户,此时是看不到上一步安装的应用,接着我们再次安装同一个应用。在 data/system/packages.xml 中查看,userId 仍然是 10068

我们在应用中调用:

java 复制代码
Process.myUid()   // 返回值为 10068
Process.myUserHandle()  // 返回值为 userHandle{0}

这里可以看出这里的多用户其实是通过 UserHandle 和 userId 的组合来实现的。

参考资料

相关推荐
编程洪同学1 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
氤氲息3 小时前
Android 底部tab,使用recycleview实现
android
Clockwiseee4 小时前
PHP之伪协议
android·开发语言·php
小林爱4 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
小何开发5 小时前
Android Studio 安装教程
android·ide·android studio
开发者阿伟5 小时前
Android Jetpack LiveData源码解析
android·android jetpack
weixin_438150996 小时前
广州大彩串口屏安卓/linux触摸屏四路CVBS输入实现同时显示!
android·单片机
CheungChunChiu6 小时前
Android10 rk3399 以太网接入流程分析
android·framework·以太网·eth·net·netd
木头没有瓜7 小时前
ruoyi 请求参数类型不匹配,参数[giftId]要求类型为:‘java.lang.Long‘,但输入值为:‘orderGiftUnionList
android·java·okhttp
键盘侠0077 小时前
springboot 上传图片 转存成webp
android·spring boot·okhttp