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;
204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.os.Binder;
214528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport android.os.Build;
22aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganovimport android.os.Bundle;
238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.Message;
244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.os.Process;
258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.RemoteException;
268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.SystemClock;
27d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.Log;
288b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganovimport android.util.LongSparseArray;
29d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.SparseArray;
308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganovimport java.util.ArrayList;
328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.Collections;
334528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport java.util.HashSet;
344528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport java.util.LinkedList;
358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.List;
364528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganovimport java.util.Queue;
378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.concurrent.atomic.AtomicInteger;
388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/**
408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * This class is a singleton that performs accessibility interaction
418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * which is it queries remote view hierarchies about snapshots of their
428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * views as well requests from these hierarchies to perform certain
438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * actions on their views.
448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Rationale: The content retrieval APIs are synchronous from a client's
468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     perspective but internally they are asynchronous. The client thread
478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     calls into the system requesting an action and providing a callback
488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to receive the result after which it waits up to a timeout for that
498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result. The system enforces security and the delegates the request
508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to a given view hierarchy where a message is posted (from a binder
518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread) describing what to be performed by the main UI thread the
528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result of which it delivered via the mentioned callback. However,
538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the blocked client thread and the main UI thread of the target view
548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     hierarchy can be the same thread, for example an accessibility service
558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     and an activity run in the same process, thus they are executed on the
568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     same main thread. In such a case the retrieval will fail since the UI
578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread that has to process the message describing the work to be done
588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is blocked waiting for a result is has to compute! To avoid this scenario
598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     when making a call the client also passes its process and thread ids so
608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the accessed view hierarchy can detect if the client making the request
618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is running in its main UI thread. In such a case the view hierarchy,
628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     specifically the binder thread performing the IPC to it, does not post a
638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     message to be run on the UI thread but passes it to the singleton
648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     interaction client through which all interactions occur and the latter is
658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     responsible to execute the message before starting to wait for the
668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     asynchronous result delivered via the callback. In this case the expected
678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result is already received so no waiting is performed.
688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @hide
708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */
718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpublic final class AccessibilityInteractionClient
728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        extends IAccessibilityInteractionConnectionCallback.Stub {
738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
74d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public static final int NO_ID = -1;
75d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
76d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final String LOG_TAG = "AccessibilityInteractionClient";
77d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
78d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final boolean DEBUG = false;
79d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
804528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov    private static final boolean CHECK_INTEGRITY = true;
814528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov
828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final Object sStaticLock = new Object();
858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
86021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    private static final LongSparseArray<AccessibilityInteractionClient> sClients =
87a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav        new LongSparseArray<>();
888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Object mInstanceLock = new Object();
928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
93fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    private volatile int mInteractionId = -1;
948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean mPerformAccessibilityActionResult;
1008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message mSameThreadMessage;
1028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
10336bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
104a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav        new SparseArray<>();
105d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
1068e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    private static final AccessibilityCache sAccessibilityCache =
107b010b1270fc524befdb9ebc3b3687a37ef1b71b9Phil Weaver        new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
10879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
1098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
110021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * @return The client for the current thread.
1118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
1128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public static AccessibilityInteractionClient getInstance() {
113021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        final long threadId = Thread.currentThread().getId();
114021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        return getInstanceForThread(threadId);
115021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    }
116021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov
117021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    /**
118021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * <strong>Note:</strong> We keep one instance per interrogating thread since
119021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * the instance contains state which can lead to undesired thread interleavings.
120021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * We do not have a thread local variable since other threads should be able to
121021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * look up the correct client knowing a thread id. See ViewRootImpl for details.
122021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     *
123021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * @return The client for a given <code>threadId</code>.
124021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     */
125021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    public static AccessibilityInteractionClient getInstanceForThread(long threadId) {
1268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (sStaticLock) {
127021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov            AccessibilityInteractionClient client = sClients.get(threadId);
128021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov            if (client == null) {
129021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov                client = new AccessibilityInteractionClient();
130021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov                sClients.put(threadId, client);
1318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
132021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov            return client;
1338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
136021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    private AccessibilityInteractionClient() {
137021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        /* reducing constructor visibility */
138021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    }
139021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov
1408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
1418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Sets the message to be processed if the interacted view hierarchy
1428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * and the interacting client are running in the same thread.
1438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
1448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param message The message.
1458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
1468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setSameThreadMessage(Message message) {
1478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
1488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = message;
1496bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov            mInstanceLock.notifyAll();
1508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
154fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     * Gets the root {@link AccessibilityNodeInfo} in the currently active window.
155fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     *
156fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
157fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     * @return The root {@link AccessibilityNodeInfo} if found, null otherwise.
158fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     */
159fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) {
160fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov        return findAccessibilityNodeInfoByAccessibilityId(connectionId,
161f00cd14f17c0acd6bffe78947d32ea0a2900d139Phil Weaver                AccessibilityWindowInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
162c2e28932d22faece6e7179c78d4e7656dc63052cPhil Weaver                false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS, null);
1631e0d4af9986c8c2a658769a63bf8b385d25e0435Svetoslav    }
1641e0d4af9986c8c2a658769a63bf8b385d25e0435Svetoslav
1651e0d4af9986c8c2a658769a63bf8b385d25e0435Svetoslav    /**
1668e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * Gets the info for a window.
1678e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     *
1688e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * @param connectionId The id of a connection for interacting with the system.
1698e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * @param accessibilityWindowId A unique window id. Use
1708e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
1718e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     *     to query the currently active window.
1728e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * @return The {@link AccessibilityWindowInfo}.
1738e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     */
1748e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId) {
1758e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        try {
1768e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            IAccessibilityServiceConnection connection = getConnection(connectionId);
1778e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            if (connection != null) {
1788e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                AccessibilityWindowInfo window = sAccessibilityCache.getWindow(
1798e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                        accessibilityWindowId);
1808e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                if (window != null) {
1818e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    if (DEBUG) {
1828e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                        Log.i(LOG_TAG, "Window cache hit");
1838e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    }
1848e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    return window;
1858e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                }
1868e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                if (DEBUG) {
1878e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    Log.i(LOG_TAG, "Window cache miss");
1888e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                }
18956bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
1908e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                window = connection.getWindow(accessibilityWindowId);
19156bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
1928e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                if (window != null) {
1938e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    sAccessibilityCache.addWindow(window);
1948e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    return window;
1958e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                }
1968e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            } else {
1978e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                if (DEBUG) {
1988e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
1998e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                }
2008e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            }
2018e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        } catch (RemoteException re) {
2028e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.e(LOG_TAG, "Error while calling remote getWindow", re);
2038e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        }
2048e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        return null;
2058e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    }
2068e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav
2078e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    /**
2088e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * Gets the info for all windows.
2098e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     *
2108e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * @param connectionId The id of a connection for interacting with the system.
2118e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     * @return The {@link AccessibilityWindowInfo} list.
2128e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav     */
2138e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    public List<AccessibilityWindowInfo> getWindows(int connectionId) {
2148e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        try {
2158e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            IAccessibilityServiceConnection connection = getConnection(connectionId);
2168e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            if (connection != null) {
217a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
218a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                if (windows != null) {
219a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                    if (DEBUG) {
220a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                        Log.i(LOG_TAG, "Windows cache hit");
221a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                    }
222a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                    return windows;
223a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                }
224a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                if (DEBUG) {
225a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                    Log.i(LOG_TAG, "Windows cache miss");
226a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                }
22756bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
228a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                windows = connection.getWindows();
22956bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
2308e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                if (windows != null) {
231c3281200fa8a27a2a89328f7db552f540ca92d1dMaxim Bogatov                    sAccessibilityCache.setWindows(windows);
2328e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    return windows;
2338e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                }
2348e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            } else {
2358e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                if (DEBUG) {
2368e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
2378e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                }
2388e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            }
2398e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        } catch (RemoteException re) {
2408e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.e(LOG_TAG, "Error while calling remote getWindows", re);
2418e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        }
2428e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        return Collections.emptyList();
2438e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    }
2448e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav
2458e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    /**
2468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
2478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
248d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
24979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2500d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
25179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2520d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2530d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2540d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
2550d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
2566254f4806dd3db53b7380e77fbb183065685573eSvetoslav     * @param bypassCache Whether to bypass the cache while looking for the node.
25757c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov     * @param prefetchFlags flags to guide prefetching.
2588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
2598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
260d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
2616254f4806dd3db53b7380e77fbb183065685573eSvetoslav            int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
262c2e28932d22faece6e7179c78d4e7656dc63052cPhil Weaver            int prefetchFlags, Bundle arguments) {
2638e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
2648e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                && (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
2658e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
2668e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                + " requires FLAG_PREFETCH_PREDECESSORS");
2678e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        }
2688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
269d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
270d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
2716254f4806dd3db53b7380e77fbb183065685573eSvetoslav                if (!bypassCache) {
2728e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
2738e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                            accessibilityWindowId, accessibilityNodeId);
2746254f4806dd3db53b7380e77fbb183065685573eSvetoslav                    if (cachedInfo != null) {
2758e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                        if (DEBUG) {
2768e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                            Log.i(LOG_TAG, "Node cache hit");
2778e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                        }
2786254f4806dd3db53b7380e77fbb183065685573eSvetoslav                        return cachedInfo;
2796254f4806dd3db53b7380e77fbb183065685573eSvetoslav                    }
2808e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    if (DEBUG) {
2818e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                        Log.i(LOG_TAG, "Node cache miss");
2828e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    }
28379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
284d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
28556bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
286152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
287f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, interactionId, this,
288c2e28932d22faece6e7179c78d4e7656dc63052cPhil Weaver                        prefetchFlags, Thread.currentThread().getId(), arguments);
28956bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
290152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                if (success) {
29179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
292d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
293152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
29479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    if (infos != null && !infos.isEmpty()) {
295b010b1270fc524befdb9ebc3b3687a37ef1b71b9Phil Weaver                        for (int i = 1; i < infos.size(); i++) {
296b010b1270fc524befdb9ebc3b3687a37ef1b71b9Phil Weaver                            infos.get(i).recycle();
297b010b1270fc524befdb9ebc3b3687a37ef1b71b9Phil Weaver                        }
29879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        return infos.get(0);
29979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    }
300d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
301d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
302d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
303d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
304d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
3058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
3078e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.e(LOG_TAG, "Error while calling remote"
3088e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    + " findAccessibilityNodeInfoByAccessibilityId", re);
3098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
3118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
31479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
31579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * the window whose id is specified and starts from the node whose accessibility
31679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified.
3178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
318d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
31979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3200d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
32179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
3220d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3230d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
3240d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
32579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to start from the root.
32680943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov     * @param viewId The fully qualified resource name of the view id to find.
32780943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov     * @return An list of {@link AccessibilityNodeInfo} if found, empty list otherwise.
3288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
32980943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(int connectionId,
33080943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, String viewId) {
3318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
332d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
333d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
334d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
33556bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
33680943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                final boolean success = connection.findAccessibilityNodeInfosByViewId(
337152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
338152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                        Thread.currentThread().getId());
33956bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
340152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                if (success) {
34180943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
342d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
34380943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                    if (infos != null) {
34480943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                        finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
34580943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                        return infos;
34680943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                    }
347d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
348d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
349d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
350d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
351d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
3528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
3548e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.w(LOG_TAG, "Error while calling remote"
3558e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
3568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
35780943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov        return Collections.emptyList();
3588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
3628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * insensitive containment. The search is performed in the window whose
36379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified and starts from the node whose accessibility id is
3648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * specified.
3658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
366d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
36779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3680d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
36979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
3700d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3710d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
3720d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3730d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
3748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param text The searched text.
3758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return A list of found {@link AccessibilityNodeInfo}s.
3768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
37766922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,
37879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, String text) {
3798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
380d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
381d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
382d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
38356bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
384152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                final boolean success = connection.findAccessibilityNodeInfosByText(
38579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
386d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
38756bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
388152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                if (success) {
389d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
390d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
39180943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                    if (infos != null) {
39280943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                        finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
39380943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                        return infos;
39480943d8daa6ab31ab5c486d57aea406aa0730d58Svetoslav Ganov                    }
395d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
396d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
397d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
398d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
399d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
4008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
4028e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.w(LOG_TAG, "Error while calling remote"
4038e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav                    + " findAccessibilityNodeInfosByViewText", re);
4048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return Collections.emptyList();
4068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
4104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * specified focus type. The search is performed in the window whose id is specified
4114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * and starts from the node whose accessibility id is specified.
4124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
4134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
4144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
4154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
4164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
4174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
4184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
4194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
4204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
4214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param focusType The focus type.
4224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
4234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
4244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
4254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int focusType) {
4264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
4274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
4284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
4294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
43056bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
431152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                final boolean success = connection.findFocus(accessibilityWindowId,
4324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, focusType, interactionId, this,
4334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
43456bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
435152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                if (success) {
4364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
4374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
438152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
4394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
4404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
4414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
4424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
4434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
4444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
4454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
4464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
4478e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.w(LOG_TAG, "Error while calling remote findFocus", re);
4484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
4494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
4504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
4514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
4524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
4534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
4544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The search is performed in the window whose id is specified and starts from the
4554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * node whose accessibility id is specified.
4564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
4574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
4584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
4594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
4604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
4614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
4624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
4634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
4644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
4654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param direction The direction in which to search for focusable.
4664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
4674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
4684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId,
4694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int direction) {
4704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
4714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
4724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
4734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
47456bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
475152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                final boolean success = connection.focusSearch(accessibilityWindowId,
4764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, direction, interactionId, this,
4774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
47856bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
479152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                if (success) {
4804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
4814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
482152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
4834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
4844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
4854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
4864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
4874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
4884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
4894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
4904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
4918e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
4924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
4934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
4954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
4964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
4978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
4988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
499d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
50079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
5010d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
50279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
5030d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
5040d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
5050d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
5060d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
5078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param action The action to perform.
508aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov     * @param arguments Optional action arguments.
5098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
5108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
511d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
512aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov            long accessibilityNodeId, int action, Bundle arguments) {
5138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
514d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
515d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
516d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
51756bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                final long identityToken = Binder.clearCallingIdentity();
518d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final boolean success = connection.performAccessibilityAction(
519aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, action, arguments,
520aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov                        interactionId, this, Thread.currentThread().getId());
52156bbeff99a628be8e1d805db2638a4b87fedb106Alan Viverette                Binder.restoreCallingIdentity(identityToken);
522d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (success) {
52379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return getPerformAccessibilityActionResultAndClear(interactionId);
524d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
525d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
526d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
527d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
528d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
5298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
5318e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
5328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return false;
5348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
53679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void clearCache() {
5378e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        sAccessibilityCache.clear();
53879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
53979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
54079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void onAccessibilityEvent(AccessibilityEvent event) {
5418e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav        sAccessibilityCache.onAccessibilityEvent(event);
5428e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav    }
5438e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav
5448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
5468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}.
5498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
5518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
5538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
5548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
5558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
5568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
5638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
5648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
5668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mFindAccessibilityNodeInfoResult = info;
5678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
5758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}s.
5788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
5808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
5818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
5834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            List<AccessibilityNodeInfo> result = null;
5844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (success) {
5854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = mFindAccessibilityNodeInfosResult;
5864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
5874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = Collections.emptyList();
5884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
5898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
5904528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) {
5914528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                checkFindAccessibilityNodeInfoResultIntegrity(result);
5924528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            }
5938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
5948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
6018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
6028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
6038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
6044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (infos != null) {
6054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // If the call is not an IPC, i.e. it is made from the same process, we need to
6064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // instantiate new result list to avoid passing internal instances to clients.
6074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
6084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (!isIpcCall) {
609a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav                        mFindAccessibilityNodeInfosResult = new ArrayList<>(infos);
6104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    } else {
6114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult = infos;
6124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
61379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                } else {
6144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mFindAccessibilityNodeInfosResult = Collections.emptyList();
61579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
6168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
6178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
6188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
6198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the result of a request to perform an accessibility action.
6248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
6268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
6278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
62879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    private boolean getPerformAccessibilityActionResultAndClear(int interactionId) {
6298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
6308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
6318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean result = success ? mPerformAccessibilityActionResult : false;
6328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
6338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
6348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
6398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
6418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
6428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
6438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mPerformAccessibilityActionResult = succeeded;
6448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
6458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
6468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
6478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Clears the result state.
6528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void clearResultLocked() {
6548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mInteractionId = -1;
6558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfoResult = null;
6568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfosResult = null;
6578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mPerformAccessibilityActionResult = false;
6588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Waits up to a given bound for a result of a request and returns it.
6628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
6648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the result was received.
6658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean waitForResultTimedLocked(int interactionId) {
6678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
6688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        final long startTimeMillis = SystemClock.uptimeMillis();
6698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        while (true) {
6708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            try {
6716bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                Message sameProcessMessage = getSameProcessMessageAndClear();
6726bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                if (sameProcessMessage != null) {
6736bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                    sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
6746bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                }
6756bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov
6768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId == interactionId) {
6778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return true;
6788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
6798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId > interactionId) {
6808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
6818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
6828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
6838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
6848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (waitTimeMillis <= 0) {
6858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
6868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
6878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInstanceLock.wait(waitTimeMillis);
6888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            } catch (InterruptedException ie) {
6898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                /* ignore */
6908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
6918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
6968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info.
698d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
700152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info,
701152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov            int connectionId) {
7028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (info != null) {
703d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            info.setConnectionId(connectionId);
7048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            info.setSealed(true);
7058e3feb15c5aec2c72b0ef120a1da325e1e8f0ddaSvetoslav            sAccessibilityCache.add(info);
7068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
7078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
7088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
7098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
7108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
7118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
7128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param infos The {@link AccessibilityNodeInfo}s.
713d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
7148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
7150d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
716152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov            int connectionId) {
7178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (infos != null) {
7188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final int infosCount = infos.size();
7198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            for (int i = 0; i < infosCount; i++) {
7208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                AccessibilityNodeInfo info = infos.get(i);
721152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891Svetoslav Ganov                finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
7228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
7238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
7248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
7258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
7268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
7278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the message stored if the interacted and interacting
7288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * threads are the same.
7298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
7308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The message.
7318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
7328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message getSameProcessMessageAndClear() {
7338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
7348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            Message result = mSameThreadMessage;
7358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = null;
7368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
7378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
7388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
739d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
740d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
741d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Gets a cached accessibility service connection.
742d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
743d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
744d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @return The cached connection if such.
745d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
746d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public IAccessibilityServiceConnection getConnection(int connectionId) {
74736bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
74836bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            return sConnectionCache.get(connectionId);
749d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
750d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
751d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
752d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
753d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Adds a cached accessibility service connection.
754d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
755d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
756d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connection The connection.
757d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
758d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
75936bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
76036bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.put(connectionId, connection);
761d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
762d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
763d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
764d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
765d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Removes a cached accessibility service connection.
766d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
767d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
768d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
769d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void removeConnection(int connectionId) {
77036bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
77136bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.remove(connectionId);
772d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
773d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
7744528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov
7754528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov    /**
7764528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov     * Checks whether the infos are a fully connected tree with no duplicates.
7774528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov     *
7784528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov     * @param infos The result list to check.
7794528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov     */
7804528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov    private void checkFindAccessibilityNodeInfoResultIntegrity(List<AccessibilityNodeInfo> infos) {
7814528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        if (infos.size() == 0) {
7824528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            return;
7834528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        }
7844528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        // Find the root node.
7854528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        AccessibilityNodeInfo root = infos.get(0);
7864528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        final int infoCount = infos.size();
7874528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        for (int i = 1; i < infoCount; i++) {
7884528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            for (int j = i; j < infoCount; j++) {
7894528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                AccessibilityNodeInfo candidate = infos.get(j);
7904528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                if (root.getParentNodeId() == candidate.getSourceNodeId()) {
7914528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                    root = candidate;
7924528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                    break;
7934528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                }
7944528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            }
7954528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        }
7964528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        if (root == null) {
7974528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            Log.e(LOG_TAG, "No root.");
7984528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        }
7994528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        // Check for duplicates.
800a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav        HashSet<AccessibilityNodeInfo> seen = new HashSet<>();
801a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72Svetoslav        Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
8024528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        fringe.add(root);
8034528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        while (!fringe.isEmpty()) {
8044528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            AccessibilityNodeInfo current = fringe.poll();
8054528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            if (!seen.add(current)) {
8064528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                Log.e(LOG_TAG, "Duplicate node.");
8074528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                return;
8084528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            }
809f0aed09ed8153043e40b3ac99788d47ba0831306Alan Viverette            final int childCount = current.getChildCount();
8104528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            for (int i = 0; i < childCount; i++) {
811f0aed09ed8153043e40b3ac99788d47ba0831306Alan Viverette                final long childId = current.getChildId(i);
8124528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                for (int j = 0; j < infoCount; j++) {
8134528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                    AccessibilityNodeInfo child = infos.get(j);
8144528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                    if (child.getSourceNodeId() == childId) {
8154528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                        fringe.add(child);
8164528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                    }
8174528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov                }
8184528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            }
8194528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        }
8204528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        final int disconnectedCount = infos.size() - seen.size();
8214528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        if (disconnectedCount > 0) {
8224528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes.");
8234528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov        }
8244528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov    }
8258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov}
826