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