blob: 4b929683fd6dd158453910ae6cdfbdd8687e1d54 [file] [log] [blame]
Svetoslav Ganov42138042012-03-20 11:51:39 -07001/*
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
17package android.view;
18
Jackal Guoecd09bb2019-03-20 17:43:19 +080019import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
Phil Weaver193520e2016-12-13 09:39:06 -080020import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
Phil Weaver08cccc12017-02-28 09:55:31 -080021import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_REQUESTED_KEY;
Phil Weaverc2e28932016-12-08 12:29:25 -080022import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
Phil Weaver193520e2016-12-13 09:39:06 -080023
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070024import android.graphics.Point;
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070025import android.graphics.Rect;
Phil Weaverc2e28932016-12-08 12:29:25 -080026import android.graphics.RectF;
Svetoslav9ae9ed22014-09-02 16:36:35 -070027import android.graphics.Region;
Nirmal Patel386a8242015-06-02 18:11:32 -070028import android.os.Binder;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -070029import android.os.Bundle;
Svetoslav Ganov42138042012-03-20 11:51:39 -070030import android.os.Handler;
31import android.os.Looper;
32import android.os.Message;
Phil Weaver193520e2016-12-13 09:39:06 -080033import android.os.Parcelable;
Svetoslav Ganov42138042012-03-20 11:51:39 -070034import android.os.Process;
35import android.os.RemoteException;
Jackal Guoac2b62e2018-08-22 18:28:46 +080036import android.os.SystemClock;
Phil Weaver193520e2016-12-13 09:39:06 -080037import android.text.style.AccessibilityClickableSpan;
38import android.text.style.ClickableSpan;
Svetoslav8e3feb12014-02-24 13:46:47 -080039import android.util.LongSparseArray;
Phil Weaver08cccc12017-02-28 09:55:31 -080040import android.util.Slog;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070041import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070042import android.view.accessibility.AccessibilityInteractionClient;
Phil Weaver08cccc12017-02-28 09:55:31 -080043import android.view.accessibility.AccessibilityManager;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -080044import android.view.accessibility.AccessibilityNodeIdManager;
Svetoslav Ganov42138042012-03-20 11:51:39 -070045import android.view.accessibility.AccessibilityNodeInfo;
Rhed Jao23813d92018-12-18 21:30:52 +080046import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
Svetoslav Ganov42138042012-03-20 11:51:39 -070047import android.view.accessibility.AccessibilityNodeProvider;
Phil Weaver08cccc12017-02-28 09:55:31 -080048import android.view.accessibility.AccessibilityRequestPreparer;
Svetoslav Ganov42138042012-03-20 11:51:39 -070049import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
50
Phil Weaver193520e2016-12-13 09:39:06 -080051import com.android.internal.R;
Phil Weaver08cccc12017-02-28 09:55:31 -080052import com.android.internal.annotations.GuardedBy;
Rhed Jao23813d92018-12-18 21:30:52 +080053import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070054import com.android.internal.os.SomeArgs;
55
Svetoslav Ganov42138042012-03-20 11:51:39 -070056import java.util.ArrayList;
57import java.util.HashMap;
Svetoslav8e3feb12014-02-24 13:46:47 -080058import java.util.HashSet;
59import java.util.LinkedList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070060import java.util.List;
61import java.util.Map;
Svetoslav8e3feb12014-02-24 13:46:47 -080062import java.util.Queue;
Paul Duffinca4964c2017-02-07 15:04:10 +000063import java.util.function.Predicate;
Svetoslav Ganov42138042012-03-20 11:51:39 -070064
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.
Rhed Jao23813d92018-12-18 21:30:52 +080071 *
72 * @hide
Svetoslav Ganov42138042012-03-20 11:51:39 -070073 */
Rhed Jao23813d92018-12-18 21:30:52 +080074@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
75public final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070076
Phil Weaver08cccc12017-02-28 09:55:31 -080077 private static final String LOG_TAG = "AccessibilityInteractionController";
78
79 // Debugging flag
Svetoslavaaa11422014-03-28 13:31:13 -070080 private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
Svetoslav8e3feb12014-02-24 13:46:47 -080081
Phil Weaver08cccc12017-02-28 09:55:31 -080082 // 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
Svetoslav Ganov80943d82013-01-02 10:25:37 -080090 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070091 new ArrayList<AccessibilityNodeInfo>();
92
Phil Weaver08cccc12017-02-28 09:55:31 -080093 private final Object mLock = new Object();
94
Rhed Jao23813d92018-12-18 21:30:52 +080095 private final PrivateHandler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070096
97 private final ViewRootImpl mViewRootImpl;
98
99 private final AccessibilityNodePrefetcher mPrefetcher;
100
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700101 private final long mMyLooperThreadId;
102
103 private final int mMyProcessId;
104
Phil Weaver08cccc12017-02-28 09:55:31 -0800105 private final AccessibilityManager mA11yManager;
106
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700107 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
108
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700109 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700110 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700111 private final Rect mTempRect1 = new Rect();
112 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700113
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800114 private AddNodeInfosForViewId mAddNodeInfosForViewId;
115
Phil Weaver08cccc12017-02-28 09:55:31 -0800116 @GuardedBy("mLock")
117 private int mNumActiveRequestPreparers;
118 @GuardedBy("mLock")
119 private List<MessageHolder> mMessagesWaitingForRequestPreparer;
120 @GuardedBy("mLock")
121 private int mActiveRequestPreparerId;
122
Svetoslav Ganov42138042012-03-20 11:51:39 -0700123 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700124 Looper looper = viewRootImpl.mHandler.getLooper();
125 mMyLooperThreadId = looper.getThread().getId();
126 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700127 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700128 mViewRootImpl = viewRootImpl;
129 mPrefetcher = new AccessibilityNodePrefetcher();
Phil Weaver08cccc12017-02-28 09:55:31 -0800130 mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700131 }
132
Phil Weaver08cccc12017-02-28 09:55:31 -0800133 private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
134 boolean ignoreRequestPreparers) {
135 if (ignoreRequestPreparers
136 || !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {
137 // If the interrogation is performed by the same thread as the main UI
138 // thread in this process, set the message as a static reference so
139 // after this call completes the same thread but in the interrogating
140 // client can handle the message to generate the result.
Rhed Jao23813d92018-12-18 21:30:52 +0800141 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
142 && mHandler.hasAccessibilityCallback(message)) {
Phil Weaver08cccc12017-02-28 09:55:31 -0800143 AccessibilityInteractionClient.getInstanceForThread(
144 interrogatingTid).setSameThreadMessage(message);
145 } else {
Rhed Jao23813d92018-12-18 21:30:52 +0800146 // For messages without callback of interrogating client, just handle the
147 // message immediately if this is UI thread.
148 if (!mHandler.hasAccessibilityCallback(message)
149 && Thread.currentThread().getId() == mMyLooperThreadId) {
150 mHandler.handleMessage(message);
151 } else {
152 mHandler.sendMessage(message);
153 }
Phil Weaver08cccc12017-02-28 09:55:31 -0800154 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800155 }
156 }
157
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700158 private boolean isShown(View view) {
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800159 return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700160 }
161
Svetoslav Ganov42138042012-03-20 11:51:39 -0700162 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav9ae9ed22014-09-02 16:36:35 -0700163 long accessibilityNodeId, Region interactiveRegion, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700164 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Phil Weaverc2e28932016-12-08 12:29:25 -0800165 long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
Phil Weaver08cccc12017-02-28 09:55:31 -0800166 final Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700167 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700168 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700169
Phil Weaver08cccc12017-02-28 09:55:31 -0800170 final SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700171 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
172 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
173 args.argi3 = interactionId;
174 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700175 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700176 args.arg3 = interactiveRegion;
Phil Weaverc2e28932016-12-08 12:29:25 -0800177 args.arg4 = arguments;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700178 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700179
Phil Weaver08cccc12017-02-28 09:55:31 -0800180 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
181 }
182
183 /**
184 * Check if this message needs to be held off while the app prepares to meet either this
185 * request, or a request ahead of it.
186 *
187 * @param originalMessage The message to be processed
188 * @param callingPid The calling process id
189 * @param callingTid The calling thread id
190 *
191 * @return {@code true} if the message is held off and will be processed later, {@code false} if
192 * the message should be posted.
193 */
194 private boolean holdOffMessageIfNeeded(
195 Message originalMessage, int callingPid, long callingTid) {
196 synchronized (mLock) {
197 // If a request is already pending, queue this request for when it's finished
198 if (mNumActiveRequestPreparers != 0) {
199 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
200 return true;
201 }
202
203 // Currently the only message that can hold things off is findByA11yId with extra data.
204 if (originalMessage.what
205 != PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID) {
206 return false;
207 }
208 SomeArgs originalMessageArgs = (SomeArgs) originalMessage.obj;
209 Bundle requestArguments = (Bundle) originalMessageArgs.arg4;
210 if (requestArguments == null) {
211 return false;
212 }
213
214 // If nothing it registered for this view, nothing to do
215 int accessibilityViewId = originalMessageArgs.argi1;
216 final List<AccessibilityRequestPreparer> preparers =
217 mA11yManager.getRequestPreparersForAccessibilityId(accessibilityViewId);
218 if (preparers == null) {
219 return false;
220 }
221
222 // If the bundle doesn't request the extra data, nothing to do
223 final String extraDataKey = requestArguments.getString(EXTRA_DATA_REQUESTED_KEY);
224 if (extraDataKey == null) {
225 return false;
226 }
227
228 // Send the request to the AccessibilityRequestPreparers on the UI thread
229 mNumActiveRequestPreparers = preparers.size();
230 for (int i = 0; i < preparers.size(); i++) {
231 final Message requestPreparerMessage = mHandler.obtainMessage(
232 PrivateHandler.MSG_PREPARE_FOR_EXTRA_DATA_REQUEST);
233 final SomeArgs requestPreparerArgs = SomeArgs.obtain();
234 // virtualDescendentId
235 requestPreparerArgs.argi1 =
236 (originalMessageArgs.argi2 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
237 ? AccessibilityNodeProvider.HOST_VIEW_ID : originalMessageArgs.argi2;
238 requestPreparerArgs.arg1 = preparers.get(i);
239 requestPreparerArgs.arg2 = extraDataKey;
240 requestPreparerArgs.arg3 = requestArguments;
241 Message preparationFinishedMessage = mHandler.obtainMessage(
242 PrivateHandler.MSG_APP_PREPARATION_FINISHED);
243 preparationFinishedMessage.arg1 = ++mActiveRequestPreparerId;
244 requestPreparerArgs.arg4 = preparationFinishedMessage;
245
246 requestPreparerMessage.obj = requestPreparerArgs;
247 scheduleMessage(requestPreparerMessage, callingPid, callingTid,
248 IGNORE_REQUEST_PREPARERS);
249 mHandler.obtainMessage(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
250 mHandler.sendEmptyMessageDelayed(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT,
251 REQUEST_PREPARER_TIMEOUT_MS);
252 }
253
254 // Set the initial request aside
255 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
256 return true;
257 }
258 }
259
260 private void prepareForExtraDataRequestUiThread(Message message) {
261 SomeArgs args = (SomeArgs) message.obj;
262 final int virtualDescendantId = args.argi1;
263 final AccessibilityRequestPreparer preparer = (AccessibilityRequestPreparer) args.arg1;
264 final String extraDataKey = (String) args.arg2;
265 final Bundle requestArguments = (Bundle) args.arg3;
266 final Message preparationFinishedMessage = (Message) args.arg4;
267
268 preparer.onPrepareExtraData(virtualDescendantId, extraDataKey,
269 requestArguments, preparationFinishedMessage);
270 }
271
272 private void queueMessageToHandleOncePrepared(Message message, int interrogatingPid,
273 long interrogatingTid) {
274 if (mMessagesWaitingForRequestPreparer == null) {
275 mMessagesWaitingForRequestPreparer = new ArrayList<>(1);
276 }
277 MessageHolder messageHolder =
278 new MessageHolder(message, interrogatingPid, interrogatingTid);
279 mMessagesWaitingForRequestPreparer.add(messageHolder);
280 }
281
282 private void requestPreparerDoneUiThread(Message message) {
283 synchronized (mLock) {
284 if (message.arg1 != mActiveRequestPreparerId) {
285 Slog.e(LOG_TAG, "Surprising AccessibilityRequestPreparer callback (likely late)");
286 return;
287 }
288 mNumActiveRequestPreparers--;
289 if (mNumActiveRequestPreparers <= 0) {
290 mHandler.removeMessages(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
291 scheduleAllMessagesWaitingForRequestPreparerLocked();
292 }
293 }
294 }
295
296 private void requestPreparerTimeoutUiThread() {
297 synchronized (mLock) {
298 Slog.e(LOG_TAG, "AccessibilityRequestPreparer timed out");
299 scheduleAllMessagesWaitingForRequestPreparerLocked();
300 }
301 }
302
303 @GuardedBy("mLock")
304 private void scheduleAllMessagesWaitingForRequestPreparerLocked() {
305 int numMessages = mMessagesWaitingForRequestPreparer.size();
306 for (int i = 0; i < numMessages; i++) {
307 MessageHolder request = mMessagesWaitingForRequestPreparer.get(i);
308 scheduleMessage(request.mMessage, request.mInterrogatingPid,
309 request.mInterrogatingTid,
310 (i == 0) /* the app is ready for the first request */);
311 }
312 mMessagesWaitingForRequestPreparer.clear();
313 mNumActiveRequestPreparers = 0; // Just to be safe - should be unnecessary
314 mActiveRequestPreparerId = -1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700315 }
316
317 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
318 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700319
Svetoslav Ganov42138042012-03-20 11:51:39 -0700320 SomeArgs args = (SomeArgs) message.obj;
321 final int accessibilityViewId = args.argi1;
322 final int virtualDescendantId = args.argi2;
323 final int interactionId = args.argi3;
324 final IAccessibilityInteractionConnectionCallback callback =
325 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700326 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700327 final Region interactiveRegion = (Region) args.arg3;
Phil Weaverc2e28932016-12-08 12:29:25 -0800328 final Bundle arguments = (Bundle) args.arg4;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700329
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700330 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700331
Svetoslav Ganov42138042012-03-20 11:51:39 -0700332 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
333 infos.clear();
334 try {
335 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
336 return;
337 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800338 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800339 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800340 if (root != null && isShown(root)) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800341 mPrefetcher.prefetchAccessibilityNodeInfos(
342 root, virtualDescendantId, flags, infos, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700343 }
344 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800345 updateInfosForViewportAndReturnFindNodeResult(
346 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700347 }
348 }
349
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800350 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700351 String viewId, Region interactiveRegion, int interactionId,
352 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
353 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700354 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700355 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700356 message.arg1 = flags;
357 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700358
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700359 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800360 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700361 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700362 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800363 args.arg3 = viewId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700364 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700365 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700366
Phil Weaver08cccc12017-02-28 09:55:31 -0800367 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700368 }
369
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800370 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700371 final int flags = message.arg1;
372 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700373
Svetoslav Ganov42138042012-03-20 11:51:39 -0700374 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800375 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700376 final IAccessibilityInteractionConnectionCallback callback =
377 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700378 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800379 final String viewId = (String) args.arg3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700380 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700381 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700382
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800383 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
384 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700385 try {
386 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
387 return;
388 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800389 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800390 final View root = findViewByAccessibilityId(accessibilityViewId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700391 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800392 final int resolvedViewId = root.getContext().getResources()
393 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800394 if (resolvedViewId <= 0) {
395 return;
396 }
397 if (mAddNodeInfosForViewId == null) {
398 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
399 }
400 mAddNodeInfosForViewId.init(resolvedViewId, infos);
401 root.findViewByPredicate(mAddNodeInfosForViewId);
402 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700403 }
404 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800405 updateInfosForViewportAndReturnFindNodeResult(
406 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700407 }
408 }
409
410 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700411 String text, Region interactiveRegion, int interactionId,
412 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
413 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700414 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700415 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700416 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700417
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700418 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700419 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700420 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700421 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700422 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
423 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
424 args.argi3 = interactionId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700425 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700426 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700427
Phil Weaver08cccc12017-02-28 09:55:31 -0800428 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700429 }
430
431 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
432 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700433
Svetoslav Ganov42138042012-03-20 11:51:39 -0700434 SomeArgs args = (SomeArgs) message.obj;
435 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700436 final IAccessibilityInteractionConnectionCallback callback =
437 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700438 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700439 final int accessibilityViewId = args.argi1;
440 final int virtualDescendantId = args.argi2;
441 final int interactionId = args.argi3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700442 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700443 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700444
Svetoslav Ganov42138042012-03-20 11:51:39 -0700445 List<AccessibilityNodeInfo> infos = null;
446 try {
447 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
448 return;
449 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800450 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800451 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800452 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700453 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
454 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800455 infos = provider.findAccessibilityNodeInfosByText(text,
456 virtualDescendantId);
457 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700458 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700459 foundViews.clear();
460 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
461 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
462 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
463 if (!foundViews.isEmpty()) {
464 infos = mTempAccessibilityNodeInfoList;
465 infos.clear();
466 final int viewCount = foundViews.size();
467 for (int i = 0; i < viewCount; i++) {
468 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700469 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700470 provider = foundView.getAccessibilityNodeProvider();
471 if (provider != null) {
472 List<AccessibilityNodeInfo> infosFromProvider =
473 provider.findAccessibilityNodeInfosByText(text,
Svetoslav8e3feb12014-02-24 13:46:47 -0800474 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700475 if (infosFromProvider != null) {
476 infos.addAll(infosFromProvider);
477 }
478 } else {
479 infos.add(foundView.createAccessibilityNodeInfo());
480 }
481 }
482 }
483 }
484 }
485 }
486 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800487 updateInfosForViewportAndReturnFindNodeResult(
488 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700489 }
490 }
491
Svetoslav9ae9ed22014-09-02 16:36:35 -0700492 public void findFocusClientThread(long accessibilityNodeId, int focusType,
493 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800494 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700495 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700496 Message message = mHandler.obtainMessage();
497 message.what = PrivateHandler.MSG_FIND_FOCUS;
498 message.arg1 = flags;
499 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700500
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700501 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700502 args.argi1 = interactionId;
503 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
504 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
505 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700506 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700507 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700508
Svetoslav Ganov42138042012-03-20 11:51:39 -0700509 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700510
Phil Weaver08cccc12017-02-28 09:55:31 -0800511 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700512 }
513
514 private void findFocusUiThread(Message message) {
515 final int flags = message.arg1;
516 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700517
Svetoslav Ganov42138042012-03-20 11:51:39 -0700518 SomeArgs args = (SomeArgs) message.obj;
519 final int interactionId = args.argi1;
520 final int accessibilityViewId = args.argi2;
521 final int virtualDescendantId = args.argi3;
522 final IAccessibilityInteractionConnectionCallback callback =
523 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700524 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700525 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700526 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700527
Svetoslav Ganov42138042012-03-20 11:51:39 -0700528 AccessibilityNodeInfo focused = null;
529 try {
530 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
531 return;
532 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800533 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800534 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800535 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700536 switch (focusType) {
537 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
538 View host = mViewRootImpl.mAccessibilityFocusedHost;
539 // If there is no accessibility focus host or it is not a descendant
540 // of the root from which to start the search, then the search failed.
541 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
542 break;
543 }
Svetoslav6254f482013-06-04 17:22:14 -0700544 // The focused view not shown, we failed.
545 if (!isShown(host)) {
546 break;
547 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700548 // If the host has a provider ask this provider to search for the
549 // focus instead fetching all provider nodes to do the search here.
550 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
551 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700552 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
553 focused = AccessibilityNodeInfo.obtain(
554 mViewRootImpl.mAccessibilityFocusedVirtualView);
555 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800556 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700557 focused = host.createAccessibilityNodeInfo();
558 }
559 } break;
560 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700561 View target = root.findFocus();
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800562 if (!isShown(target)) {
Alan Viverette2e1e0812013-09-30 13:45:55 -0700563 break;
564 }
565 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
566 if (provider != null) {
567 focused = provider.findFocus(focusType);
568 }
569 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700570 focused = target.createAccessibilityNodeInfo();
571 }
572 } break;
573 default:
574 throw new IllegalArgumentException("Unknown focus type: " + focusType);
575 }
576 }
577 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800578 updateInfoForViewportAndReturnFindNodeResult(
579 focused, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700580 }
581 }
582
Svetoslav9ae9ed22014-09-02 16:36:35 -0700583 public void focusSearchClientThread(long accessibilityNodeId, int direction,
584 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800585 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700586 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700587 Message message = mHandler.obtainMessage();
588 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
589 message.arg1 = flags;
590 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700591
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700592 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700593 args.argi2 = direction;
594 args.argi3 = interactionId;
595 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700596 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700597 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700598
Svetoslav Ganov42138042012-03-20 11:51:39 -0700599 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700600
Phil Weaver08cccc12017-02-28 09:55:31 -0800601 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700602 }
603
604 private void focusSearchUiThread(Message message) {
605 final int flags = message.arg1;
606 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700607
Svetoslav Ganov42138042012-03-20 11:51:39 -0700608 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700609 final int direction = args.argi2;
610 final int interactionId = args.argi3;
611 final IAccessibilityInteractionConnectionCallback callback =
612 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700613 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700614 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700615
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700616 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700617
Svetoslav Ganov42138042012-03-20 11:51:39 -0700618 AccessibilityNodeInfo next = null;
619 try {
620 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
621 return;
622 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800623 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800624 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800625 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700626 View nextView = root.focusSearch(direction);
627 if (nextView != null) {
628 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700629 }
630 }
631 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800632 updateInfoForViewportAndReturnFindNodeResult(
633 next, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700634 }
635 }
636
637 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700638 Bundle arguments, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800639 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700640 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700641 Message message = mHandler.obtainMessage();
642 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
643 message.arg1 = flags;
644 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700645
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700646 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700647 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
648 args.argi2 = action;
649 args.argi3 = interactionId;
650 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700651 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700652
Svetoslav Ganov42138042012-03-20 11:51:39 -0700653 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700654
Phil Weaver08cccc12017-02-28 09:55:31 -0800655 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700656 }
657
George Mount41725de2015-04-09 08:23:05 -0700658 private void performAccessibilityActionUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700659 final int flags = message.arg1;
660 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700661
Svetoslav Ganov42138042012-03-20 11:51:39 -0700662 SomeArgs args = (SomeArgs) message.obj;
663 final int virtualDescendantId = args.argi1;
664 final int action = args.argi2;
665 final int interactionId = args.argi3;
666 final IAccessibilityInteractionConnectionCallback callback =
667 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700668 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700669
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700670 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700671
Svetoslav Ganov42138042012-03-20 11:51:39 -0700672 boolean succeeded = false;
673 try {
George Mount41725de2015-04-09 08:23:05 -0700674 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
675 mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700676 return;
677 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800678 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800679 final View target = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800680 if (target != null && isShown(target)) {
Phil Weaver193520e2016-12-13 09:39:06 -0800681 if (action == R.id.accessibilityActionClickOnClickableSpan) {
682 // Handle this hidden action separately
683 succeeded = handleClickableSpanActionUiThread(
684 target, virtualDescendantId, arguments);
685 } else {
686 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
687 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800688 succeeded = provider.performAction(virtualDescendantId, action,
689 arguments);
690 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800691 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav8e3feb12014-02-24 13:46:47 -0800692 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700693 }
694 }
695 } finally {
696 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800697 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700698 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
699 } catch (RemoteException re) {
700 /* ignore - the other side will time out */
701 }
702 }
703 }
704
Rhed Jao23813d92018-12-18 21:30:52 +0800705 /**
706 * Finds the accessibility focused node in the root, and clears the accessibility focus.
707 */
708 public void clearAccessibilityFocusClientThread() {
709 final Message message = mHandler.obtainMessage();
710 message.what = PrivateHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS;
711
712 // Don't care about pid and tid because there's no interrogating client for this message.
713 scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
714 }
715
716 private void clearAccessibilityFocusUiThread() {
717 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
718 return;
719 }
720 try {
721 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags =
722 AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
723 final View root = mViewRootImpl.mView;
724 if (root != null && isShown(root)) {
725 final View host = mViewRootImpl.mAccessibilityFocusedHost;
726 // If there is no accessibility focus host or it is not a descendant
727 // of the root from which to start the search, then the search failed.
728 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
729 return;
730 }
731 final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
732 final AccessibilityNodeInfo focusNode =
733 mViewRootImpl.mAccessibilityFocusedVirtualView;
734 if (provider != null && focusNode != null) {
735 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
736 focusNode.getSourceNodeId());
737 provider.performAction(virtualNodeId,
738 AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
739 null);
740 } else {
741 host.performAccessibilityAction(
742 AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
743 null);
744 }
745 }
746 } finally {
747 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
748 }
749 }
750
Jackal Guo8bcc0a92019-04-16 15:26:00 +0800751 /**
752 * Notify outside touch event to the target window.
753 */
754 public void notifyOutsideTouchClientThread() {
755 final Message message = mHandler.obtainMessage();
756 message.what = PrivateHandler.MSG_NOTIFY_OUTSIDE_TOUCH;
757
758 // Don't care about pid and tid because there's no interrogating client for this message.
759 scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
760 }
761
762 private void notifyOutsideTouchUiThread() {
763 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null
764 || mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
765 return;
766 }
767 final View root = mViewRootImpl.mView;
768 if (root != null && isShown(root)) {
769 // trigger ACTION_OUTSIDE to notify windows
770 final long now = SystemClock.uptimeMillis();
771 final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
772 0, 0, 0);
773 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
774 mViewRootImpl.dispatchInputEvent(event);
775 }
776 }
777
Svetoslav Ganov42138042012-03-20 11:51:39 -0700778 private View findViewByAccessibilityId(int accessibilityId) {
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800779 if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
780 return mViewRootImpl.mView;
781 } else {
Rhed Jao4b87f312019-02-13 17:45:02 +0800782 return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700783 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700784 }
785
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700786 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
787 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700788 if (infos == null) {
789 return;
790 }
791 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700792 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700793 final int infoCount = infos.size();
794 for (int i = 0; i < infoCount; i++) {
795 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700796 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700797 }
798 }
799 }
800
Svetoslav9ae9ed22014-09-02 16:36:35 -0700801 private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
802 Region interactiveRegion) {
803 if (interactiveRegion == null || infos == null) {
804 return;
805 }
806 final int infoCount = infos.size();
807 for (int i = 0; i < infoCount; i++) {
808 AccessibilityNodeInfo info = infos.get(i);
809 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
810 }
811 }
812
813 private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
814 Region interactiveRegion) {
815 if (interactiveRegion == null || info == null) {
816 return;
817 }
818 Rect boundsInScreen = mTempRect;
819 info.getBoundsInScreen(boundsInScreen);
Jackal Guoecd09bb2019-03-20 17:43:19 +0800820 if (interactiveRegion.quickReject(boundsInScreen) && !shouldBypassAdjustIsVisible()) {
Svetoslav9ae9ed22014-09-02 16:36:35 -0700821 info.setVisibleToUser(false);
822 }
823 }
824
Jackal Guoecd09bb2019-03-20 17:43:19 +0800825 private boolean shouldBypassAdjustIsVisible() {
826 final int windowType = mViewRootImpl.mOrigWindowType;
827 if (windowType == TYPE_INPUT_METHOD) {
828 return true;
829 }
830 return false;
831 }
832
Jackal Guoc43a0a62019-04-23 09:15:14 +0800833 private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) {
834 if (infos == null || shouldBypassAdjustBoundsInScreen()) {
835 return;
836 }
837 final int infoCount = infos.size();
838 for (int i = 0; i < infoCount; i++) {
839 final AccessibilityNodeInfo info = infos.get(i);
840 adjustBoundsInScreenIfNeeded(info);
841 }
842 }
843
844 private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) {
845 if (info == null || shouldBypassAdjustBoundsInScreen()) {
846 return;
847 }
848 final Rect boundsInScreen = mTempRect;
849 info.getBoundsInScreen(boundsInScreen);
850 boundsInScreen.offset(mViewRootImpl.mAttachInfo.mLocationInParentDisplay.x,
851 mViewRootImpl.mAttachInfo.mLocationInParentDisplay.y);
852 info.setBoundsInScreen(boundsInScreen);
853 }
854
855 private boolean shouldBypassAdjustBoundsInScreen() {
856 return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
857 }
858
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700859 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
860 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700861 if (info == null) {
862 return;
863 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700864
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700865 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700866 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
867 return;
868 }
869
870 Rect boundsInParent = mTempRect;
871 Rect boundsInScreen = mTempRect1;
872
873 info.getBoundsInParent(boundsInParent);
874 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700875 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700876 boundsInParent.scale(applicationScale);
877 boundsInScreen.scale(applicationScale);
878 }
879 if (spec != null) {
880 boundsInParent.scale(spec.scale);
881 // boundsInParent must not be offset.
882 boundsInScreen.scale(spec.scale);
883 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
884 }
885 info.setBoundsInParent(boundsInParent);
886 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700887
Phil Weaverc2e28932016-12-08 12:29:25 -0800888 // Scale text locations if they are present
889 if (info.hasExtras()) {
890 Bundle extras = info.getExtras();
891 Parcelable[] textLocations =
892 extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
893 if (textLocations != null) {
894 for (int i = 0; i < textLocations.length; i++) {
895 // Unchecked cast - an app that puts other objects in this bundle with this
896 // key will crash.
897 RectF textLocation = ((RectF) textLocations[i]);
898 textLocation.scale(applicationScale);
899 if (spec != null) {
900 textLocation.scale(spec.scale);
901 textLocation.offset(spec.offsetX, spec.offsetY);
902 }
903 }
904 }
905 }
906
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700907 if (spec != null) {
908 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
909 if (attachInfo.mDisplay == null) {
910 return;
911 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700912
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700913 final float scale = attachInfo.mApplicationScale * spec.scale;
914
915 Rect visibleWinFrame = mTempRect1;
916 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
917 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
918 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
919 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
920
921 attachInfo.mDisplay.getRealSize(mTempPoint);
922 final int displayWidth = mTempPoint.x;
923 final int displayHeight = mTempPoint.y;
924
925 Rect visibleDisplayFrame = mTempRect2;
926 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
927
Doris Liu9607fbe2015-05-28 17:17:28 -0700928 if (!visibleWinFrame.intersect(visibleDisplayFrame)) {
929 // If there's no intersection with display, set visibleWinFrame empty.
930 visibleDisplayFrame.setEmpty();
931 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700932
933 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
934 boundsInScreen.right, boundsInScreen.bottom)) {
935 info.setVisibleToUser(false);
936 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700937 }
938 }
939
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700940 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
941 MagnificationSpec spec) {
942 return (appScale != 1.0f || (spec != null && !spec.isNop()));
943 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700944
Phil Weaverc2e28932016-12-08 12:29:25 -0800945 private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
946 IAccessibilityInteractionConnectionCallback callback, int interactionId,
947 MagnificationSpec spec, Region interactiveRegion) {
948 try {
949 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Jackal Guoc43a0a62019-04-23 09:15:14 +0800950 adjustBoundsInScreenIfNeeded(infos);
Phil Weaverc2e28932016-12-08 12:29:25 -0800951 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
952 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
953 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Phil Weaverf00cd142017-03-03 13:44:00 -0800954 if (infos != null) {
955 infos.clear();
956 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800957 } catch (RemoteException re) {
958 /* ignore - the other side will time out */
959 } finally {
960 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
961 }
962 }
963
964 private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
965 IAccessibilityInteractionConnectionCallback callback, int interactionId,
966 MagnificationSpec spec, Region interactiveRegion) {
967 try {
968 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Jackal Guoc43a0a62019-04-23 09:15:14 +0800969 adjustBoundsInScreenIfNeeded(info);
Phil Weaverc2e28932016-12-08 12:29:25 -0800970 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
971 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
972 callback.setFindAccessibilityNodeInfoResult(info, interactionId);
973 } catch (RemoteException re) {
974 /* ignore - the other side will time out */
975 } finally {
976 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
977 }
978 }
979
980 private void recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region) {
981 if (android.os.Process.myPid() != Binder.getCallingPid()) {
982 // Specs are cached in the system process and obtained from a pool when read from
983 // a parcel, so only recycle the spec if called from another process.
984 if (spec != null) {
985 spec.recycle();
986 }
987 } else {
988 // Regions are obtained in the system process and instantiated when read from
989 // a parcel, so only recycle the region if caled from the same process.
990 if (region != null) {
991 region.recycle();
992 }
993 }
994 }
995
Phil Weaver193520e2016-12-13 09:39:06 -0800996 private boolean handleClickableSpanActionUiThread(
997 View view, int virtualDescendantId, Bundle arguments) {
998 Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
999 if (!(span instanceof AccessibilityClickableSpan)) {
1000 return false;
1001 }
1002
1003 // Find the original ClickableSpan if it's still on the screen
1004 AccessibilityNodeInfo infoWithSpan = null;
1005 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
1006 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -08001007 infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
1008 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -08001009 infoWithSpan = view.createAccessibilityNodeInfo();
1010 }
1011 if (infoWithSpan == null) {
1012 return false;
1013 }
1014
1015 // Click on the corresponding span
1016 ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
1017 infoWithSpan.getOriginalText());
1018 if (clickableSpan != null) {
1019 clickableSpan.onClick(view);
1020 return true;
1021 }
1022 return false;
1023 }
1024
Svetoslav Ganov42138042012-03-20 11:51:39 -07001025 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -07001026 * This class encapsulates a prefetching strategy for the accessibility APIs for
1027 * querying window content. It is responsible to prefetch a batch of
1028 * AccessibilityNodeInfos in addition to the one for a requested node.
1029 */
1030 private class AccessibilityNodePrefetcher {
1031
1032 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
1033
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001034 private final ArrayList<View> mTempViewList = new ArrayList<View>();
1035
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001036 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Phil Weaverc2e28932016-12-08 12:29:25 -08001037 List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001038 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
Phil Weaverc2e28932016-12-08 12:29:25 -08001039 // Determine if we'll be populating extra data
1040 final String extraDataRequested = (arguments == null) ? null
Phil Weaver08cccc12017-02-28 09:55:31 -08001041 : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001042 if (provider == null) {
1043 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
1044 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -08001045 if (extraDataRequested != null) {
1046 view.addExtraDataToAccessibilityNodeInfo(
1047 root, extraDataRequested, arguments);
1048 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001049 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001050 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001051 prefetchPredecessorsOfRealNode(view, outInfos);
1052 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001053 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001054 prefetchSiblingsOfRealNode(view, outInfos);
1055 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001056 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001057 prefetchDescendantsOfRealNode(view, outInfos);
1058 }
1059 }
1060 } else {
Phil Weaverf00cd142017-03-03 13:44:00 -08001061 final AccessibilityNodeInfo root =
1062 provider.createAccessibilityNodeInfo(virtualViewId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001063 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -08001064 if (extraDataRequested != null) {
1065 provider.addExtraDataToAccessibilityNodeInfo(
Phil Weaverf00cd142017-03-03 13:44:00 -08001066 virtualViewId, root, extraDataRequested, arguments);
Phil Weaverc2e28932016-12-08 12:29:25 -08001067 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001068 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001069 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001070 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
1071 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001072 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001073 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
1074 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001075 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001076 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
1077 }
1078 }
1079 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001080 if (ENFORCE_NODE_TREE_CONSISTENT) {
1081 enforceNodeTreeConsistent(outInfos);
1082 }
1083 }
1084
1085 private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
1086 LongSparseArray<AccessibilityNodeInfo> nodeMap =
1087 new LongSparseArray<AccessibilityNodeInfo>();
1088 final int nodeCount = nodes.size();
1089 for (int i = 0; i < nodeCount; i++) {
1090 AccessibilityNodeInfo node = nodes.get(i);
1091 nodeMap.put(node.getSourceNodeId(), node);
1092 }
1093
1094 // If the nodes are a tree it does not matter from
1095 // which node we start to search for the root.
1096 AccessibilityNodeInfo root = nodeMap.valueAt(0);
1097 AccessibilityNodeInfo parent = root;
1098 while (parent != null) {
1099 root = parent;
1100 parent = nodeMap.get(parent.getParentNodeId());
1101 }
1102
1103 // Traverse the tree and do some checks.
1104 AccessibilityNodeInfo accessFocus = null;
1105 AccessibilityNodeInfo inputFocus = null;
1106 HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
1107 Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
1108 fringe.add(root);
1109
1110 while (!fringe.isEmpty()) {
1111 AccessibilityNodeInfo current = fringe.poll();
1112
1113 // Check for duplicates
1114 if (!seen.add(current)) {
1115 throw new IllegalStateException("Duplicate node: "
1116 + current + " in window:"
1117 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1118 }
1119
1120 // Check for one accessibility focus.
1121 if (current.isAccessibilityFocused()) {
1122 if (accessFocus != null) {
1123 throw new IllegalStateException("Duplicate accessibility focus:"
1124 + current
1125 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1126 } else {
1127 accessFocus = current;
1128 }
1129 }
1130
1131 // Check for one input focus.
1132 if (current.isFocused()) {
1133 if (inputFocus != null) {
1134 throw new IllegalStateException("Duplicate input focus: "
1135 + current + " in window:"
1136 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1137 } else {
1138 inputFocus = current;
1139 }
1140 }
1141
1142 final int childCount = current.getChildCount();
1143 for (int j = 0; j < childCount; j++) {
1144 final long childId = current.getChildId(j);
1145 final AccessibilityNodeInfo child = nodeMap.get(childId);
1146 if (child != null) {
1147 fringe.add(child);
1148 }
1149 }
1150 }
1151
1152 // Check for disconnected nodes.
1153 for (int j = nodeMap.size() - 1; j >= 0; j--) {
1154 AccessibilityNodeInfo info = nodeMap.valueAt(j);
1155 if (!seen.contains(info)) {
1156 throw new IllegalStateException("Disconnected node: " + info);
1157 }
1158 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001159 }
1160
1161 private void prefetchPredecessorsOfRealNode(View view,
1162 List<AccessibilityNodeInfo> outInfos) {
1163 ViewParent parent = view.getParentForAccessibility();
1164 while (parent instanceof View
1165 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1166 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001167 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
1168 if (info != null) {
1169 outInfos.add(info);
1170 }
1171 parent = parent.getParentForAccessibility();
1172 }
1173 }
1174
1175 private void prefetchSiblingsOfRealNode(View current,
1176 List<AccessibilityNodeInfo> outInfos) {
1177 ViewParent parent = current.getParentForAccessibility();
1178 if (parent instanceof ViewGroup) {
1179 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001180 ArrayList<View> children = mTempViewList;
1181 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001182 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001183 parentGroup.addChildrenForAccessibility(children);
1184 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001185 for (int i = 0; i < childCount; i++) {
1186 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1187 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001188 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001189 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001190 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -08001191 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001192 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001193 AccessibilityNodeProvider provider =
1194 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001195 if (provider == null) {
1196 info = child.createAccessibilityNodeInfo();
1197 } else {
1198 info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001199 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001200 }
1201 if (info != null) {
1202 outInfos.add(info);
1203 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001204 }
1205 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001206 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001207 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001208 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001209 }
1210 }
1211
1212 private void prefetchDescendantsOfRealNode(View root,
1213 List<AccessibilityNodeInfo> outInfos) {
1214 if (!(root instanceof ViewGroup)) {
1215 return;
1216 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001217 HashMap<View, AccessibilityNodeInfo> addedChildren =
1218 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001219 ArrayList<View> children = mTempViewList;
1220 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001221 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001222 root.addChildrenForAccessibility(children);
1223 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001224 for (int i = 0; i < childCount; i++) {
1225 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1226 return;
1227 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001228 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001229 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001230 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1231 if (provider == null) {
1232 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1233 if (info != null) {
1234 outInfos.add(info);
1235 addedChildren.put(child, null);
1236 }
1237 } else {
1238 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001239 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001240 if (info != null) {
1241 outInfos.add(info);
1242 addedChildren.put(child, info);
1243 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001244 }
1245 }
1246 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001247 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001248 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001249 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001250 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1251 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1252 View addedChild = entry.getKey();
1253 AccessibilityNodeInfo virtualRoot = entry.getValue();
1254 if (virtualRoot == null) {
1255 prefetchDescendantsOfRealNode(addedChild, outInfos);
1256 } else {
1257 AccessibilityNodeProvider provider =
1258 addedChild.getAccessibilityNodeProvider();
1259 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1260 }
1261 }
1262 }
1263 }
1264
1265 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1266 View providerHost, AccessibilityNodeProvider provider,
1267 List<AccessibilityNodeInfo> outInfos) {
Svet Ganov75e58162016-02-19 16:29:24 -08001268 final int initialResultSize = outInfos.size();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001269 long parentNodeId = root.getParentNodeId();
1270 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001271 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001272 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1273 return;
1274 }
1275 final int virtualDescendantId =
1276 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001277 if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001278 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001279 final AccessibilityNodeInfo parent;
Phil Weaverf00cd142017-03-03 13:44:00 -08001280 parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
Alan Viverette84feea112014-11-04 15:59:55 -08001281 if (parent == null) {
Svet Ganov75e58162016-02-19 16:29:24 -08001282 // Going up the parent relation we found a null predecessor,
1283 // so remove these disconnected nodes form the result.
1284 final int currentResultSize = outInfos.size();
1285 for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1286 outInfos.remove(i);
1287 }
Alan Viverette84feea112014-11-04 15:59:55 -08001288 // Couldn't obtain the parent, which means we have a
1289 // disconnected sub-tree. Abort prefetch immediately.
1290 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001291 }
Alan Viverette84feea112014-11-04 15:59:55 -08001292 outInfos.add(parent);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001293 parentNodeId = parent.getParentNodeId();
1294 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1295 parentNodeId);
1296 } else {
1297 prefetchPredecessorsOfRealNode(providerHost, outInfos);
1298 return;
1299 }
1300 }
1301 }
1302
1303 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1304 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1305 final long parentNodeId = current.getParentNodeId();
1306 final int parentAccessibilityViewId =
1307 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1308 final int parentVirtualDescendantId =
1309 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001310 if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001311 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
Phil Weaverf00cd142017-03-03 13:44:00 -08001312 final AccessibilityNodeInfo parent =
1313 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001314 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -08001315 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001316 for (int i = 0; i < childCount; i++) {
1317 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1318 return;
1319 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001320 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001321 if (childNodeId != current.getSourceNodeId()) {
1322 final int childVirtualDescendantId =
1323 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1324 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1325 childVirtualDescendantId);
1326 if (child != null) {
1327 outInfos.add(child);
1328 }
1329 }
1330 }
1331 }
1332 } else {
1333 prefetchSiblingsOfRealNode(providerHost, outInfos);
1334 }
1335 }
1336
1337 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1338 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001339 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -08001340 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001341 for (int i = 0; i < childCount; i++) {
1342 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1343 return;
1344 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001345 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001346 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1347 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1348 if (child != null) {
1349 outInfos.add(child);
1350 }
1351 }
1352 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1353 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1354 for (int i = 0; i < addedChildCount; i++) {
1355 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1356 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1357 }
1358 }
1359 }
1360 }
1361
1362 private class PrivateHandler extends Handler {
Phil Weaverc2e28932016-12-08 12:29:25 -08001363 private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
1364 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1365 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1366 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
1367 private static final int MSG_FIND_FOCUS = 5;
1368 private static final int MSG_FOCUS_SEARCH = 6;
Phil Weaver08cccc12017-02-28 09:55:31 -08001369 private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;
1370 private static final int MSG_APP_PREPARATION_FINISHED = 8;
1371 private static final int MSG_APP_PREPARATION_TIMEOUT = 9;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001372
Rhed Jao23813d92018-12-18 21:30:52 +08001373 // Uses FIRST_NO_ACCESSIBILITY_CALLBACK_MSG for messages that don't need to call back
1374 // results to interrogating client.
1375 private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100;
1376 private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS =
1377 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1;
Jackal Guo8bcc0a92019-04-16 15:26:00 +08001378 private static final int MSG_NOTIFY_OUTSIDE_TOUCH =
1379 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 2;
Rhed Jao23813d92018-12-18 21:30:52 +08001380
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001381 public PrivateHandler(Looper looper) {
1382 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001383 }
1384
1385 @Override
1386 public String getMessageName(Message message) {
1387 final int type = message.what;
1388 switch (type) {
1389 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1390 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
Svet Ganov7498efd2014-09-03 21:33:00 -07001391 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1392 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1393 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1394 return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1395 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1396 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001397 case MSG_FIND_FOCUS:
1398 return "MSG_FIND_FOCUS";
1399 case MSG_FOCUS_SEARCH:
1400 return "MSG_FOCUS_SEARCH";
Phil Weaver08cccc12017-02-28 09:55:31 -08001401 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:
1402 return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";
1403 case MSG_APP_PREPARATION_FINISHED:
1404 return "MSG_APP_PREPARATION_FINISHED";
1405 case MSG_APP_PREPARATION_TIMEOUT:
1406 return "MSG_APP_PREPARATION_TIMEOUT";
Rhed Jao23813d92018-12-18 21:30:52 +08001407 case MSG_CLEAR_ACCESSIBILITY_FOCUS:
1408 return "MSG_CLEAR_ACCESSIBILITY_FOCUS";
Jackal Guo8bcc0a92019-04-16 15:26:00 +08001409 case MSG_NOTIFY_OUTSIDE_TOUCH:
1410 return "MSG_NOTIFY_OUTSIDE_TOUCH";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001411 default:
1412 throw new IllegalArgumentException("Unknown message type: " + type);
1413 }
1414 }
1415
1416 @Override
1417 public void handleMessage(Message message) {
1418 final int type = message.what;
1419 switch (type) {
Svet Ganov7498efd2014-09-03 21:33:00 -07001420 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001421 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1422 } break;
1423 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
George Mount41725de2015-04-09 08:23:05 -07001424 performAccessibilityActionUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001425 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001426 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001427 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001428 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001429 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001430 findAccessibilityNodeInfosByTextUiThread(message);
1431 } break;
1432 case MSG_FIND_FOCUS: {
1433 findFocusUiThread(message);
1434 } break;
1435 case MSG_FOCUS_SEARCH: {
1436 focusSearchUiThread(message);
1437 } break;
Phil Weaver08cccc12017-02-28 09:55:31 -08001438 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
1439 prepareForExtraDataRequestUiThread(message);
1440 } break;
1441 case MSG_APP_PREPARATION_FINISHED: {
1442 requestPreparerDoneUiThread(message);
1443 } break;
1444 case MSG_APP_PREPARATION_TIMEOUT: {
1445 requestPreparerTimeoutUiThread();
1446 } break;
Rhed Jao23813d92018-12-18 21:30:52 +08001447 case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
1448 clearAccessibilityFocusUiThread();
1449 } break;
Jackal Guo8bcc0a92019-04-16 15:26:00 +08001450 case MSG_NOTIFY_OUTSIDE_TOUCH: {
1451 notifyOutsideTouchUiThread();
1452 } break;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001453 default:
1454 throw new IllegalArgumentException("Unknown message type: " + type);
1455 }
1456 }
Rhed Jao23813d92018-12-18 21:30:52 +08001457
1458 boolean hasAccessibilityCallback(Message message) {
1459 return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false;
1460 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001461 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001462
1463 private final class AddNodeInfosForViewId implements Predicate<View> {
1464 private int mViewId = View.NO_ID;
1465 private List<AccessibilityNodeInfo> mInfos;
1466
1467 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1468 mViewId = viewId;
1469 mInfos = infos;
1470 }
1471
1472 public void reset() {
1473 mViewId = View.NO_ID;
1474 mInfos = null;
1475 }
1476
1477 @Override
Paul Duffinca4964c2017-02-07 15:04:10 +00001478 public boolean test(View view) {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001479 if (view.getId() == mViewId && isShown(view)) {
1480 mInfos.add(view.createAccessibilityNodeInfo());
1481 }
1482 return false;
1483 }
1484 }
Phil Weaver08cccc12017-02-28 09:55:31 -08001485
1486 private static final class MessageHolder {
1487 final Message mMessage;
1488 final int mInterrogatingPid;
1489 final long mInterrogatingTid;
1490
1491 MessageHolder(Message message, int interrogatingPid, long interrogatingTid) {
1492 mMessage = message;
1493 mInterrogatingPid = interrogatingPid;
1494 mInterrogatingTid = interrogatingTid;
1495 }
1496 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001497}