Kaede Akatsuki

中二病也要开发 Android

AOSP Bug 报告: AMS#getRunningAppProcesses NPE 异常

熟悉 MultiProcess 开发的你应该不会对 AMS#getRunningAppProcesses 这个接口感到陌生,虽然从高级 Android 版本开始,App 层已经无法获取其他 UID 应用的进程信息,但也多进程 App 也经常需要获取同个 UID 下其他 Process 的状态,这也是这个接口现在存在的意义。然而这个标准接口却一直埋着一个 Null-check 的坑,时不时就会导致 NPE Crash,而且还会反复在同个坑里跌倒。

现有问题

根据现在最新 master 分支的 AOSP 代码,AMS#getRunningAppProcesses 这个 API 依然没有添加 @Nullable 注解(而同个类下其他 API 都分别带有 Nullable/NonNull 注解),所以导致每次调用这个接口的时候,IDE 都不会发出 Null-check 的 Inspection,时间一久,是个正常人都会忘了添加 Null-check 代码。

不巧的是,如果没有特别深刻的印象,这个接口从语义上,很容易就让人产生天然的 NonNull 的错觉:毕竟,在 App 内获取 App 所有进程的信息,至少也得包含当前调用进程自己的信息吧。然而就是这么反直觉,这个 API 确实是会返回 null 的:

1
2
3
4
5
6
/**
* @return Returns a list of RunningAppProcessInfo records, or null if there are no
* running processes (it will not return an empty list). This list ordering is not
* specified.
*/
public List<RunningAppProcessInfo> getRunningAppProcesses() {}

其实原因也不是很复杂,AMS 进程所保持的 App 的 ProcessRecord 清单,并不是严格跟 App 进程的声明周期对应的。比如有个 AMS 检查 App 相机权限的 AOSP Bug 就是因为 App 进程切换太频繁,导致 AMS 里持有的该 App 的 ProcessRecord 记录实际上是上个 pid 的旧数据,从而导致 AMS 没有给当前 pid 的 App 进行正确的相机权限授权,最终导致了该 App 出现相机黑屏。(具体 Bug Report 见:Fix race condition in UidRecord cleanup

所以,如果不给 AMS#getRunningAppProcesses 加上 @Nullable,我感觉这里的 NPE 悲剧会反复重演。实际上我自己就是如此,而且我这里可不是故意把情况说得那么糟糕,毕竟 Google 自己挖的这个坑,ta 自己也跳了不少次了。

AOSP 现有调用清单

截止目前,在 AOSP master 分支发现以下几处非 UnitTest 的 Null-check 遗漏代码:

  1. com.android.server.media.MediaResourceMonitorService.MediaResourceMonitorImpl#getPackageNamesFromPid
  2. com.android.server.tv.tunerresourcemanager.TunerResourceManagerService#checkIsForeground
  3. com.android.packageinstaller.wear.WearPackageIconProvider#isSystemApp