原文地址:http://www.cnblogs.com/hyddd/p/3995281.html,欢迎拍砖!
框架调用了 Robotium 的 ClickOnScreen,源码如下:
(com.jayway.android.robotium.solo.Clicker)
当 sendPointerSync(发送点击事件给被测 app)10 次都失败,便会断言异常:Click can not be completed。
而引发 sendPointerSync 的异常是:SecurityException。在 Android 中,出现 SecurityException 异常是因为权限不足,一般的情况是:被测试 App 和测试案例的签名不一致,而这在 instrumention 启动被测试 app 就出现,不用等到 instrument.sendPointerSync,这里是其他的问题引发的。
继续跟进问题。
(1)android.app.Instrumentation
(2)android.hardware.input.InputManager
(3)android.hardware.input.IInputManager
最后是调用 Binder.transect 进行跨进程调用。
被调用者是系统服务,可追朔到 InputManagerService 的 injectInputEvent。
(1)/frameworks/base/services/java/com/android/server/input/InputManagerService.java
终于发现了我们要找的 SecurityException,导致 INPUT_EVENT_INJECTION_PERMISSION_DENIED 是 jni 层的 nativeInjectInputEvent。
(2)/frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
(3)/frameworks/base/services/input/InputDispatcher.cpp
终于发现了 INPUT_EVENT_INJECTION_PERMISSION_DENIED 踪迹,继续查看 checkInjectionPermission:
查看系统日志,发现是造成 Permission 验证失败的原因是:当前 windowHandle(被测 app)->owneruid 与注入者(instrument)->injectoruid 不一致。并且 windowHandle(被测 app)的 owneruid 竟然是 1000(系统账户)!
(4)系统日志如下:
同时,根据 windowHandle->getName().string(),我意外发现 “凶手” 的 Name 是:hidden nav(隐藏的 nav??….)。
在 被测 app 源码 和 Android 源码中分别查找,最终发在 “凶手” 匿藏在 PhoneWindowManager 中……
(1)/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
mHideNavFakeWindow 的创建了 addFackWindow,mHideNavFakeWindow 的 owneruid=Process.getmyid()(这里不列代码了), 而 PhoneWindowManager 是内部服务 PhoneWindowService 的策略类,属于系统用户。所以 mHideNavFakeWindow 这个透明的窗口,也是系统用户级别了 (mHideNavFakeWindow 为了满足某特殊需要,不得不设置为系统用户)…
而执行这个分支,需要满足一个条件:在重渲染 ui 界面时,被设置了标记:View.SYSTEM_UI_FLAG_HIDE_NAVIGATION。
在被测 App 源码代码中 search,终于发现疑似凶手:view.getRootView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); 而它确实就在出现 ClickOnScreen 异常的 Activity 里。最后经调试,确认问题确实如此。