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 android.graphics.Matrix;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.graphics.RectF;
28 import android.graphics.Region;
29 import android.os.Binder;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.Parcelable;
35 import android.os.Process;
36 import android.os.RemoteException;
37 import android.os.SystemClock;
38 import android.text.style.AccessibilityClickableSpan;
39 import android.text.style.ClickableSpan;
40 import android.util.LongSparseArray;
41 import android.util.Slog;
42 import android.view.accessibility.AccessibilityInteractionClient;
43 import android.view.accessibility.AccessibilityManager;
44 import android.view.accessibility.AccessibilityNodeIdManager;
45 import android.view.accessibility.AccessibilityNodeInfo;
46 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
47 import android.view.accessibility.AccessibilityNodeProvider;
48 import android.view.accessibility.AccessibilityRequestPreparer;
49 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
50 
51 import com.android.internal.R;
52 import com.android.internal.annotations.GuardedBy;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.os.SomeArgs;
55 
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.LinkedList;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Queue;
63 import java.util.function.Predicate;
64 
65 /**
66  * Class for managing accessibility interactions initiated from the system
67  * and targeting the view hierarchy. A *ClientThread method is to be
68  * called from the interaction connection ViewAncestor gives the system to
69  * talk to it and a corresponding *UiThread method that is executed on the
70  * UI thread.
71  *
72  * @hide
73  */
74 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
75 public final class AccessibilityInteractionController {
76 
77     private static final String LOG_TAG = "AccessibilityInteractionController";
78 
79     // Debugging flag
80     private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
81 
82     // Constants for readability
83     private static final boolean IGNORE_REQUEST_PREPARERS = true;
84     private static final boolean CONSIDER_REQUEST_PREPARERS = false;
85 
86     // If an app holds off accessibility for longer than this, the hold-off is canceled to prevent
87     // accessibility from hanging
88     private static final long REQUEST_PREPARER_TIMEOUT_MS = 500;
89 
90     private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
91         new ArrayList<AccessibilityNodeInfo>();
92 
93     private final Object mLock = new Object();
94 
95     private final PrivateHandler mHandler;
96 
97     private final ViewRootImpl mViewRootImpl;
98 
99     private final AccessibilityNodePrefetcher mPrefetcher;
100 
101     private final long mMyLooperThreadId;
102 
103     private final int mMyProcessId;
104 
105     private final AccessibilityManager mA11yManager;
106 
107     private final ArrayList<View> mTempArrayList = new ArrayList<View>();
108 
109     private final Point mTempPoint = new Point();
110     private final Rect mTempRect = new Rect();
111     private final Rect mTempRect1 = new Rect();
112     private final Rect mTempRect2 = new Rect();
113     private final RectF mTempRectF = new RectF();
114 
115     private AddNodeInfosForViewId mAddNodeInfosForViewId;
116 
117     @GuardedBy("mLock")
118     private int mNumActiveRequestPreparers;
119     @GuardedBy("mLock")
120     private List<MessageHolder> mMessagesWaitingForRequestPreparer;
121     @GuardedBy("mLock")
122     private int mActiveRequestPreparerId;
123 
AccessibilityInteractionController(ViewRootImpl viewRootImpl)124     public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
125         Looper looper =  viewRootImpl.mHandler.getLooper();
126         mMyLooperThreadId = looper.getThread().getId();
127         mMyProcessId = Process.myPid();
128         mHandler = new PrivateHandler(looper);
129         mViewRootImpl = viewRootImpl;
130         mPrefetcher = new AccessibilityNodePrefetcher();
131         mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
132     }
133 
scheduleMessage(Message message, int interrogatingPid, long interrogatingTid, boolean ignoreRequestPreparers)134     private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
135             boolean ignoreRequestPreparers) {
136         if (ignoreRequestPreparers
137                 || !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {
138             // If the interrogation is performed by the same thread as the main UI
139             // thread in this process, set the message as a static reference so
140             // after this call completes the same thread but in the interrogating
141             // client can handle the message to generate the result.
142             if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
143                     && mHandler.hasAccessibilityCallback(message)) {
144                 AccessibilityInteractionClient.getInstanceForThread(
145                         interrogatingTid).setSameThreadMessage(message);
146             } else {
147                 // For messages without callback of interrogating client, just handle the
148                 // message immediately if this is UI thread.
149                 if (!mHandler.hasAccessibilityCallback(message)
150                         && Thread.currentThread().getId() == mMyLooperThreadId) {
151                     mHandler.handleMessage(message);
152                 } else {
153                     mHandler.sendMessage(message);
154                 }
155             }
156         }
157     }
158 
isShown(View view)159     private boolean isShown(View view) {
160         return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
161     }
162 
findAccessibilityNodeInfoByAccessibilityIdClientThread( long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle arguments)163     public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
164             long accessibilityNodeId, Region interactiveRegion, int interactionId,
165             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
166             long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
167         final Message message = mHandler.obtainMessage();
168         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
169         message.arg1 = flags;
170 
171         final SomeArgs args = SomeArgs.obtain();
172         args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
173         args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
174         args.argi3 = interactionId;
175         args.arg1 = callback;
176         args.arg2 = spec;
177         args.arg3 = interactiveRegion;
178         args.arg4 = arguments;
179         message.obj = args;
180 
181         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
182     }
183 
184     /**
185      * Check if this message needs to be held off while the app prepares to meet either this
186      * request, or a request ahead of it.
187      *
188      * @param originalMessage The message to be processed
189      * @param callingPid The calling process id
190      * @param callingTid The calling thread id
191      *
192      * @return {@code true} if the message is held off and will be processed later, {@code false} if
193      *         the message should be posted.
194      */
holdOffMessageIfNeeded( Message originalMessage, int callingPid, long callingTid)195     private boolean holdOffMessageIfNeeded(
196             Message originalMessage, int callingPid, long callingTid) {
197         synchronized (mLock) {
198             // If a request is already pending, queue this request for when it's finished
199             if (mNumActiveRequestPreparers != 0) {
200                 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
201                 return true;
202             }
203 
204             // Currently the only message that can hold things off is findByA11yId with extra data.
205             if (originalMessage.what
206                     != PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID) {
207                 return false;
208             }
209             SomeArgs originalMessageArgs = (SomeArgs) originalMessage.obj;
210             Bundle requestArguments = (Bundle) originalMessageArgs.arg4;
211             if (requestArguments == null) {
212                 return false;
213             }
214 
215             // If nothing it registered for this view, nothing to do
216             int accessibilityViewId = originalMessageArgs.argi1;
217             final List<AccessibilityRequestPreparer> preparers =
218                     mA11yManager.getRequestPreparersForAccessibilityId(accessibilityViewId);
219             if (preparers == null) {
220                 return false;
221             }
222 
223             // If the bundle doesn't request the extra data, nothing to do
224             final String extraDataKey = requestArguments.getString(EXTRA_DATA_REQUESTED_KEY);
225             if (extraDataKey == null) {
226                 return false;
227             }
228 
229             // Send the request to the AccessibilityRequestPreparers on the UI thread
230             mNumActiveRequestPreparers = preparers.size();
231             for (int i = 0; i < preparers.size(); i++) {
232                 final Message requestPreparerMessage = mHandler.obtainMessage(
233                         PrivateHandler.MSG_PREPARE_FOR_EXTRA_DATA_REQUEST);
234                 final SomeArgs requestPreparerArgs = SomeArgs.obtain();
235                 // virtualDescendentId
236                 requestPreparerArgs.argi1 =
237                         (originalMessageArgs.argi2 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
238                         ? AccessibilityNodeProvider.HOST_VIEW_ID : originalMessageArgs.argi2;
239                 requestPreparerArgs.arg1 = preparers.get(i);
240                 requestPreparerArgs.arg2 = extraDataKey;
241                 requestPreparerArgs.arg3 = requestArguments;
242                 Message preparationFinishedMessage = mHandler.obtainMessage(
243                         PrivateHandler.MSG_APP_PREPARATION_FINISHED);
244                 preparationFinishedMessage.arg1 = ++mActiveRequestPreparerId;
245                 requestPreparerArgs.arg4 = preparationFinishedMessage;
246 
247                 requestPreparerMessage.obj = requestPreparerArgs;
248                 scheduleMessage(requestPreparerMessage, callingPid, callingTid,
249                         IGNORE_REQUEST_PREPARERS);
250                 mHandler.obtainMessage(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
251                 mHandler.sendEmptyMessageDelayed(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT,
252                         REQUEST_PREPARER_TIMEOUT_MS);
253             }
254 
255             // Set the initial request aside
256             queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
257             return true;
258         }
259     }
260 
prepareForExtraDataRequestUiThread(Message message)261     private void prepareForExtraDataRequestUiThread(Message message) {
262         SomeArgs args = (SomeArgs) message.obj;
263         final int virtualDescendantId = args.argi1;
264         final AccessibilityRequestPreparer preparer = (AccessibilityRequestPreparer) args.arg1;
265         final String extraDataKey = (String) args.arg2;
266         final Bundle requestArguments = (Bundle) args.arg3;
267         final Message preparationFinishedMessage = (Message) args.arg4;
268 
269         preparer.onPrepareExtraData(virtualDescendantId, extraDataKey,
270                 requestArguments, preparationFinishedMessage);
271     }
272 
queueMessageToHandleOncePrepared(Message message, int interrogatingPid, long interrogatingTid)273     private void queueMessageToHandleOncePrepared(Message message, int interrogatingPid,
274             long interrogatingTid) {
275         if (mMessagesWaitingForRequestPreparer == null) {
276             mMessagesWaitingForRequestPreparer = new ArrayList<>(1);
277         }
278         MessageHolder messageHolder =
279                 new MessageHolder(message, interrogatingPid, interrogatingTid);
280         mMessagesWaitingForRequestPreparer.add(messageHolder);
281     }
282 
requestPreparerDoneUiThread(Message message)283     private void requestPreparerDoneUiThread(Message message) {
284         synchronized (mLock) {
285             if (message.arg1 != mActiveRequestPreparerId) {
286                 Slog.e(LOG_TAG, "Surprising AccessibilityRequestPreparer callback (likely late)");
287                 return;
288             }
289             mNumActiveRequestPreparers--;
290             if (mNumActiveRequestPreparers <= 0) {
291                 mHandler.removeMessages(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
292                 scheduleAllMessagesWaitingForRequestPreparerLocked();
293             }
294         }
295     }
296 
requestPreparerTimeoutUiThread()297     private void requestPreparerTimeoutUiThread() {
298         synchronized (mLock) {
299             Slog.e(LOG_TAG, "AccessibilityRequestPreparer timed out");
300             scheduleAllMessagesWaitingForRequestPreparerLocked();
301         }
302     }
303 
304     @GuardedBy("mLock")
scheduleAllMessagesWaitingForRequestPreparerLocked()305     private void scheduleAllMessagesWaitingForRequestPreparerLocked() {
306         int numMessages = mMessagesWaitingForRequestPreparer.size();
307         for (int i = 0; i < numMessages; i++) {
308             MessageHolder request = mMessagesWaitingForRequestPreparer.get(i);
309             scheduleMessage(request.mMessage, request.mInterrogatingPid,
310                     request.mInterrogatingTid,
311                     (i == 0) /* the app is ready for the first request */);
312         }
313         mMessagesWaitingForRequestPreparer.clear();
314         mNumActiveRequestPreparers = 0; // Just to be safe - should be unnecessary
315         mActiveRequestPreparerId = -1;
316     }
317 
findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message)318     private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
319         final int flags = message.arg1;
320 
321         SomeArgs args = (SomeArgs) message.obj;
322         final int accessibilityViewId = args.argi1;
323         final int virtualDescendantId = args.argi2;
324         final int interactionId = args.argi3;
325         final IAccessibilityInteractionConnectionCallback callback =
326             (IAccessibilityInteractionConnectionCallback) args.arg1;
327         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
328         final Region interactiveRegion = (Region) args.arg3;
329         final Bundle arguments = (Bundle) args.arg4;
330 
331         args.recycle();
332 
333         List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
334         infos.clear();
335         try {
336             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
337                 return;
338             }
339             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
340             final View root = findViewByAccessibilityId(accessibilityViewId);
341             if (root != null && isShown(root)) {
342                 mPrefetcher.prefetchAccessibilityNodeInfos(
343                         root, virtualDescendantId, flags, infos, arguments);
344             }
345         } finally {
346             updateInfosForViewportAndReturnFindNodeResult(
347                     infos, callback, interactionId, spec, interactiveRegion);
348         }
349     }
350 
findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)351     public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
352             String viewId, Region interactiveRegion, int interactionId,
353             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
354             long interrogatingTid, MagnificationSpec spec) {
355         Message message = mHandler.obtainMessage();
356         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
357         message.arg1 = flags;
358         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
359 
360         SomeArgs args = SomeArgs.obtain();
361         args.argi1 = interactionId;
362         args.arg1 = callback;
363         args.arg2 = spec;
364         args.arg3 = viewId;
365         args.arg4 = interactiveRegion;
366         message.obj = args;
367 
368         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
369     }
370 
findAccessibilityNodeInfosByViewIdUiThread(Message message)371     private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
372         final int flags = message.arg1;
373         final int accessibilityViewId = message.arg2;
374 
375         SomeArgs args = (SomeArgs) message.obj;
376         final int interactionId = args.argi1;
377         final IAccessibilityInteractionConnectionCallback callback =
378             (IAccessibilityInteractionConnectionCallback) args.arg1;
379         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
380         final String viewId = (String) args.arg3;
381         final Region interactiveRegion = (Region) args.arg4;
382         args.recycle();
383 
384         final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
385         infos.clear();
386         try {
387             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
388                 return;
389             }
390             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
391             final View root = findViewByAccessibilityId(accessibilityViewId);
392             if (root != null) {
393                 final int resolvedViewId = root.getContext().getResources()
394                         .getIdentifier(viewId, null, null);
395                 if (resolvedViewId <= 0) {
396                     return;
397                 }
398                 if (mAddNodeInfosForViewId == null) {
399                     mAddNodeInfosForViewId = new AddNodeInfosForViewId();
400                 }
401                 mAddNodeInfosForViewId.init(resolvedViewId, infos);
402                 root.findViewByPredicate(mAddNodeInfosForViewId);
403                 mAddNodeInfosForViewId.reset();
404             }
405         } finally {
406             updateInfosForViewportAndReturnFindNodeResult(
407                     infos, callback, interactionId, spec, interactiveRegion);
408         }
409     }
410 
findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)411     public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
412             String text, Region interactiveRegion, int interactionId,
413             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
414             long interrogatingTid, MagnificationSpec spec) {
415         Message message = mHandler.obtainMessage();
416         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
417         message.arg1 = flags;
418 
419         SomeArgs args = SomeArgs.obtain();
420         args.arg1 = text;
421         args.arg2 = callback;
422         args.arg3 = spec;
423         args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
424         args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
425         args.argi3 = interactionId;
426         args.arg4 = interactiveRegion;
427         message.obj = args;
428 
429         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
430     }
431 
findAccessibilityNodeInfosByTextUiThread(Message message)432     private void findAccessibilityNodeInfosByTextUiThread(Message message) {
433         final int flags = message.arg1;
434 
435         SomeArgs args = (SomeArgs) message.obj;
436         final String text = (String) args.arg1;
437         final IAccessibilityInteractionConnectionCallback callback =
438             (IAccessibilityInteractionConnectionCallback) args.arg2;
439         final MagnificationSpec spec = (MagnificationSpec) args.arg3;
440         final int accessibilityViewId = args.argi1;
441         final int virtualDescendantId = args.argi2;
442         final int interactionId = args.argi3;
443         final Region interactiveRegion = (Region) args.arg4;
444         args.recycle();
445 
446         List<AccessibilityNodeInfo> infos = null;
447         try {
448             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
449                 return;
450             }
451             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
452             final View root = findViewByAccessibilityId(accessibilityViewId);
453             if (root != null && isShown(root)) {
454                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
455                 if (provider != null) {
456                     infos = provider.findAccessibilityNodeInfosByText(text,
457                             virtualDescendantId);
458                 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
459                     ArrayList<View> foundViews = mTempArrayList;
460                     foundViews.clear();
461                     root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
462                             | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
463                             | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
464                     if (!foundViews.isEmpty()) {
465                         infos = mTempAccessibilityNodeInfoList;
466                         infos.clear();
467                         final int viewCount = foundViews.size();
468                         for (int i = 0; i < viewCount; i++) {
469                             View foundView = foundViews.get(i);
470                             if (isShown(foundView)) {
471                                 provider = foundView.getAccessibilityNodeProvider();
472                                 if (provider != null) {
473                                     List<AccessibilityNodeInfo> infosFromProvider =
474                                         provider.findAccessibilityNodeInfosByText(text,
475                                                 AccessibilityNodeProvider.HOST_VIEW_ID);
476                                     if (infosFromProvider != null) {
477                                         infos.addAll(infosFromProvider);
478                                     }
479                                 } else  {
480                                     infos.add(foundView.createAccessibilityNodeInfo());
481                                 }
482                             }
483                         }
484                     }
485                 }
486             }
487         } finally {
488             updateInfosForViewportAndReturnFindNodeResult(
489                     infos, callback, interactionId, spec, interactiveRegion);
490         }
491     }
492 
findFocusClientThread(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)493     public void findFocusClientThread(long accessibilityNodeId, int focusType,
494             Region interactiveRegion, int interactionId,
495             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
496             long interrogatingTid, MagnificationSpec spec) {
497         Message message = mHandler.obtainMessage();
498         message.what = PrivateHandler.MSG_FIND_FOCUS;
499         message.arg1 = flags;
500         message.arg2 = focusType;
501 
502         SomeArgs args = SomeArgs.obtain();
503         args.argi1 = interactionId;
504         args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
505         args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
506         args.arg1 = callback;
507         args.arg2 = spec;
508         args.arg3 = interactiveRegion;
509 
510         message.obj = args;
511 
512         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
513     }
514 
findFocusUiThread(Message message)515     private void findFocusUiThread(Message message) {
516         final int flags = message.arg1;
517         final int focusType = message.arg2;
518 
519         SomeArgs args = (SomeArgs) message.obj;
520         final int interactionId = args.argi1;
521         final int accessibilityViewId = args.argi2;
522         final int virtualDescendantId = args.argi3;
523         final IAccessibilityInteractionConnectionCallback callback =
524             (IAccessibilityInteractionConnectionCallback) args.arg1;
525         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
526         final Region interactiveRegion = (Region) args.arg3;
527         args.recycle();
528 
529         AccessibilityNodeInfo focused = null;
530         try {
531             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
532                 return;
533             }
534             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
535             final View root = findViewByAccessibilityId(accessibilityViewId);
536             if (root != null && isShown(root)) {
537                 switch (focusType) {
538                     case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
539                         View host = mViewRootImpl.mAccessibilityFocusedHost;
540                         // If there is no accessibility focus host or it is not a descendant
541                         // of the root from which to start the search, then the search failed.
542                         if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
543                             break;
544                         }
545                         // The focused view not shown, we failed.
546                         if (!isShown(host)) {
547                             break;
548                         }
549                         // If the host has a provider ask this provider to search for the
550                         // focus instead fetching all provider nodes to do the search here.
551                         AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
552                         if (provider != null) {
553                             if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
554                                 focused = AccessibilityNodeInfo.obtain(
555                                         mViewRootImpl.mAccessibilityFocusedVirtualView);
556                             }
557                         } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
558                             focused = host.createAccessibilityNodeInfo();
559                         }
560                     } break;
561                     case AccessibilityNodeInfo.FOCUS_INPUT: {
562                         View target = root.findFocus();
563                         if (!isShown(target)) {
564                             break;
565                         }
566                         AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
567                         if (provider != null) {
568                             focused = provider.findFocus(focusType);
569                         }
570                         if (focused == null) {
571                             focused = target.createAccessibilityNodeInfo();
572                         }
573                     } break;
574                     default:
575                         throw new IllegalArgumentException("Unknown focus type: " + focusType);
576                 }
577             }
578         } finally {
579             updateInfoForViewportAndReturnFindNodeResult(
580                     focused, callback, interactionId, spec, interactiveRegion);
581         }
582     }
583 
focusSearchClientThread(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)584     public void focusSearchClientThread(long accessibilityNodeId, int direction,
585             Region interactiveRegion, int interactionId,
586             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
587             long interrogatingTid, MagnificationSpec spec) {
588         Message message = mHandler.obtainMessage();
589         message.what = PrivateHandler.MSG_FOCUS_SEARCH;
590         message.arg1 = flags;
591         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
592 
593         SomeArgs args = SomeArgs.obtain();
594         args.argi2 = direction;
595         args.argi3 = interactionId;
596         args.arg1 = callback;
597         args.arg2 = spec;
598         args.arg3 = interactiveRegion;
599 
600         message.obj = args;
601 
602         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
603     }
604 
focusSearchUiThread(Message message)605     private void focusSearchUiThread(Message message) {
606         final int flags = message.arg1;
607         final int accessibilityViewId = message.arg2;
608 
609         SomeArgs args = (SomeArgs) message.obj;
610         final int direction = args.argi2;
611         final int interactionId = args.argi3;
612         final IAccessibilityInteractionConnectionCallback callback =
613             (IAccessibilityInteractionConnectionCallback) args.arg1;
614         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
615         final Region interactiveRegion = (Region) args.arg3;
616 
617         args.recycle();
618 
619         AccessibilityNodeInfo next = null;
620         try {
621             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
622                 return;
623             }
624             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
625             final View root = findViewByAccessibilityId(accessibilityViewId);
626             if (root != null && isShown(root)) {
627                 View nextView = root.focusSearch(direction);
628                 if (nextView != null) {
629                     next = nextView.createAccessibilityNodeInfo();
630                 }
631             }
632         } finally {
633             updateInfoForViewportAndReturnFindNodeResult(
634                     next, callback, interactionId, spec, interactiveRegion);
635         }
636     }
637 
performAccessibilityActionClientThread(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid)638     public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
639             Bundle arguments, int interactionId,
640             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
641             long interrogatingTid) {
642         Message message = mHandler.obtainMessage();
643         message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
644         message.arg1 = flags;
645         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
646 
647         SomeArgs args = SomeArgs.obtain();
648         args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
649         args.argi2 = action;
650         args.argi3 = interactionId;
651         args.arg1 = callback;
652         args.arg2 = arguments;
653 
654         message.obj = args;
655 
656         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
657     }
658 
performAccessibilityActionUiThread(Message message)659     private void performAccessibilityActionUiThread(Message message) {
660         final int flags = message.arg1;
661         final int accessibilityViewId = message.arg2;
662 
663         SomeArgs args = (SomeArgs) message.obj;
664         final int virtualDescendantId = args.argi1;
665         final int action = args.argi2;
666         final int interactionId = args.argi3;
667         final IAccessibilityInteractionConnectionCallback callback =
668             (IAccessibilityInteractionConnectionCallback) args.arg1;
669         Bundle arguments = (Bundle) args.arg2;
670 
671         args.recycle();
672 
673         boolean succeeded = false;
674         try {
675             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
676                     mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
677                 return;
678             }
679             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
680             final View target = findViewByAccessibilityId(accessibilityViewId);
681             if (target != null && isShown(target)) {
682                 if (action == R.id.accessibilityActionClickOnClickableSpan) {
683                     // Handle this hidden action separately
684                     succeeded = handleClickableSpanActionUiThread(
685                             target, virtualDescendantId, arguments);
686                 } else {
687                     AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
688                     if (provider != null) {
689                         succeeded = provider.performAction(virtualDescendantId, action,
690                                 arguments);
691                     } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
692                         succeeded = target.performAccessibilityAction(action, arguments);
693                     }
694                 }
695             }
696         } finally {
697             try {
698                 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
699                 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
700             } catch (RemoteException re) {
701                 /* ignore - the other side will time out */
702             }
703         }
704     }
705 
706     /**
707      * Finds the accessibility focused node in the root, and clears the accessibility focus.
708      */
clearAccessibilityFocusClientThread()709     public void clearAccessibilityFocusClientThread() {
710         final Message message = mHandler.obtainMessage();
711         message.what = PrivateHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS;
712 
713         // Don't care about pid and tid because there's no interrogating client for this message.
714         scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
715     }
716 
clearAccessibilityFocusUiThread()717     private void clearAccessibilityFocusUiThread() {
718         if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
719             return;
720         }
721         try {
722             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags =
723                     AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
724             final View root = mViewRootImpl.mView;
725             if (root != null && isShown(root)) {
726                 final View host = mViewRootImpl.mAccessibilityFocusedHost;
727                 // If there is no accessibility focus host or it is not a descendant
728                 // of the root from which to start the search, then the search failed.
729                 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
730                     return;
731                 }
732                 final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
733                 final AccessibilityNodeInfo focusNode =
734                         mViewRootImpl.mAccessibilityFocusedVirtualView;
735                 if (provider != null && focusNode != null) {
736                     final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
737                             focusNode.getSourceNodeId());
738                     provider.performAction(virtualNodeId,
739                             AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
740                             null);
741                 } else {
742                     host.performAccessibilityAction(
743                             AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
744                             null);
745                 }
746             }
747         } finally {
748             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
749         }
750     }
751 
752     /**
753      * Notify outside touch event to the target window.
754      */
notifyOutsideTouchClientThread()755     public void notifyOutsideTouchClientThread() {
756         final Message message = mHandler.obtainMessage();
757         message.what = PrivateHandler.MSG_NOTIFY_OUTSIDE_TOUCH;
758 
759         // Don't care about pid and tid because there's no interrogating client for this message.
760         scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
761     }
762 
notifyOutsideTouchUiThread()763     private void notifyOutsideTouchUiThread() {
764         if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null
765                 || mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
766             return;
767         }
768         final View root = mViewRootImpl.mView;
769         if (root != null && isShown(root)) {
770             // trigger ACTION_OUTSIDE to notify windows
771             final long now = SystemClock.uptimeMillis();
772             final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
773                     0, 0, 0);
774             event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
775             mViewRootImpl.dispatchInputEvent(event);
776         }
777     }
778 
findViewByAccessibilityId(int accessibilityId)779     private View findViewByAccessibilityId(int accessibilityId) {
780         if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
781             return mViewRootImpl.mView;
782         } else {
783             return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
784         }
785     }
786 
applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos, MagnificationSpec spec)787     private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
788             MagnificationSpec spec) {
789         if (infos == null) {
790             return;
791         }
792         final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
793         if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
794             final int infoCount = infos.size();
795             for (int i = 0; i < infoCount; i++) {
796                 AccessibilityNodeInfo info = infos.get(i);
797                 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
798             }
799         }
800     }
801 
adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos, Region interactiveRegion)802     private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
803             Region interactiveRegion) {
804         if (interactiveRegion == null || infos == null) {
805             return;
806         }
807         final int infoCount = infos.size();
808         for (int i = 0; i < infoCount; i++) {
809             AccessibilityNodeInfo info = infos.get(i);
810             adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
811         }
812     }
813 
adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, Region interactiveRegion)814     private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
815             Region interactiveRegion) {
816         if (interactiveRegion == null || info == null) {
817             return;
818         }
819         Rect boundsInScreen = mTempRect;
820         info.getBoundsInScreen(boundsInScreen);
821         if (interactiveRegion.quickReject(boundsInScreen) && !shouldBypassAdjustIsVisible()) {
822             info.setVisibleToUser(false);
823         }
824     }
825 
shouldBypassAdjustIsVisible()826     private boolean shouldBypassAdjustIsVisible() {
827         final int windowType = mViewRootImpl.mOrigWindowType;
828         if (windowType == TYPE_INPUT_METHOD) {
829             return true;
830         }
831         return false;
832     }
833 
adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos)834     private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) {
835         if (infos == null || shouldBypassAdjustBoundsInScreen()) {
836             return;
837         }
838         final int infoCount = infos.size();
839         for (int i = 0; i < infoCount; i++) {
840             final AccessibilityNodeInfo info = infos.get(i);
841             adjustBoundsInScreenIfNeeded(info);
842         }
843     }
844 
adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info)845     private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) {
846         if (info == null || shouldBypassAdjustBoundsInScreen()) {
847             return;
848         }
849         final Rect boundsInScreen = mTempRect;
850         info.getBoundsInScreen(boundsInScreen);
851         boundsInScreen.offset(mViewRootImpl.mAttachInfo.mLocationInParentDisplay.x,
852                 mViewRootImpl.mAttachInfo.mLocationInParentDisplay.y);
853         info.setBoundsInScreen(boundsInScreen);
854     }
855 
shouldBypassAdjustBoundsInScreen()856     private boolean shouldBypassAdjustBoundsInScreen() {
857         return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
858     }
859 
applyScreenMatrixIfNeeded(List<AccessibilityNodeInfo> infos)860     private void applyScreenMatrixIfNeeded(List<AccessibilityNodeInfo> infos) {
861         if (infos == null || shouldBypassApplyScreenMatrix()) {
862             return;
863         }
864         final int infoCount = infos.size();
865         for (int i = 0; i < infoCount; i++) {
866             final AccessibilityNodeInfo info = infos.get(i);
867             applyScreenMatrixIfNeeded(info);
868         }
869     }
870 
applyScreenMatrixIfNeeded(AccessibilityNodeInfo info)871     private void applyScreenMatrixIfNeeded(AccessibilityNodeInfo info) {
872         if (info == null || shouldBypassApplyScreenMatrix()) {
873             return;
874         }
875         final Rect boundsInScreen = mTempRect;
876         final RectF transformedBounds = mTempRectF;
877         final Matrix screenMatrix = mViewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy;
878 
879         info.getBoundsInScreen(boundsInScreen);
880         transformedBounds.set(boundsInScreen);
881         screenMatrix.mapRect(transformedBounds);
882         boundsInScreen.set((int) transformedBounds.left, (int) transformedBounds.top,
883                 (int) transformedBounds.right, (int) transformedBounds.bottom);
884         info.setBoundsInScreen(boundsInScreen);
885     }
886 
shouldBypassApplyScreenMatrix()887     private boolean shouldBypassApplyScreenMatrix() {
888         final Matrix screenMatrix = mViewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy;
889         return screenMatrix == null || screenMatrix.isIdentity();
890     }
891 
associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos)892     private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
893         if (infos == null || shouldBypassAssociateLeashedParent()) {
894             return;
895         }
896         final int infoCount = infos.size();
897         for (int i = 0; i < infoCount; i++) {
898             final AccessibilityNodeInfo info = infos.get(i);
899             associateLeashedParentIfNeeded(info);
900         }
901     }
902 
associateLeashedParentIfNeeded(AccessibilityNodeInfo info)903     private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
904         if (info == null || shouldBypassAssociateLeashedParent()) {
905             return;
906         }
907         // The node id of root node in embedded maybe not be ROOT_NODE_ID so we compare the id
908         // with root view.
909         if (mViewRootImpl.mView.getAccessibilityViewId()
910                 != AccessibilityNodeInfo.getAccessibilityViewId(info.getSourceNodeId())) {
911             return;
912         }
913         info.setLeashedParent(mViewRootImpl.mAttachInfo.mLeashedParentToken,
914                 mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId);
915     }
916 
shouldBypassAssociateLeashedParent()917     private boolean shouldBypassAssociateLeashedParent() {
918         return (mViewRootImpl.mAttachInfo.mLeashedParentToken == null
919                 && mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId == View.NO_ID);
920     }
921 
applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info, MagnificationSpec spec)922     private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
923             MagnificationSpec spec) {
924         if (info == null) {
925             return;
926         }
927 
928         final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
929         if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
930             return;
931         }
932 
933         Rect boundsInParent = mTempRect;
934         Rect boundsInScreen = mTempRect1;
935 
936         info.getBoundsInParent(boundsInParent);
937         info.getBoundsInScreen(boundsInScreen);
938         if (applicationScale != 1.0f) {
939             boundsInParent.scale(applicationScale);
940             boundsInScreen.scale(applicationScale);
941         }
942         if (spec != null) {
943             boundsInParent.scale(spec.scale);
944             // boundsInParent must not be offset.
945             boundsInScreen.scale(spec.scale);
946             boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
947         }
948         info.setBoundsInParent(boundsInParent);
949         info.setBoundsInScreen(boundsInScreen);
950 
951         // Scale text locations if they are present
952         if (info.hasExtras()) {
953             Bundle extras = info.getExtras();
954             Parcelable[] textLocations =
955                     extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
956             if (textLocations != null) {
957                 for (int i = 0; i < textLocations.length; i++) {
958                     // Unchecked cast - an app that puts other objects in this bundle with this
959                     // key will crash.
960                     RectF textLocation = ((RectF) textLocations[i]);
961                     textLocation.scale(applicationScale);
962                     if (spec != null) {
963                         textLocation.scale(spec.scale);
964                         textLocation.offset(spec.offsetX, spec.offsetY);
965                     }
966                 }
967             }
968         }
969     }
970 
shouldApplyAppScaleAndMagnificationSpec(float appScale, MagnificationSpec spec)971     private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
972             MagnificationSpec spec) {
973         return (appScale != 1.0f || (spec != null && !spec.isNop()));
974     }
975 
updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion)976     private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
977             IAccessibilityInteractionConnectionCallback callback, int interactionId,
978             MagnificationSpec spec, Region interactiveRegion) {
979         try {
980             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
981             associateLeashedParentIfNeeded(infos);
982             applyScreenMatrixIfNeeded(infos);
983             adjustBoundsInScreenIfNeeded(infos);
984             // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
985             // then impact the visibility result, we need to adjust visibility before apply scale.
986             adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
987             applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
988             callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
989             if (infos != null) {
990                 infos.clear();
991             }
992         } catch (RemoteException re) {
993             /* ignore - the other side will time out */
994         } finally {
995             recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
996         }
997     }
998 
updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion)999     private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
1000             IAccessibilityInteractionConnectionCallback callback, int interactionId,
1001             MagnificationSpec spec, Region interactiveRegion) {
1002         try {
1003             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
1004             associateLeashedParentIfNeeded(info);
1005             applyScreenMatrixIfNeeded(info);
1006             adjustBoundsInScreenIfNeeded(info);
1007             // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
1008             // then impact the visibility result, we need to adjust visibility before apply scale.
1009             adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
1010             applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
1011             callback.setFindAccessibilityNodeInfoResult(info, interactionId);
1012         } catch (RemoteException re) {
1013                 /* ignore - the other side will time out */
1014         } finally {
1015             recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
1016         }
1017     }
1018 
recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region)1019     private void recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region) {
1020         if (android.os.Process.myPid() != Binder.getCallingPid()) {
1021             // Specs are cached in the system process and obtained from a pool when read from
1022             // a parcel, so only recycle the spec if called from another process.
1023             if (spec != null) {
1024                 spec.recycle();
1025             }
1026         } else {
1027             // Regions are obtained in the system process and instantiated when read from
1028             // a parcel, so only recycle the region if caled from the same process.
1029             if (region != null) {
1030                 region.recycle();
1031             }
1032         }
1033     }
1034 
handleClickableSpanActionUiThread( View view, int virtualDescendantId, Bundle arguments)1035     private boolean handleClickableSpanActionUiThread(
1036             View view, int virtualDescendantId, Bundle arguments) {
1037         Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
1038         if (!(span instanceof AccessibilityClickableSpan)) {
1039             return false;
1040         }
1041 
1042         // Find the original ClickableSpan if it's still on the screen
1043         AccessibilityNodeInfo infoWithSpan = null;
1044         AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
1045         if (provider != null) {
1046             infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
1047         } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
1048             infoWithSpan = view.createAccessibilityNodeInfo();
1049         }
1050         if (infoWithSpan == null) {
1051             return false;
1052         }
1053 
1054         // Click on the corresponding span
1055         ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
1056                 infoWithSpan.getOriginalText());
1057         if (clickableSpan != null) {
1058             clickableSpan.onClick(view);
1059             return true;
1060         }
1061         return false;
1062     }
1063 
1064     /**
1065      * This class encapsulates a prefetching strategy for the accessibility APIs for
1066      * querying window content. It is responsible to prefetch a batch of
1067      * AccessibilityNodeInfos in addition to the one for a requested node.
1068      */
1069     private class AccessibilityNodePrefetcher {
1070 
1071         private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
1072 
1073         private final ArrayList<View> mTempViewList = new ArrayList<View>();
1074 
prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos, Bundle arguments)1075         public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
1076                 List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
1077             AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
1078             // Determine if we'll be populating extra data
1079             final String extraDataRequested = (arguments == null) ? null
1080                     : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
1081             if (provider == null) {
1082                 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
1083                 if (root != null) {
1084                     if (extraDataRequested != null) {
1085                         view.addExtraDataToAccessibilityNodeInfo(
1086                                 root, extraDataRequested, arguments);
1087                     }
1088                     outInfos.add(root);
1089                     if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
1090                         prefetchPredecessorsOfRealNode(view, outInfos);
1091                     }
1092                     if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
1093                         prefetchSiblingsOfRealNode(view, outInfos);
1094                     }
1095                     if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
1096                         prefetchDescendantsOfRealNode(view, outInfos);
1097                     }
1098                 }
1099             } else {
1100                 final AccessibilityNodeInfo root =
1101                         provider.createAccessibilityNodeInfo(virtualViewId);
1102                 if (root != null) {
1103                     if (extraDataRequested != null) {
1104                         provider.addExtraDataToAccessibilityNodeInfo(
1105                                 virtualViewId, root, extraDataRequested, arguments);
1106                     }
1107                     outInfos.add(root);
1108                     if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
1109                         prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
1110                     }
1111                     if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
1112                         prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
1113                     }
1114                     if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
1115                         prefetchDescendantsOfVirtualNode(root, provider, outInfos);
1116                     }
1117                 }
1118             }
1119             if (ENFORCE_NODE_TREE_CONSISTENT) {
1120                 enforceNodeTreeConsistent(outInfos);
1121             }
1122         }
1123 
enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes)1124         private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
1125             LongSparseArray<AccessibilityNodeInfo> nodeMap =
1126                     new LongSparseArray<AccessibilityNodeInfo>();
1127             final int nodeCount = nodes.size();
1128             for (int i = 0; i < nodeCount; i++) {
1129                 AccessibilityNodeInfo node = nodes.get(i);
1130                 nodeMap.put(node.getSourceNodeId(), node);
1131             }
1132 
1133             // If the nodes are a tree it does not matter from
1134             // which node we start to search for the root.
1135             AccessibilityNodeInfo root = nodeMap.valueAt(0);
1136             AccessibilityNodeInfo parent = root;
1137             while (parent != null) {
1138                 root = parent;
1139                 parent = nodeMap.get(parent.getParentNodeId());
1140             }
1141 
1142             // Traverse the tree and do some checks.
1143             AccessibilityNodeInfo accessFocus = null;
1144             AccessibilityNodeInfo inputFocus = null;
1145             HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
1146             Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
1147             fringe.add(root);
1148 
1149             while (!fringe.isEmpty()) {
1150                 AccessibilityNodeInfo current = fringe.poll();
1151 
1152                 // Check for duplicates
1153                 if (!seen.add(current)) {
1154                     throw new IllegalStateException("Duplicate node: "
1155                             + current + " in window:"
1156                             + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1157                 }
1158 
1159                 // Check for one accessibility focus.
1160                 if (current.isAccessibilityFocused()) {
1161                     if (accessFocus != null) {
1162                         throw new IllegalStateException("Duplicate accessibility focus:"
1163                                 + current
1164                                 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1165                     } else {
1166                         accessFocus = current;
1167                     }
1168                 }
1169 
1170                 // Check for one input focus.
1171                 if (current.isFocused()) {
1172                     if (inputFocus != null) {
1173                         throw new IllegalStateException("Duplicate input focus: "
1174                             + current + " in window:"
1175                             + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1176                     } else {
1177                         inputFocus = current;
1178                     }
1179                 }
1180 
1181                 final int childCount = current.getChildCount();
1182                 for (int j = 0; j < childCount; j++) {
1183                     final long childId = current.getChildId(j);
1184                     final AccessibilityNodeInfo child = nodeMap.get(childId);
1185                     if (child != null) {
1186                         fringe.add(child);
1187                     }
1188                 }
1189             }
1190 
1191             // Check for disconnected nodes.
1192             for (int j = nodeMap.size() - 1; j >= 0; j--) {
1193                 AccessibilityNodeInfo info = nodeMap.valueAt(j);
1194                 if (!seen.contains(info)) {
1195                     throw new IllegalStateException("Disconnected node: " + info);
1196                 }
1197             }
1198         }
1199 
prefetchPredecessorsOfRealNode(View view, List<AccessibilityNodeInfo> outInfos)1200         private void prefetchPredecessorsOfRealNode(View view,
1201                 List<AccessibilityNodeInfo> outInfos) {
1202             ViewParent parent = view.getParentForAccessibility();
1203             while (parent instanceof View
1204                     && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1205                 View parentView = (View) parent;
1206                 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
1207                 if (info != null) {
1208                     outInfos.add(info);
1209                 }
1210                 parent = parent.getParentForAccessibility();
1211             }
1212         }
1213 
prefetchSiblingsOfRealNode(View current, List<AccessibilityNodeInfo> outInfos)1214         private void prefetchSiblingsOfRealNode(View current,
1215                 List<AccessibilityNodeInfo> outInfos) {
1216             ViewParent parent = current.getParentForAccessibility();
1217             if (parent instanceof ViewGroup) {
1218                 ViewGroup parentGroup = (ViewGroup) parent;
1219                 ArrayList<View> children = mTempViewList;
1220                 children.clear();
1221                 try {
1222                     parentGroup.addChildrenForAccessibility(children);
1223                     final int childCount = children.size();
1224                     for (int i = 0; i < childCount; i++) {
1225                         if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1226                             return;
1227                         }
1228                         View child = children.get(i);
1229                         if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
1230                                 && isShown(child)) {
1231                             AccessibilityNodeInfo info = null;
1232                             AccessibilityNodeProvider provider =
1233                                 child.getAccessibilityNodeProvider();
1234                             if (provider == null) {
1235                                 info = child.createAccessibilityNodeInfo();
1236                             } else {
1237                                 info = provider.createAccessibilityNodeInfo(
1238                                         AccessibilityNodeProvider.HOST_VIEW_ID);
1239                             }
1240                             if (info != null) {
1241                                 outInfos.add(info);
1242                             }
1243                         }
1244                     }
1245                 } finally {
1246                     children.clear();
1247                 }
1248             }
1249         }
1250 
prefetchDescendantsOfRealNode(View root, List<AccessibilityNodeInfo> outInfos)1251         private void prefetchDescendantsOfRealNode(View root,
1252                 List<AccessibilityNodeInfo> outInfos) {
1253             if (!(root instanceof ViewGroup)) {
1254                 return;
1255             }
1256             HashMap<View, AccessibilityNodeInfo> addedChildren =
1257                 new HashMap<View, AccessibilityNodeInfo>();
1258             ArrayList<View> children = mTempViewList;
1259             children.clear();
1260             try {
1261                 root.addChildrenForAccessibility(children);
1262                 final int childCount = children.size();
1263                 for (int i = 0; i < childCount; i++) {
1264                     if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1265                         return;
1266                     }
1267                     View child = children.get(i);
1268                     if (isShown(child)) {
1269                         AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1270                         if (provider == null) {
1271                             AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1272                             if (info != null) {
1273                                 outInfos.add(info);
1274                                 addedChildren.put(child, null);
1275                             }
1276                         } else {
1277                             AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
1278                                    AccessibilityNodeProvider.HOST_VIEW_ID);
1279                             if (info != null) {
1280                                 outInfos.add(info);
1281                                 addedChildren.put(child, info);
1282                             }
1283                         }
1284                     }
1285                 }
1286             } finally {
1287                 children.clear();
1288             }
1289             if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1290                 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1291                     View addedChild = entry.getKey();
1292                     AccessibilityNodeInfo virtualRoot = entry.getValue();
1293                     if (virtualRoot == null) {
1294                         prefetchDescendantsOfRealNode(addedChild, outInfos);
1295                     } else {
1296                         AccessibilityNodeProvider provider =
1297                             addedChild.getAccessibilityNodeProvider();
1298                         prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1299                     }
1300                 }
1301             }
1302         }
1303 
prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root, View providerHost, AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos)1304         private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1305                 View providerHost, AccessibilityNodeProvider provider,
1306                 List<AccessibilityNodeInfo> outInfos) {
1307             final int initialResultSize = outInfos.size();
1308             long parentNodeId = root.getParentNodeId();
1309             int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1310             while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
1311                 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1312                     return;
1313                 }
1314                 final int virtualDescendantId =
1315                     AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
1316                 if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
1317                         || accessibilityViewId == providerHost.getAccessibilityViewId()) {
1318                     final AccessibilityNodeInfo parent;
1319                     parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
1320                     if (parent == null) {
1321                         // Going up the parent relation we found a null predecessor,
1322                         // so remove these disconnected nodes form the result.
1323                         final int currentResultSize = outInfos.size();
1324                         for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1325                             outInfos.remove(i);
1326                         }
1327                         // Couldn't obtain the parent, which means we have a
1328                         // disconnected sub-tree. Abort prefetch immediately.
1329                         return;
1330                     }
1331                     outInfos.add(parent);
1332                     parentNodeId = parent.getParentNodeId();
1333                     accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1334                             parentNodeId);
1335                 } else {
1336                     prefetchPredecessorsOfRealNode(providerHost, outInfos);
1337                     return;
1338                 }
1339             }
1340         }
1341 
prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost, AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos)1342         private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1343                 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1344             final long parentNodeId = current.getParentNodeId();
1345             final int parentAccessibilityViewId =
1346                 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1347             final int parentVirtualDescendantId =
1348                 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
1349             if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
1350                     || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
1351                 final AccessibilityNodeInfo parent =
1352                         provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
1353                 if (parent != null) {
1354                     final int childCount = parent.getChildCount();
1355                     for (int i = 0; i < childCount; i++) {
1356                         if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1357                             return;
1358                         }
1359                         final long childNodeId = parent.getChildId(i);
1360                         if (childNodeId != current.getSourceNodeId()) {
1361                             final int childVirtualDescendantId =
1362                                 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1363                             AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1364                                     childVirtualDescendantId);
1365                             if (child != null) {
1366                                 outInfos.add(child);
1367                             }
1368                         }
1369                     }
1370                 }
1371             } else {
1372                 prefetchSiblingsOfRealNode(providerHost, outInfos);
1373             }
1374         }
1375 
prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root, AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos)1376         private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1377                 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1378             final int initialOutInfosSize = outInfos.size();
1379             final int childCount = root.getChildCount();
1380             for (int i = 0; i < childCount; i++) {
1381                 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1382                     return;
1383                 }
1384                 final long childNodeId = root.getChildId(i);
1385                 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1386                         AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1387                 if (child != null) {
1388                     outInfos.add(child);
1389                 }
1390             }
1391             if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1392                 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1393                 for (int i = 0; i < addedChildCount; i++) {
1394                     AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1395                     prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1396                 }
1397             }
1398         }
1399     }
1400 
1401     private class PrivateHandler extends Handler {
1402         private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
1403         private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1404         private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1405         private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
1406         private static final int MSG_FIND_FOCUS = 5;
1407         private static final int MSG_FOCUS_SEARCH = 6;
1408         private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;
1409         private static final int MSG_APP_PREPARATION_FINISHED = 8;
1410         private static final int MSG_APP_PREPARATION_TIMEOUT = 9;
1411 
1412         // Uses FIRST_NO_ACCESSIBILITY_CALLBACK_MSG for messages that don't need to call back
1413         // results to interrogating client.
1414         private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100;
1415         private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS =
1416                 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1;
1417         private static final int MSG_NOTIFY_OUTSIDE_TOUCH =
1418                 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 2;
1419 
PrivateHandler(Looper looper)1420         public PrivateHandler(Looper looper) {
1421             super(looper);
1422         }
1423 
1424         @Override
getMessageName(Message message)1425         public String getMessageName(Message message) {
1426             final int type = message.what;
1427             switch (type) {
1428                 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1429                     return "MSG_PERFORM_ACCESSIBILITY_ACTION";
1430                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1431                     return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1432                 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1433                     return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1434                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1435                     return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
1436                 case MSG_FIND_FOCUS:
1437                     return "MSG_FIND_FOCUS";
1438                 case MSG_FOCUS_SEARCH:
1439                     return "MSG_FOCUS_SEARCH";
1440                 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:
1441                     return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";
1442                 case MSG_APP_PREPARATION_FINISHED:
1443                     return "MSG_APP_PREPARATION_FINISHED";
1444                 case MSG_APP_PREPARATION_TIMEOUT:
1445                     return "MSG_APP_PREPARATION_TIMEOUT";
1446                 case MSG_CLEAR_ACCESSIBILITY_FOCUS:
1447                     return "MSG_CLEAR_ACCESSIBILITY_FOCUS";
1448                 case MSG_NOTIFY_OUTSIDE_TOUCH:
1449                     return "MSG_NOTIFY_OUTSIDE_TOUCH";
1450                 default:
1451                     throw new IllegalArgumentException("Unknown message type: " + type);
1452             }
1453         }
1454 
1455         @Override
handleMessage(Message message)1456         public void handleMessage(Message message) {
1457             final int type = message.what;
1458             switch (type) {
1459                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
1460                     findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1461                 } break;
1462                 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
1463                     performAccessibilityActionUiThread(message);
1464                 } break;
1465                 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
1466                     findAccessibilityNodeInfosByViewIdUiThread(message);
1467                 } break;
1468                 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
1469                     findAccessibilityNodeInfosByTextUiThread(message);
1470                 } break;
1471                 case MSG_FIND_FOCUS: {
1472                     findFocusUiThread(message);
1473                 } break;
1474                 case MSG_FOCUS_SEARCH: {
1475                     focusSearchUiThread(message);
1476                 } break;
1477                 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
1478                     prepareForExtraDataRequestUiThread(message);
1479                 } break;
1480                 case MSG_APP_PREPARATION_FINISHED: {
1481                     requestPreparerDoneUiThread(message);
1482                 } break;
1483                 case MSG_APP_PREPARATION_TIMEOUT: {
1484                     requestPreparerTimeoutUiThread();
1485                 } break;
1486                 case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
1487                     clearAccessibilityFocusUiThread();
1488                 } break;
1489                 case MSG_NOTIFY_OUTSIDE_TOUCH: {
1490                     notifyOutsideTouchUiThread();
1491                 } break;
1492                 default:
1493                     throw new IllegalArgumentException("Unknown message type: " + type);
1494             }
1495         }
1496 
hasAccessibilityCallback(Message message)1497         boolean hasAccessibilityCallback(Message message) {
1498             return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false;
1499         }
1500     }
1501 
1502     private final class AddNodeInfosForViewId implements Predicate<View> {
1503         private int mViewId = View.NO_ID;
1504         private List<AccessibilityNodeInfo> mInfos;
1505 
init(int viewId, List<AccessibilityNodeInfo> infos)1506         public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1507             mViewId = viewId;
1508             mInfos = infos;
1509         }
1510 
reset()1511         public void reset() {
1512             mViewId = View.NO_ID;
1513             mInfos = null;
1514         }
1515 
1516         @Override
test(View view)1517         public boolean test(View view) {
1518             if (view.getId() == mViewId && isShown(view)) {
1519                 mInfos.add(view.createAccessibilityNodeInfo());
1520             }
1521             return false;
1522         }
1523     }
1524 
1525     private static final class MessageHolder {
1526         final Message mMessage;
1527         final int mInterrogatingPid;
1528         final long mInterrogatingTid;
1529 
MessageHolder(Message message, int interrogatingPid, long interrogatingTid)1530         MessageHolder(Message message, int interrogatingPid, long interrogatingTid) {
1531             mMessage = message;
1532             mInterrogatingPid = interrogatingPid;
1533             mInterrogatingTid = interrogatingTid;
1534         }
1535     }
1536 }
1537