問題場景
Android4.3,進入被測app某個Activity後,測試案例ClickOnScreen出現異常(Click can not be completed!)。
Android4.4正常。
前置說明
測試案例使用的是本人實作的測試架構,它底層調用了Robotium。
分析過程
1. 架構調用了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,這裡是其他的問題引發的。
2. 繼續跟進問題。
(1)android.app.Instrumentation
(2)android.hardware.input.InputManager
(3)android.hardware.input.IInputManager
最後是調用Binder.transect進行跨程序調用。
3. 被調用者是系統服務,可追朔到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??….)。
4. 在 被測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建立,進而也避免異常的發生。
總結
1. 個人認為這是Android設計上的bug,它的instrument在極端的情況下會失效。
2. 除非測試案例在系統中以系統使用者啟動,否則在4.3以下系統,該問題無法避免!
3. 隻要有mHideNavFakeWindow的存在,原則上所有的操作都會失敗,包括drag在内,但Robotium對drag的異常進行了攔截并直接丢棄,是以使用drag表面是成功的,但實際上是失敗,這會導緻運作中出現無法解釋的詭異問題。
<b></b>
<b>本文轉自hyddd部落格園部落格,原文連結:http://www.cnblogs.com/hyddd/p/3995281.html,如需轉載請自行聯系原作者。</b>