1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
20 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
21 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_REQUESTED_KEY;
22 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
23 
24 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
25 
26 import android.accessibilityservice.AccessibilityService;
27 import android.annotation.NonNull;
28 import android.graphics.Matrix;
29 import android.graphics.Rect;
30 import android.graphics.RectF;
31 import android.graphics.Region;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.Looper;
35 import android.os.Message;
36 import android.os.Parcelable;
37 import android.os.Process;
38 import android.os.RemoteException;
39 import android.os.SystemClock;
40 import android.text.style.AccessibilityClickableSpan;
41 import android.text.style.ClickableSpan;
42 import android.util.LongSparseArray;
43 import android.util.Slog;
44 import android.view.accessibility.AccessibilityInteractionClient;
45 import android.view.accessibility.AccessibilityManager;
46 import android.view.accessibility.AccessibilityNodeIdManager;
47 import android.view.accessibility.AccessibilityNodeInfo;
48 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
49 import android.view.accessibility.AccessibilityNodeProvider;
50 import android.view.accessibility.AccessibilityRequestPreparer;
51 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
52 import android.window.ScreenCapture;
53 
54 import com.android.internal.R;
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.internal.os.SomeArgs;
58 import com.android.internal.util.function.pooled.PooledLambda;
59 
60 import java.util.ArrayDeque;
61 import java.util.ArrayList;
62 import java.util.HashSet;
63 import java.util.LinkedHashMap;
64 import java.util.LinkedList;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Queue;
68 import java.util.function.Predicate;
69 
70 /**
71  * Class for managing accessibility interactions initiated from the system
72  * and targeting the view hierarchy. A *ClientThread method is to be
73  * called from the interaction connection ViewAncestor gives the system to
74  * talk to it and a corresponding *UiThread method that is executed on the
75  * UI thread.
76  *
77  * @hide
78  */
79 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
80 public final class AccessibilityInteractionController {
81 
82     private static final String LOG_TAG = "AccessibilityInteractionController";
83 
84     // Debugging flag
85     private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
86 
87     // Constants for readability
88     private static final boolean IGNORE_REQUEST_PREPARERS = true;
89     private static final boolean CONSIDER_REQUEST_PREPARERS = false;
90 
91     // If an app holds off accessibility for longer than this, the hold-off is canceled to prevent
92     // accessibility from hanging
93     private static final long REQUEST_PREPARER_TIMEOUT_MS = 500;
94 
95     // Callbacks should have the same configuration of the flags below to allow satisfying a pending
96     // node request on prefetch
97     private static final int FLAGS_AFFECTING_REPORTED_DATA = AccessibilityNodeInfo.FLAG_REPORT_MASK;
98 
99     private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
100         new ArrayList<AccessibilityNodeInfo>();
101 
102     private final Object mLock = new Object();
103 
104     private final PrivateHandler mHandler;
105 
106     private final ViewRootImpl mViewRootImpl;
107 
108     private final AccessibilityNodePrefetcher mPrefetcher;
109 
110     private final long mMyLooperThreadId;
111 
112     private final int mMyProcessId;
113 
114     private final AccessibilityManager mA11yManager;
115 
116     private final ArrayList<View> mTempArrayList = new ArrayList<View>();
117 
118     private final Rect mTempRect = new Rect();
119     private final RectF mTempRectF = new RectF();
120 
121     private AddNodeInfosForViewId mAddNodeInfosForViewId;
122 
123     @GuardedBy("mLock")
124     private ArrayList<Message> mPendingFindNodeByIdMessages;
125 
126     @GuardedBy("mLock")
127     private int mNumActiveRequestPreparers;
128     @GuardedBy("mLock")
129     private List<MessageHolder> mMessagesWaitingForRequestPreparer;
130     @GuardedBy("mLock")
131     private int mActiveRequestPreparerId;
132 
AccessibilityInteractionController(ViewRootImpl viewRootImpl)133     public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
134         Looper looper = viewRootImpl.mHandler.getLooper();
135         mMyLooperThreadId = looper.getThread().getId();
136         mMyProcessId = Process.myPid();
137         mHandler = new PrivateHandler(looper);
138         mViewRootImpl = viewRootImpl;
139         mPrefetcher = new AccessibilityNodePrefetcher();
140         mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
141         mPendingFindNodeByIdMessages = new ArrayList<>();
142     }
143 
scheduleMessage(Message message, int interrogatingPid, long interrogatingTid, boolean ignoreRequestPreparers)144     private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
145             boolean ignoreRequestPreparers) {
146         if (ignoreRequestPreparers
147                 || !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {
148             // If the interrogation is performed by the same thread as the main UI
149             // thread in this process, set the message as a static reference so
150             // after this call completes the same thread but in the interrogating
151             // client can handle the message to generate the result.
152             if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
153                     && mHandler.hasAccessibilityCallback(message)) {
154                 AccessibilityInteractionClient.getInstanceForThread(
155                         interrogatingTid).setSameThreadMessage(message);
156             } else {
157                 // For messages without callback of interrogating client, just handle the
158                 // message immediately if this is UI thread.
159                 if (!mHandler.hasAccessibilityCallback(message)
160                         && Thread.currentThread().getId() == mMyLooperThreadId) {
161                     mHandler.handleMessage(message);
162                 } else {
163                     mHandler.sendMessage(message);
164                 }
165             }
166         }
167     }
168 
isShown(View view)169     private boolean isShown(View view) {
170         return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
171     }
172 
isVisibleToAccessibilityService(View view)173     private boolean isVisibleToAccessibilityService(View view) {
174         return view != null && (mA11yManager.isRequestFromAccessibilityTool()
175                 || !view.isAccessibilityDataSensitive());
176     }
177 
findAccessibilityNodeInfoByAccessibilityIdClientThread( long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrixValues, Bundle arguments)178     public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
179             long accessibilityNodeId, Region interactiveRegion, int interactionId,
180             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
181             long interrogatingTid, MagnificationSpec spec, float[] matrixValues,
182             Bundle arguments) {
183         final Message message = mHandler.obtainMessage();
184         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
185         message.arg1 = flags;
186 
187         final SomeArgs args = SomeArgs.obtain();
188         args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
189         args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
190         args.argi3 = interactionId;
191         args.arg1 = callback;
192         args.arg2 = spec;
193         args.arg3 = interactiveRegion;
194         args.arg4 = arguments;
195         args.arg5 = matrixValues;
196         message.obj = args;
197 
198         synchronized (mLock) {
199             mPendingFindNodeByIdMessages.add(message);
200             scheduleMessage(message, interrogatingPid, interrogatingTid,
201                     CONSIDER_REQUEST_PREPARERS);
202         }
203     }
204 
205     /**
206      * Check if this message needs to be held off while the app prepares to meet either this
207      * request, or a request ahead of it.
208      *
209      * @param originalMessage The message to be processed
210      * @param callingPid The calling process id
211      * @param callingTid The calling thread id
212      *
213      * @return {@code true} if the message is held off and will be processed later, {@code false} if
214      *         the message should be posted.
215      */
holdOffMessageIfNeeded( Message originalMessage, int callingPid, long callingTid)216     private boolean holdOffMessageIfNeeded(
217             Message originalMessage, int callingPid, long callingTid) {
218         synchronized (mLock) {
219             // If a request is already pending, queue this request for when it's finished
220             if (mNumActiveRequestPreparers != 0) {
221                 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
222                 return true;
223             }
224 
225             // Currently the only message that can hold things off is findByA11yId with extra data.
226             if (originalMessage.what
227                     != PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID) {
228                 return false;
229             }
230             SomeArgs originalMessageArgs = (SomeArgs) originalMessage.obj;
231             Bundle requestArguments = (Bundle) originalMessageArgs.arg4;
232             if (requestArguments == null) {
233                 return false;
234             }
235 
236             // If nothing it registered for this view, nothing to do
237             int accessibilityViewId = originalMessageArgs.argi1;
238             final List<AccessibilityRequestPreparer> preparers =
239                     mA11yManager.getRequestPreparersForAccessibilityId(accessibilityViewId);
240             if (preparers == null) {
241                 return false;
242             }
243 
244             // If the bundle doesn't request the extra data, nothing to do
245             final String extraDataKey = requestArguments.getString(EXTRA_DATA_REQUESTED_KEY);
246             if (extraDataKey == null) {
247                 return false;
248             }
249 
250             // Send the request to the AccessibilityRequestPreparers on the UI thread
251             mNumActiveRequestPreparers = preparers.size();
252             for (int i = 0; i < preparers.size(); i++) {
253                 final Message requestPreparerMessage = mHandler.obtainMessage(
254                         PrivateHandler.MSG_PREPARE_FOR_EXTRA_DATA_REQUEST);
255                 final SomeArgs requestPreparerArgs = SomeArgs.obtain();
256                 // virtualDescendentId
257                 requestPreparerArgs.argi1 =
258                         (originalMessageArgs.argi2 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
259                         ? AccessibilityNodeProvider.HOST_VIEW_ID : originalMessageArgs.argi2;
260                 requestPreparerArgs.arg1 = preparers.get(i);
261                 requestPreparerArgs.arg2 = extraDataKey;
262                 requestPreparerArgs.arg3 = requestArguments;
263                 Message preparationFinishedMessage = mHandler.obtainMessage(
264                         PrivateHandler.MSG_APP_PREPARATION_FINISHED);
265                 preparationFinishedMessage.arg1 = ++mActiveRequestPreparerId;
266                 requestPreparerArgs.arg4 = preparationFinishedMessage;
267 
268                 requestPreparerMessage.obj = requestPreparerArgs;
269                 scheduleMessage(requestPreparerMessage, callingPid, callingTid,
270                         IGNORE_REQUEST_PREPARERS);
271                 mHandler.obtainMessage(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
272                 mHandler.sendEmptyMessageDelayed(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT,
273                         REQUEST_PREPARER_TIMEOUT_MS);
274             }
275 
276             // Set the initial request aside
277             queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
278             return true;
279         }
280     }
281 
prepareForExtraDataRequestUiThread(Message message)282     private void prepareForExtraDataRequestUiThread(Message message) {
283         SomeArgs args = (SomeArgs) message.obj;
284         final int virtualDescendantId = args.argi1;
285         final AccessibilityRequestPreparer preparer = (AccessibilityRequestPreparer) args.arg1;
286         final String extraDataKey = (String) args.arg2;
287         final Bundle requestArguments = (Bundle) args.arg3;
288         final Message preparationFinishedMessage = (Message) args.arg4;
289 
290         preparer.onPrepareExtraData(virtualDescendantId, extraDataKey,
291                 requestArguments, preparationFinishedMessage);
292     }
293 
queueMessageToHandleOncePrepared(Message message, int interrogatingPid, long interrogatingTid)294     private void queueMessageToHandleOncePrepared(Message message, int interrogatingPid,
295             long interrogatingTid) {
296         if (mMessagesWaitingForRequestPreparer == null) {
297             mMessagesWaitingForRequestPreparer = new ArrayList<>(1);
298         }
299         MessageHolder messageHolder =
300                 new MessageHolder(message, interrogatingPid, interrogatingTid);
301         mMessagesWaitingForRequestPreparer.add(messageHolder);
302     }
303 
requestPreparerDoneUiThread(Message message)304     private void requestPreparerDoneUiThread(Message message) {
305         synchronized (mLock) {
306             if (message.arg1 != mActiveRequestPreparerId) {
307                 Slog.e(LOG_TAG, "Surprising AccessibilityRequestPreparer callback (likely late)");
308                 return;
309             }
310             mNumActiveRequestPreparers--;
311             if (mNumActiveRequestPreparers <= 0) {
312                 mHandler.removeMessages(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
313                 scheduleAllMessagesWaitingForRequestPreparerLocked();
314             }
315         }
316     }
317 
requestPreparerTimeoutUiThread()318     private void requestPreparerTimeoutUiThread() {
319         synchronized (mLock) {
320             Slog.e(LOG_TAG, "AccessibilityRequestPreparer timed out");
321             scheduleAllMessagesWaitingForRequestPreparerLocked();
322         }
323     }
324 
325     @GuardedBy("mLock")
scheduleAllMessagesWaitingForRequestPreparerLocked()326     private void scheduleAllMessagesWaitingForRequestPreparerLocked() {
327         int numMessages = mMessagesWaitingForRequestPreparer.size();
328         for (int i = 0; i < numMessages; i++) {
329             MessageHolder request = mMessagesWaitingForRequestPreparer.get(i);
330             scheduleMessage(request.mMessage, request.mInterrogatingPid,
331                     request.mInterrogatingTid,
332                     (i == 0) /* the app is ready for the first request */);
333         }
334         mMessagesWaitingForRequestPreparer.clear();
335         mNumActiveRequestPreparers = 0; // Just to be safe - should be unnecessary
336         mActiveRequestPreparerId = -1;
337     }
338 
findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message)339     private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
340         synchronized (mLock) {
341             mPendingFindNodeByIdMessages.remove(message);
342         }
343         final int flags = message.arg1;
344 
345         SomeArgs args = (SomeArgs) message.obj;
346         final int accessibilityViewId = args.argi1;
347         final int virtualDescendantId = args.argi2;
348         final int interactionId = args.argi3;
349         final IAccessibilityInteractionConnectionCallback callback =
350             (IAccessibilityInteractionConnectionCallback) args.arg1;
351         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
352         final Region interactiveRegion = (Region) args.arg3;
353         final Bundle arguments = (Bundle) args.arg4;
354         final float[] matrixValues = (float[]) args.arg5;
355 
356         args.recycle();
357 
358         View requestedView = null;
359         AccessibilityNodeInfo requestedNode = null;
360         boolean interruptPrefetch =
361                 ((flags & AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE) == 0);
362 
363         ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
364         infos.clear();
365         try {
366             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
367                 return;
368             }
369             setAccessibilityFetchFlags(flags);
370             requestedView = findViewByAccessibilityId(accessibilityViewId);
371             if (requestedView != null && isShown(requestedView)) {
372                 requestedNode = populateAccessibilityNodeInfoForView(
373                         requestedView, arguments, virtualDescendantId);
374                 mPrefetcher.mInterruptPrefetch = interruptPrefetch;
375                 mPrefetcher.mFetchFlags = flags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
376 
377                 if (!interruptPrefetch) {
378                     infos.add(requestedNode);
379                     mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
380                             requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
381                             infos);
382                     resetAccessibilityFetchFlags();
383                 }
384             }
385         } finally {
386             if (!interruptPrefetch) {
387                 // Return found node and prefetched nodes in one IPC.
388                 updateInfosForViewportAndReturnFindNodeResult(infos, callback, interactionId, spec,
389                         matrixValues, interactiveRegion);
390 
391                 final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
392                         getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode,
393                                infos, flags);
394                 if (satisfiedRequest != null) {
395                     returnFindNodeResult(satisfiedRequest);
396                 }
397                 return;
398             } else {
399                 // Return found node.
400                 updateInfoForViewportAndReturnFindNodeResult(
401                         requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
402                         callback, interactionId, spec, matrixValues, interactiveRegion);
403             }
404         }
405         mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
406                 requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos);
407         resetAccessibilityFetchFlags();
408         updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
409         final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
410                 getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos,
411                         flags);
412 
413         // Return prefetch result separately.
414         returnPrefetchResult(interactionId, infos, callback);
415 
416         if (satisfiedRequest != null) {
417             returnFindNodeResult(satisfiedRequest);
418         }
419     }
420 
populateAccessibilityNodeInfoForView( View view, Bundle arguments, int virtualViewId)421     private AccessibilityNodeInfo populateAccessibilityNodeInfoForView(
422             View view, Bundle arguments, int virtualViewId) {
423         AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
424         // Determine if we'll be populating extra data
425         final String extraDataRequested = (arguments == null) ? null
426                 : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
427         AccessibilityNodeInfo root = null;
428         if (provider == null) {
429             root = view.createAccessibilityNodeInfo();
430             if (root != null) {
431                 if (extraDataRequested != null) {
432                     view.addExtraDataToAccessibilityNodeInfo(root, extraDataRequested, arguments);
433                 }
434             }
435         } else {
436             root = provider.createAccessibilityNodeInfo(virtualViewId);
437             if (root != null) {
438                 if (extraDataRequested != null) {
439                     provider.addExtraDataToAccessibilityNodeInfo(
440                             virtualViewId, root, extraDataRequested, arguments);
441                 }
442             }
443         }
444         return root;
445     }
446 
findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrixValues)447     public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
448             String viewId, Region interactiveRegion, int interactionId,
449             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
450             long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
451         Message message = mHandler.obtainMessage();
452         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
453         message.arg1 = flags;
454         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
455 
456         SomeArgs args = SomeArgs.obtain();
457         args.argi1 = interactionId;
458         args.arg1 = callback;
459         args.arg2 = spec;
460         args.arg3 = viewId;
461         args.arg4 = interactiveRegion;
462         args.arg5 = matrixValues;
463         message.obj = args;
464 
465         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
466     }
467 
findAccessibilityNodeInfosByViewIdUiThread(Message message)468     private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
469         final int flags = message.arg1;
470         final int accessibilityViewId = message.arg2;
471 
472         SomeArgs args = (SomeArgs) message.obj;
473         final int interactionId = args.argi1;
474         final IAccessibilityInteractionConnectionCallback callback =
475             (IAccessibilityInteractionConnectionCallback) args.arg1;
476         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
477         final String viewId = (String) args.arg3;
478         final Region interactiveRegion = (Region) args.arg4;
479         final float[] matrixValues = (float[]) args.arg5;
480         args.recycle();
481 
482         final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
483         infos.clear();
484         try {
485             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null
486                     || viewId == null) {
487                 return;
488             }
489             setAccessibilityFetchFlags(flags);
490             final View root = findViewByAccessibilityId(accessibilityViewId);
491             if (root != null) {
492                 final int resolvedViewId = root.getContext().getResources()
493                         .getIdentifier(viewId, null, null);
494                 if (resolvedViewId <= 0) {
495                     return;
496                 }
497                 if (mAddNodeInfosForViewId == null) {
498                     mAddNodeInfosForViewId = new AddNodeInfosForViewId();
499                 }
500                 mAddNodeInfosForViewId.init(resolvedViewId, infos);
501                 root.findViewByPredicate(mAddNodeInfosForViewId);
502                 mAddNodeInfosForViewId.reset();
503             }
504         } finally {
505             resetAccessibilityFetchFlags();
506             updateInfosForViewportAndReturnFindNodeResult(
507                     infos, callback, interactionId, spec, matrixValues, interactiveRegion);
508         }
509     }
510 
findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrixValues)511     public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
512             String text, Region interactiveRegion, int interactionId,
513             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
514             long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
515         Message message = mHandler.obtainMessage();
516         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
517         message.arg1 = flags;
518 
519         SomeArgs args = SomeArgs.obtain();
520         args.arg1 = text;
521         args.arg2 = callback;
522         args.arg3 = spec;
523         args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
524         args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
525         args.argi3 = interactionId;
526         args.arg4 = interactiveRegion;
527         args.arg5 = matrixValues;
528         message.obj = args;
529 
530         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
531     }
532 
findAccessibilityNodeInfosByTextUiThread(Message message)533     private void findAccessibilityNodeInfosByTextUiThread(Message message) {
534         final int flags = message.arg1;
535 
536         SomeArgs args = (SomeArgs) message.obj;
537         final String text = (String) args.arg1;
538         final IAccessibilityInteractionConnectionCallback callback =
539             (IAccessibilityInteractionConnectionCallback) args.arg2;
540         final MagnificationSpec spec = (MagnificationSpec) args.arg3;
541         final int accessibilityViewId = args.argi1;
542         final int virtualDescendantId = args.argi2;
543         final int interactionId = args.argi3;
544         final Region interactiveRegion = (Region) args.arg4;
545         final float[] matrixValues = (float[]) args.arg5;
546         args.recycle();
547 
548         List<AccessibilityNodeInfo> infos = null;
549         try {
550             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
551                 return;
552             }
553             setAccessibilityFetchFlags(flags);
554             final View root = findViewByAccessibilityId(accessibilityViewId);
555             if (root != null && isShown(root)) {
556                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
557                 if (provider != null) {
558                     infos = provider.findAccessibilityNodeInfosByText(text,
559                             virtualDescendantId);
560                 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
561                     ArrayList<View> foundViews = mTempArrayList;
562                     foundViews.clear();
563                     root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
564                             | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
565                             | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
566                     if (!foundViews.isEmpty()) {
567                         infos = mTempAccessibilityNodeInfoList;
568                         infos.clear();
569                         final int viewCount = foundViews.size();
570                         for (int i = 0; i < viewCount; i++) {
571                             View foundView = foundViews.get(i);
572                             if (isShown(foundView) && isVisibleToAccessibilityService(foundView)) {
573                                 provider = foundView.getAccessibilityNodeProvider();
574                                 if (provider != null) {
575                                     List<AccessibilityNodeInfo> infosFromProvider =
576                                         provider.findAccessibilityNodeInfosByText(text,
577                                                 AccessibilityNodeProvider.HOST_VIEW_ID);
578                                     if (infosFromProvider != null) {
579                                         infos.addAll(infosFromProvider);
580                                     }
581                                 } else  {
582                                     infos.add(foundView.createAccessibilityNodeInfo());
583                                 }
584                             }
585                         }
586                     }
587                 }
588             }
589         } finally {
590             resetAccessibilityFetchFlags();
591             updateInfosForViewportAndReturnFindNodeResult(
592                     infos, callback, interactionId, spec, matrixValues, interactiveRegion);
593         }
594     }
595 
596     /**
597      * Take a screenshot using {@link ScreenCapture} of this {@link ViewRootImpl}'s {@link
598      * SurfaceControl}.
599      */
takeScreenshotOfWindowClientThread(int interactionId, ScreenCapture.ScreenCaptureListener listener, IAccessibilityInteractionConnectionCallback callback)600     public void takeScreenshotOfWindowClientThread(int interactionId,
601             ScreenCapture.ScreenCaptureListener listener,
602             IAccessibilityInteractionConnectionCallback callback) {
603         Message message = PooledLambda.obtainMessage(
604                 AccessibilityInteractionController::takeScreenshotOfWindowUiThread,
605                 this, interactionId, listener, callback);
606 
607         // Screenshot results are returned to the service asynchronously, so the same-thread
608         // message wait logic from #scheduleMessage() is not needed.
609         mHandler.sendMessage(message);
610     }
611 
takeScreenshotOfWindowUiThread(int interactionId, ScreenCapture.ScreenCaptureListener listener, IAccessibilityInteractionConnectionCallback callback)612     private void takeScreenshotOfWindowUiThread(int interactionId,
613             ScreenCapture.ScreenCaptureListener listener,
614             IAccessibilityInteractionConnectionCallback callback) {
615         try {
616             if ((mViewRootImpl.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
617                 callback.sendTakeScreenshotOfWindowError(
618                         AccessibilityService.ERROR_TAKE_SCREENSHOT_SECURE_WINDOW, interactionId);
619                 return;
620             }
621             final ScreenCapture.LayerCaptureArgs captureArgs =
622                     new ScreenCapture.LayerCaptureArgs.Builder(mViewRootImpl.getSurfaceControl())
623                             .setChildrenOnly(false).setUid(Process.myUid()).build();
624             if (ScreenCapture.captureLayers(captureArgs, listener) != 0) {
625                 callback.sendTakeScreenshotOfWindowError(
626                         AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, interactionId);
627             }
628         } catch (RemoteException re) {
629             /* ignore - the other side will time out */
630         }
631     }
632 
findFocusClientThread(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrixValues)633     public void findFocusClientThread(long accessibilityNodeId, int focusType,
634             Region interactiveRegion, int interactionId,
635             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
636             long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
637         Message message = mHandler.obtainMessage();
638         message.what = PrivateHandler.MSG_FIND_FOCUS;
639         message.arg1 = flags;
640         message.arg2 = focusType;
641 
642         SomeArgs args = SomeArgs.obtain();
643         args.argi1 = interactionId;
644         args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
645         args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
646         args.arg1 = callback;
647         args.arg2 = spec;
648         args.arg3 = interactiveRegion;
649         args.arg4 = matrixValues;
650         message.obj = args;
651 
652         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
653     }
654 
findFocusUiThread(Message message)655     private void findFocusUiThread(Message message) {
656         final int flags = message.arg1;
657         final int focusType = message.arg2;
658 
659         SomeArgs args = (SomeArgs) message.obj;
660         final int interactionId = args.argi1;
661         final int accessibilityViewId = args.argi2;
662         final int virtualDescendantId = args.argi3;
663         final IAccessibilityInteractionConnectionCallback callback =
664             (IAccessibilityInteractionConnectionCallback) args.arg1;
665         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
666         final Region interactiveRegion = (Region) args.arg3;
667         final float[] matrixValues = (float[]) args.arg4;
668         args.recycle();
669 
670         AccessibilityNodeInfo focused = null;
671         try {
672             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
673                 return;
674             }
675             setAccessibilityFetchFlags(flags);
676             final View root = findViewByAccessibilityId(accessibilityViewId);
677             if (root != null && isShown(root)) {
678                 switch (focusType) {
679                     case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
680                         View host = mViewRootImpl.mAccessibilityFocusedHost;
681                         // If there is no accessibility focus host or it is not a descendant
682                         // of the root from which to start the search, then the search failed.
683                         if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
684                             break;
685                         }
686                         // The focused view not shown, we failed.
687                         if (!isShown(host)) {
688                             break;
689                         }
690                         if (!isVisibleToAccessibilityService(host)) {
691                             break;
692                         }
693                         // If the host has a provider ask this provider to search for the
694                         // focus instead fetching all provider nodes to do the search here.
695                         AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
696                         if (provider != null) {
697                             final AccessibilityNodeInfo focusNode =
698                                     mViewRootImpl.mAccessibilityFocusedVirtualView;
699                             if (focusNode != null) {
700                                 final int virtualNodeId = AccessibilityNodeInfo
701                                         .getVirtualDescendantId(focusNode.getSourceNodeId());
702                                 focused = provider.createAccessibilityNodeInfo(virtualNodeId);
703                             }
704                         } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
705                             focused = host.createAccessibilityNodeInfo();
706                         }
707                     } break;
708                     case AccessibilityNodeInfo.FOCUS_INPUT: {
709                         View target = root.findFocus();
710                         if (!isShown(target)) {
711                             break;
712                         }
713                         if (!isVisibleToAccessibilityService(target)) {
714                             break;
715                         }
716                         AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
717                         if (provider != null) {
718                             focused = provider.findFocus(focusType);
719                         }
720                         if (focused == null) {
721                             focused = target.createAccessibilityNodeInfo();
722                         }
723                     } break;
724                     default:
725                         throw new IllegalArgumentException("Unknown focus type: " + focusType);
726                 }
727             }
728         } finally {
729             resetAccessibilityFetchFlags();
730             updateInfoForViewportAndReturnFindNodeResult(
731                     focused, callback, interactionId, spec, matrixValues, interactiveRegion);
732         }
733     }
734 
focusSearchClientThread(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrixValues)735     public void focusSearchClientThread(long accessibilityNodeId, int direction,
736             Region interactiveRegion, int interactionId,
737             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
738             long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
739         Message message = mHandler.obtainMessage();
740         message.what = PrivateHandler.MSG_FOCUS_SEARCH;
741         message.arg1 = flags;
742         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
743 
744         SomeArgs args = SomeArgs.obtain();
745         args.argi2 = direction;
746         args.argi3 = interactionId;
747         args.arg1 = callback;
748         args.arg2 = spec;
749         args.arg3 = interactiveRegion;
750         args.arg4 = matrixValues;
751 
752         message.obj = args;
753 
754         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
755     }
756 
focusSearchUiThread(Message message)757     private void focusSearchUiThread(Message message) {
758         final int flags = message.arg1;
759         final int accessibilityViewId = message.arg2;
760 
761         SomeArgs args = (SomeArgs) message.obj;
762         final int direction = args.argi2;
763         final int interactionId = args.argi3;
764         final IAccessibilityInteractionConnectionCallback callback =
765             (IAccessibilityInteractionConnectionCallback) args.arg1;
766         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
767         final Region interactiveRegion = (Region) args.arg3;
768         final float[] matrixValues = (float[]) args.arg4;
769         args.recycle();
770 
771         AccessibilityNodeInfo next = null;
772         try {
773             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
774                 return;
775             }
776             setAccessibilityFetchFlags(flags);
777             final View root = findViewByAccessibilityId(accessibilityViewId);
778             if (root != null && isShown(root)) {
779                 View nextView = root.focusSearch(direction);
780                 if (nextView != null) {
781                     next = nextView.createAccessibilityNodeInfo();
782                 }
783             }
784         } finally {
785             resetAccessibilityFetchFlags();
786             updateInfoForViewportAndReturnFindNodeResult(
787                     next, callback, interactionId, spec, matrixValues, interactiveRegion);
788         }
789     }
790 
performAccessibilityActionClientThread(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid)791     public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
792             Bundle arguments, int interactionId,
793             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
794             long interrogatingTid) {
795         Message message = mHandler.obtainMessage();
796         message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
797         message.arg1 = flags;
798         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
799 
800         SomeArgs args = SomeArgs.obtain();
801         args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
802         args.argi2 = action;
803         args.argi3 = interactionId;
804         args.arg1 = callback;
805         args.arg2 = arguments;
806 
807         message.obj = args;
808 
809         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
810     }
811 
performAccessibilityActionUiThread(Message message)812     private void performAccessibilityActionUiThread(Message message) {
813         final int flags = message.arg1;
814         final int accessibilityViewId = message.arg2;
815 
816         SomeArgs args = (SomeArgs) message.obj;
817         final int virtualDescendantId = args.argi1;
818         final int action = args.argi2;
819         final int interactionId = args.argi3;
820         final IAccessibilityInteractionConnectionCallback callback =
821             (IAccessibilityInteractionConnectionCallback) args.arg1;
822         Bundle arguments = (Bundle) args.arg2;
823 
824         args.recycle();
825 
826         boolean succeeded = false;
827         try {
828             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
829                     mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
830                 return;
831             }
832             setAccessibilityFetchFlags(flags);
833             final View target = findViewByAccessibilityId(accessibilityViewId);
834             if (target != null && isShown(target) && isVisibleToAccessibilityService(target)) {
835                 mA11yManager.notifyPerformingAction(action);
836                 if (action == R.id.accessibilityActionClickOnClickableSpan) {
837                     // Handle this hidden action separately
838                     succeeded = handleClickableSpanActionUiThread(
839                             target, virtualDescendantId, arguments);
840                 } else {
841                     AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
842                     if (provider != null) {
843                         succeeded = provider.performAction(virtualDescendantId, action,
844                                 arguments);
845                     } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
846                         succeeded = target.performAccessibilityAction(action, arguments);
847                     }
848                 }
849                 mA11yManager.notifyPerformingAction(0);
850             }
851         } finally {
852             try {
853                 resetAccessibilityFetchFlags();
854                 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
855             } catch (RemoteException re) {
856                 /* ignore - the other side will time out */
857             }
858         }
859     }
860 
861     /**
862      * Finds the accessibility focused node in the root, and clears the accessibility focus.
863      */
clearAccessibilityFocusClientThread()864     public void clearAccessibilityFocusClientThread() {
865         final Message message = mHandler.obtainMessage();
866         message.what = PrivateHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS;
867 
868         // Don't care about pid and tid because there's no interrogating client for this message.
869         scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
870     }
871 
clearAccessibilityFocusUiThread()872     private void clearAccessibilityFocusUiThread() {
873         if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
874             return;
875         }
876         try {
877             // Clearing focus does not expose sensitive data, so set fetch flags to ensure that the
878             // root view is always returned if present.
879             setAccessibilityFetchFlags(
880                     AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
881                             | AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL);
882             final View root = getRootView();
883             if (root != null && isShown(root)) {
884                 final View host = mViewRootImpl.mAccessibilityFocusedHost;
885                 // If there is no accessibility focus host or it is not a descendant
886                 // of the root from which to start the search, then the search failed.
887                 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
888                     return;
889                 }
890                 final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
891                 final AccessibilityNodeInfo focusNode =
892                         mViewRootImpl.mAccessibilityFocusedVirtualView;
893                 if (provider != null && focusNode != null) {
894                     final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
895                             focusNode.getSourceNodeId());
896                     provider.performAction(virtualNodeId,
897                             AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
898                             null);
899                 } else {
900                     host.performAccessibilityAction(
901                             AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
902                             null);
903                 }
904             }
905         } finally {
906             resetAccessibilityFetchFlags();
907         }
908     }
909 
910     /**
911      * Notify outside touch event to the target window.
912      */
notifyOutsideTouchClientThread()913     public void notifyOutsideTouchClientThread() {
914         final Message message = mHandler.obtainMessage();
915         message.what = PrivateHandler.MSG_NOTIFY_OUTSIDE_TOUCH;
916 
917         // Don't care about pid and tid because there's no interrogating client for this message.
918         scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
919     }
920 
notifyOutsideTouchUiThread()921     private void notifyOutsideTouchUiThread() {
922         if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null
923                 || mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
924             return;
925         }
926         final View root = getRootView();
927         if (root != null && isShown(root)) {
928             // trigger ACTION_OUTSIDE to notify windows
929             final long now = SystemClock.uptimeMillis();
930             final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
931                     0, 0, 0);
932             event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
933             mViewRootImpl.dispatchInputEvent(event);
934         }
935     }
936 
findViewByAccessibilityId(int accessibilityId)937     private View findViewByAccessibilityId(int accessibilityId) {
938         if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
939             return getRootView();
940         } else {
941             return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
942         }
943     }
944 
getRootView()945     private View getRootView() {
946         if (!isVisibleToAccessibilityService(mViewRootImpl.mView)) {
947             return null;
948         }
949         return mViewRootImpl.mView;
950     }
951 
setAccessibilityFetchFlags(int flags)952     private void setAccessibilityFetchFlags(int flags) {
953         mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
954         mA11yManager.setRequestFromAccessibilityTool(
955                 (flags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) != 0);
956     }
957 
resetAccessibilityFetchFlags()958     private void resetAccessibilityFetchFlags() {
959         mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
960         mA11yManager.setRequestFromAccessibilityTool(false);
961     }
962 
963     // The boundInScreen includes magnification effect, so we need to normalize it before
964     // determine the visibility.
adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, Region interactiveRegion, MagnificationSpec spec)965     private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
966             Region interactiveRegion, MagnificationSpec spec) {
967         if (interactiveRegion == null || info == null) {
968             return;
969         }
970         Rect boundsInScreen = mTempRect;
971         info.getBoundsInScreen(boundsInScreen);
972         if (spec != null && !spec.isNop()) {
973             boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
974             boundsInScreen.scale(1 / spec.scale);
975         }
976 
977         if (interactiveRegion.quickReject(boundsInScreen) && !shouldBypassAdjustIsVisible()) {
978             info.setVisibleToUser(false);
979         }
980     }
981 
shouldBypassAdjustIsVisible()982     private boolean shouldBypassAdjustIsVisible() {
983         final int windowType = mViewRootImpl.mOrigWindowType;
984         if (windowType == TYPE_INPUT_METHOD) {
985             return true;
986         }
987         return false;
988     }
989 
990     /**
991      * Applies the host-window matrix to the embedded node. After this transform, The node bounds
992      *  will be transformed from embedded window coordinates to host-window coordinates.
993      *
994      */
applyHostWindowMatrixIfNeeded(AccessibilityNodeInfo info)995     private void applyHostWindowMatrixIfNeeded(AccessibilityNodeInfo info) {
996         if (info == null || shouldBypassApplyWindowMatrix()) {
997             return;
998         }
999         final Rect boundsInScreen = mTempRect;
1000         final RectF transformedBounds = mTempRectF;
1001         final Matrix windowMatrix = mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy;
1002 
1003         info.getBoundsInScreen(boundsInScreen);
1004         transformedBounds.set(boundsInScreen);
1005         windowMatrix.mapRect(transformedBounds);
1006         boundsInScreen.set((int) transformedBounds.left, (int) transformedBounds.top,
1007                 (int) transformedBounds.right, (int) transformedBounds.bottom);
1008         info.setBoundsInScreen(boundsInScreen);
1009     }
1010 
shouldBypassApplyWindowMatrix()1011     private boolean shouldBypassApplyWindowMatrix() {
1012         final Matrix windowMatrix = mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy;
1013         return windowMatrix == null || windowMatrix.isIdentity();
1014     }
1015 
associateLeashedParentIfNeeded(AccessibilityNodeInfo info)1016     private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
1017         if (info == null || shouldBypassAssociateLeashedParent()) {
1018             return;
1019         }
1020         // The node id of root node in embedded maybe not be ROOT_NODE_ID so we compare the id
1021         // with root view.
1022         if (mViewRootImpl.mView.getAccessibilityViewId()
1023                 != AccessibilityNodeInfo.getAccessibilityViewId(info.getSourceNodeId())) {
1024             return;
1025         }
1026         info.setLeashedParent(mViewRootImpl.mAttachInfo.mLeashedParentToken,
1027                 mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId);
1028     }
1029 
shouldBypassAssociateLeashedParent()1030     private boolean shouldBypassAssociateLeashedParent() {
1031         return (mViewRootImpl.mAttachInfo.mLeashedParentToken == null
1032                 && mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId == View.NO_ID);
1033     }
1034 
shouldApplyAppScaleAndMagnificationSpec(float appScale, MagnificationSpec spec)1035     private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
1036             MagnificationSpec spec) {
1037         return (appScale != 1.0f || (spec != null && !spec.isNop()));
1038     }
1039 
updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec, float[] matrixValues, Region interactiveRegion)1040     private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec,
1041             float[] matrixValues, Region interactiveRegion) {
1042         for (int i = 0; i < infos.size(); i++) {
1043             updateInfoForViewPort(infos.get(i), spec, matrixValues, interactiveRegion);
1044         }
1045     }
1046 
updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec, float[] matrixValues, Region interactiveRegion)1047     private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec,
1048             float[] matrixValues, Region interactiveRegion) {
1049         associateLeashedParentIfNeeded(info);
1050 
1051         applyHostWindowMatrixIfNeeded(info);
1052         // Transform view bounds from window coordinates to screen coordinates.
1053         transformBoundsWithScreenMatrix(info, matrixValues);
1054         adjustIsVisibleToUserIfNeeded(info, interactiveRegion, spec);
1055     }
1056 
1057 
1058     /**
1059      * Transforms the regions from local screen coordinate to global screen coordinate with the
1060      * given transform matrix used in on-screen coordinate.
1061      *
1062      * @param info the AccessibilityNodeInfo that has the region in application screen coordinate
1063      * @param matrixValues the matrix to be applied
1064      */
transformBoundsWithScreenMatrix(AccessibilityNodeInfo info, float[] matrixValues)1065     private void transformBoundsWithScreenMatrix(AccessibilityNodeInfo info,
1066             float[] matrixValues) {
1067         if (info == null || matrixValues == null) {
1068             return;
1069         }
1070         final Rect boundInScreen = mTempRect;
1071         final RectF transformedBounds = mTempRectF;
1072 
1073         info.getBoundsInScreen(boundInScreen);
1074         transformedBounds.set(boundInScreen);
1075 
1076         final Matrix transformMatrix = new Matrix();
1077         transformMatrix.setValues(matrixValues);
1078         final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
1079         if (applicationScale != 1f) {
1080             transformMatrix.preScale(applicationScale, applicationScale);
1081         }
1082         // Transform the bounds from application screen coordinates to global window coordinates.
1083         // For the embedded node, the bounds we get is already in window coordinates, so we don't
1084         // need to do it.
1085         if (mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy == null) {
1086             transformMatrix.preTranslate(-mViewRootImpl.mAttachInfo.mWindowLeft,
1087                     -mViewRootImpl.mAttachInfo.mWindowTop);
1088         }
1089 
1090         if (transformMatrix.isIdentity()) {
1091             return;
1092         }
1093         transformMatrix.mapRect(transformedBounds);
1094         roundRectFToRect(transformedBounds, boundInScreen);
1095         info.setBoundsInScreen(boundInScreen);
1096         // Scale text locations if they are present
1097         if (info.hasExtras()) {
1098             final Bundle extras = info.getExtras();
1099             final RectF[] textLocations =
1100                     extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY, RectF.class);
1101             if (textLocations != null) {
1102                 for (int i = 0; i < textLocations.length; i++) {
1103                     // Unchecked cast - an app that puts other objects in this bundle with this
1104                     // key will crash.
1105                     final RectF textLocation = textLocations[i];
1106                     if (textLocation != null) {
1107                         transformMatrix.mapRect(textLocation);
1108                     }
1109                 }
1110             }
1111         }
1112         applyTransformMatrixToBoundsInParentIfNeeded(info, transformMatrix);
1113     }
1114 
applyTransformMatrixToBoundsInParentIfNeeded(AccessibilityNodeInfo info, Matrix transformMatrix)1115     private void applyTransformMatrixToBoundsInParentIfNeeded(AccessibilityNodeInfo info,
1116             Matrix transformMatrix) {
1117         final float[] screenMatrixValues = new float[9];
1118         transformMatrix.getValues(screenMatrixValues);
1119         final Matrix scaleMatrix = new Matrix();
1120         scaleMatrix.setScale(screenMatrixValues[Matrix.MSCALE_X],
1121                 screenMatrixValues[Matrix.MSCALE_X]);
1122         if (scaleMatrix.isIdentity()) {
1123             return;
1124         }
1125         Rect boundsInParent = mTempRect;
1126         final RectF transformedBounds = mTempRectF;
1127         info.getBoundsInParent(boundsInParent);
1128         transformedBounds.set(boundsInParent);
1129         scaleMatrix.mapRect(transformedBounds);
1130         roundRectFToRect(transformedBounds, boundsInParent);
1131         info.setBoundsInParent(boundsInParent);
1132     }
1133 
updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, float[] matrixValues, Region interactiveRegion)1134     private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
1135             IAccessibilityInteractionConnectionCallback callback, int interactionId,
1136             MagnificationSpec spec, float[] matrixValues, Region interactiveRegion) {
1137         if (infos != null) {
1138             updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
1139         }
1140         returnFindNodesResult(infos, callback, interactionId);
1141     }
1142 
returnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId)1143     private void returnFindNodeResult(AccessibilityNodeInfo info,
1144                                       IAccessibilityInteractionConnectionCallback callback,
1145                                       int interactionId) {
1146         try {
1147             callback.setFindAccessibilityNodeInfoResult(info, interactionId);
1148         } catch (RemoteException re) {
1149             /* ignore - the other side will time out */
1150         }
1151     }
1152 
returnFindNodeResult(SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest)1153     private void returnFindNodeResult(SatisfiedFindAccessibilityNodeByAccessibilityIdRequest
1154             satisfiedRequest) {
1155         try {
1156             final AccessibilityNodeInfo info = satisfiedRequest.mSatisfiedRequestNode;
1157             final IAccessibilityInteractionConnectionCallback callback =
1158                     satisfiedRequest.mSatisfiedRequestCallback;
1159             final int interactionId = satisfiedRequest.mSatisfiedRequestInteractionId;
1160             callback.setFindAccessibilityNodeInfoResult(info, interactionId);
1161         } catch (RemoteException re) {
1162             /* ignore - the other side will time out */
1163         }
1164     }
1165 
returnFindNodesResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId)1166     private void returnFindNodesResult(List<AccessibilityNodeInfo> infos,
1167             IAccessibilityInteractionConnectionCallback callback, int interactionId) {
1168         try {
1169             callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
1170             if (infos != null) {
1171                 infos.clear();
1172             }
1173         } catch (RemoteException re) {
1174             /* ignore - the other side will time out */
1175         }
1176     }
1177 
getSatisfiedRequestInPrefetch( AccessibilityNodeInfo requestedNode, List<AccessibilityNodeInfo> infos, int flags)1178     private SatisfiedFindAccessibilityNodeByAccessibilityIdRequest getSatisfiedRequestInPrefetch(
1179             AccessibilityNodeInfo requestedNode, List<AccessibilityNodeInfo> infos, int flags) {
1180         SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest = null;
1181         synchronized (mLock) {
1182             for (int i = 0; i < mPendingFindNodeByIdMessages.size(); i++) {
1183                 final Message pendingMessage = mPendingFindNodeByIdMessages.get(i);
1184                 final int pendingFlags = pendingMessage.arg1;
1185                 if ((pendingFlags & FLAGS_AFFECTING_REPORTED_DATA)
1186                         != (flags & FLAGS_AFFECTING_REPORTED_DATA)) {
1187                     continue;
1188                 }
1189                 SomeArgs args = (SomeArgs) pendingMessage.obj;
1190                 final int accessibilityViewId = args.argi1;
1191                 final int virtualDescendantId = args.argi2;
1192 
1193                 final AccessibilityNodeInfo satisfiedRequestNode = nodeWithIdFromList(requestedNode,
1194                         infos, AccessibilityNodeInfo.makeNodeId(
1195                                 accessibilityViewId, virtualDescendantId));
1196 
1197                 if (satisfiedRequestNode != null) {
1198                     mHandler.removeMessages(
1199                             PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID,
1200                             pendingMessage.obj);
1201                     final IAccessibilityInteractionConnectionCallback satisfiedRequestCallback =
1202                             (IAccessibilityInteractionConnectionCallback) args.arg1;
1203                     final int satisfiedRequestInteractionId = args.argi3;
1204                     satisfiedRequest = new SatisfiedFindAccessibilityNodeByAccessibilityIdRequest(
1205                                     satisfiedRequestNode, satisfiedRequestCallback,
1206                                     satisfiedRequestInteractionId);
1207                     args.recycle();
1208                     break;
1209                 }
1210             }
1211             mPendingFindNodeByIdMessages.clear();
1212             // Remove node from prefetched infos.
1213             if (satisfiedRequest != null && satisfiedRequest.mSatisfiedRequestNode
1214                     != requestedNode) {
1215                 infos.remove(satisfiedRequest.mSatisfiedRequestNode);
1216             }
1217             return satisfiedRequest;
1218         }
1219     }
1220 
nodeWithIdFromList(AccessibilityNodeInfo requestedNode, List<AccessibilityNodeInfo> infos, long nodeId)1221     private AccessibilityNodeInfo nodeWithIdFromList(AccessibilityNodeInfo requestedNode,
1222             List<AccessibilityNodeInfo> infos, long nodeId) {
1223         if (requestedNode != null && requestedNode.getSourceNodeId() == nodeId) {
1224             return requestedNode;
1225         }
1226         for (int j = 0; j < infos.size(); j++) {
1227             AccessibilityNodeInfo info = infos.get(j);
1228             if (info.getSourceNodeId() == nodeId) {
1229                 return info;
1230             }
1231         }
1232         return null;
1233     }
1234 
returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback)1235     private void returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos,
1236                                       IAccessibilityInteractionConnectionCallback callback) {
1237         if (infos.size() > 0) {
1238             try {
1239                 callback.setPrefetchAccessibilityNodeInfoResult(infos, interactionId);
1240             } catch (RemoteException re) {
1241                 /* ignore - other side isn't too bothered if this doesn't arrive */
1242             }
1243         }
1244     }
1245 
updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, float[] matrixValues, Region interactiveRegion)1246     private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
1247             IAccessibilityInteractionConnectionCallback callback, int interactionId,
1248             MagnificationSpec spec, float[] matrixValues, Region interactiveRegion) {
1249         updateInfoForViewPort(info, spec, matrixValues, interactiveRegion);
1250         returnFindNodeResult(info, callback, interactionId);
1251     }
1252 
handleClickableSpanActionUiThread( View view, int virtualDescendantId, Bundle arguments)1253     private boolean handleClickableSpanActionUiThread(
1254             View view, int virtualDescendantId, Bundle arguments) {
1255         Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
1256         if (!(span instanceof AccessibilityClickableSpan)) {
1257             return false;
1258         }
1259 
1260         // Find the original ClickableSpan if it's still on the screen
1261         AccessibilityNodeInfo infoWithSpan = null;
1262         AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
1263         if (provider != null) {
1264             infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
1265         } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
1266             infoWithSpan = view.createAccessibilityNodeInfo();
1267         }
1268         if (infoWithSpan == null) {
1269             return false;
1270         }
1271 
1272         // Click on the corresponding span
1273         ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
1274                 infoWithSpan.getOriginalText());
1275         if (clickableSpan != null) {
1276             clickableSpan.onClick(view);
1277             return true;
1278         }
1279         return false;
1280     }
1281 
roundRectFToRect(@onNull RectF sourceRectF, @NonNull Rect outRect)1282     private static void roundRectFToRect(@NonNull  RectF sourceRectF, @NonNull Rect outRect) {
1283         // Offset 0.5f to round after casting.
1284         outRect.set((int) (sourceRectF.left + 0.5), (int) (sourceRectF.top + 0.5),
1285                 (int) (sourceRectF.right + 0.5), (int) (sourceRectF.bottom + 0.5));
1286     }
1287 
1288     /**
1289      * This class encapsulates a prefetching strategy for the accessibility APIs for
1290      * querying window content. It is responsible to prefetch a batch of
1291      * AccessibilityNodeInfos in addition to the one for a requested node.
1292      */
1293     private class AccessibilityNodePrefetcher {
1294 
1295         private final ArrayList<View> mTempViewList = new ArrayList<View>();
1296         private boolean mInterruptPrefetch;
1297         private int mFetchFlags;
1298 
prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root, List<AccessibilityNodeInfo> outInfos)1299         public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root,
1300                 List<AccessibilityNodeInfo> outInfos) {
1301             if (root == null) {
1302                 return;
1303             }
1304             AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
1305             final boolean prefetchPredecessors =
1306                     isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS);
1307             if (provider == null) {
1308                 if (prefetchPredecessors) {
1309                     prefetchPredecessorsOfRealNode(view, outInfos);
1310                 }
1311                 if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS)) {
1312                     prefetchSiblingsOfRealNode(view, outInfos, prefetchPredecessors);
1313                 }
1314                 if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID)) {
1315                     prefetchDescendantsOfRealNode(view, outInfos);
1316                 }
1317             } else {
1318                 if (prefetchPredecessors) {
1319                     prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
1320                 }
1321                 if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS)) {
1322                     prefetchSiblingsOfVirtualNode(root, view, provider, outInfos,
1323                             prefetchPredecessors);
1324                 }
1325                 if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID)) {
1326                     prefetchDescendantsOfVirtualNode(root, provider, outInfos);
1327                 }
1328             }
1329             if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST)
1330                     || isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST)) {
1331                 if (shouldStopPrefetching(outInfos)) {
1332                     return;
1333                 }
1334                 PrefetchDeque<DequeNode> deque = new PrefetchDeque<>(
1335                         mFetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_MASK,
1336                         outInfos);
1337                 addChildrenOfRoot(view, root, provider, deque);
1338                 deque.performTraversalAndPrefetch();
1339             }
1340             if (ENFORCE_NODE_TREE_CONSISTENT) {
1341                 enforceNodeTreeConsistent(root, outInfos);
1342             }
1343         }
1344 
addChildrenOfRoot(View root, AccessibilityNodeInfo rootInfo, AccessibilityNodeProvider rootProvider, PrefetchDeque deque)1345         private void addChildrenOfRoot(View root, AccessibilityNodeInfo rootInfo,
1346                 AccessibilityNodeProvider rootProvider, PrefetchDeque deque) {
1347             DequeNode rootDequeNode;
1348             if (rootProvider == null) {
1349                 rootDequeNode = new ViewNode(root);
1350             } else {
1351                 rootDequeNode = new VirtualNode(
1352                         AccessibilityNodeProvider.HOST_VIEW_ID, rootProvider);
1353             }
1354             rootDequeNode.addChildren(rootInfo, deque);
1355         }
1356 
isFlagSet(@ccessibilityNodeInfo.PrefetchingStrategy int strategy)1357         private boolean isFlagSet(@AccessibilityNodeInfo.PrefetchingStrategy int strategy) {
1358             return (mFetchFlags & strategy) != 0;
1359         }
1360 
shouldStopPrefetching(List prefetchedInfos)1361         public boolean shouldStopPrefetching(List prefetchedInfos) {
1362             return ((mHandler.hasUserInteractiveMessagesWaiting() && mInterruptPrefetch)
1363                     || prefetchedInfos.size()
1364                     >= AccessibilityNodeInfo.MAX_NUMBER_OF_PREFETCHED_NODES);
1365         }
1366 
enforceNodeTreeConsistent( AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes)1367         private void enforceNodeTreeConsistent(
1368                 AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes) {
1369             LongSparseArray<AccessibilityNodeInfo> nodeMap =
1370                     new LongSparseArray<AccessibilityNodeInfo>();
1371             final int nodeCount = nodes.size();
1372             for (int i = 0; i < nodeCount; i++) {
1373                 AccessibilityNodeInfo node = nodes.get(i);
1374                 nodeMap.put(node.getSourceNodeId(), node);
1375             }
1376 
1377             // If the nodes are a tree it does not matter from
1378             // which node we start to search for the root.
1379             AccessibilityNodeInfo parent = root;
1380             while (parent != null) {
1381                 root = parent;
1382                 parent = nodeMap.get(parent.getParentNodeId());
1383             }
1384 
1385             // Traverse the tree and do some checks.
1386             AccessibilityNodeInfo accessFocus = null;
1387             AccessibilityNodeInfo inputFocus = null;
1388             HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
1389             Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
1390             fringe.add(root);
1391 
1392             while (!fringe.isEmpty()) {
1393                 AccessibilityNodeInfo current = fringe.poll();
1394 
1395                 // Check for duplicates
1396                 if (!seen.add(current)) {
1397                     throw new IllegalStateException("Duplicate node: "
1398                             + current + " in window:"
1399                             + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1400                 }
1401 
1402                 // Check for one accessibility focus.
1403                 if (current.isAccessibilityFocused()) {
1404                     if (accessFocus != null) {
1405                         throw new IllegalStateException("Duplicate accessibility focus:"
1406                                 + current
1407                                 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1408                     } else {
1409                         accessFocus = current;
1410                     }
1411                 }
1412 
1413                 // Check for one input focus.
1414                 if (current.isFocused()) {
1415                     if (inputFocus != null) {
1416                         throw new IllegalStateException("Duplicate input focus: "
1417                             + current + " in window:"
1418                             + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1419                     } else {
1420                         inputFocus = current;
1421                     }
1422                 }
1423 
1424                 final int childCount = current.getChildCount();
1425                 for (int j = 0; j < childCount; j++) {
1426                     final long childId = current.getChildId(j);
1427                     final AccessibilityNodeInfo child = nodeMap.get(childId);
1428                     if (child != null) {
1429                         fringe.add(child);
1430                     }
1431                 }
1432             }
1433 
1434             // Check for disconnected nodes.
1435             for (int j = nodeMap.size() - 1; j >= 0; j--) {
1436                 AccessibilityNodeInfo info = nodeMap.valueAt(j);
1437                 if (!seen.contains(info)) {
1438                     throw new IllegalStateException("Disconnected node: " + info);
1439                 }
1440             }
1441         }
1442 
prefetchPredecessorsOfRealNode(View view, List<AccessibilityNodeInfo> outInfos)1443         private void prefetchPredecessorsOfRealNode(View view,
1444                 List<AccessibilityNodeInfo> outInfos) {
1445             if (shouldStopPrefetching(outInfos)) {
1446                 return;
1447             }
1448             ViewParent parent = view.getParentForAccessibility();
1449             while (parent instanceof View && !shouldStopPrefetching(outInfos)) {
1450                 View parentView = (View) parent;
1451                 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
1452                 if (info != null) {
1453                     outInfos.add(info);
1454                 }
1455                 parent = parent.getParentForAccessibility();
1456             }
1457         }
1458 
prefetchSiblingsOfRealNode(View current, List<AccessibilityNodeInfo> outInfos, boolean predecessorsPrefetched)1459         private void prefetchSiblingsOfRealNode(View current,
1460                 List<AccessibilityNodeInfo> outInfos, boolean predecessorsPrefetched) {
1461             if (shouldStopPrefetching(outInfos)) {
1462                 return;
1463             }
1464             ViewParent parent = current.getParentForAccessibility();
1465             if (parent instanceof ViewGroup) {
1466                 ViewGroup parentGroup = (ViewGroup) parent;
1467                 ArrayList<View> children = mTempViewList;
1468                 children.clear();
1469                 try {
1470                     if (!predecessorsPrefetched) {
1471                         AccessibilityNodeInfo parentInfo =
1472                                 ((ViewGroup) parent).createAccessibilityNodeInfo();
1473                         if (parentInfo != null) {
1474                             outInfos.add(parentInfo);
1475                         }
1476                     }
1477                     parentGroup.addChildrenForAccessibility(children);
1478                     final int childCount = children.size();
1479                     for (int i = 0; i < childCount; i++) {
1480                         if (shouldStopPrefetching(outInfos)) {
1481                             return;
1482                         }
1483                         View child = children.get(i);
1484                         if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
1485                                 && isShown(child)) {
1486                             AccessibilityNodeInfo info = null;
1487                             AccessibilityNodeProvider provider =
1488                                     child.getAccessibilityNodeProvider();
1489                             if (provider == null) {
1490                                 info = child.createAccessibilityNodeInfo();
1491                             } else {
1492                                 info = provider.createAccessibilityNodeInfo(
1493                                         AccessibilityNodeProvider.HOST_VIEW_ID);
1494                             }
1495                             if (info != null) {
1496                                 outInfos.add(info);
1497                             }
1498                         }
1499                     }
1500                 } finally {
1501                     children.clear();
1502                 }
1503             }
1504         }
1505 
prefetchDescendantsOfRealNode(View root, List<AccessibilityNodeInfo> outInfos)1506         private void prefetchDescendantsOfRealNode(View root,
1507                 List<AccessibilityNodeInfo> outInfos) {
1508             if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) {
1509                 return;
1510             }
1511             LinkedHashMap<View, AccessibilityNodeInfo> addedChildren =
1512                     new LinkedHashMap<View, AccessibilityNodeInfo>();
1513             ArrayList<View> children = mTempViewList;
1514             children.clear();
1515             try {
1516                 root.addChildrenForAccessibility(children);
1517                 final int childCount = children.size();
1518                 for (int i = 0; i < childCount; i++) {
1519                     if (shouldStopPrefetching(outInfos)) {
1520                         return;
1521                     }
1522                     View child = children.get(i);
1523                     if (isShown(child)) {
1524                         AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1525                         if (provider == null) {
1526                             AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1527                             if (info != null) {
1528                                 outInfos.add(info);
1529                                 addedChildren.put(child, null);
1530                             }
1531                         } else {
1532                             AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
1533                                    AccessibilityNodeProvider.HOST_VIEW_ID);
1534                             if (info != null) {
1535                                 outInfos.add(info);
1536                                 addedChildren.put(child, info);
1537                             }
1538                         }
1539                     }
1540                 }
1541             } finally {
1542                 children.clear();
1543             }
1544             if (!shouldStopPrefetching(outInfos)) {
1545                 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1546                     View addedChild = entry.getKey();
1547                     AccessibilityNodeInfo virtualRoot = entry.getValue();
1548                     if (virtualRoot == null) {
1549                         prefetchDescendantsOfRealNode(addedChild, outInfos);
1550                     } else {
1551                         AccessibilityNodeProvider provider =
1552                             addedChild.getAccessibilityNodeProvider();
1553                         prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1554                     }
1555                 }
1556             }
1557         }
1558 
prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root, View providerHost, AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos)1559         private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1560                 View providerHost, AccessibilityNodeProvider provider,
1561                 List<AccessibilityNodeInfo> outInfos) {
1562             final int initialResultSize = outInfos.size();
1563             long parentNodeId = root.getParentNodeId();
1564             int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1565             while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
1566                 if (shouldStopPrefetching(outInfos)) {
1567                     return;
1568                 }
1569                 final int virtualDescendantId =
1570                     AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
1571                 if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
1572                         || accessibilityViewId == providerHost.getAccessibilityViewId()) {
1573                     final AccessibilityNodeInfo parent;
1574                     parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
1575                     if (parent == null) {
1576                         // Going up the parent relation we found a null predecessor,
1577                         // so remove these disconnected nodes from the result.
1578                         final int currentResultSize = outInfos.size();
1579                         for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1580                             outInfos.remove(i);
1581                         }
1582                         // Couldn't obtain the parent, which means we have a
1583                         // disconnected sub-tree. Abort prefetch immediately.
1584                         return;
1585                     }
1586                     outInfos.add(parent);
1587                     parentNodeId = parent.getParentNodeId();
1588                     accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1589                             parentNodeId);
1590                 } else {
1591                     prefetchPredecessorsOfRealNode(providerHost, outInfos);
1592                     return;
1593                 }
1594             }
1595         }
1596 
prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost, AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos, boolean predecessorsPrefetched)1597         private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1598                 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos,
1599                 boolean predecessorsPrefetched) {
1600             final long parentNodeId = current.getParentNodeId();
1601             final int parentAccessibilityViewId =
1602                     AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1603             final int parentVirtualDescendantId =
1604                     AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
1605             if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
1606                     || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
1607                 final AccessibilityNodeInfo parent =
1608                         provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
1609                 if (parent != null) {
1610                     if (!predecessorsPrefetched) {
1611                         outInfos.add(parent);
1612                     }
1613                     final int childCount = parent.getChildCount();
1614                     for (int i = 0; i < childCount; i++) {
1615                         if (shouldStopPrefetching(outInfos)) {
1616                             return;
1617                         }
1618                         final long childNodeId = parent.getChildId(i);
1619                         if (childNodeId != current.getSourceNodeId()) {
1620                             final int childVirtualDescendantId =
1621                                 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1622                             AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1623                                     childVirtualDescendantId);
1624                             if (child != null) {
1625                                 outInfos.add(child);
1626                             }
1627                         }
1628                     }
1629                 }
1630             } else {
1631                 prefetchSiblingsOfRealNode(providerHost, outInfos, predecessorsPrefetched);
1632             }
1633         }
1634 
prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root, AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos)1635         private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1636                 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1637             final int initialOutInfosSize = outInfos.size();
1638             final int childCount = root.getChildCount();
1639             for (int i = 0; i < childCount; i++) {
1640                 if (shouldStopPrefetching(outInfos)) {
1641                     return;
1642                 }
1643                 final long childNodeId = root.getChildId(i);
1644                 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1645                         AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1646                 if (child != null) {
1647                     outInfos.add(child);
1648                 }
1649             }
1650             if (!shouldStopPrefetching(outInfos)) {
1651                 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1652                 for (int i = 0; i < addedChildCount; i++) {
1653                     AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1654                     prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1655                 }
1656             }
1657         }
1658     }
1659 
1660     private class PrivateHandler extends Handler {
1661         private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
1662         private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1663         private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1664         private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
1665         private static final int MSG_FIND_FOCUS = 5;
1666         private static final int MSG_FOCUS_SEARCH = 6;
1667         private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;
1668         private static final int MSG_APP_PREPARATION_FINISHED = 8;
1669         private static final int MSG_APP_PREPARATION_TIMEOUT = 9;
1670 
1671         // Uses FIRST_NO_ACCESSIBILITY_CALLBACK_MSG for messages that don't need to call back
1672         // results to interrogating client.
1673         private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100;
1674         private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS =
1675                 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1;
1676         private static final int MSG_NOTIFY_OUTSIDE_TOUCH =
1677                 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 2;
1678 
PrivateHandler(Looper looper)1679         public PrivateHandler(Looper looper) {
1680             super(looper);
1681         }
1682 
1683         @Override
getMessageName(Message message)1684         public String getMessageName(Message message) {
1685             final int type = message.what;
1686             switch (type) {
1687                 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1688                     return "MSG_PERFORM_ACCESSIBILITY_ACTION";
1689                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1690                     return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1691                 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1692                     return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1693                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1694                     return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
1695                 case MSG_FIND_FOCUS:
1696                     return "MSG_FIND_FOCUS";
1697                 case MSG_FOCUS_SEARCH:
1698                     return "MSG_FOCUS_SEARCH";
1699                 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:
1700                     return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";
1701                 case MSG_APP_PREPARATION_FINISHED:
1702                     return "MSG_APP_PREPARATION_FINISHED";
1703                 case MSG_APP_PREPARATION_TIMEOUT:
1704                     return "MSG_APP_PREPARATION_TIMEOUT";
1705                 case MSG_CLEAR_ACCESSIBILITY_FOCUS:
1706                     return "MSG_CLEAR_ACCESSIBILITY_FOCUS";
1707                 case MSG_NOTIFY_OUTSIDE_TOUCH:
1708                     return "MSG_NOTIFY_OUTSIDE_TOUCH";
1709                 default:
1710                     throw new IllegalArgumentException("Unknown message type: " + type);
1711             }
1712         }
1713 
1714         @Override
handleMessage(Message message)1715         public void handleMessage(Message message) {
1716             final int type = message.what;
1717             switch (type) {
1718                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
1719                     findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1720                 } break;
1721                 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
1722                     performAccessibilityActionUiThread(message);
1723                 } break;
1724                 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
1725                     findAccessibilityNodeInfosByViewIdUiThread(message);
1726                 } break;
1727                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
1728                     findAccessibilityNodeInfosByTextUiThread(message);
1729                 } break;
1730                 case MSG_FIND_FOCUS: {
1731                     findFocusUiThread(message);
1732                 } break;
1733                 case MSG_FOCUS_SEARCH: {
1734                     focusSearchUiThread(message);
1735                 } break;
1736                 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
1737                     prepareForExtraDataRequestUiThread(message);
1738                 } break;
1739                 case MSG_APP_PREPARATION_FINISHED: {
1740                     requestPreparerDoneUiThread(message);
1741                 } break;
1742                 case MSG_APP_PREPARATION_TIMEOUT: {
1743                     requestPreparerTimeoutUiThread();
1744                 } break;
1745                 case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
1746                     clearAccessibilityFocusUiThread();
1747                 } break;
1748                 case MSG_NOTIFY_OUTSIDE_TOUCH: {
1749                     notifyOutsideTouchUiThread();
1750                 } break;
1751                 default:
1752                     throw new IllegalArgumentException("Unknown message type: " + type);
1753             }
1754         }
1755 
hasAccessibilityCallback(Message message)1756         boolean hasAccessibilityCallback(Message message) {
1757             return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false;
1758         }
1759 
hasUserInteractiveMessagesWaiting()1760         boolean hasUserInteractiveMessagesWaiting() {
1761             return hasMessagesOrCallbacks();
1762         }
1763     }
1764 
1765     private final class AddNodeInfosForViewId implements Predicate<View> {
1766         private int mViewId = View.NO_ID;
1767         private List<AccessibilityNodeInfo> mInfos;
1768 
init(int viewId, List<AccessibilityNodeInfo> infos)1769         public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1770             mViewId = viewId;
1771             mInfos = infos;
1772         }
1773 
reset()1774         public void reset() {
1775             mViewId = View.NO_ID;
1776             mInfos = null;
1777         }
1778 
1779         @Override
test(View view)1780         public boolean test(View view) {
1781             if (view.getId() == mViewId && isShown(view) && isVisibleToAccessibilityService(view)) {
1782                 mInfos.add(view.createAccessibilityNodeInfo());
1783             }
1784             return false;
1785         }
1786     }
1787 
1788     private static final class MessageHolder {
1789         final Message mMessage;
1790         final int mInterrogatingPid;
1791         final long mInterrogatingTid;
1792 
MessageHolder(Message message, int interrogatingPid, long interrogatingTid)1793         MessageHolder(Message message, int interrogatingPid, long interrogatingTid) {
1794             mMessage = message;
1795             mInterrogatingPid = interrogatingPid;
1796             mInterrogatingTid = interrogatingTid;
1797         }
1798     }
1799 
1800     private static class SatisfiedFindAccessibilityNodeByAccessibilityIdRequest {
1801         final AccessibilityNodeInfo mSatisfiedRequestNode;
1802         final IAccessibilityInteractionConnectionCallback mSatisfiedRequestCallback;
1803         final int mSatisfiedRequestInteractionId;
1804 
SatisfiedFindAccessibilityNodeByAccessibilityIdRequest( AccessibilityNodeInfo satisfiedRequestNode, IAccessibilityInteractionConnectionCallback satisfiedRequestCallback, int satisfiedRequestInteractionId)1805         SatisfiedFindAccessibilityNodeByAccessibilityIdRequest(
1806                 AccessibilityNodeInfo satisfiedRequestNode,
1807                 IAccessibilityInteractionConnectionCallback satisfiedRequestCallback,
1808                 int satisfiedRequestInteractionId) {
1809             mSatisfiedRequestNode = satisfiedRequestNode;
1810             mSatisfiedRequestCallback = satisfiedRequestCallback;
1811             mSatisfiedRequestInteractionId = satisfiedRequestInteractionId;
1812         }
1813     }
1814 
1815     private class PrefetchDeque<E extends DequeNode>
1816             extends ArrayDeque<E> {
1817         int mStrategy;
1818         List<AccessibilityNodeInfo> mPrefetchOutput;
1819 
PrefetchDeque(int strategy, List<AccessibilityNodeInfo> output)1820         PrefetchDeque(int strategy, List<AccessibilityNodeInfo> output) {
1821             mStrategy = strategy;
1822             mPrefetchOutput = output;
1823         }
1824 
1825         /** Performs depth-first or breadth-first traversal.
1826          *
1827          * For depth-first search, we iterate through the children in backwards order and push them
1828          * to the stack before taking from the head. For breadth-first search, we iterate through
1829          * the children in order and push them to the stack before taking from the tail.
1830          *
1831          * Depth-first search:  0 has children 0, 1, 2, 4. 1 has children 5 and 6.
1832          * Head         Tail
1833          * 1  2  3  4 ->  pop: 1 -> 5  6  2  3  4
1834          *
1835          * Breadth-first search
1836          * Head         Tail
1837          * 4  3  2  1 -> remove last: 1 -> 6  5  3  2
1838          *
1839          **/
performTraversalAndPrefetch()1840         void performTraversalAndPrefetch() {
1841             try {
1842                 while (!isEmpty()) {
1843                     E child = getNext();
1844                     AccessibilityNodeInfo childInfo = child.getA11yNodeInfo();
1845                     if (childInfo != null) {
1846                         mPrefetchOutput.add(childInfo);
1847                     }
1848                     if (mPrefetcher.shouldStopPrefetching(mPrefetchOutput)) {
1849                         return;
1850                     }
1851                     // Add children to deque.
1852                     child.addChildren(childInfo, this);
1853                 }
1854             } finally {
1855                 clear();
1856             }
1857         }
1858 
getNext()1859         E getNext() {
1860             if (isStack()) {
1861                 return pop();
1862             }
1863             return removeLast();
1864         }
1865 
isStack()1866         boolean isStack() {
1867             return (mStrategy & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST) != 0;
1868         }
1869     }
1870 
1871     interface DequeNode {
getA11yNodeInfo()1872         AccessibilityNodeInfo getA11yNodeInfo();
addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque)1873         void addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque);
1874     }
1875 
1876     private class ViewNode implements DequeNode {
1877         View mView;
1878         private final ArrayList<View> mTempViewList = new ArrayList<>();
1879 
ViewNode(View view)1880         ViewNode(View view) {
1881             mView = view;
1882         }
1883 
1884         @Override
getA11yNodeInfo()1885         public AccessibilityNodeInfo getA11yNodeInfo() {
1886             if (mView == null) {
1887                 return null;
1888             }
1889             return mView.createAccessibilityNodeInfo();
1890         }
1891 
1892         @Override
addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque)1893         public void addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque) {
1894             if (mView == null) {
1895                 return;
1896             }
1897             if (!(mView instanceof ViewGroup)) {
1898                 return;
1899             }
1900             ArrayList<View> children = mTempViewList;
1901             children.clear();
1902             try {
1903                 mView.addChildrenForAccessibility(children);
1904                 final int childCount = children.size();
1905 
1906                 if (deque.isStack()) {
1907                     for (int i = childCount - 1; i >= 0; i--) {
1908                         addChild(deque, children.get(i));
1909                     }
1910                 } else {
1911                     for (int i = 0; i < childCount; i++) {
1912                         addChild(deque, children.get(i));
1913                     }
1914                 }
1915             } finally {
1916                 children.clear();
1917             }
1918         }
1919 
addChild(ArrayDeque deque, View child)1920         private void addChild(ArrayDeque deque, View child) {
1921             if (isShown(child)) {
1922                 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1923                 if (provider == null) {
1924                     deque.push(new ViewNode(child));
1925                 } else {
1926                     deque.push(new VirtualNode(AccessibilityNodeProvider.HOST_VIEW_ID,
1927                             provider));
1928                 }
1929             }
1930         }
1931     }
1932 
1933     private class VirtualNode implements DequeNode {
1934         long mInfoId;
1935         AccessibilityNodeProvider mProvider;
1936 
VirtualNode(long id, AccessibilityNodeProvider provider)1937         VirtualNode(long id, AccessibilityNodeProvider provider) {
1938             mInfoId = id;
1939             mProvider = provider;
1940         }
1941         @Override
getA11yNodeInfo()1942         public AccessibilityNodeInfo getA11yNodeInfo() {
1943             if (mProvider == null) {
1944                 return null;
1945             }
1946             return mProvider.createAccessibilityNodeInfo(
1947                     AccessibilityNodeInfo.getVirtualDescendantId(mInfoId));
1948         }
1949 
1950         @Override
addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque)1951         public void addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque) {
1952             if (virtualRoot == null) {
1953                 return;
1954             }
1955             final int childCount = virtualRoot.getChildCount();
1956             if (deque.isStack()) {
1957                 for (int i = childCount - 1; i >= 0; i--) {
1958                     final long childNodeId = virtualRoot.getChildId(i);
1959                     deque.push(new VirtualNode(childNodeId, mProvider));
1960                 }
1961             } else {
1962                 for (int i = 0; i < childCount; i++) {
1963                     final long childNodeId = virtualRoot.getChildId(i);
1964                     deque.push(new VirtualNode(childNodeId, mProvider));
1965                 }
1966             }
1967         }
1968     }
1969 
1970     /** Attaches an accessibility overlay to the specified window. */
attachAccessibilityOverlayToWindowClientThread(SurfaceControl sc)1971     public void attachAccessibilityOverlayToWindowClientThread(SurfaceControl sc) {
1972         mHandler.sendMessage(
1973                 obtainMessage(
1974                         AccessibilityInteractionController
1975                                 ::attachAccessibilityOverlayToWindowUiThread,
1976                         this,
1977                         sc));
1978     }
1979 
attachAccessibilityOverlayToWindowUiThread(SurfaceControl sc)1980     private void attachAccessibilityOverlayToWindowUiThread(SurfaceControl sc) {
1981         SurfaceControl parent = mViewRootImpl.getSurfaceControl();
1982         if (parent.isValid()) {
1983             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
1984             t.reparent(sc, parent).apply();
1985             t.close();
1986         }
1987     }
1988 }
1989