Android AMS 完全剖析 —— Android 中的各种

2024-01-28  本文已影响0人  BlueSocks

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

1. Linux 中的 uid 与 gid

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

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

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

whoami
zzh0838

可以通过 id 命令查看 uid

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 命令查看当前用户的组信息:

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 中执行下面的命令:

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

查看进程信息:

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 用户的相关信息:

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 目录下:

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"

我们在应用中调用:

Process.myUid()   // 返回值为 1010068
Process.myUserHandle()  // 返回值为 userHandle{10}

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

我们在应用中调用:

Process.myUid()   // 返回值为 10068
Process.myUserHandle()  // 返回值为 userHandle{0}

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

上一篇下一篇

猜你喜欢

热点阅读