AccessibilityInteractionClient.java revision 36bcdb535e14a8a2e2c8643fb577569f7a2b6aed
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
198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.accessibilityservice.IAccessibilityServiceConnection;
208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.graphics.Rect;
218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.Message;
228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.RemoteException;
238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport android.os.SystemClock;
24d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.Log;
25d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganovimport android.util.SparseArray;
268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.Collections;
288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.List;
298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovimport java.util.concurrent.atomic.AtomicInteger;
308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov/**
328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * This class is a singleton that performs accessibility interaction
338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * which is it queries remote view hierarchies about snapshots of their
348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * views as well requests from these hierarchies to perform certain
358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * actions on their views.
368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * Rationale: The content retrieval APIs are synchronous from a client's
388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     perspective but internally they are asynchronous. The client thread
398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     calls into the system requesting an action and providing a callback
408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to receive the result after which it waits up to a timeout for that
418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result. The system enforces security and the delegates the request
428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     to a given view hierarchy where a message is posted (from a binder
438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread) describing what to be performed by the main UI thread the
448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result of which it delivered via the mentioned callback. However,
458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the blocked client thread and the main UI thread of the target view
468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     hierarchy can be the same thread, for example an accessibility service
478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     and an activity run in the same process, thus they are executed on the
488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     same main thread. In such a case the retrieval will fail since the UI
498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     thread that has to process the message describing the work to be done
508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is blocked waiting for a result is has to compute! To avoid this scenario
518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     when making a call the client also passes its process and thread ids so
528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     the accessed view hierarchy can detect if the client making the request
538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     is running in its main UI thread. In such a case the view hierarchy,
548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     specifically the binder thread performing the IPC to it, does not post a
558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     message to be run on the UI thread but passes it to the singleton
568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     interaction client through which all interactions occur and the latter is
578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     responsible to execute the message before starting to wait for the
588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     asynchronous result delivered via the callback. In this case the expected
598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *     result is already received so no waiting is performed.
608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov *
618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov * @hide
628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov */
638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganovpublic final class AccessibilityInteractionClient
648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        extends IAccessibilityInteractionConnectionCallback.Stub {
658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
66d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public static final int NO_ID = -1;
67d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
68d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final String LOG_TAG = "AccessibilityInteractionClient";
69d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
70d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private static final boolean DEBUG = false;
71d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static final Object sStaticLock = new Object();
758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private static AccessibilityInteractionClient sInstance;
778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Object mInstanceLock = new Object();
818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private int mInteractionId = -1;
838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean mPerformAccessibilityActionResult;
898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message mSameThreadMessage;
918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private final Rect mTempBounds = new Rect();
938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
9436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    // The connection cache is shared between all interrogating threads.
9536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov    private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
96d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        new SparseArray<IAccessibilityServiceConnection>();
97d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The singleton of this class.
1008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
1018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public static AccessibilityInteractionClient getInstance() {
1028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (sStaticLock) {
1038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (sInstance == null) {
1048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                sInstance = new AccessibilityInteractionClient();
1058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
1068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return sInstance;
1078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
1118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Sets the message to be processed if the interacted view hierarchy
1128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * and the interacting client are running in the same thread.
1138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
1148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param message The message.
1158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
1168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setSameThreadMessage(Message message) {
1178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
1188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = message;
1196bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov            mInstanceLock.notifyAll();
1208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
1248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
1258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
126d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
1278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param accessibilityWindowId A unique window id.
1288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param accessibilityViewId A unique View accessibility id.
1298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
1308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
131d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
132d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int accessibilityWindowId, int accessibilityViewId) {
1338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
134d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
135d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
136d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
137d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
138d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        accessibilityWindowId, accessibilityViewId, interactionId, this,
139d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
140d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
141d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
142d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
143d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
144d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
145d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return info;
146d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
147d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
148d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
149d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
150d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
1518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
1528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
153d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
154d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
155d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByAccessibilityId", re);
156d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
1578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
1598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
1628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
1638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * in the currently active window and starts from the root View in the window.
1648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
165d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
1666bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov     * @param viewId The id of the view.
1678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
1688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
169d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int connectionId,
170d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int viewId) {
1718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
172d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
173d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
174d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
175d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale =
176d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    connection.findAccessibilityNodeInfoByViewIdInActiveWindow(viewId,
177d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId, this, Thread.currentThread().getId());
178d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
179d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
180d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
181d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
182d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
183d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return info;
184d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
185d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
186d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
187d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
188d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
1898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
1908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
191d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
192d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
193d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
194d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
1958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
1968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
1978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
1988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
1998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
2018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * insensitive containment. The search is performed in the currently
2028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * active window and starts from the root View in the window.
2038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
204d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
2058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param text The searched text.
2068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return A list of found {@link AccessibilityNodeInfo}s.
2078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
2088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
209d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int connectionId, String text) {
2108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
211d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
212d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
213d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
214d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale =
215d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    connection.findAccessibilityNodeInfosByViewTextInActiveWindow(text,
216d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId, this, Thread.currentThread().getId());
217d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
218d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
219d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
220d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
221d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    finalizeAccessibilityNodeInfos(infos, connectionId, windowScale);
222d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return infos;
223d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
224d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
225d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
226d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
227d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
230d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
231d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
232d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfosByViewTextInActiveWindow", re);
233d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return null;
2368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
2408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * insensitive containment. The search is performed in the window whose
2418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * id is specified and starts from the View whose accessibility id is
2428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * specified.
2438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
244d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
2458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param text The searched text.
2468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param accessibilityWindowId A unique window id.
2478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param accessibilityViewId A unique View accessibility id from where to start the search.
2488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *        Use {@link android.view.View#NO_ID} to start from the root.
2498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return A list of found {@link AccessibilityNodeInfo}s.
2508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
251d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(int connectionId,
252d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            String text, int accessibilityWindowId, int accessibilityViewId) {
2538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
254d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
255d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
256d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
257d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final float windowScale = connection.findAccessibilityNodeInfosByViewText(text,
258d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        accessibilityWindowId, accessibilityViewId, interactionId, this,
259d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
260d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                // If the scale is zero the call has failed.
261d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (windowScale > 0) {
262d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
263d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                            interactionId);
264d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    finalizeAccessibilityNodeInfos(infos, connectionId, windowScale);
265d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return infos;
266d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
267d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
268d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
269d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
270d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
2718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
2728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
273d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
274d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote"
275d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        + " findAccessibilityNodeInfosByViewText", re);
276d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
2778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
2788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return Collections.emptyList();
2798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
2808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
2818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
2828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
2838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
284d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of a connection for interacting with the system.
2858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param accessibilityWindowId The id of the window.
2868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param accessibilityViewId A unique View accessibility id.
2878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param action The action to perform.
2888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
2898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
290d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
291d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int accessibilityViewId, int action) {
2928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        try {
293d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            IAccessibilityServiceConnection connection = getConnection(connectionId);
294d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (connection != null) {
295d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final int interactionId = mInteractionIdCounter.getAndIncrement();
296d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                final boolean success = connection.performAccessibilityAction(
297d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        accessibilityWindowId, accessibilityViewId, action, interactionId, this,
298d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                        Thread.currentThread().getId());
299d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (success) {
300d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    return getPerformAccessibilityActionResult(interactionId);
301d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
302d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            } else {
303d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                if (DEBUG) {
304d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
305d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                }
3068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        } catch (RemoteException re) {
308d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            if (DEBUG) {
309d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
310d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            }
3118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        return false;
3138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
3178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
3188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
3198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}.
3208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
3218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
3228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
3238bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
3248bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
3258bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
3268bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
3278bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
3328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
3338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
3348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
3358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
3368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
3378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mFindAccessibilityNodeInfoResult = info;
3388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
3398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
3418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
3468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
3478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
3488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The result {@link AccessibilityNodeInfo}s.
3498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
3508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
3518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
3528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
3538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
3548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            List<AccessibilityNodeInfo> result = success ? mFindAccessibilityNodeInfosResult : null;
3558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
3568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
3578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
3628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
3638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
3648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                int interactionId) {
3658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
3668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
3678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mFindAccessibilityNodeInfosResult = infos;
3688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
3698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3708bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
3718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3738bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3748bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the result of a request to perform an accessibility action.
3768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
3778bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
3788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the action was performed.
3798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
3808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean getPerformAccessibilityActionResult(int interactionId) {
3818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
3828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean success = waitForResultTimedLocked(interactionId);
3838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final boolean result = success ? mPerformAccessibilityActionResult : false;
3848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            clearResultLocked();
3858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
3868bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
3878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
3888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
3898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
3908bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * {@inheritDoc}
3918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
3928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
3938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
3948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            if (interactionId > mInteractionId) {
3958bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mPerformAccessibilityActionResult = succeeded;
3968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInteractionId = interactionId;
3978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
3988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mInstanceLock.notifyAll();
3998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Clears the result state.
4048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void clearResultLocked() {
4068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mInteractionId = -1;
4078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfoResult = null;
4088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mFindAccessibilityNodeInfosResult = null;
4098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        mPerformAccessibilityActionResult = false;
4108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4138bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Waits up to a given bound for a result of a request and returns it.
4148bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4158bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param interactionId The interaction id to match the result with the request.
4168bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return Whether the result was received.
4178bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4188bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private boolean waitForResultTimedLocked(int interactionId) {
4198bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
4208bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        final long startTimeMillis = SystemClock.uptimeMillis();
4218bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        while (true) {
4228bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            try {
4236bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                Message sameProcessMessage = getSameProcessMessageAndClear();
4246bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                if (sameProcessMessage != null) {
4256bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                    sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
4266bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov                }
4276bc5e530016928027c7b390a8368ecdd5bff072fSvetoslav Ganov
4288bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId == interactionId) {
4298bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return true;
4308bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
4318bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (mInteractionId > interactionId) {
4328bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
4338bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
4348bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
4358bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
4368bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                if (waitTimeMillis <= 0) {
4378bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                    return false;
4388bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                }
4398bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                mInstanceLock.wait(waitTimeMillis);
4408bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            } catch (InterruptedException ie) {
4418bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                /* ignore */
4428bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4438bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4448bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4458bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4468bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4478bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Applies compatibility scale to the info bounds if it is not equal to one.
4488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4498bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info whose bounds to scale.
4508bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param scale The scale to apply.
4518bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4528bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
4538bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (scale == 1.0f) {
4548bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return;
4558bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4568bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        Rect bounds = mTempBounds;
4578bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInParent(bounds);
4588bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
4598bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInParent(bounds);
4608bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4618bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.getBoundsInScreen(bounds);
4628bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        bounds.scale(scale);
4638bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        info.setBoundsInScreen(bounds);
4648bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4658bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4668bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4678bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
4688bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4698bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param info The info.
470d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
4718bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
4728bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
473d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
474d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            float windowScale) {
4758bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (info != null) {
4768bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            applyCompatibilityScaleIfNeeded(info, windowScale);
477d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            info.setConnectionId(connectionId);
4788bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            info.setSealed(true);
4798bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4808bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4818bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
4828bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
4838bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
4848bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
4858bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param infos The {@link AccessibilityNodeInfo}s.
486d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The id of the connection to the system.
4878bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @param windowScale The source window compatibility scale.
4888bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
4898bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
490d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov            int connectionId, float windowScale) {
4918bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        if (infos != null) {
4928bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            final int infosCount = infos.size();
4938bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            for (int i = 0; i < infosCount; i++) {
4948bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov                AccessibilityNodeInfo info = infos.get(i);
495d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov                finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
4968bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            }
4978bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
4988bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
4998bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov
5008bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    /**
5018bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * Gets the message stored if the interacted and interacting
5028bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * threads are the same.
5038bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     *
5048bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     * @return The message.
5058bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov     */
5068bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    private Message getSameProcessMessageAndClear() {
5078bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        synchronized (mInstanceLock) {
5088bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            Message result = mSameThreadMessage;
5098bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            mSameThreadMessage = null;
5108bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov            return result;
5118bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov        }
5128bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov    }
513d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
514d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
515d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Gets a cached accessibility service connection.
516d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
517d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
518d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @return The cached connection if such.
519d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
520d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public IAccessibilityServiceConnection getConnection(int connectionId) {
52136bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
52236bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            return sConnectionCache.get(connectionId);
523d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
524d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
525d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
526d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
527d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Adds a cached accessibility service connection.
528d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
529d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
530d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connection The connection.
531d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
532d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
53336bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
53436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.put(connectionId, connection);
535d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
536d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
537d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov
538d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    /**
539d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * Removes a cached accessibility service connection.
540d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     *
541d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     * @param connectionId The connection id.
542d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov     */
543d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    public void removeConnection(int connectionId) {
54436bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov        synchronized (sConnectionCache) {
54536bcdb535e14a8a2e2c8643fb577569f7a2b6aedSvetoslav Ganov            sConnectionCache.remove(connectionId);
546d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov        }
547d116d7c78a9c53f30a73bf273bd7618312cf3847Svetoslav Ganov    }
5488bd69610aafc6995126965d1d23b771fe02a9084Svetoslav Ganov}
549