在剖析 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 的组合来完成的。

参考资料