18bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/* 28bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** Copyright 2011, The Android Open Source Project 38bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** 48bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** Licensed under the Apache License, Version 2.0 (the "License"); 58bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** you may not use this file except in compliance with the License. 68bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** You may obtain a copy of the License at 78bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** 88bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** http://www.apache.org/licenses/LICENSE-2.0 98bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** 108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** Unless required by applicable law or agreed to in writing, software 118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** distributed under the License is distributed on an "AS IS" BASIS, 128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** See the License for the specific language governing permissions and 148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov ** limitations under the License. 158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpackage android.view.accessibility; 188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 198b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganovimport android.accessibilityservice.IAccessibilityServiceConnection; 208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.graphics.Rect; 214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.os.Binder; 224528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport android.os.Build; 23aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganovimport android.os.Bundle; 248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.Message; 254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.os.Process; 268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.RemoteException; 278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.SystemClock; 28d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.Log; 298b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganovimport android.util.LongSparseArray; 30d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.SparseArray; 314528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport android.util.SparseLongArray; 328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganovimport java.util.ArrayList; 348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.Collections; 354528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport java.util.HashSet; 364528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport java.util.LinkedList; 378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.List; 384528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport java.util.Queue; 398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.concurrent.atomic.AtomicInteger; 408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/** 428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * This class is a singleton that performs accessibility interaction 438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * which is it queries remote view hierarchies about snapshots of their 448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * views as well requests from these hierarchies to perform certain 458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * actions on their views. 468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Rationale: The content retrieval APIs are synchronous from a client's 488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * perspective but internally they are asynchronous. The client thread 498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * calls into the system requesting an action and providing a callback 508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * to receive the result after which it waits up to a timeout for that 518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * result. The system enforces security and the delegates the request 528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * to a given view hierarchy where a message is posted (from a binder 538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * thread) describing what to be performed by the main UI thread the 548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * result of which it delivered via the mentioned callback. However, 558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * the blocked client thread and the main UI thread of the target view 568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * hierarchy can be the same thread, for example an accessibility service 578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * and an activity run in the same process, thus they are executed on the 588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * same main thread. In such a case the retrieval will fail since the UI 598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * thread that has to process the message describing the work to be done 608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * is blocked waiting for a result is has to compute! To avoid this scenario 618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * when making a call the client also passes its process and thread ids so 628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * the accessed view hierarchy can detect if the client making the request 638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * is running in its main UI thread. In such a case the view hierarchy, 648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * specifically the binder thread performing the IPC to it, does not post a 658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * message to be run on the UI thread but passes it to the singleton 668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * interaction client through which all interactions occur and the latter is 678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * responsible to execute the message before starting to wait for the 688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * asynchronous result delivered via the callback. In this case the expected 698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * result is already received so no waiting is performed. 708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @hide 728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpublic final class AccessibilityInteractionClient 748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov extends IAccessibilityInteractionConnectionCallback.Stub { 758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 76d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public static final int NO_ID = -1; 77d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 78d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov private static final String LOG_TAG = "AccessibilityInteractionClient"; 79d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 80d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov private static final boolean DEBUG = false; 81d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 824528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov private static final boolean CHECK_INTEGRITY = true; 834528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov 848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private static final long TIMEOUT_INTERACTION_MILLIS = 5000; 858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private static final Object sStaticLock = new Object(); 878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 88021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov private static final LongSparseArray<AccessibilityInteractionClient> sClients = 89021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov new LongSparseArray<AccessibilityInteractionClient>(); 908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private final AtomicInteger mInteractionIdCounter = new AtomicInteger(); 928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private final Object mInstanceLock = new Object(); 948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 95fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov private volatile int mInteractionId = -1; 968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult; 988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult; 1008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 1018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private boolean mPerformAccessibilityActionResult; 1028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 1038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private Message mSameThreadMessage; 1048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 1058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private final Rect mTempBounds = new Rect(); 1068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 10736bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov // The connection cache is shared between all interrogating threads. 10836bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = 109d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov new SparseArray<IAccessibilityServiceConnection>(); 110d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 11179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov // The connection cache is shared between all interrogating threads since 11279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov // at any given time there is only one window allowing querying. 11379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache = 11457c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov new AccessibilityNodeInfoCache(); 11579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov 1168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 117021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @return The client for the current thread. 1188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 1198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public static AccessibilityInteractionClient getInstance() { 120021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov final long threadId = Thread.currentThread().getId(); 121021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov return getInstanceForThread(threadId); 122021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov } 123021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov 124021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov /** 125021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * <strong>Note:</strong> We keep one instance per interrogating thread since 126021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * the instance contains state which can lead to undesired thread interleavings. 127021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * We do not have a thread local variable since other threads should be able to 128021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * look up the correct client knowing a thread id. See ViewRootImpl for details. 129021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * 130021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @return The client for a given <code>threadId</code>. 131021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov */ 132021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov public static AccessibilityInteractionClient getInstanceForThread(long threadId) { 1338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (sStaticLock) { 134021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov AccessibilityInteractionClient client = sClients.get(threadId); 135021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov if (client == null) { 136021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov client = new AccessibilityInteractionClient(); 137021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov sClients.put(threadId, client); 1388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 139021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov return client; 1408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 143021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov private AccessibilityInteractionClient() { 144021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov /* reducing constructor visibility */ 145021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov } 146021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov 1478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 1488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Sets the message to be processed if the interacted view hierarchy 1498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * and the interacting client are running in the same thread. 1508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 1518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param message The message. 1528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 1538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setSameThreadMessage(Message message) { 1548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 1558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mSameThreadMessage = message; 1566bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov mInstanceLock.notifyAll(); 1578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 1608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 161fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov * Gets the root {@link AccessibilityNodeInfo} in the currently active window. 162fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov * 163fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 164fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov * @return The root {@link AccessibilityNodeInfo} if found, null otherwise. 165fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov */ 166fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) { 167fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov return findAccessibilityNodeInfoByAccessibilityId(connectionId, 168fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, 169fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); 170fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov } 171fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov 172fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov /** 1738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finds an {@link AccessibilityNodeInfo} by accessibility id. 1748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 175d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 17679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * @param accessibilityWindowId A unique window id. Use 1770d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} 17879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * to query the currently active window. 1790d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * @param accessibilityNodeId A unique view id or virtual descendant id from 1800d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * where to start the search. Use 1810d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} 1820d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * to start from the root. 18357c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov * @param prefetchFlags flags to guide prefetching. 1848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return An {@link AccessibilityNodeInfo} if found, null otherwise. 1858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 186d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, 18757c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) { 1888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 189d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 190d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 1910d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get( 1920d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov accessibilityNodeId); 19379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov if (cachedInfo != null) { 19479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov return cachedInfo; 19579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov } 196d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 197d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( 198f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov accessibilityWindowId, accessibilityNodeId, interactionId, this, 1994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov prefetchFlags, Thread.currentThread().getId()); 200d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 201d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 20279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( 203d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 2040d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); 20579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov if (infos != null && !infos.isEmpty()) { 20679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov return infos.get(0); 20779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov } 208d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 209d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 210d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 211d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 212d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 215d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 216d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 217d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfoByAccessibilityId", re); 218d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return null; 2218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 2238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 22479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in 22579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * the window whose id is specified and starts from the node whose accessibility 22679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * id is specified. 2278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 228d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 22979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * @param accessibilityWindowId A unique window id. Use 2300d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} 23179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * to query the currently active window. 2320d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * @param accessibilityNodeId A unique view id or virtual descendant id from 2330d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * where to start the search. Use 2340d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} 23579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * to start from the root. 2366bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov * @param viewId The id of the view. 2378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return An {@link AccessibilityNodeInfo} if found, null otherwise. 2388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 23979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId, 24079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov int accessibilityWindowId, long accessibilityNodeId, int viewId) { 2418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 242d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 243d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 244d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 245d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final float windowScale = 24679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId, 24779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov accessibilityNodeId, viewId, interactionId, this, 24879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov Thread.currentThread().getId()); 249d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 250d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 251d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( 252d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 2530d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); 254d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return info; 255d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 256d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 257d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 258d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 259d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 262d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 263d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 264d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfoByViewIdInActiveWindow", re); 265d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return null; 2688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 2708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 2718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finds {@link AccessibilityNodeInfo}s by View text. The match is case 2728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * insensitive containment. The search is performed in the window whose 27379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * id is specified and starts from the node whose accessibility id is 2748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * specified. 2758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 276d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 27779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * @param accessibilityWindowId A unique window id. Use 2780d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} 27979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * to query the currently active window. 2800d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * @param accessibilityNodeId A unique view id or virtual descendant id from 2810d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * where to start the search. Use 2820d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} 2830d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * to start from the root. 2848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param text The searched text. 2858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return A list of found {@link AccessibilityNodeInfo}s. 2868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 28766922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId, 28879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov int accessibilityWindowId, long accessibilityNodeId, String text) { 2898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 290d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 291d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 292d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 29379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov final float windowScale = connection.findAccessibilityNodeInfosByText( 29479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov accessibilityWindowId, accessibilityNodeId, text, interactionId, this, 295d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Thread.currentThread().getId()); 296d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 297d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 298d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( 299d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 3000d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); 301d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return infos; 302d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 303d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 304d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 305d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 306d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 3078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 309d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 310d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 311d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfosByViewText", re); 312d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 3138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return Collections.emptyList(); 3158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the 3194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * specified focus type. The search is performed in the window whose id is specified 3204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * and starts from the node whose accessibility id is specified. 3214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 3224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 3234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param accessibilityWindowId A unique window id. Use 3244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} 3254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * to query the currently active window. 3264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param accessibilityNodeId A unique view id or virtual descendant id from 3274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * where to start the search. Use 3284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} 3294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * to start from the root. 3304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param focusType The focus type. 3314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The accessibility focused {@link AccessibilityNodeInfo}. 3324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 3334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId, 3344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov long accessibilityNodeId, int focusType) { 3354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov try { 3364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 3374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (connection != null) { 3384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 3394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float windowScale = connection.findFocus(accessibilityWindowId, 3404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov accessibilityNodeId, focusType, interactionId, this, 3414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Thread.currentThread().getId()); 3424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // If the scale is zero the call has failed. 3434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (windowScale > 0) { 3444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( 3454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov interactionId); 3464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); 3474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return info; 3484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } else { 3504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 3514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 3524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } catch (RemoteException re) { 3554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 3564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re); 3574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return null; 3604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 3624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 3634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}. 3644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * The search is performed in the window whose id is specified and starts from the 3654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * node whose accessibility id is specified. 3664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 3674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 3684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param accessibilityWindowId A unique window id. Use 3694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} 3704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * to query the currently active window. 3714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param accessibilityNodeId A unique view id or virtual descendant id from 3724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * where to start the search. Use 3734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} 3744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * to start from the root. 3754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param direction The direction in which to search for focusable. 3764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The accessibility focused {@link AccessibilityNodeInfo}. 3774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 3784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId, 3794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov long accessibilityNodeId, int direction) { 3804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov try { 3814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 3824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (connection != null) { 3834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 3844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float windowScale = connection.focusSearch(accessibilityWindowId, 3854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov accessibilityNodeId, direction, interactionId, this, 3864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Thread.currentThread().getId()); 3874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // If the scale is zero the call has failed. 3884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (windowScale > 0) { 3894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( 3904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov interactionId); 3914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); 3924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return info; 3934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } else { 3954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 3964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 3974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 3994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } catch (RemoteException re) { 4004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 4014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re); 4024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 4034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 4044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return null; 4054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 4064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 4074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 4088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Performs an accessibility action on an {@link AccessibilityNodeInfo}. 4098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 410d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 41179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * @param accessibilityWindowId A unique window id. Use 4120d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} 41379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov * to query the currently active window. 4140d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * @param accessibilityNodeId A unique view id or virtual descendant id from 4150d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * where to start the search. Use 4160d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} 4170d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov * to start from the root. 4188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param action The action to perform. 419aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov * @param arguments Optional action arguments. 4208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return Whether the action was performed. 4218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 422d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId, 423aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov long accessibilityNodeId, int action, Bundle arguments) { 4248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 425d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 426d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 427d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 428d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final boolean success = connection.performAccessibilityAction( 429aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov accessibilityWindowId, accessibilityNodeId, action, arguments, 430aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov interactionId, this, Thread.currentThread().getId()); 431d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (success) { 43279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov return getPerformAccessibilityActionResultAndClear(interactionId); 433d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 434d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 435d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 436d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 437d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 4388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 440d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 441d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re); 442d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 4438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return false; 4458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 44779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov public void clearCache() { 44879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov sAccessibilityNodeInfoCache.clear(); 44979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov } 45079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov 45179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov public void onAccessibilityEvent(AccessibilityEvent event) { 45279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov sAccessibilityNodeInfoCache.onAccessibilityEvent(event); 45379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov } 45479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov 4558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}. 4578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 4588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 4598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return The result {@link AccessibilityNodeInfo}. 4608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) { 4628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 4638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean success = waitForResultTimedLocked(interactionId); 4648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null; 4658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov clearResultLocked(); 4668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 4678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * {@inheritDoc} 4728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, 4748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov int interactionId) { 4758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 4768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (interactionId > mInteractionId) { 4778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfoResult = info; 4788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = interactionId; 4798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.notifyAll(); 4818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s. 4868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 4878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 4888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return The result {@link AccessibilityNodeInfo}s. 4898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear( 4918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov int interactionId) { 4928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 4938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean success = waitForResultTimedLocked(interactionId); 4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov List<AccessibilityNodeInfo> result = null; 4954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (success) { 4964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov result = mFindAccessibilityNodeInfosResult; 4974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } else { 4984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov result = Collections.emptyList(); 4994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 5008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov clearResultLocked(); 5014528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) { 5024528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov checkFindAccessibilityNodeInfoResultIntegrity(result); 5034528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 5048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 5058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * {@inheritDoc} 5108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 5118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos, 5128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov int interactionId) { 5138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 5148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (interactionId > mInteractionId) { 5154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (infos != null) { 5164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // If the call is not an IPC, i.e. it is made from the same process, we need to 5174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // instantiate new result list to avoid passing internal instances to clients. 5184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid()); 5194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!isIpcCall) { 5204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mFindAccessibilityNodeInfosResult = 5214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov new ArrayList<AccessibilityNodeInfo>(infos); 5224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } else { 5234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mFindAccessibilityNodeInfosResult = infos; 5244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 52579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov } else { 5264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mFindAccessibilityNodeInfosResult = Collections.emptyList(); 52779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov } 5288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = interactionId; 5298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.notifyAll(); 5318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the result of a request to perform an accessibility action. 5368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 5378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 5388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return Whether the action was performed. 5398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 54079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov private boolean getPerformAccessibilityActionResultAndClear(int interactionId) { 5418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 5428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean success = waitForResultTimedLocked(interactionId); 5438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean result = success ? mPerformAccessibilityActionResult : false; 5448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov clearResultLocked(); 5458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 5468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * {@inheritDoc} 5518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 5528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) { 5538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 5548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (interactionId > mInteractionId) { 5558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mPerformAccessibilityActionResult = succeeded; 5568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = interactionId; 5578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.notifyAll(); 5598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Clears the result state. 5648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 5658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private void clearResultLocked() { 5668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = -1; 5678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfoResult = null; 5688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfosResult = null; 5698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mPerformAccessibilityActionResult = false; 5708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Waits up to a given bound for a result of a request and returns it. 5748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 5758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 5768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return Whether the result was received. 5778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 5788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private boolean waitForResultTimedLocked(int interactionId) { 5798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS; 5808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final long startTimeMillis = SystemClock.uptimeMillis(); 5818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov while (true) { 5828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 5836bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov Message sameProcessMessage = getSameProcessMessageAndClear(); 5846bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov if (sameProcessMessage != null) { 5856bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov sameProcessMessage.getTarget().handleMessage(sameProcessMessage); 5866bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov } 5876bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov 5888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (mInteractionId == interactionId) { 5898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return true; 5908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (mInteractionId > interactionId) { 5928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return false; 5938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 5958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis; 5968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (waitTimeMillis <= 0) { 5978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return false; 5988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.wait(waitTimeMillis); 6008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (InterruptedException ie) { 6018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /* ignore */ 6028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 6068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 6078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Applies compatibility scale to the info bounds if it is not equal to one. 6088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 6098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param info The info whose bounds to scale. 6108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param scale The scale to apply. 6118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 6128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) { 6138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (scale == 1.0f) { 6148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return; 6158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov Rect bounds = mTempBounds; 6178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.getBoundsInParent(bounds); 6188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov bounds.scale(scale); 6198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.setBoundsInParent(bounds); 6208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 6218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.getBoundsInScreen(bounds); 6228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov bounds.scale(scale); 6238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.setBoundsInScreen(bounds); 6248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 6268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 6278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finalize an {@link AccessibilityNodeInfo} before passing it to the client. 6288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 6298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param info The info. 630d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of the connection to the system. 6318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param windowScale The source window compatibility scale. 6328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 6330d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, 634d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov float windowScale) { 6358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (info != null) { 6368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov applyCompatibilityScaleIfNeeded(info, windowScale); 637d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov info.setConnectionId(connectionId); 6388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.setSealed(true); 639c406be9036643ebe41bafcd94fe4aa861b4e4f4fSvetoslav Ganov sAccessibilityNodeInfoCache.add(info); 6408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 6438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 6448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finalize {@link AccessibilityNodeInfo}s before passing them to the client. 6458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 6468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param infos The {@link AccessibilityNodeInfo}s. 647d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of the connection to the system. 6488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param windowScale The source window compatibility scale. 6498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 6500d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, 651d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov int connectionId, float windowScale) { 6528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (infos != null) { 6538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final int infosCount = infos.size(); 6548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov for (int i = 0; i < infosCount; i++) { 6558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov AccessibilityNodeInfo info = infos.get(i); 6560d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); 6578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 6618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 6628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the message stored if the interacted and interacting 6638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * threads are the same. 6648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 6658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return The message. 6668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 6678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private Message getSameProcessMessageAndClear() { 6688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 6698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov Message result = mSameThreadMessage; 6708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mSameThreadMessage = null; 6718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 6728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 6738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 674d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 675d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov /** 676d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * Gets a cached accessibility service connection. 677d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * 678d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The connection id. 679d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @return The cached connection if such. 680d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov */ 681d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public IAccessibilityServiceConnection getConnection(int connectionId) { 68236bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov synchronized (sConnectionCache) { 68336bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov return sConnectionCache.get(connectionId); 684d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 685d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 686d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 687d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov /** 688d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * Adds a cached accessibility service connection. 689d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * 690d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The connection id. 691d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connection The connection. 692d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov */ 693d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public void addConnection(int connectionId, IAccessibilityServiceConnection connection) { 69436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov synchronized (sConnectionCache) { 69536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov sConnectionCache.put(connectionId, connection); 696d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 697d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 698d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 699d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov /** 700d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * Removes a cached accessibility service connection. 701d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * 702d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The connection id. 703d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov */ 704d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public void removeConnection(int connectionId) { 70536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov synchronized (sConnectionCache) { 70636bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov sConnectionCache.remove(connectionId); 707d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 708d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 7094528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov 7104528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov /** 7114528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov * Checks whether the infos are a fully connected tree with no duplicates. 7124528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov * 7134528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov * @param infos The result list to check. 7144528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov */ 7154528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov private void checkFindAccessibilityNodeInfoResultIntegrity(List<AccessibilityNodeInfo> infos) { 7164528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (infos.size() == 0) { 7174528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov return; 7184528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7194528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov // Find the root node. 7204528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov AccessibilityNodeInfo root = infos.get(0); 7214528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov final int infoCount = infos.size(); 7224528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov for (int i = 1; i < infoCount; i++) { 7234528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov for (int j = i; j < infoCount; j++) { 7244528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov AccessibilityNodeInfo candidate = infos.get(j); 7254528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (root.getParentNodeId() == candidate.getSourceNodeId()) { 7264528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov root = candidate; 7274528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov break; 7284528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7294528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7304528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7314528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (root == null) { 7324528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov Log.e(LOG_TAG, "No root."); 7334528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7344528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov // Check for duplicates. 7354528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>(); 7364528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>(); 7374528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov fringe.add(root); 7384528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov while (!fringe.isEmpty()) { 7394528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov AccessibilityNodeInfo current = fringe.poll(); 7404528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (!seen.add(current)) { 7414528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov Log.e(LOG_TAG, "Duplicate node."); 7424528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov return; 7434528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7444528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov SparseLongArray childIds = current.getChildNodeIds(); 7454528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov final int childCount = childIds.size(); 7464528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov for (int i = 0; i < childCount; i++) { 7474528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov final long childId = childIds.valueAt(i); 7484528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov for (int j = 0; j < infoCount; j++) { 7494528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov AccessibilityNodeInfo child = infos.get(j); 7504528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (child.getSourceNodeId() == childId) { 7514528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov fringe.add(child); 7524528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7534528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7544528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7554528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7564528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov final int disconnectedCount = infos.size() - seen.size(); 7574528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov if (disconnectedCount > 0) { 7584528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes."); 7594528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7604528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov } 7618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov} 762