AccessibilityInteractionClient.java revision c406be9036643ebe41bafcd94fe4aa861b4e4f4f
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;
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;
338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.List;
348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.concurrent.atomic.AtomicInteger;
358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/**
378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * This class is a singleton that performs accessibility interaction
388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * which is it queries remote view hierarchies about snapshots of their
398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * views as well requests from these hierarchies to perform certain
408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * actions on their views.
418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Rationale: The content retrieval APIs are synchronous from a client's
438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     perspective but internally they are asynchronous. The client thread
448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     calls into the system requesting an action and providing a callback
458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to receive the result after which it waits up to a timeout for that
468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result. The system enforces security and the delegates the request
478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to a given view hierarchy where a message is posted (from a binder
488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread) describing what to be performed by the main UI thread the
498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result of which it delivered via the mentioned callback. However,
508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the blocked client thread and the main UI thread of the target view
518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     hierarchy can be the same thread, for example an accessibility service
528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     and an activity run in the same process, thus they are executed on the
538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     same main thread. In such a case the retrieval will fail since the UI
548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread that has to process the message describing the work to be done
558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is blocked waiting for a result is has to compute! To avoid this scenario
568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     when making a call the client also passes its process and thread ids so
578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the accessed view hierarchy can detect if the client making the request
588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is running in its main UI thread. In such a case the view hierarchy,
598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     specifically the binder thread performing the IPC to it, does not post a
608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     message to be run on the UI thread but passes it to the singleton
618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     interaction client through which all interactions occur and the latter is
628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     responsible to execute the message before starting to wait for the
638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     asynchronous result delivered via the callback. In this case the expected
648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result is already received so no waiting is performed.
658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @hide
678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */
688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpublic final class AccessibilityInteractionClient
698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        extends IAccessibilityInteractionConnectionCallback.Stub {
708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
71d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public static final int NO_ID = -1;
72d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
73d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final String LOG_TAG = "AccessibilityInteractionClient";
74d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
75d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final boolean DEBUG = false;
76d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final Object sStaticLock = new Object();
808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
81021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    private static final LongSparseArray<AccessibilityInteractionClient> sClients =
82021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        new LongSparseArray<AccessibilityInteractionClient>();
838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Object mInstanceLock = new Object();
878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
88fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    private volatile int mInteractionId = -1;
898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean mPerformAccessibilityActionResult;
958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message mSameThreadMessage;
978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Rect mTempBounds = new Rect();
998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
10036bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    // The connection cache is shared between all interrogating threads.
10136bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
102d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        new SparseArray<IAccessibilityServiceConnection>();
103d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
10479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    // The connection cache is shared between all interrogating threads since
10579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    // at any given time there is only one window allowing querying.
10679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
10757c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov        new AccessibilityNodeInfoCache();
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,
161fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov                AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
162fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov                AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
163fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    }
164fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov
165fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    /**
1668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
1678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
168d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
16979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
1700d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
17179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
1720d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
1730d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
1740d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
1750d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
17657c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov     * @param prefetchFlags flags to guide prefetching.
1778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
1788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
179d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
18057c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) {
1818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
182d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
183d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
1840d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
1850d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                        accessibilityNodeId);
18679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                if (cachedInfo != null) {
18779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return cachedInfo;
18879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
189d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
190d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
191f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, interactionId, this,
1924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        prefetchFlags, Thread.currentThread().getId());
193d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
194d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
19579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
196d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
1970d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
19879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    if (infos != null && !infos.isEmpty()) {
19979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        return infos.get(0);
20079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    }
201d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
202d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
203d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
204d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
205d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
208d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
209d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
210d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByAccessibilityId", re);
211d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
21779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
21879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * the window whose id is specified and starts from the node whose accessibility
21979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified.
2208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
221d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
22279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2230d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
22479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2250d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2260d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2270d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
22879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to start from the root.
2296bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov     * @param viewId The id of the view.
2308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
2318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
23279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId,
23379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, int viewId) {
2348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
235d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
236d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
237d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
238d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale =
23979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId,
24079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                            accessibilityNodeId, viewId, interactionId, this,
24179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                            Thread.currentThread().getId());
242d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
243d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
244d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
245d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
2460d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
247d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return info;
248d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
249d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
250d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
251d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
252d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
255d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
256d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
257d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
258d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
2658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * insensitive containment. The search is performed in the window whose
26679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified and starts from the node whose accessibility id is
2678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * specified.
2688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
269d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
27079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2710d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
27279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2730d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2740d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2750d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
2760d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
2778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param text The searched text.
2788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return A list of found {@link AccessibilityNodeInfo}s.
2798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
28066922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,
28179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, String text) {
2828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
283d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
284d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
285d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
28679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfosByText(
28779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
288d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
289d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
290d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
291d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
292d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
2930d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
294d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return infos;
295d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
296d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
297d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
298d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
299d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
3008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
302d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
303d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
304d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfosByViewText", re);
305d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
3068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return Collections.emptyList();
3088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
3124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * specified focus type. The search is performed in the window whose id is specified
3134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * and starts from the node whose accessibility id is specified.
3144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
3154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
3164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
3184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
3194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
3214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
3234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param focusType The focus type.
3244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
3254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
3264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
3274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int focusType) {
3284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
3294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
3304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
3314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
3324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float windowScale = connection.findFocus(accessibilityWindowId,
3334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, focusType, interactionId, this,
3344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
3354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                // If the scale is zero the call has failed.
3364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (windowScale > 0) {
3374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
3384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
3394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
3404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
3414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
3434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
3444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
3454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
3484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
3494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re);
3504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
3524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
3534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
3544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
3554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
3564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
3574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The search is performed in the window whose id is specified and starts from the
3584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * node whose accessibility id is specified.
3594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
3604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
3614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
3634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
3644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
3664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
3684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param direction The direction in which to search for focusable.
3694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
3704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
3714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId,
3724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int direction) {
3734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
3744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
3754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
3764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
3774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float windowScale = connection.focusSearch(accessibilityWindowId,
3784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, direction, interactionId, this,
3794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
3804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                // If the scale is zero the call has failed.
3814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (windowScale > 0) {
3824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
3834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
3844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
3854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
3864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
3884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
3894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
3904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
3934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
3944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
3954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
3974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
3984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
3994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
4004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
4018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
4028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
403d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
40479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
4050d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
40679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
4070d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
4080d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
4090d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
4100d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
4118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param action The action to perform.
412aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov     * @param arguments Optional action arguments.
4138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
4148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
415d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
416aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov            long accessibilityNodeId, int action, Bundle arguments) {
4178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
418d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
419d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
420d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
421d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final boolean success = connection.performAccessibilityAction(
422aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, action, arguments,
423aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov                        interactionId, this, Thread.currentThread().getId());
424d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (success) {
42579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return getPerformAccessibilityActionResultAndClear(interactionId);
426d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
427d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
428d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
429d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
430d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
4318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
433d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
434d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
435d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
4368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return false;
4388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
44079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void clearCache() {
44179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.clear();
44279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
44379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
44479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void onAccessibilityEvent(AccessibilityEvent event) {
44579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
44679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
44779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
4488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
4508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}.
4538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
4558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
4578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
4588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
4598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
4608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
4658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
4678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
4708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mFindAccessibilityNodeInfoResult = info;
4718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
4728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
4748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
4798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}s.
4828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
4848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
4874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            List<AccessibilityNodeInfo> result = null;
4884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (success) {
4894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = mFindAccessibilityNodeInfosResult;
4904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
4914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = Collections.emptyList();
4924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
4938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
4948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
4958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
5028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
5038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
5054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (infos != null) {
5064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // If the call is not an IPC, i.e. it is made from the same process, we need to
5074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // instantiate new result list to avoid passing internal instances to clients.
5084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
5094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (!isIpcCall) {
5104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult =
5114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            new ArrayList<AccessibilityNodeInfo>(infos);
5124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    } else {
5134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult = infos;
5144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
51579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                } else {
5164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mFindAccessibilityNodeInfosResult = Collections.emptyList();
51779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
5188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the result of a request to perform an accessibility action.
5268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
5298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
53079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    private boolean getPerformAccessibilityActionResultAndClear(int interactionId) {
5318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
5338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean result = success ? mPerformAccessibilityActionResult : false;
5348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
5358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
5368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
5438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
5458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mPerformAccessibilityActionResult = succeeded;
5468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Clears the result state.
5548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void clearResultLocked() {
5568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mInteractionId = -1;
5578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfoResult = null;
5588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfosResult = null;
5598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mPerformAccessibilityActionResult = false;
5608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Waits up to a given bound for a result of a request and returns it.
5648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the result was received.
5678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean waitForResultTimedLocked(int interactionId) {
5698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
5708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        final long startTimeMillis = SystemClock.uptimeMillis();
5718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        while (true) {
5728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            try {
5736bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                Message sameProcessMessage = getSameProcessMessageAndClear();
5746bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                if (sameProcessMessage != null) {
5756bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                    sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
5766bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                }
5776bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov
5788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId == interactionId) {
5798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return true;
5808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId > interactionId) {
5828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
5838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
5858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
5868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (waitTimeMillis <= 0) {
5878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
5888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInstanceLock.wait(waitTimeMillis);
5908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            } catch (InterruptedException ie) {
5918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                /* ignore */
5928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Applies compatibility scale to the info bounds if it is not equal to one.
5988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info whose bounds to scale.
6008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param scale The scale to apply.
6018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
6038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (scale == 1.0f) {
6048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return;
6058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        Rect bounds = mTempBounds;
6078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInParent(bounds);
6088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
6098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInParent(bounds);
6108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInScreen(bounds);
6128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
6138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInScreen(bounds);
6148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
6188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info.
620d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
6228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6230d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
624d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            float windowScale) {
6258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (info != null) {
6268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            applyCompatibilityScaleIfNeeded(info, windowScale);
627d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            info.setConnectionId(connectionId);
6288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            info.setSealed(true);
629c406be9036643ebe41bafcd94fe4aa861b4e4f4fSvetoslav Ganov            sAccessibilityNodeInfoCache.add(info);
6308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
6358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param infos The {@link AccessibilityNodeInfo}s.
637d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
6398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6400d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
641d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int connectionId, float windowScale) {
6428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (infos != null) {
6438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final int infosCount = infos.size();
6448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            for (int i = 0; i < infosCount; i++) {
6458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                AccessibilityNodeInfo info = infos.get(i);
6460d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
6478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
6488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the message stored if the interacted and interacting
6538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * threads are the same.
6548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The message.
6568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message getSameProcessMessageAndClear() {
6588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
6598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            Message result = mSameThreadMessage;
6608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = null;
6618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
6628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
664d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
665d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
666d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Gets a cached accessibility service connection.
667d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
668d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
669d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @return The cached connection if such.
670d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
671d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public IAccessibilityServiceConnection getConnection(int connectionId) {
67236bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
67336bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            return sConnectionCache.get(connectionId);
674d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
675d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
676d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
677d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
678d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Adds a cached accessibility service connection.
679d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
680d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
681d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connection The connection.
682d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
683d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
68436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
68536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.put(connectionId, connection);
686d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
687d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
688d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
689d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
690d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Removes a cached accessibility service connection.
691d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
692d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
693d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
694d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void removeConnection(int connectionId) {
69536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
69636bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.remove(connectionId);
697d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
698d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
6998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov}
700