Robotium 【分享】Android Instrumention.sendPointerSync 发送 Event 失败分析

匿名 · 2014年09月26日 · 2486 次阅读
本帖已被设为精华帖!

原文地址:http://www.cnblogs.com/hyddd/p/3995281.html,欢迎拍砖!

问题场景

  • Android4.3,进入被测 app 某个 Activity 后,测试案例 ClickOnScreen 出现异常(Click can not be completed!)。
  • Android4.4 正常。

前置说明

  • 测试案例使用的是本人实现的测试框架,它底层调用了 Robotium。

分析过程

  • 框架调用了 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 里。最后经调试,确认问题确实如此。

4.3 与 4.4 表现不一致的原因

  • 对比 PhoneWindowManager 4.3 和 4.4 的代码,发 4.4 做了改进,如下(右为 4.4):
  • 而在被测试 app 码中….. 就是被测试 App 在 4.4 时,会增加一个 flag,导致了它撞大运般地在 4.4 中执行流程发生了改变,避免了 mHideNavFakeWindow 创建,从而也避免异常的发生。

总结

  • 个人认为这是 Android 设计上的 bug,它的 instrument 在极端的情况下会失效。
  • 除非测试案例在系统中以系统用户启动,否则在 4.3 以下系统,该问题无法避免!
  • 只要有 mHideNavFakeWindow 的存在,原则上所有的操作都会失败,包括 drag 在内,但 Robotium 对 drag 的异常进行了拦截并直接丢弃,所以使用 drag 表面是成功的,但实际上是失败,这会导致运行中出现无法解释的诡异问题。
共收到 6 条回复 时间 点赞

很好的分享~

赞!~

这种 level 的文章,这里还是第一次出现啊,大赞!

赞~!

匿名 #2 · 2014年09月28日

太高端了 😑

好高端!

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册