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