AccessibilityInteractionClient.java revision fefd20e927b7252d63acb7bb1852c5188e3c1b2e
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;
228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.Message;
234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.os.Process;
248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.RemoteException;
258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.SystemClock;
26d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.Log;
278b5a814f6a36045b06bee36f44703503c03714d4Svetoslav Ganovimport android.util.LongSparseArray;
28d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.SparseArray;
298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganovimport java.util.ArrayList;
318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.Collections;
328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.List;
338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.concurrent.atomic.AtomicInteger;
348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/**
368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * This class is a singleton that performs accessibility interaction
378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * which is it queries remote view hierarchies about snapshots of their
388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * views as well requests from these hierarchies to perform certain
398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * actions on their views.
408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Rationale: The content retrieval APIs are synchronous from a client's
428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     perspective but internally they are asynchronous. The client thread
438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     calls into the system requesting an action and providing a callback
448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to receive the result after which it waits up to a timeout for that
458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result. The system enforces security and the delegates the request
468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to a given view hierarchy where a message is posted (from a binder
478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread) describing what to be performed by the main UI thread the
488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result of which it delivered via the mentioned callback. However,
498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the blocked client thread and the main UI thread of the target view
508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     hierarchy can be the same thread, for example an accessibility service
518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     and an activity run in the same process, thus they are executed on the
528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     same main thread. In such a case the retrieval will fail since the UI
538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread that has to process the message describing the work to be done
548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is blocked waiting for a result is has to compute! To avoid this scenario
558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     when making a call the client also passes its process and thread ids so
568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the accessed view hierarchy can detect if the client making the request
578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is running in its main UI thread. In such a case the view hierarchy,
588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     specifically the binder thread performing the IPC to it, does not post a
598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     message to be run on the UI thread but passes it to the singleton
608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     interaction client through which all interactions occur and the latter is
618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     responsible to execute the message before starting to wait for the
628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     asynchronous result delivered via the callback. In this case the expected
638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result is already received so no waiting is performed.
648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @hide
668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */
678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpublic final class AccessibilityInteractionClient
688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        extends IAccessibilityInteractionConnectionCallback.Stub {
698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
70d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public static final int NO_ID = -1;
71d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
72d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final String LOG_TAG = "AccessibilityInteractionClient";
73d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
74d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final boolean DEBUG = false;
75d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final Object sStaticLock = new Object();
798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
80021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    private static final LongSparseArray<AccessibilityInteractionClient> sClients =
81021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        new LongSparseArray<AccessibilityInteractionClient>();
828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Object mInstanceLock = new Object();
868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
87fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    private volatile int mInteractionId = -1;
888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean mPerformAccessibilityActionResult;
948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message mSameThreadMessage;
968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Rect mTempBounds = new Rect();
988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
9936bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    // The connection cache is shared between all interrogating threads.
10036bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
101d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        new SparseArray<IAccessibilityServiceConnection>();
102d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
10379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    // The connection cache is shared between all interrogating threads since
10479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    // at any given time there is only one window allowing querying.
10579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
10657c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov        new AccessibilityNodeInfoCache();
10779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
1088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
109021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * @return The client for the current thread.
1108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
1118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public static AccessibilityInteractionClient getInstance() {
112021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        final long threadId = Thread.currentThread().getId();
113021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        return getInstanceForThread(threadId);
114021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    }
115021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov
116021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    /**
117021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * <strong>Note:</strong> We keep one instance per interrogating thread since
118021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * the instance contains state which can lead to undesired thread interleavings.
119021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * We do not have a thread local variable since other threads should be able to
120021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * look up the correct client knowing a thread id. See ViewRootImpl for details.
121021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     *
122021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     * @return The client for a given <code>threadId</code>.
123021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov     */
124021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    public static AccessibilityInteractionClient getInstanceForThread(long threadId) {
1258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (sStaticLock) {
126021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov            AccessibilityInteractionClient client = sClients.get(threadId);
127021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov            if (client == null) {
128021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov                client = new AccessibilityInteractionClient();
129021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov                sClients.put(threadId, client);
1308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
131021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov            return client;
1328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
135021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    private AccessibilityInteractionClient() {
136021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov        /* reducing constructor visibility */
137021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov    }
138021078554b902179442a345a9d080a165c3b5139Svetoslav Ganov
1398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
1408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Sets the message to be processed if the interacted view hierarchy
1418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * and the interacting client are running in the same thread.
1428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
1438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param message The message.
1448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
1458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setSameThreadMessage(Message message) {
1468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
1478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = message;
1486bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov            mInstanceLock.notifyAll();
1498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
153fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     * Gets the root {@link AccessibilityNodeInfo} in the currently active window.
154fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     *
155fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
156fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     * @return The root {@link AccessibilityNodeInfo} if found, null otherwise.
157fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov     */
158fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) {
159fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov        return findAccessibilityNodeInfoByAccessibilityId(connectionId,
160fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov                AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
161fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov                AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
162fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    }
163fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov
164fefd20e927b7252d63acb7bb1852c5188e3c1b2eSvetoslav Ganov    /**
1658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
1668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
167d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
16879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
1690d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
17079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
1710d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
1720d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
1730d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
1740d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
17557c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov     * @param prefetchFlags flags to guide prefetching.
1768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
1778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
178d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
17957c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) {
1808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
181d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
182d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
1830d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
1840d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                        accessibilityNodeId);
18579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                if (cachedInfo != null) {
18679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return cachedInfo;
18779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
188d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
189d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
190f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, interactionId, this,
1914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        prefetchFlags, Thread.currentThread().getId());
192d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
193d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
19479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
195d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
1960d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
19779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    if (infos != null && !infos.isEmpty()) {
19879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        return infos.get(0);
19979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    }
200d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
201d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
202d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
203d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
204d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
207d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
208d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
209d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByAccessibilityId", re);
210d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
21679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
21779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * the window whose id is specified and starts from the node whose accessibility
21879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified.
2198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
220d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
22179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2220d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
22379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2240d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2250d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2260d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
22779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to start from the root.
2286bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov     * @param viewId The id of the view.
2298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
2308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
23179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId,
23279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, int viewId) {
2338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
234d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
235d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
236d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
237d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale =
23879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId,
23979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                            accessibilityNodeId, viewId, interactionId, this,
24079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                            Thread.currentThread().getId());
241d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
242d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
243d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
244d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
2450d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
246d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return info;
247d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
248d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
249d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
250d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
251d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
254d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
255d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
256d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
257d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
2648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * insensitive containment. The search is performed in the window whose
26579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified and starts from the node whose accessibility id is
2668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * specified.
2678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
268d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
26979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2700d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
27179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2720d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2730d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2740d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
2750d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
2768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param text The searched text.
2778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return A list of found {@link AccessibilityNodeInfo}s.
2788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
27966922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,
28079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, String text) {
2818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
282d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
283d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
284d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
28579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfosByText(
28679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
287d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
288d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
289d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
290d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
291d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
2920d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
293d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return infos;
294d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
295d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
296d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
297d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
298d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
301d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
302d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
303d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfosByViewText", re);
304d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
3058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return Collections.emptyList();
3078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
3114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * specified focus type. The search is performed in the window whose id is specified
3124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * and starts from the node whose accessibility id is specified.
3134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
3144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
3154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
3174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
3184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
3204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
3224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param focusType The focus type.
3234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
3244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
3254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
3264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int focusType) {
3274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
3284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
3294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
3304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
3314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float windowScale = connection.findFocus(accessibilityWindowId,
3324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, focusType, interactionId, this,
3334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
3344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                // If the scale is zero the call has failed.
3354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (windowScale > 0) {
3364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
3374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
3384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
3394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
3404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
3424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
3434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
3444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
3474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
3484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re);
3494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
3514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
3524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
3534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
3544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
3554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
3564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The search is performed in the window whose id is specified and starts from the
3574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * node whose accessibility id is specified.
3584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
3594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
3604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
3624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
3634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
3654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
3674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param direction The direction in which to search for focusable.
3684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
3694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
3704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId,
3714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int direction) {
3724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
3734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
3744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
3754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
3764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float windowScale = connection.focusSearch(accessibilityWindowId,
3774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, direction, interactionId, this,
3784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
3794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                // If the scale is zero the call has failed.
3804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (windowScale > 0) {
3814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
3824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
3834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
3844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
3854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
3874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
3884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
3894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
3924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
3934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
3944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
3964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
3974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
3984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
3994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
4008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
4018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
402d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
40379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
4040d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
40579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
4060d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
4070d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
4080d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
4090d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
4108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param action The action to perform.
4118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
4128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
413d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
41466922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov            long accessibilityNodeId, int action) {
4158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
416d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
417d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
418d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
419d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final boolean success = connection.performAccessibilityAction(
420f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
421d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
422d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (success) {
42379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return getPerformAccessibilityActionResultAndClear(interactionId);
424d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
425d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
426d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
427d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
428d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
4298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
431d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
432d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
433d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
4348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return false;
4368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
43879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void clearCache() {
43979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.clear();
44079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
44179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
44279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void removeCachedNode(long accessibilityNodeId) {
44379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.remove(accessibilityNodeId);
44479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
44579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
44679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void onAccessibilityEvent(AccessibilityEvent event) {
44779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
44879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
44979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
4508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
4528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}.
4558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
4578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
4598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
4608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
4618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
4628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
4678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
4698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
4728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mFindAccessibilityNodeInfoResult = info;
4738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
4748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
4768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
4818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}s.
4848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
4868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
4894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            List<AccessibilityNodeInfo> result = null;
4904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (success) {
4914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = mFindAccessibilityNodeInfosResult;
4924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
4934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = Collections.emptyList();
4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
4958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
4968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
4978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
5048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
5058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
5074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (infos != null) {
5084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // If the call is not an IPC, i.e. it is made from the same process, we need to
5094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // instantiate new result list to avoid passing internal instances to clients.
5104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
5114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (!isIpcCall) {
5124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult =
5134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            new ArrayList<AccessibilityNodeInfo>(infos);
5144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    } else {
5154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult = infos;
5164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
51779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                } else {
5184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mFindAccessibilityNodeInfosResult = Collections.emptyList();
51979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
5208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the result of a request to perform an accessibility action.
5288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
5318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
53279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    private boolean getPerformAccessibilityActionResultAndClear(int interactionId) {
5338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
5358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean result = success ? mPerformAccessibilityActionResult : false;
5368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
5378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
5388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
5458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
5478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mPerformAccessibilityActionResult = succeeded;
5488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Clears the result state.
5568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void clearResultLocked() {
5588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mInteractionId = -1;
5598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfoResult = null;
5608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfosResult = null;
5618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mPerformAccessibilityActionResult = false;
5628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Waits up to a given bound for a result of a request and returns it.
5668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the result was received.
5698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean waitForResultTimedLocked(int interactionId) {
5718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
5728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        final long startTimeMillis = SystemClock.uptimeMillis();
5738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        while (true) {
5748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            try {
5756bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                Message sameProcessMessage = getSameProcessMessageAndClear();
5766bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                if (sameProcessMessage != null) {
5776bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                    sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
5786bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                }
5796bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov
5808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId == interactionId) {
5818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return true;
5828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId > interactionId) {
5848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
5858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
5878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
5888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (waitTimeMillis <= 0) {
5898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
5908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInstanceLock.wait(waitTimeMillis);
5928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            } catch (InterruptedException ie) {
5938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                /* ignore */
5948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Applies compatibility scale to the info bounds if it is not equal to one.
6008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info whose bounds to scale.
6028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param scale The scale to apply.
6038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
6058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (scale == 1.0f) {
6068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return;
6078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        Rect bounds = mTempBounds;
6098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInParent(bounds);
6108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
6118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInParent(bounds);
6128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInScreen(bounds);
6148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
6158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInScreen(bounds);
6168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
6208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info.
622d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
6248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6250d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
626d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            float windowScale) {
6278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (info != null) {
6288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            applyCompatibilityScaleIfNeeded(info, windowScale);
629d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            info.setConnectionId(connectionId);
6308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            info.setSealed(true);
6310d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov            sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
6328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
6378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param infos The {@link AccessibilityNodeInfo}s.
639d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
6418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6420d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
643d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int connectionId, float windowScale) {
6448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (infos != null) {
6458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final int infosCount = infos.size();
6468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            for (int i = 0; i < infosCount; i++) {
6478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                AccessibilityNodeInfo info = infos.get(i);
6480d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
6498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
6508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the message stored if the interacted and interacting
6558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * threads are the same.
6568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The message.
6588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message getSameProcessMessageAndClear() {
6608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
6618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            Message result = mSameThreadMessage;
6628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = null;
6638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
6648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
666d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
667d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
668d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Gets a cached accessibility service connection.
669d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
670d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
671d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @return The cached connection if such.
672d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
673d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public IAccessibilityServiceConnection getConnection(int connectionId) {
67436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
67536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            return sConnectionCache.get(connectionId);
676d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
677d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
678d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
679d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
680d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Adds a cached accessibility service connection.
681d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
682d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
683d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connection The connection.
684d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
685d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
68636bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
68736bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.put(connectionId, connection);
688d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
689d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
690d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
691d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
692d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Removes a cached accessibility service connection.
693d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
694d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
695d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
696d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void removeConnection(int connectionId) {
69736bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
69836bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.remove(connectionId);
699d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
700d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
7018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov}
702