AccessibilityInteractionClient.java revision 8b5a814f6a36045b06bee36f44703503c03714d4
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; 218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.Message; 228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.RemoteException; 238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.SystemClock; 24d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.Log; 258b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganovimport android.util.LongSparseArray; 26d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.SparseArray; 278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.Collections; 298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.List; 308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.concurrent.atomic.AtomicInteger; 318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/** 338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * This class is a singleton that performs accessibility interaction 348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * which is it queries remote view hierarchies about snapshots of their 358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * views as well requests from these hierarchies to perform certain 368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * actions on their views. 378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Rationale: The content retrieval APIs are synchronous from a client's 398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * perspective but internally they are asynchronous. The client thread 408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * calls into the system requesting an action and providing a callback 418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * to receive the result after which it waits up to a timeout for that 428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * result. The system enforces security and the delegates the request 438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * to a given view hierarchy where a message is posted (from a binder 448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * thread) describing what to be performed by the main UI thread the 458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * result of which it delivered via the mentioned callback. However, 468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * the blocked client thread and the main UI thread of the target view 478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * hierarchy can be the same thread, for example an accessibility service 488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * and an activity run in the same process, thus they are executed on the 498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * same main thread. In such a case the retrieval will fail since the UI 508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * thread that has to process the message describing the work to be done 518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * is blocked waiting for a result is has to compute! To avoid this scenario 528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * when making a call the client also passes its process and thread ids so 538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * the accessed view hierarchy can detect if the client making the request 548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * is running in its main UI thread. In such a case the view hierarchy, 558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * specifically the binder thread performing the IPC to it, does not post a 568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * message to be run on the UI thread but passes it to the singleton 578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * interaction client through which all interactions occur and the latter is 588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * responsible to execute the message before starting to wait for the 598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * asynchronous result delivered via the callback. In this case the expected 608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * result is already received so no waiting is performed. 618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @hide 638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpublic final class AccessibilityInteractionClient 658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov extends IAccessibilityInteractionConnectionCallback.Stub { 668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 67d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public static final int NO_ID = -1; 68d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 69d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov private static final String LOG_TAG = "AccessibilityInteractionClient"; 70d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 71d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov private static final boolean DEBUG = false; 72d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private static final long TIMEOUT_INTERACTION_MILLIS = 5000; 748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private static final Object sStaticLock = new Object(); 768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 77021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov private static final LongSparseArray<AccessibilityInteractionClient> sClients = 78021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov new LongSparseArray<AccessibilityInteractionClient>(); 798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private final AtomicInteger mInteractionIdCounter = new AtomicInteger(); 818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private final Object mInstanceLock = new Object(); 838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private int mInteractionId = -1; 858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult; 878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult; 898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private boolean mPerformAccessibilityActionResult; 918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private Message mSameThreadMessage; 938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private final Rect mTempBounds = new Rect(); 958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 96d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov private final SparseArray<IAccessibilityServiceConnection> mConnectionCache = 97d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov new SparseArray<IAccessibilityServiceConnection>(); 98d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 100021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @return The client for the current thread. 1018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 1028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public static AccessibilityInteractionClient getInstance() { 103021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov final long threadId = Thread.currentThread().getId(); 104021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov return getInstanceForThread(threadId); 105021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov } 106021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov 107021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov /** 108021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * <strong>Note:</strong> We keep one instance per interrogating thread since 109021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * the instance contains state which can lead to undesired thread interleavings. 110021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * We do not have a thread local variable since other threads should be able to 111021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * look up the correct client knowing a thread id. See ViewRootImpl for details. 112021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * 113021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @return The client for a given <code>threadId</code>. 114021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov */ 115021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov public static AccessibilityInteractionClient getInstanceForThread(long threadId) { 1168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (sStaticLock) { 117021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov AccessibilityInteractionClient client = sClients.get(threadId); 118021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov if (client == null) { 119021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov client = new AccessibilityInteractionClient(); 120021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov sClients.put(threadId, client); 1218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 122021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov return client; 1238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 126021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov private AccessibilityInteractionClient() { 127021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov /* reducing constructor visibility */ 128021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov } 129021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov 1308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 1318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Sets the message to be processed if the interacted view hierarchy 1328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * and the interacting client are running in the same thread. 1338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 1348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param message The message. 1358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 1368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setSameThreadMessage(Message message) { 1378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 1388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mSameThreadMessage = message; 1396bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov mInstanceLock.notifyAll(); 1408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 1438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 1448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finds an {@link AccessibilityNodeInfo} by accessibility id. 1458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 146d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 1478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param accessibilityWindowId A unique window id. 148021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @param accessibilityNodeId A unique node accessibility id 149021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * (accessibility view and virtual descendant id). 1508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return An {@link AccessibilityNodeInfo} if found, null otherwise. 1518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 152d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, 153f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov int accessibilityWindowId, int accessibilityNodeId) { 1548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 155d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 156d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 157d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 158d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( 159f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov accessibilityWindowId, accessibilityNodeId, interactionId, this, 160d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Thread.currentThread().getId()); 161d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 162d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 163d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( 164d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 165d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov finalizeAccessibilityNodeInfo(info, connectionId, windowScale); 166d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return info; 167d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 168d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 169d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 170d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 171d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 1728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 174d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 175d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 176d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfoByAccessibilityId", re); 177d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 1788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return null; 1808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 1818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 1828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 1838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed 1848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * in the currently active window and starts from the root View in the window. 1858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 186d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 1876bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov * @param viewId The id of the view. 1888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return An {@link AccessibilityNodeInfo} if found, null otherwise. 1898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 190d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int connectionId, 191d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov int viewId) { 1928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 193d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 194d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 195d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 196d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final float windowScale = 197d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov connection.findAccessibilityNodeInfoByViewIdInActiveWindow(viewId, 198d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId, this, Thread.currentThread().getId()); 199d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 200d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 201d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( 202d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 203d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov finalizeAccessibilityNodeInfo(info, connectionId, windowScale); 204d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return info; 205d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 206d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 207d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 208d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 209d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 212d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 213d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 214d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfoByViewIdInActiveWindow", re); 215d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return null; 2188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 2208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 2218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finds {@link AccessibilityNodeInfo}s by View text. The match is case 2228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * insensitive containment. The search is performed in the currently 2238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * active window and starts from the root View in the window. 2248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 225d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 2268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param text The searched text. 2278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return A list of found {@link AccessibilityNodeInfo}s. 2288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 2298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow( 230d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov int connectionId, String text) { 2318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 232d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 233d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 234d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 235d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final float windowScale = 2368b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganov connection.findAccessibilityNodeInfosByTextInActiveWindow(text, 237d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId, this, Thread.currentThread().getId()); 238d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 239d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 240d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( 241d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 242d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); 243d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return infos; 244d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 245d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 246d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 247d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 248021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov } 2498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 251d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 252d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 253d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfosByViewTextInActiveWindow", re); 254d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return null; 2578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 2598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 2608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finds {@link AccessibilityNodeInfo}s by View text. The match is case 2618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * insensitive containment. The search is performed in the window whose 2628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * id is specified and starts from the View whose accessibility id is 2638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * specified. 2648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 265d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 2668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param text The searched text. 2678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param accessibilityWindowId A unique window id. 268021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id) from 269021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * where to start the search. Use {@link android.view.View#NO_ID} to start from the root. 2708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return A list of found {@link AccessibilityNodeInfo}s. 2718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 272d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(int connectionId, 273f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov String text, int accessibilityWindowId, int accessibilityNodeId) { 2748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 275d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 276d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 277d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 2788b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganov final float windowScale = connection.findAccessibilityNodeInfosByText(text, 279f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov accessibilityWindowId, accessibilityNodeId, interactionId, this, 280d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Thread.currentThread().getId()); 281d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov // If the scale is zero the call has failed. 282d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (windowScale > 0) { 283d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( 284d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov interactionId); 285d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); 286d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return infos; 287d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 288d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 289d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 290d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 291d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 294d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 295d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote" 296d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov + " findAccessibilityNodeInfosByViewText", re); 297d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 2988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 2998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return Collections.emptyList(); 3008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Performs an accessibility action on an {@link AccessibilityNodeInfo}. 3048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 305d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of a connection for interacting with the system. 3068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param accessibilityWindowId The id of the window. 307021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). 3088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param action The action to perform. 3098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return Whether the action was performed. 3108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 311d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId, 312f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov int accessibilityNodeId, int action) { 3138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 314d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov IAccessibilityServiceConnection connection = getConnection(connectionId); 315d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (connection != null) { 316d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final int interactionId = mInteractionIdCounter.getAndIncrement(); 317d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov final boolean success = connection.performAccessibilityAction( 318f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov accessibilityWindowId, accessibilityNodeId, action, interactionId, this, 319d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Thread.currentThread().getId()); 320d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (success) { 321d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return getPerformAccessibilityActionResult(interactionId); 322d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 323d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } else { 324d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 325d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "No connection for connection id: " + connectionId); 326d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 3278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (RemoteException re) { 329d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov if (DEBUG) { 330d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re); 331d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 3328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return false; 3348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}. 3388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 3398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 3408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return The result {@link AccessibilityNodeInfo}. 3418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 3428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) { 3438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 3448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean success = waitForResultTimedLocked(interactionId); 3458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null; 3468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov clearResultLocked(); 3478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 3488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * {@inheritDoc} 3538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 3548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, 3558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov int interactionId) { 3568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 3578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (interactionId > mInteractionId) { 3588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfoResult = info; 3598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = interactionId; 3608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.notifyAll(); 3628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s. 3678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 3688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 3698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return The result {@link AccessibilityNodeInfo}s. 3708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 3718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear( 3728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov int interactionId) { 3738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 3748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean success = waitForResultTimedLocked(interactionId); 3758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov List<AccessibilityNodeInfo> result = success ? mFindAccessibilityNodeInfosResult : null; 3768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov clearResultLocked(); 3778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 3788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * {@inheritDoc} 3838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 3848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos, 3858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov int interactionId) { 3868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 3878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (interactionId > mInteractionId) { 3888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfosResult = infos; 3898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = interactionId; 3908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.notifyAll(); 3928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 3948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 3958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 3968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the result of a request to perform an accessibility action. 3978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 3988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 3998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return Whether the action was performed. 4008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private boolean getPerformAccessibilityActionResult(int interactionId) { 4028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 4038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean success = waitForResultTimedLocked(interactionId); 4048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final boolean result = success ? mPerformAccessibilityActionResult : false; 4058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov clearResultLocked(); 4068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 4078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * {@inheritDoc} 4128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) { 4148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 4158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (interactionId > mInteractionId) { 4168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mPerformAccessibilityActionResult = succeeded; 4178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = interactionId; 4188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.notifyAll(); 4208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Clears the result state. 4258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private void clearResultLocked() { 4278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInteractionId = -1; 4288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfoResult = null; 4298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mFindAccessibilityNodeInfosResult = null; 4308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mPerformAccessibilityActionResult = false; 4318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Waits up to a given bound for a result of a request and returns it. 4358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 4368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param interactionId The interaction id to match the result with the request. 4378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return Whether the result was received. 4388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private boolean waitForResultTimedLocked(int interactionId) { 4408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS; 4418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final long startTimeMillis = SystemClock.uptimeMillis(); 4428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov while (true) { 4438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov try { 4446bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov Message sameProcessMessage = getSameProcessMessageAndClear(); 4456bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov if (sameProcessMessage != null) { 4466bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov sameProcessMessage.getTarget().handleMessage(sameProcessMessage); 4476bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov } 4486bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov 4498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (mInteractionId == interactionId) { 4508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return true; 4518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (mInteractionId > interactionId) { 4538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return false; 4548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 4568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis; 4578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (waitTimeMillis <= 0) { 4588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return false; 4598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mInstanceLock.wait(waitTimeMillis); 4618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } catch (InterruptedException ie) { 4628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /* ignore */ 4638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Applies compatibility scale to the info bounds if it is not equal to one. 4698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 4708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param info The info whose bounds to scale. 4718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param scale The scale to apply. 4728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 4738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) { 4748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (scale == 1.0f) { 4758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return; 4768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov Rect bounds = mTempBounds; 4788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.getBoundsInParent(bounds); 4798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov bounds.scale(scale); 4808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.setBoundsInParent(bounds); 4818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.getBoundsInScreen(bounds); 4838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov bounds.scale(scale); 4848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.setBoundsInScreen(bounds); 4858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 4868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 4878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 4888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finalize an {@link AccessibilityNodeInfo} before passing it to the client. 4898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 4908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param info The info. 491d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of the connection to the system. 4928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param windowScale The source window compatibility scale. 4938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 494d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, 495d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov float windowScale) { 4968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (info != null) { 4978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov applyCompatibilityScaleIfNeeded(info, windowScale); 498d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov info.setConnectionId(connectionId); 4998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov info.setSealed(true); 5008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Finalize {@link AccessibilityNodeInfo}s before passing them to the client. 5058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 5068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param infos The {@link AccessibilityNodeInfo}s. 507d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The id of the connection to the system. 5088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @param windowScale The source window compatibility scale. 5098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 5108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, 511d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov int connectionId, float windowScale) { 5128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov if (infos != null) { 5138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov final int infosCount = infos.size(); 5148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov for (int i = 0; i < infosCount; i++) { 5158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov AccessibilityNodeInfo info = infos.get(i); 516d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov finalizeAccessibilityNodeInfo(info, connectionId, windowScale); 5178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov 5218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov /** 5228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Gets the message stored if the interacted and interacting 5238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * threads are the same. 5248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * 5258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @return The message. 5268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */ 5278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov private Message getSameProcessMessageAndClear() { 5288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov synchronized (mInstanceLock) { 5298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov Message result = mSameThreadMessage; 5308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov mSameThreadMessage = null; 5318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov return result; 5328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 5338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov } 534d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 535d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov /** 536d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * Gets a cached accessibility service connection. 537d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * 538d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The connection id. 539d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @return The cached connection if such. 540d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov */ 541d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public IAccessibilityServiceConnection getConnection(int connectionId) { 542d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov synchronized (mConnectionCache) { 543d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov return mConnectionCache.get(connectionId); 544d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 545d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 546d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 547d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov /** 548d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * Adds a cached accessibility service connection. 549d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * 550d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The connection id. 551d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connection The connection. 552d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov */ 553d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public void addConnection(int connectionId, IAccessibilityServiceConnection connection) { 554d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov synchronized (mConnectionCache) { 555d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov mConnectionCache.put(connectionId, connection); 556d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 557d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 558d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov 559d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov /** 560d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * Removes a cached accessibility service connection. 561d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * 562d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov * @param connectionId The connection id. 563d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov */ 564d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov public void removeConnection(int connectionId) { 565d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov synchronized (mConnectionCache) { 566d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov mConnectionCache.remove(connectionId); 567d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 568d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov } 5698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov} 570