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