AccessibilityInteractionClient.java revision 4213804541a8b05cd0587b138a2fd9a3b7fd9350
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
878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private 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    /**
1538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
1548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
155d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
15679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
1570d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
15879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
1590d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
1600d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
1610d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
1620d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
16357c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov     * @param prefetchFlags flags to guide prefetching.
1648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
1658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
166d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
16757c7fd5a43237afc5e8ef31a076e862c0c16c328Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) {
1688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
169d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
170d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
1710d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
1720d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                        accessibilityNodeId);
17379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                if (cachedInfo != null) {
17479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return cachedInfo;
17579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
176d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
177d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
178f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, interactionId, this,
1794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        prefetchFlags, Thread.currentThread().getId());
180d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
181d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
18279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
183d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
1840d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
18579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    if (infos != null && !infos.isEmpty()) {
18679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        return infos.get(0);
18779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    }
188d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
189d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
190d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
191d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
192d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
1938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
1948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
195d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
196d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
197d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByAccessibilityId", re);
198d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
1998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
20479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
20579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * the window whose id is specified and starts from the node whose accessibility
20679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified.
2078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
208d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
20979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2100d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
21179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2120d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2130d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2140d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
21579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to start from the root.
2166bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov     * @param viewId The id of the view.
2178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
2188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
21979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId,
22079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, int viewId) {
2218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
222d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
223d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
224d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
225d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale =
22679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId,
22779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                            accessibilityNodeId, viewId, interactionId, this,
22879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                            Thread.currentThread().getId());
229d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
230d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
231d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
232d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
2330d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
234d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return info;
235d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
236d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
237d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
238d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
239d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
242d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
243d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
244d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
245d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
2528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * insensitive containment. The search is performed in the window whose
25379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * id is specified and starts from the node whose accessibility id is
2548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * specified.
2558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
256d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
25779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
2580d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
25979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
2600d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
2610d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
2620d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
2630d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
2648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param text The searched text.
2658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return A list of found {@link AccessibilityNodeInfo}s.
2668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
26766922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,
26879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov            int accessibilityWindowId, long accessibilityNodeId, String text) {
2698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
270d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
271d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
272d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
27379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfosByText(
27479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
275d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
276d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
277d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
278d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
279d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
2800d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
281d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return infos;
282d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
283d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
284d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
285d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
286d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
289d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
290d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
291d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfosByViewText", re);
292d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return Collections.emptyList();
2958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
2994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * specified focus type. The search is performed in the window whose id is specified
3004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * and starts from the node whose accessibility id is specified.
3014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
3024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
3034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
3054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
3064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
3084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
3104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param focusType The focus type.
3114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
3124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
3134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
3144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int focusType) {
3154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
3164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
3174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
3184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
3194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float windowScale = connection.findFocus(accessibilityWindowId,
3204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, focusType, interactionId, this,
3214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
3224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                // If the scale is zero the call has failed.
3234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (windowScale > 0) {
3244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
3254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
3264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
3274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
3284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
3304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
3314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
3324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
3354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
3364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote findAccessibilityFocus", re);
3374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
3394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
3404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
3414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
3424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
3434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
3444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The search is performed in the window whose id is specified and starts from the
3454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * node whose accessibility id is specified.
3464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *
3474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
3484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
3504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to query the currently active window.
3514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     where to start the search. Use
3534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     *     to start from the root.
3554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @param direction The direction in which to search for focusable.
3564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * @return The accessibility focused {@link AccessibilityNodeInfo}.
3574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
3584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public AccessibilityNodeInfo focusSearch(int connectionId, int accessibilityWindowId,
3594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            long accessibilityNodeId, int direction) {
3604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        try {
3614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
3624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (connection != null) {
3634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
3644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float windowScale = connection.focusSearch(accessibilityWindowId,
3654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        accessibilityNodeId, direction, interactionId, this,
3664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        Thread.currentThread().getId());
3674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                // If the scale is zero the call has failed.
3684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (windowScale > 0) {
3694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
3704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            interactionId);
3714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
3724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    return info;
3734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
3754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (DEBUG) {
3764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
3774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
3784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        } catch (RemoteException re) {
3804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
3814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
3824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
3834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
3844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return null;
3854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
3864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
3874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
3888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
3898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
390d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
39179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     * @param accessibilityWindowId A unique window id. Use
3920d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
39379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov     *     to query the currently active window.
3940d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     * @param accessibilityNodeId A unique view id or virtual descendant id from
3950d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     where to start the search. Use
3960d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
3970d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov     *     to start from the root.
3988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param action The action to perform.
3998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
4008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
401d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
40266922db828eab153c15bf3ca0b007313d9376e5eSvetoslav Ganov            long accessibilityNodeId, int action) {
4038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
404d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
405d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
406d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
407d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final boolean success = connection.performAccessibilityAction(
408f3b4f3163b5b4c0a54a2643f07c97c47b14a1eb7Svetoslav Ganov                        accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
409d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
410d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (success) {
41179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                    return getPerformAccessibilityActionResultAndClear(interactionId);
412d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
413d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
414d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
415d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
416d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
4178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
419d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
420d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
421d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
4228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return false;
4248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
42679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void clearCache() {
42779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.clear();
42879311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
42979311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
43079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void removeCachedNode(long accessibilityNodeId) {
43179311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.remove(accessibilityNodeId);
43279311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
43379311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
43479311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    public void onAccessibilityEvent(AccessibilityEvent event) {
43579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov        sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
43679311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    }
43779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov
4388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
4408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}.
4438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
4458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
4478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
4488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
4498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
4508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
4558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
4578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
4608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mFindAccessibilityNodeInfoResult = info;
4618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
4628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
4648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
4698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}s.
4728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
4748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
4774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            List<AccessibilityNodeInfo> result = null;
4784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (success) {
4794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = mFindAccessibilityNodeInfosResult;
4804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } else {
4814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                result = Collections.emptyList();
4824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
4838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
4848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
4858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
4908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
4928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
4938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
4948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
4954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (infos != null) {
4964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // If the call is not an IPC, i.e. it is made from the same process, we need to
4974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    // instantiate new result list to avoid passing internal instances to clients.
4984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
4994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (!isIpcCall) {
5004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult =
5014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            new ArrayList<AccessibilityNodeInfo>(infos);
5024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    } else {
5034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mFindAccessibilityNodeInfosResult = infos;
5044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
50579311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                } else {
5064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mFindAccessibilityNodeInfosResult = Collections.emptyList();
50779311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov                }
5088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the result of a request to perform an accessibility action.
5168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
5198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
52079311c4af8b54d3cd47ab37a120c648bfc990511Svetoslav Ganov    private boolean getPerformAccessibilityActionResultAndClear(int interactionId) {
5218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
5238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean result = success ? mPerformAccessibilityActionResult : false;
5248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
5258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
5268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
5318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
5338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
5358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mPerformAccessibilityActionResult = succeeded;
5368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
5378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
5398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Clears the result state.
5448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void clearResultLocked() {
5468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mInteractionId = -1;
5478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfoResult = null;
5488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfosResult = null;
5498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mPerformAccessibilityActionResult = false;
5508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Waits up to a given bound for a result of a request and returns it.
5548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
5568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the result was received.
5578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean waitForResultTimedLocked(int interactionId) {
5598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
5608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        final long startTimeMillis = SystemClock.uptimeMillis();
5618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        while (true) {
5628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            try {
5636bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                Message sameProcessMessage = getSameProcessMessageAndClear();
5646bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                if (sameProcessMessage != null) {
5656bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                    sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
5666bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                }
5676bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov
5688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId == interactionId) {
5698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return true;
5708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId > interactionId) {
5728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
5738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
5758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
5768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (waitTimeMillis <= 0) {
5778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
5788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
5798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInstanceLock.wait(waitTimeMillis);
5808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            } catch (InterruptedException ie) {
5818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                /* ignore */
5828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
5838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
5858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Applies compatibility scale to the info bounds if it is not equal to one.
5888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info whose bounds to scale.
5908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param scale The scale to apply.
5918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
5938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (scale == 1.0f) {
5948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return;
5958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        Rect bounds = mTempBounds;
5978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInParent(bounds);
5988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
5998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInParent(bounds);
6008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInScreen(bounds);
6028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
6038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInScreen(bounds);
6048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
6088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info.
610d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
6128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6130d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
614d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            float windowScale) {
6158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (info != null) {
6168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            applyCompatibilityScaleIfNeeded(info, windowScale);
617d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            info.setConnectionId(connectionId);
6188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            info.setSealed(true);
6190d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov            sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
6208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
6258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param infos The {@link AccessibilityNodeInfo}s.
627d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
6288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
6298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6300d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov    private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
631d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int connectionId, float windowScale) {
6328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (infos != null) {
6338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final int infosCount = infos.size();
6348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            for (int i = 0; i < infosCount; i++) {
6358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                AccessibilityNodeInfo info = infos.get(i);
6360d04e245534cf777dfaf16dce3c51553837c14ffSvetoslav Ganov                finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
6378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
6388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
6408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
6418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
6428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the message stored if the interacted and interacting
6438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * threads are the same.
6448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
6458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The message.
6468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
6478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message getSameProcessMessageAndClear() {
6488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
6498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            Message result = mSameThreadMessage;
6508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = null;
6518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
6528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
6538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
654d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
655d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
656d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Gets a cached accessibility service connection.
657d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
658d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
659d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @return The cached connection if such.
660d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
661d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public IAccessibilityServiceConnection getConnection(int connectionId) {
66236bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
66336bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            return sConnectionCache.get(connectionId);
664d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
665d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
666d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
667d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
668d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Adds a cached accessibility service connection.
669d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
670d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
671d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connection The connection.
672d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
673d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
67436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
67536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.put(connectionId, connection);
676d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
677d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
678d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
679d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
680d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Removes a cached accessibility service connection.
681d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
682d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
683d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
684d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void removeConnection(int connectionId) {
68536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
68636bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.remove(connectionId);
687d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
688d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
6898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov}
690