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