最近有同学在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的代码。