157e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le/* 21194ec356a16f3c6dcf408289e36e42c149d6dc8Kevin Jin * Copyright (C) 2013 DroidDriver committers 357e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * 457e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * Licensed under the Apache License, Version 2.0 (the "License"); 557e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * you may not use this file except in compliance with the License. 657e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * You may obtain a copy of the License at 757e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * 857e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * http://www.apache.org/licenses/LICENSE-2.0 957e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * 1057e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * Unless required by applicable law or agreed to in writing, software 1157e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * distributed under the License is distributed on an "AS IS" BASIS, 1257e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1357e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * See the License for the specific language governing permissions and 1457e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le * limitations under the License. 1557e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le */ 1657e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le 171194ec356a16f3c6dcf408289e36e42c149d6dc8Kevin Jinpackage com.google.android.droiddriver.uiautomation; 1857e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le 197dde4b200c490587409e0301e58261210e7a5896Tony Wickhamimport android.app.Instrumentation; 2074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport android.app.UiAutomation; 2174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport android.content.Context; 2274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport android.os.SystemClock; 2374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport android.view.accessibility.AccessibilityEvent; 2474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport android.view.accessibility.AccessibilityManager; 2574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport android.view.accessibility.AccessibilityNodeInfo; 2639f9784bdf3d4663263c344139454736af13e111Kevin Jin 2774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport com.google.android.droiddriver.actions.InputInjector; 2874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport com.google.android.droiddriver.base.BaseDroidDriver; 2974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport com.google.android.droiddriver.exceptions.TimeoutException; 3074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport com.google.android.droiddriver.uiautomation.UiAutomationContext.UiAutomationCallable; 3174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport com.google.android.droiddriver.util.Logs; 3257e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le 3357e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le/** 34a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin * Implementation of DroidDriver that gets attributes via the Accessibility API 35a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin * and is acted upon via synthesized events. 3657e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le */ 3774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinpublic class UiAutomationDriver extends BaseDroidDriver<AccessibilityNodeInfo, UiAutomationElement> { 3874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin // TODO: magic const from UiAutomator, but may not be useful 3974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin /** 4074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * This value has the greatest bearing on the appearance of test execution 4174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * speeds. This value is used as the minimum time to wait before considering 4274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * the UI idle after each action. 4374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin */ 4474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private static final long QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE = 500;// ms 4574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 4674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private final UiAutomationContext context; 4774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private final InputInjector injector; 4874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private final UiAutomationUiDevice uiDevice; 49988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin private AccessibilityNodeInfoCacheClearer clearer = 50988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin new WindowStateAccessibilityNodeInfoCacheClearer(); 5174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 527dde4b200c490587409e0301e58261210e7a5896Tony Wickham public UiAutomationDriver(Instrumentation instrumentation) { 5374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin context = new UiAutomationContext(instrumentation, this); 5474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin injector = new UiAutomationInputInjector(context); 5574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin uiDevice = new UiAutomationUiDevice(context); 5674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 5774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 5874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin @Override 5974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public InputInjector getInjector() { 6074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return injector; 6174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 6274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 6374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin @Override 6474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin protected UiAutomationElement newRootElement() { 6574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return context.newRootElement(getRootNode()); 6674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 6774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 6874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin @Override 6974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin protected UiAutomationElement newUiElement(AccessibilityNodeInfo rawElement, 7074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin UiAutomationElement parent) { 7174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return new UiAutomationElement(context, rawElement, parent); 7274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 7374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 7474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private AccessibilityNodeInfo getRootNode() { 7574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin final long timeoutMillis = getPoller().getTimeoutMillis(); 7674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin context.callUiAutomation(new UiAutomationCallable<Void>() { 7774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin @Override 7874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public Void call(UiAutomation uiAutomation) { 7974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin try { 8074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin uiAutomation.waitForIdle(QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE, timeoutMillis); 8174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return null; 8274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } catch (java.util.concurrent.TimeoutException e) { 8374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin throw new TimeoutException(e); 8474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 8574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 8674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin }); 8774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 8874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin long end = SystemClock.uptimeMillis() + timeoutMillis; 8974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin while (true) { 9074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin AccessibilityNodeInfo root = 9174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin context.callUiAutomation(new UiAutomationCallable<AccessibilityNodeInfo>() { 9274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin @Override 9374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public AccessibilityNodeInfo call(UiAutomation uiAutomation) { 9474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return uiAutomation.getRootInActiveWindow(); 9574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 9674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin }); 9774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin if (root != null) { 9874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return root; 9974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 10074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin long remainingMillis = end - SystemClock.uptimeMillis(); 10174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin if (remainingMillis < 0) { 10274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin throw new TimeoutException( 10374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin String.format("Timed out after %d milliseconds waiting for root AccessibilityNodeInfo", 10474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin timeoutMillis)); 10574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 10674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin SystemClock.sleep(Math.min(250, remainingMillis)); 10774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 10874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 10974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 11074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin /** 11174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * Some widgets fail to trigger some AccessibilityEvent's after actions, 11274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * resulting in stale AccessibilityNodeInfo's. As a work-around, force to 11374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * clear the AccessibilityNodeInfoCache. 11474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin */ 11574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public void clearAccessibilityNodeInfoCache() { 11674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin Logs.call(this, "clearAccessibilityNodeInfoCache"); 117988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin clearer.clearAccessibilityNodeInfoCache(this); 118988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin } 119988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin 120988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin public interface AccessibilityNodeInfoCacheClearer { 121988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin void clearAccessibilityNodeInfoCache(UiAutomationDriver driver); 122988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin } 123988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin 124988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin /** 125988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin * Clears AccessibilityNodeInfoCache by turning screen off then on. 126988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin */ 127988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin public static class ScreenOffAccessibilityNodeInfoCacheClearer implements 128988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin AccessibilityNodeInfoCacheClearer { 129988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin public void clearAccessibilityNodeInfoCache(UiAutomationDriver driver) { 130988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin driver.getUiDevice().sleep(); 131988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin driver.getUiDevice().wakeUp(); 132988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin } 13374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 13474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 13574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin /** 136988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin * Clears AccessibilityNodeInfoCache by exploiting an implementation detail of 137988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin * AccessibilityNodeInfoCache. This is a hack; use it at your own discretion. 13874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin */ 139988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin public static class WindowStateAccessibilityNodeInfoCacheClearer implements 140988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin AccessibilityNodeInfoCacheClearer { 141988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin public void clearAccessibilityNodeInfoCache(UiAutomationDriver driver) { 142988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin AccessibilityManager accessibilityManager = 143988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin (AccessibilityManager) driver.context.getInstrumentation().getTargetContext() 144988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin .getSystemService(Context.ACCESSIBILITY_SERVICE); 145988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin accessibilityManager.sendAccessibilityEvent(AccessibilityEvent 146988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin .obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)); 147988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin } 148988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin } 149988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin 150988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin public void setAccessibilityNodeInfoCacheClearer(AccessibilityNodeInfoCacheClearer clearer) { 151988386cd9cc46bf5399846a414c09e0af48b1e5aKevin Jin this.clearer = clearer; 15270e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin } 15370e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin 15470e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin @Override 15574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public UiAutomationUiDevice getUiDevice() { 15674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return uiDevice; 15770e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin } 15857e46577852ffa1dde4662f6018f7fbcfacb6148Thanh Le} 159