问题描述
Android 5.0版本上按照下述的步骤, 会触发SystemUI进程反复ANR然后crash的问题
1. 下拉systemui面板
2. adb –host shell
3. ps –ax systemui
4. kill -SIGSTOP <systemui pid>
5. 然后在systemui面板中不停触摸,由于systemui 已经stop 所以会触发一次ANR,紧接着就进一步触发本问题中提到的systemui 反复ANR,AMS 反复restart system
Root Cause分析:
如上面的ANR 时序分析图所示, 导致问题的关键步骤是InputMonitor模块处理notifyANR过程中
longtimeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(...
这里timeout的返回值会被InputDispacther模块作为下一次超时判断的计算变量,AMS 返回的timeout 单位是ms,而InputDispatcher模块中使用的单位是nanos,差别为10^6级别. InputMonitor模块没有及时将ms级别的timeout转换成nanos,就直接将timeout值送回InputDispacther了(实际上这里的timeout要求下次检查时间超时等待为8秒钟)
最终导致InputDispatcher模块错误的将一个ms级别的timout(没有在换算成为nano second,差了10^6级别)值去计算下一次事件超时等待时间
mInputTargetWaitTimeoutTime= now() + newTimeout;
从而导致systemui 还没有足够的时间去重新启动(本来应用有8秒超时等待时间),而下一次InputDispatcher dispatchOnce调用马上发生事件派发超时,进而导致重复发生了 ANR kill systemui 的时序过程
解决方案
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
windowState.mSession.mPid, aboveSystem, reason);
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return timeout * 1000000L; // nanoseconds
}
} catch (RemoteException ex) {
}
如上图所示, 在<AOSP>/framework/base/services/core/java/com/android/server/wm/InputMonitor.java 的notifyANR 函数中将return 的timeout (ms) 乘以 1000000换算为nanosecond再进行返回. Google 在后续的Android 版本中已经按照这个方式做了fix