最近有同学在Android S(12)
上遇到了一个奇怪的网络问题,说自己的audio HAL
服务尝试通过以太网创建socket
与其他局域网的节点通讯时,总是提示Operation Not Permitted
。原先怀疑是Selinux
的问题,但是目前在开发版本中selinux
是完全关闭的;从问题发生的现象看,只有属于audioserver
这个UID
的进程才有问题,其他的如system/root
的进程则没有问题。
据此,我们可以推断,audioserver
这个UID
的进程没有相关权限,所以导致无法使用局域网的网络。记得在早前的Android
版本中,很多网络系统调用会通过netd
代理进行权限检查,比如socket/connect/bind
等系统调用都会先通过netdClient
这个库的接口进行权限检查,而后才真正进行系统调用。查看了Android S
中netd/client
中的代码,果真有一个socket
的代理接口会检查权限:
1 |
|
有关
netd
的详细介绍可以参考Android Netd工作原理详解
继续跟踪这个代码路径,发现Android
的Zygote
进程创建应用进程的时候会调用setAllowNetworkingForProcess
接口,根据进程是否在一个INET_GID
用户组来设置该进程是否有权限访问网络:
1 |
|
再次检查HAL
服务的rc
配置,发现group
用户组中已经包含了inet
了。而且,从问题的现象来看,是跟进程的UID
有关,与GID
的关系不大。那么,到底是哪里设置了UID
相关的权限了? 跟同事一起确认发现,只要在/system/etc/permission/platform.xml
中为audioserver
添加一个INTERNET
权限网络就可以正常访问了:
1 |
|
问题好像比较清晰了,应该是系统启动时,对于那些无法通过AndroidManifest.xml
声明权限的服务,比如native
的服务,包管理服务PMS
会去解析对应UID
的权限,然后再通过某种方式设置到系统,确保其在创建socket
时相关的权限会被检查。查看frameworks/base/services/../pm/permission
代码可以大概知道,Android
中的所有权限都统一由系统服务PermissionManagerService
来管理,系统启动时,就会通过这个服务解析platform.xml
文件中各个UID
的权限声明:
1 |
|
但继续跟踪代码,却没有看到是这个权限配置是如何与socket
的创建控制关联在一起的。这个想来十分奇怪,按照实现逻辑来说,这种权限控制不会放在内核实现,而是应该在用户空间的某个地方设置。过了两天再来看这个问题,突然想起之前做流量统计功能时,Android
中使用了BPF
,很有可能这个网络权限的控制也是在netd
对应的BPF
中实现的。查看了下/system/netd/bpf_progs/netd.c
的代码,确实有一个bpf map
来控制socket
的创建:
1 |
|
这个UID
权限map
对象正是通过Netd
的接口进行设置的,大致的路径如下:
1 |
|
就是说ConnectivityService
在启动过程中,通过PermissionMonitor
读取到SystemConfig
的用户权限配置后,会调用NETD
的接口设置对应的UID
的网络权限:
1 |
|
至此,谜底终于解开了。感兴趣的可以查看packages/modules/Connectivity/
下PermissionMonitor.java
的代码。