1 背景
在前几天vip群中有一起讨论一个关于安卓权限的面试题:
“framework层定义的权限是怎么让linux认识的”

针对这个题目的审题大家其实也是有一定争议的:
其实有同学就提出到底这里说的linux是系统内核认识还是linux的普通native进程认识。
所以是有两种回答方向:
1、linux系统内核识别权限方向:往framework的权限最后是如何影响到进程,gid,groups,从platform.xml解析权限到fork进设置相关gid等。
2、linux的native进程识别方向:比如SurfaceFlinger,audioserver中如何判断一些跨进程的进是否有某个权限.
那今天我们就主要针对方向1部分进行深入剖析,用一个经典的网络权限来作为案例进行说明,为啥在Manifest文件中声明一行
<uses-permission android:name="android.permission.INTERNET" />
就可以顺利的进行网络访问了,如果不声明权限则百分百会报错如下:
E MainActivity: java.lang.SecurityException: Permission denied (missing INTERNET permission?)
E MainActivity: at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:150)
E MainActivity: at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
E MainActivity: at java.net.InetAddress.getAllByName(InetAddress.java:1152)
2. 第一层:Manifest 声明与框架侧权限定义
App 在 AndroidManifest.xml 里写:
<uses-permission android:name="android.permission.INTERNET" />
而这条权限的「官方定义」在框架资源里,AOSP 15 位于:
frameworks/base/core/res/AndroidManifest.xml:2096-2099
<permission android:name="android.permission.INTERNET"
android:description="@string/permdesc_createNetworkSockets"
android:label="@string/permlab_createNetworkSockets"
android:protectionLevel="normal|instant" />
关键点就在 protectionLevel="normal":
normal权限 → 安装时自动授予,不需要用户确认;- 没有
runtime运行时授权逻辑,所以checkSelfPermission()对它而言意义不大。
这意味着:决定「能不能联网」的不是 PMS 的运行时检查,而是下面这套 GID 机制。
3. 第二层:权限名 → GID 的映射表 platform.xml
Android 把「权限名 → Linux 组」的映射集中放在 platform.xml,AOSP 15:
frameworks/base/data/etc/platform.xml:59-61
<permission name="android.permission.INTERNET" >
<group gid="inet" />
</permission>
同一份文件里还有两条特殊的 assign-permission(把权限直接固定派给某个系统 uid,不走正常授权流程):
<assign-permission name="android.permission.INTERNET" uid="media" /> <!-- :169 -->
<assign-permission name="android.permission.INTERNET" uid="shell" /> <!-- :171 -->
这里揭示了一个重要事实:网络权限的载体是组名 inet,而不是什么运行时标记。
inet 这个名字对应的数字,定义在 AOSP 的 AID 表里:
system/core/libcutils/include/private/android_filesystem_config.h:166
#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
内核侧同样有一份(供 in_egroup_p() 使用):
kernel/xiaomi/sdm710/include/linux/android_aid.h:22
#define AID_INET KGIDT_INIT(3003)
两处一致,都是 3003。
4. 第三层:SystemConfig 解析并落地 GID
系统启动时 SystemConfig 读取 platform.xml,把 <group gid="inet"/> 转成数字 GID:
frameworks/base/services/core/java/com/android/server/SystemConfig.java:1797-1836(readPermission)
String tagName = parser.getName();
if ("group".equals(tagName)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr); // "inet" -> 3003
if (gid != -1) {
perm.gids = appendInt(perm.gids, gid); // 写入 PermissionEntry.gids
}
...
}
}
随后 PermissionManagerServiceImpl 在构造时,把这份 PermissionEntry.gids 挂到权限对象 Permission 上:
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java:471-473
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
至此,「INTERNET 权限」在 Java 世界里就等价于「Permission 对象携带 mGids = {3003}」。
5. 第四层:授权判定 → 按 uid 汇总 GID
normal 权限安装即授,所以对声明了 INTERNET 的 app,PermissionState.isGranted() 为 true。
真正把「已授权权限」折算成「进程该带的 GID 列表」的是这一段:
frameworks/base/services/core/java/com/android/server/pm/permission/UidPermissionState.java:331-349
public int[] computeGids(@NonNull int[] globalGids, @UserIdInt int userId) {
IntArray gids = IntArray.wrap(globalGids);
...
for (int i = 0; i < permissionsSize; i++) {
PermissionState permissionState = mPermissions.valueAt(i);
if (!permissionState.isGranted()) { // 只统计已授权的
continue;
}
final int[] permissionGids = permissionState.computeGids(userId);
if (permissionGids.length != 0) {
gids.addAll(permissionGids); // 3003 被加进来
}
}
return gids.toArray();
}
对外的统一入口:
PermissionManagerServiceImpl.java:5124-5136
public int[] getGidsForUid(int uid) {
...
return uidState.computeGids(mGlobalGids, userId);
}
注意
Permission.computeGids()里还有gidsPerUser的分支:若配置了perUser,会把 GID 变成userId * AID_USER_OFFSET + gid这种 per-user 形式;inet没有标perUser,所以始终是裸的 3003。
6. 第五层:进程启动时把 GID 带进 zygote
真正拉 app 进程时,ActivityManager 通过 ProcessList 向 PM 要 GID:
frameworks/base/services/core/java/com/android/server/am/ProcessList.java:1892-1926
permGids = pm.getPackageGids(app.info.packageName,
MATCH_DIRECT_BOOT_AUTO, app.userId); // ← 拿到 [3003, ...]
// 若有被拒绝的权限,把对应 GID 摘掉
if (app.processInfo != null && app.processInfo.deniedPermissions != null) {
for (int i = ...; i >= 0; i--) {
int[] denyGids = mService.mPackageManagerInt.getPermissionGids(
app.processInfo.deniedPermissions.valueAt(i), app.userId);
if (denyGids != null) {
for (int gid : denyGids) {
permGids = ArrayUtils.removeInt(permGids, gid); // 动态剔除 3003
}
}
}
}
gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess);
computeGidsForProcess 在 perm GID 之外再补上 sharedAppGid / cacheAppGid / userGid 等基础设施组:
ProcessList.java:1781-1802
for (int permGid : permGids) {
gidList.add(permGid);
}
if (sharedAppGid != UserHandle.ERR_GID) gidList.add(sharedAppGid);
if (cacheAppGid != UserHandle.ERR_GID) gidList.add(cacheAppGid);
if (userGid != UserHandle.ERR_GID) gidList.add(userGid);
最终 app.setGids(gids)(ProcessList.java:2057),这些 GID 会作为 zygote 参数拼进启动命令:
--setgid=<gid>:主组--setgroups=<gid1,gid2,...>:补充组列表,3003 就在这里
(参数解析见 frameworks/base/core/java/com/android/internal/os/ZygoteArguments.java:288/349。)
7. 第六层:zygote 落地为进程的 supplementary group
zygote fork 出子进程后,native 层 SpecializeCommon 调用 SetGids,最终走 POSIX setgroups(2):
frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:532-553
static void SetGids(JNIEnv* env, jintArray managed_gids, jboolean is_child_zygote,
fail_fn_t fail_fn) {
...
ScopedIntArrayRO gids(env, managed_gids);
...
if (setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0])) == -1) {
fail_fn(CREATE_ERROR("setgroups failed: ..."));
}
}
随后降权:
com_android_internal_os_Zygote.cpp:1999-2014
if (setresgid(gid, gid, gid) == -1) { ... } // 设置真实/有效/保存组 id
...
if (setresuid(uid, uid, uid) == -1) { ... } // 设置真实/有效/保存用户 id
到这一步,app 进程在内核里的凭证就确定了:
uid=10000 + appId(应用自身 uid)gid= 对应主组- supplementary groups 里带
3003(inet) —— 这是网络权限的唯一「落地形态」。
可以用 cat /proc/<pid>/status 里的 Groups: 行验证:有 3003 就说明该进程声明了 INTERNET。
8. 第七层:内核在网络协议栈入口做检查
有了 3003 这个补充组,内核才能在创建 socket 时「认出」这个进程有网络权限。
IPv4 的 socket 创建入口:
kernel/xiaomi/sdm710/net/ipv4/af_inet.c:273-274
if (!current_has_network())
return -EACCES;
而 current_has_network() 的定义:
kernel/xiaomi/sdm710/net/ipv4/af_inet.c:124-136
#ifdef CONFIG_ANDROID_PARANOID_NETWORK
#include <linux/android_aid.h>
static inline int current_has_network(void)
{
return in_egroup_p(AID_INET) || capable(CAP_NET_RAW);
}
#else
static inline int current_has_network(void)
{
return 1;
}
#endif
IPv6 完全对称:
kernel/xiaomi/sdm710/net/ipv6/af_inet6.c:71-76, 138-139
static inline int current_has_network(void)
{
return in_egroup_p(AID_INET) || capable(CAP_NET_RAW);
}
...
if (!current_has_network())
return -EACCES;
这里的核心逻辑一句话:
当前进程在
inet(3003)这个补充组里,或者具备CAP_NET_RAWcapability,才允许创建AF_INET/AF_INET6socket;否则inet_create()/inet6_create()直接返回-EACCES。
in_egroup_p(AID_INET) 检查的就是第 7 步 setgroups() 写进进程凭证的 supplementary group。
实战验证
带上网络权限时候情况查看是否带有网络相关的gid
声明后查看相关数据
grus:/ # cat /proc/30339/status | grep Groups
Groups: 3003 9997 20186 50186
可以看到有声明网络权限时候,可以看到有3003这个值,代表属于inet(3003)组。
去除网络权限验证
查看对应的
grus:/ # cat /proc/30118/status | grep Groups
Groups: 9997 20186 50186
这里就没有看到inet(3003)这个组,自然访问网络就会报错。
总结
android.permission.INTERNET 本质上不是一次 Java 权限检查,而是一条「Manifest 声明 → platform.xml 里的 inet 组名 → AID_INET=3003 → PMS 按 uid 汇总 GID → ProcessList 传给 zygote → native setgroups() 写入进程补充组 → 内核 inet_create()/inet6_create() 检查 in_egroup_p(AID_INET)」的完整链路。控制点既在用户态的 GID 装配,也在内核态的 socket 创建入口,二者缺一不可。
注意误区:
很多人以为 INTERNET 是一个「运行时权限」(dangerous permission),会在安装后弹窗申请。它不是。
INTERNET 的 protectionLevel 是 normal,属于安装时自动授予、永不弹窗、用户也关不掉的权限。
那它的「控制力」到底从哪来?答案是:它根本不是靠 Java 层运行时检查起作用的,而是靠一个 Linux 补充组 GID,由内核在网络协议栈入口做访问控制。 下面把这个「权限 → GID → 内核」的转化链路逐层拆开。
全景链路图
APK AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
│ 安装时(PackageInstaller / PMS)
▼
frameworks/base/core/res/AndroidManifest.xml ← 框架侧权限声明
<permission android:name="android.permission.INTERNET"
android:protectionLevel="normal|instant" />
│ 系统启动时被 SystemConfig 读取
▼
frameworks/base/data/etc/platform.xml ← 权限名 → GID 映射表
<permission name="android.permission.INTERNET">
<group gid="inet" /> ← "inet" 这个名字
</permission>
│ Process.getGidForName("inet")
▼
AID_INET = 3003 ← 数字 GID
│ 存入 Permission.mGids
▼
授权判定(normal → 安装即 granted)→ UidPermissionState.computeGids()
│ getGidsForUid()
▼
进程启动 ProcessList.startProcess()
│ pm.getPackageGids() → computeGidsForProcess()
▼
zygote fork + "--setgroups=...,3003,..." "--setgid=..."
│ native SpecializeCommon → setgroups(2)
▼
app 进程携带 supplementary group: inet(3003)
│
▼
内核 inet_create() / inet6_create()
current_has_network() = in_egroup_p(AID_INET) || capable(CAP_NET_RAW)
不满足 → return -EACCES
其他更多framework系统车机收开发面试题,入门到精通学习资料:
Android Framework开发rom实战合集课表/车载车机手机高级系统开发工程必会技能
393



被折叠的 条评论
为什么被折叠?



