blob: f37c9162d98a4dc93ceeaa58db20f1b0757ededa [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
Phil Weaver193520e2016-12-13 09:39:06 -080019import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
Phil Weaver08cccc12017-02-28 09:55:31 -080020import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_REQUESTED_KEY;
Phil Weaverc2e28932016-12-08 12:29:25 -080021import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
Phil Weaver193520e2016-12-13 09:39:06 -080022
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070023import android.graphics.Point;
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070024import android.graphics.Rect;
Phil Weaverc2e28932016-12-08 12:29:25 -080025import android.graphics.RectF;
Svetoslav9ae9ed22014-09-02 16:36:35 -070026import android.graphics.Region;
Nirmal Patel386a8242015-06-02 18:11:32 -070027import android.os.Binder;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -070028import android.os.Bundle;
Svetoslav Ganov42138042012-03-20 11:51:39 -070029import android.os.Handler;
30import android.os.Looper;
31import android.os.Message;
Phil Weaver193520e2016-12-13 09:39:06 -080032import android.os.Parcelable;
Svetoslav Ganov42138042012-03-20 11:51:39 -070033import android.os.Process;
34import android.os.RemoteException;
Jackal Guoac2b62e2018-08-22 18:28:46 +080035import android.os.SystemClock;
Phil Weaver193520e2016-12-13 09:39:06 -080036import android.text.style.AccessibilityClickableSpan;
37import android.text.style.ClickableSpan;
Svetoslav8e3feb12014-02-24 13:46:47 -080038import android.util.LongSparseArray;
Phil Weaver08cccc12017-02-28 09:55:31 -080039import android.util.Slog;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070040import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070041import android.view.accessibility.AccessibilityInteractionClient;
Phil Weaver08cccc12017-02-28 09:55:31 -080042import android.view.accessibility.AccessibilityManager;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -080043import android.view.accessibility.AccessibilityNodeIdManager;
Svetoslav Ganov42138042012-03-20 11:51:39 -070044import android.view.accessibility.AccessibilityNodeInfo;
Rhed Jao23813d92018-12-18 21:30:52 +080045import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
Svetoslav Ganov42138042012-03-20 11:51:39 -070046import android.view.accessibility.AccessibilityNodeProvider;
Phil Weaver08cccc12017-02-28 09:55:31 -080047import android.view.accessibility.AccessibilityRequestPreparer;
Svetoslav Ganov42138042012-03-20 11:51:39 -070048import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
49
Phil Weaver193520e2016-12-13 09:39:06 -080050import com.android.internal.R;
Phil Weaver08cccc12017-02-28 09:55:31 -080051import com.android.internal.annotations.GuardedBy;
Rhed Jao23813d92018-12-18 21:30:52 +080052import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070053import com.android.internal.os.SomeArgs;
54
Svetoslav Ganov42138042012-03-20 11:51:39 -070055import java.util.ArrayList;
56import java.util.HashMap;
Svetoslav8e3feb12014-02-24 13:46:47 -080057import java.util.HashSet;
58import java.util.LinkedList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070059import java.util.List;
60import java.util.Map;
Svetoslav8e3feb12014-02-24 13:46:47 -080061import java.util.Queue;
Paul Duffinca4964c2017-02-07 15:04:10 +000062import java.util.function.Predicate;
Svetoslav Ganov42138042012-03-20 11:51:39 -070063
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.
Rhed Jao23813d92018-12-18 21:30:52 +080070 *
71 * @hide
Svetoslav Ganov42138042012-03-20 11:51:39 -070072 */
Rhed Jao23813d92018-12-18 21:30:52 +080073@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
74public final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070075
Phil Weaver08cccc12017-02-28 09:55:31 -080076 private static final String LOG_TAG = "AccessibilityInteractionController";
77
78 // Debugging flag
Svetoslavaaa11422014-03-28 13:31:13 -070079 private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
Svetoslav8e3feb12014-02-24 13:46:47 -080080
Phil Weaver08cccc12017-02-28 09:55:31 -080081 // 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
Svetoslav Ganov80943d82013-01-02 10:25:37 -080089 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070090 new ArrayList<AccessibilityNodeInfo>();
91
Phil Weaver08cccc12017-02-28 09:55:31 -080092 private final Object mLock = new Object();
93
Rhed Jao23813d92018-12-18 21:30:52 +080094 private final PrivateHandler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070095
96 private final ViewRootImpl mViewRootImpl;
97
98 private final AccessibilityNodePrefetcher mPrefetcher;
99
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700100 private final long mMyLooperThreadId;
101
102 private final int mMyProcessId;
103
Phil Weaver08cccc12017-02-28 09:55:31 -0800104 private final AccessibilityManager mA11yManager;
105
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700106 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
107
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700108 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700109 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700110 private final Rect mTempRect1 = new Rect();
111 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700112
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800113 private AddNodeInfosForViewId mAddNodeInfosForViewId;
114
Phil Weaver08cccc12017-02-28 09:55:31 -0800115 @GuardedBy("mLock")
116 private int mNumActiveRequestPreparers;
117 @GuardedBy("mLock")
118 private List<MessageHolder> mMessagesWaitingForRequestPreparer;
119 @GuardedBy("mLock")
120 private int mActiveRequestPreparerId;
121
Svetoslav Ganov42138042012-03-20 11:51:39 -0700122 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700123 Looper looper = viewRootImpl.mHandler.getLooper();
124 mMyLooperThreadId = looper.getThread().getId();
125 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700126 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700127 mViewRootImpl = viewRootImpl;
128 mPrefetcher = new AccessibilityNodePrefetcher();
Phil Weaver08cccc12017-02-28 09:55:31 -0800129 mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700130 }
131
Phil Weaver08cccc12017-02-28 09:55:31 -0800132 private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
133 boolean ignoreRequestPreparers) {
134 if (ignoreRequestPreparers
135 || !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {
136 // If the interrogation is performed by the same thread as the main UI
137 // thread in this process, set the message as a static reference so
138 // after this call completes the same thread but in the interrogating
139 // client can handle the message to generate the result.
Rhed Jao23813d92018-12-18 21:30:52 +0800140 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
141 && mHandler.hasAccessibilityCallback(message)) {
Phil Weaver08cccc12017-02-28 09:55:31 -0800142 AccessibilityInteractionClient.getInstanceForThread(
143 interrogatingTid).setSameThreadMessage(message);
144 } else {
Rhed Jao23813d92018-12-18 21:30:52 +0800145 // For messages without callback of interrogating client, just handle the
146 // message immediately if this is UI thread.
147 if (!mHandler.hasAccessibilityCallback(message)
148 && Thread.currentThread().getId() == mMyLooperThreadId) {
149 mHandler.handleMessage(message);
150 } else {
151 mHandler.sendMessage(message);
152 }
Phil Weaver08cccc12017-02-28 09:55:31 -0800153 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800154 }
155 }
156
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700157 private boolean isShown(View view) {
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800158 return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700159 }
160
Svetoslav Ganov42138042012-03-20 11:51:39 -0700161 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav9ae9ed22014-09-02 16:36:35 -0700162 long accessibilityNodeId, Region interactiveRegion, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700163 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Phil Weaverc2e28932016-12-08 12:29:25 -0800164 long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
Phil Weaver08cccc12017-02-28 09:55:31 -0800165 final Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700166 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700167 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700168
Phil Weaver08cccc12017-02-28 09:55:31 -0800169 final SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700170 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
171 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
172 args.argi3 = interactionId;
173 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700174 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700175 args.arg3 = interactiveRegion;
Phil Weaverc2e28932016-12-08 12:29:25 -0800176 args.arg4 = arguments;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700177 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700178
Phil Weaver08cccc12017-02-28 09:55:31 -0800179 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
180 }
181
182 /**
183 * Check if this message needs to be held off while the app prepares to meet either this
184 * request, or a request ahead of it.
185 *
186 * @param originalMessage The message to be processed
187 * @param callingPid The calling process id
188 * @param callingTid The calling thread id
189 *
190 * @return {@code true} if the message is held off and will be processed later, {@code false} if
191 * the message should be posted.
192 */
193 private boolean holdOffMessageIfNeeded(
194 Message originalMessage, int callingPid, long callingTid) {
195 synchronized (mLock) {
196 // If a request is already pending, queue this request for when it's finished
197 if (mNumActiveRequestPreparers != 0) {
198 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
199 return true;
200 }
201
202 // Currently the only message that can hold things off is findByA11yId with extra data.
203 if (originalMessage.what
204 != PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID) {
205 return false;
206 }
207 SomeArgs originalMessageArgs = (SomeArgs) originalMessage.obj;
208 Bundle requestArguments = (Bundle) originalMessageArgs.arg4;
209 if (requestArguments == null) {
210 return false;
211 }
212
213 // If nothing it registered for this view, nothing to do
214 int accessibilityViewId = originalMessageArgs.argi1;
215 final List<AccessibilityRequestPreparer> preparers =
216 mA11yManager.getRequestPreparersForAccessibilityId(accessibilityViewId);
217 if (preparers == null) {
218 return false;
219 }
220
221 // If the bundle doesn't request the extra data, nothing to do
222 final String extraDataKey = requestArguments.getString(EXTRA_DATA_REQUESTED_KEY);
223 if (extraDataKey == null) {
224 return false;
225 }
226
227 // Send the request to the AccessibilityRequestPreparers on the UI thread
228 mNumActiveRequestPreparers = preparers.size();
229 for (int i = 0; i < preparers.size(); i++) {
230 final Message requestPreparerMessage = mHandler.obtainMessage(
231 PrivateHandler.MSG_PREPARE_FOR_EXTRA_DATA_REQUEST);
232 final SomeArgs requestPreparerArgs = SomeArgs.obtain();
233 // virtualDescendentId
234 requestPreparerArgs.argi1 =
235 (originalMessageArgs.argi2 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
236 ? AccessibilityNodeProvider.HOST_VIEW_ID : originalMessageArgs.argi2;
237 requestPreparerArgs.arg1 = preparers.get(i);
238 requestPreparerArgs.arg2 = extraDataKey;
239 requestPreparerArgs.arg3 = requestArguments;
240 Message preparationFinishedMessage = mHandler.obtainMessage(
241 PrivateHandler.MSG_APP_PREPARATION_FINISHED);
242 preparationFinishedMessage.arg1 = ++mActiveRequestPreparerId;
243 requestPreparerArgs.arg4 = preparationFinishedMessage;
244
245 requestPreparerMessage.obj = requestPreparerArgs;
246 scheduleMessage(requestPreparerMessage, callingPid, callingTid,
247 IGNORE_REQUEST_PREPARERS);
248 mHandler.obtainMessage(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
249 mHandler.sendEmptyMessageDelayed(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT,
250 REQUEST_PREPARER_TIMEOUT_MS);
251 }
252
253 // Set the initial request aside
254 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
255 return true;
256 }
257 }
258
259 private void prepareForExtraDataRequestUiThread(Message message) {
260 SomeArgs args = (SomeArgs) message.obj;
261 final int virtualDescendantId = args.argi1;
262 final AccessibilityRequestPreparer preparer = (AccessibilityRequestPreparer) args.arg1;
263 final String extraDataKey = (String) args.arg2;
264 final Bundle requestArguments = (Bundle) args.arg3;
265 final Message preparationFinishedMessage = (Message) args.arg4;
266
267 preparer.onPrepareExtraData(virtualDescendantId, extraDataKey,
268 requestArguments, preparationFinishedMessage);
269 }
270
271 private void queueMessageToHandleOncePrepared(Message message, int interrogatingPid,
272 long interrogatingTid) {
273 if (mMessagesWaitingForRequestPreparer == null) {
274 mMessagesWaitingForRequestPreparer = new ArrayList<>(1);
275 }
276 MessageHolder messageHolder =
277 new MessageHolder(message, interrogatingPid, interrogatingTid);
278 mMessagesWaitingForRequestPreparer.add(messageHolder);
279 }
280
281 private void requestPreparerDoneUiThread(Message message) {
282 synchronized (mLock) {
283 if (message.arg1 != mActiveRequestPreparerId) {
284 Slog.e(LOG_TAG, "Surprising AccessibilityRequestPreparer callback (likely late)");
285 return;
286 }
287 mNumActiveRequestPreparers--;
288 if (mNumActiveRequestPreparers <= 0) {
289 mHandler.removeMessages(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
290 scheduleAllMessagesWaitingForRequestPreparerLocked();
291 }
292 }
293 }
294
295 private void requestPreparerTimeoutUiThread() {
296 synchronized (mLock) {
297 Slog.e(LOG_TAG, "AccessibilityRequestPreparer timed out");
298 scheduleAllMessagesWaitingForRequestPreparerLocked();
299 }
300 }
301
302 @GuardedBy("mLock")
303 private void scheduleAllMessagesWaitingForRequestPreparerLocked() {
304 int numMessages = mMessagesWaitingForRequestPreparer.size();
305 for (int i = 0; i < numMessages; i++) {
306 MessageHolder request = mMessagesWaitingForRequestPreparer.get(i);
307 scheduleMessage(request.mMessage, request.mInterrogatingPid,
308 request.mInterrogatingTid,
309 (i == 0) /* the app is ready for the first request */);
310 }
311 mMessagesWaitingForRequestPreparer.clear();
312 mNumActiveRequestPreparers = 0; // Just to be safe - should be unnecessary
313 mActiveRequestPreparerId = -1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700314 }
315
316 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
317 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700318
Svetoslav Ganov42138042012-03-20 11:51:39 -0700319 SomeArgs args = (SomeArgs) message.obj;
320 final int accessibilityViewId = args.argi1;
321 final int virtualDescendantId = args.argi2;
322 final int interactionId = args.argi3;
323 final IAccessibilityInteractionConnectionCallback callback =
324 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700325 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700326 final Region interactiveRegion = (Region) args.arg3;
Phil Weaverc2e28932016-12-08 12:29:25 -0800327 final Bundle arguments = (Bundle) args.arg4;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700328
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700329 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700330
Svetoslav Ganov42138042012-03-20 11:51:39 -0700331 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
332 infos.clear();
333 try {
334 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
335 return;
336 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800337 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800338 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800339 if (root != null && isShown(root)) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800340 mPrefetcher.prefetchAccessibilityNodeInfos(
341 root, virtualDescendantId, flags, infos, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700342 }
343 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800344 updateInfosForViewportAndReturnFindNodeResult(
345 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700346 }
347 }
348
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800349 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700350 String viewId, Region interactiveRegion, int interactionId,
351 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
352 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700353 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700354 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700355 message.arg1 = flags;
356 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700357
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700358 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800359 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700360 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700361 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800362 args.arg3 = viewId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700363 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700364 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700365
Phil Weaver08cccc12017-02-28 09:55:31 -0800366 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700367 }
368
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800369 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700370 final int flags = message.arg1;
371 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700372
Svetoslav Ganov42138042012-03-20 11:51:39 -0700373 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800374 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700375 final IAccessibilityInteractionConnectionCallback callback =
376 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700377 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800378 final String viewId = (String) args.arg3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700379 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700380 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700381
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800382 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
383 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700384 try {
385 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
386 return;
387 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800388 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800389 final View root = findViewByAccessibilityId(accessibilityViewId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700390 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800391 final int resolvedViewId = root.getContext().getResources()
392 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800393 if (resolvedViewId <= 0) {
394 return;
395 }
396 if (mAddNodeInfosForViewId == null) {
397 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
398 }
399 mAddNodeInfosForViewId.init(resolvedViewId, infos);
400 root.findViewByPredicate(mAddNodeInfosForViewId);
401 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700402 }
403 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800404 updateInfosForViewportAndReturnFindNodeResult(
405 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700406 }
407 }
408
409 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700410 String text, Region interactiveRegion, int interactionId,
411 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
412 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700413 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700414 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700415 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700416
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700417 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700418 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700419 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700420 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700421 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
422 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
423 args.argi3 = interactionId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700424 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700425 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700426
Phil Weaver08cccc12017-02-28 09:55:31 -0800427 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700428 }
429
430 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
431 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700432
Svetoslav Ganov42138042012-03-20 11:51:39 -0700433 SomeArgs args = (SomeArgs) message.obj;
434 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700435 final IAccessibilityInteractionConnectionCallback callback =
436 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700437 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700438 final int accessibilityViewId = args.argi1;
439 final int virtualDescendantId = args.argi2;
440 final int interactionId = args.argi3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700441 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700442 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700443
Svetoslav Ganov42138042012-03-20 11:51:39 -0700444 List<AccessibilityNodeInfo> infos = null;
445 try {
446 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
447 return;
448 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800449 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800450 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800451 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700452 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
453 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800454 infos = provider.findAccessibilityNodeInfosByText(text,
455 virtualDescendantId);
456 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700457 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700458 foundViews.clear();
459 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
460 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
461 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
462 if (!foundViews.isEmpty()) {
463 infos = mTempAccessibilityNodeInfoList;
464 infos.clear();
465 final int viewCount = foundViews.size();
466 for (int i = 0; i < viewCount; i++) {
467 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700468 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700469 provider = foundView.getAccessibilityNodeProvider();
470 if (provider != null) {
471 List<AccessibilityNodeInfo> infosFromProvider =
472 provider.findAccessibilityNodeInfosByText(text,
Svetoslav8e3feb12014-02-24 13:46:47 -0800473 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700474 if (infosFromProvider != null) {
475 infos.addAll(infosFromProvider);
476 }
477 } else {
478 infos.add(foundView.createAccessibilityNodeInfo());
479 }
480 }
481 }
482 }
483 }
484 }
485 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800486 updateInfosForViewportAndReturnFindNodeResult(
487 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700488 }
489 }
490
Svetoslav9ae9ed22014-09-02 16:36:35 -0700491 public void findFocusClientThread(long accessibilityNodeId, int focusType,
492 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800493 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700494 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700495 Message message = mHandler.obtainMessage();
496 message.what = PrivateHandler.MSG_FIND_FOCUS;
497 message.arg1 = flags;
498 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700499
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700500 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700501 args.argi1 = interactionId;
502 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
503 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
504 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700505 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700506 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700507
Svetoslav Ganov42138042012-03-20 11:51:39 -0700508 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700509
Phil Weaver08cccc12017-02-28 09:55:31 -0800510 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700511 }
512
513 private void findFocusUiThread(Message message) {
514 final int flags = message.arg1;
515 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700516
Svetoslav Ganov42138042012-03-20 11:51:39 -0700517 SomeArgs args = (SomeArgs) message.obj;
518 final int interactionId = args.argi1;
519 final int accessibilityViewId = args.argi2;
520 final int virtualDescendantId = args.argi3;
521 final IAccessibilityInteractionConnectionCallback callback =
522 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700523 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700524 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700525 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700526
Svetoslav Ganov42138042012-03-20 11:51:39 -0700527 AccessibilityNodeInfo focused = null;
528 try {
529 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
530 return;
531 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800532 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800533 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800534 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700535 switch (focusType) {
536 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
537 View host = mViewRootImpl.mAccessibilityFocusedHost;
538 // If there is no accessibility focus host or it is not a descendant
539 // of the root from which to start the search, then the search failed.
540 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
541 break;
542 }
Svetoslav6254f482013-06-04 17:22:14 -0700543 // The focused view not shown, we failed.
544 if (!isShown(host)) {
545 break;
546 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700547 // If the host has a provider ask this provider to search for the
548 // focus instead fetching all provider nodes to do the search here.
549 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
550 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700551 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
552 focused = AccessibilityNodeInfo.obtain(
553 mViewRootImpl.mAccessibilityFocusedVirtualView);
554 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800555 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700556 focused = host.createAccessibilityNodeInfo();
557 }
558 } break;
559 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700560 View target = root.findFocus();
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800561 if (!isShown(target)) {
Alan Viverette2e1e0812013-09-30 13:45:55 -0700562 break;
563 }
564 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
565 if (provider != null) {
566 focused = provider.findFocus(focusType);
567 }
568 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700569 focused = target.createAccessibilityNodeInfo();
570 }
571 } break;
572 default:
573 throw new IllegalArgumentException("Unknown focus type: " + focusType);
574 }
575 }
576 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800577 updateInfoForViewportAndReturnFindNodeResult(
578 focused, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700579 }
580 }
581
Svetoslav9ae9ed22014-09-02 16:36:35 -0700582 public void focusSearchClientThread(long accessibilityNodeId, int direction,
583 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800584 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700585 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700586 Message message = mHandler.obtainMessage();
587 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
588 message.arg1 = flags;
589 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700590
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700591 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700592 args.argi2 = direction;
593 args.argi3 = interactionId;
594 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700595 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700596 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700597
Svetoslav Ganov42138042012-03-20 11:51:39 -0700598 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700599
Phil Weaver08cccc12017-02-28 09:55:31 -0800600 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700601 }
602
603 private void focusSearchUiThread(Message message) {
604 final int flags = message.arg1;
605 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700606
Svetoslav Ganov42138042012-03-20 11:51:39 -0700607 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700608 final int direction = args.argi2;
609 final int interactionId = args.argi3;
610 final IAccessibilityInteractionConnectionCallback callback =
611 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700612 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700613 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700614
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700615 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700616
Svetoslav Ganov42138042012-03-20 11:51:39 -0700617 AccessibilityNodeInfo next = null;
618 try {
619 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
620 return;
621 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800622 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800623 final View root = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800624 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700625 View nextView = root.focusSearch(direction);
626 if (nextView != null) {
627 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700628 }
629 }
630 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800631 updateInfoForViewportAndReturnFindNodeResult(
632 next, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700633 }
634 }
635
636 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700637 Bundle arguments, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800638 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700639 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700640 Message message = mHandler.obtainMessage();
641 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
642 message.arg1 = flags;
643 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700644
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700645 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700646 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
647 args.argi2 = action;
648 args.argi3 = interactionId;
649 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700650 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700651
Svetoslav Ganov42138042012-03-20 11:51:39 -0700652 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700653
Phil Weaver08cccc12017-02-28 09:55:31 -0800654 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700655 }
656
George Mount41725de2015-04-09 08:23:05 -0700657 private void performAccessibilityActionUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700658 final int flags = message.arg1;
659 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700660
Svetoslav Ganov42138042012-03-20 11:51:39 -0700661 SomeArgs args = (SomeArgs) message.obj;
662 final int virtualDescendantId = args.argi1;
663 final int action = args.argi2;
664 final int interactionId = args.argi3;
665 final IAccessibilityInteractionConnectionCallback callback =
666 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700667 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700668
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700669 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700670
Svetoslav Ganov42138042012-03-20 11:51:39 -0700671 boolean succeeded = false;
672 try {
George Mount41725de2015-04-09 08:23:05 -0700673 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
674 mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700675 return;
676 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800677 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800678 final View target = findViewByAccessibilityId(accessibilityViewId);
Rhed Jao4b87f312019-02-13 17:45:02 +0800679 if (target != null && isShown(target)) {
Phil Weaver193520e2016-12-13 09:39:06 -0800680 if (action == R.id.accessibilityActionClickOnClickableSpan) {
681 // Handle this hidden action separately
682 succeeded = handleClickableSpanActionUiThread(
683 target, virtualDescendantId, arguments);
Jackal Guoac2b62e2018-08-22 18:28:46 +0800684 } else if (action == R.id.accessibilityActionOutsideTouch) {
685 // trigger ACTION_OUTSIDE to notify windows
686 final long now = SystemClock.uptimeMillis();
687 MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
688 0, 0, 0);
689 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
690 mViewRootImpl.dispatchInputEvent(event);
691 succeeded = true;
Phil Weaver193520e2016-12-13 09:39:06 -0800692 } else {
693 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
694 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800695 succeeded = provider.performAction(virtualDescendantId, action,
696 arguments);
697 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800698 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav8e3feb12014-02-24 13:46:47 -0800699 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700700 }
701 }
702 } finally {
703 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800704 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700705 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
706 } catch (RemoteException re) {
707 /* ignore - the other side will time out */
708 }
709 }
710 }
711
Rhed Jao23813d92018-12-18 21:30:52 +0800712 /**
713 * Finds the accessibility focused node in the root, and clears the accessibility focus.
714 */
715 public void clearAccessibilityFocusClientThread() {
716 final Message message = mHandler.obtainMessage();
717 message.what = PrivateHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS;
718
719 // Don't care about pid and tid because there's no interrogating client for this message.
720 scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
721 }
722
723 private void clearAccessibilityFocusUiThread() {
724 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
725 return;
726 }
727 try {
728 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags =
729 AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
730 final View root = mViewRootImpl.mView;
731 if (root != null && isShown(root)) {
732 final View host = mViewRootImpl.mAccessibilityFocusedHost;
733 // If there is no accessibility focus host or it is not a descendant
734 // of the root from which to start the search, then the search failed.
735 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
736 return;
737 }
738 final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
739 final AccessibilityNodeInfo focusNode =
740 mViewRootImpl.mAccessibilityFocusedVirtualView;
741 if (provider != null && focusNode != null) {
742 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
743 focusNode.getSourceNodeId());
744 provider.performAction(virtualNodeId,
745 AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
746 null);
747 } else {
748 host.performAccessibilityAction(
749 AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(),
750 null);
751 }
752 }
753 } finally {
754 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
755 }
756 }
757
Svetoslav Ganov42138042012-03-20 11:51:39 -0700758 private View findViewByAccessibilityId(int accessibilityId) {
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -0800759 if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
760 return mViewRootImpl.mView;
761 } else {
Rhed Jao4b87f312019-02-13 17:45:02 +0800762 return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700763 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700764 }
765
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700766 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
767 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700768 if (infos == null) {
769 return;
770 }
771 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700772 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700773 final int infoCount = infos.size();
774 for (int i = 0; i < infoCount; i++) {
775 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700776 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700777 }
778 }
779 }
780
Svetoslav9ae9ed22014-09-02 16:36:35 -0700781 private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
782 Region interactiveRegion) {
783 if (interactiveRegion == null || infos == null) {
784 return;
785 }
786 final int infoCount = infos.size();
787 for (int i = 0; i < infoCount; i++) {
788 AccessibilityNodeInfo info = infos.get(i);
789 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
790 }
791 }
792
793 private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
794 Region interactiveRegion) {
795 if (interactiveRegion == null || info == null) {
796 return;
797 }
798 Rect boundsInScreen = mTempRect;
799 info.getBoundsInScreen(boundsInScreen);
800 if (interactiveRegion.quickReject(boundsInScreen)) {
801 info.setVisibleToUser(false);
802 }
803 }
804
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700805 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
806 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700807 if (info == null) {
808 return;
809 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700810
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700811 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700812 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
813 return;
814 }
815
816 Rect boundsInParent = mTempRect;
817 Rect boundsInScreen = mTempRect1;
818
819 info.getBoundsInParent(boundsInParent);
820 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700821 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700822 boundsInParent.scale(applicationScale);
823 boundsInScreen.scale(applicationScale);
824 }
825 if (spec != null) {
826 boundsInParent.scale(spec.scale);
827 // boundsInParent must not be offset.
828 boundsInScreen.scale(spec.scale);
829 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
830 }
831 info.setBoundsInParent(boundsInParent);
832 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700833
Phil Weaverc2e28932016-12-08 12:29:25 -0800834 // Scale text locations if they are present
835 if (info.hasExtras()) {
836 Bundle extras = info.getExtras();
837 Parcelable[] textLocations =
838 extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
839 if (textLocations != null) {
840 for (int i = 0; i < textLocations.length; i++) {
841 // Unchecked cast - an app that puts other objects in this bundle with this
842 // key will crash.
843 RectF textLocation = ((RectF) textLocations[i]);
844 textLocation.scale(applicationScale);
845 if (spec != null) {
846 textLocation.scale(spec.scale);
847 textLocation.offset(spec.offsetX, spec.offsetY);
848 }
849 }
850 }
851 }
852
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700853 if (spec != null) {
854 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
855 if (attachInfo.mDisplay == null) {
856 return;
857 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700858
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700859 final float scale = attachInfo.mApplicationScale * spec.scale;
860
861 Rect visibleWinFrame = mTempRect1;
862 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
863 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
864 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
865 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
866
867 attachInfo.mDisplay.getRealSize(mTempPoint);
868 final int displayWidth = mTempPoint.x;
869 final int displayHeight = mTempPoint.y;
870
871 Rect visibleDisplayFrame = mTempRect2;
872 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
873
Doris Liu9607fbe2015-05-28 17:17:28 -0700874 if (!visibleWinFrame.intersect(visibleDisplayFrame)) {
875 // If there's no intersection with display, set visibleWinFrame empty.
876 visibleDisplayFrame.setEmpty();
877 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700878
879 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
880 boundsInScreen.right, boundsInScreen.bottom)) {
881 info.setVisibleToUser(false);
882 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700883 }
884 }
885
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700886 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
887 MagnificationSpec spec) {
888 return (appScale != 1.0f || (spec != null && !spec.isNop()));
889 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700890
Phil Weaverc2e28932016-12-08 12:29:25 -0800891 private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
892 IAccessibilityInteractionConnectionCallback callback, int interactionId,
893 MagnificationSpec spec, Region interactiveRegion) {
894 try {
895 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
896 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
897 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
898 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Phil Weaverf00cd142017-03-03 13:44:00 -0800899 if (infos != null) {
900 infos.clear();
901 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800902 } catch (RemoteException re) {
903 /* ignore - the other side will time out */
904 } finally {
905 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
906 }
907 }
908
909 private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
910 IAccessibilityInteractionConnectionCallback callback, int interactionId,
911 MagnificationSpec spec, Region interactiveRegion) {
912 try {
913 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
914 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
915 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
916 callback.setFindAccessibilityNodeInfoResult(info, interactionId);
917 } catch (RemoteException re) {
918 /* ignore - the other side will time out */
919 } finally {
920 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
921 }
922 }
923
924 private void recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region) {
925 if (android.os.Process.myPid() != Binder.getCallingPid()) {
926 // Specs are cached in the system process and obtained from a pool when read from
927 // a parcel, so only recycle the spec if called from another process.
928 if (spec != null) {
929 spec.recycle();
930 }
931 } else {
932 // Regions are obtained in the system process and instantiated when read from
933 // a parcel, so only recycle the region if caled from the same process.
934 if (region != null) {
935 region.recycle();
936 }
937 }
938 }
939
Phil Weaver193520e2016-12-13 09:39:06 -0800940 private boolean handleClickableSpanActionUiThread(
941 View view, int virtualDescendantId, Bundle arguments) {
942 Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
943 if (!(span instanceof AccessibilityClickableSpan)) {
944 return false;
945 }
946
947 // Find the original ClickableSpan if it's still on the screen
948 AccessibilityNodeInfo infoWithSpan = null;
949 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
950 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800951 infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
952 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800953 infoWithSpan = view.createAccessibilityNodeInfo();
954 }
955 if (infoWithSpan == null) {
956 return false;
957 }
958
959 // Click on the corresponding span
960 ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
961 infoWithSpan.getOriginalText());
962 if (clickableSpan != null) {
963 clickableSpan.onClick(view);
964 return true;
965 }
966 return false;
967 }
968
Svetoslav Ganov42138042012-03-20 11:51:39 -0700969 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700970 * This class encapsulates a prefetching strategy for the accessibility APIs for
971 * querying window content. It is responsible to prefetch a batch of
972 * AccessibilityNodeInfos in addition to the one for a requested node.
973 */
974 private class AccessibilityNodePrefetcher {
975
976 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
977
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700978 private final ArrayList<View> mTempViewList = new ArrayList<View>();
979
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800980 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Phil Weaverc2e28932016-12-08 12:29:25 -0800981 List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700982 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
Phil Weaverc2e28932016-12-08 12:29:25 -0800983 // Determine if we'll be populating extra data
984 final String extraDataRequested = (arguments == null) ? null
Phil Weaver08cccc12017-02-28 09:55:31 -0800985 : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700986 if (provider == null) {
987 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
988 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800989 if (extraDataRequested != null) {
990 view.addExtraDataToAccessibilityNodeInfo(
991 root, extraDataRequested, arguments);
992 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700993 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800994 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700995 prefetchPredecessorsOfRealNode(view, outInfos);
996 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800997 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700998 prefetchSiblingsOfRealNode(view, outInfos);
999 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001000 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001001 prefetchDescendantsOfRealNode(view, outInfos);
1002 }
1003 }
1004 } else {
Phil Weaverf00cd142017-03-03 13:44:00 -08001005 final AccessibilityNodeInfo root =
1006 provider.createAccessibilityNodeInfo(virtualViewId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001007 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -08001008 if (extraDataRequested != null) {
1009 provider.addExtraDataToAccessibilityNodeInfo(
Phil Weaverf00cd142017-03-03 13:44:00 -08001010 virtualViewId, root, extraDataRequested, arguments);
Phil Weaverc2e28932016-12-08 12:29:25 -08001011 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001012 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001013 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001014 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
1015 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001016 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001017 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
1018 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001019 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001020 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
1021 }
1022 }
1023 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001024 if (ENFORCE_NODE_TREE_CONSISTENT) {
1025 enforceNodeTreeConsistent(outInfos);
1026 }
1027 }
1028
1029 private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
1030 LongSparseArray<AccessibilityNodeInfo> nodeMap =
1031 new LongSparseArray<AccessibilityNodeInfo>();
1032 final int nodeCount = nodes.size();
1033 for (int i = 0; i < nodeCount; i++) {
1034 AccessibilityNodeInfo node = nodes.get(i);
1035 nodeMap.put(node.getSourceNodeId(), node);
1036 }
1037
1038 // If the nodes are a tree it does not matter from
1039 // which node we start to search for the root.
1040 AccessibilityNodeInfo root = nodeMap.valueAt(0);
1041 AccessibilityNodeInfo parent = root;
1042 while (parent != null) {
1043 root = parent;
1044 parent = nodeMap.get(parent.getParentNodeId());
1045 }
1046
1047 // Traverse the tree and do some checks.
1048 AccessibilityNodeInfo accessFocus = null;
1049 AccessibilityNodeInfo inputFocus = null;
1050 HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
1051 Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
1052 fringe.add(root);
1053
1054 while (!fringe.isEmpty()) {
1055 AccessibilityNodeInfo current = fringe.poll();
1056
1057 // Check for duplicates
1058 if (!seen.add(current)) {
1059 throw new IllegalStateException("Duplicate node: "
1060 + current + " in window:"
1061 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1062 }
1063
1064 // Check for one accessibility focus.
1065 if (current.isAccessibilityFocused()) {
1066 if (accessFocus != null) {
1067 throw new IllegalStateException("Duplicate accessibility focus:"
1068 + current
1069 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1070 } else {
1071 accessFocus = current;
1072 }
1073 }
1074
1075 // Check for one input focus.
1076 if (current.isFocused()) {
1077 if (inputFocus != null) {
1078 throw new IllegalStateException("Duplicate input focus: "
1079 + current + " in window:"
1080 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1081 } else {
1082 inputFocus = current;
1083 }
1084 }
1085
1086 final int childCount = current.getChildCount();
1087 for (int j = 0; j < childCount; j++) {
1088 final long childId = current.getChildId(j);
1089 final AccessibilityNodeInfo child = nodeMap.get(childId);
1090 if (child != null) {
1091 fringe.add(child);
1092 }
1093 }
1094 }
1095
1096 // Check for disconnected nodes.
1097 for (int j = nodeMap.size() - 1; j >= 0; j--) {
1098 AccessibilityNodeInfo info = nodeMap.valueAt(j);
1099 if (!seen.contains(info)) {
1100 throw new IllegalStateException("Disconnected node: " + info);
1101 }
1102 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001103 }
1104
1105 private void prefetchPredecessorsOfRealNode(View view,
1106 List<AccessibilityNodeInfo> outInfos) {
1107 ViewParent parent = view.getParentForAccessibility();
1108 while (parent instanceof View
1109 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1110 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001111 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
1112 if (info != null) {
1113 outInfos.add(info);
1114 }
1115 parent = parent.getParentForAccessibility();
1116 }
1117 }
1118
1119 private void prefetchSiblingsOfRealNode(View current,
1120 List<AccessibilityNodeInfo> outInfos) {
1121 ViewParent parent = current.getParentForAccessibility();
1122 if (parent instanceof ViewGroup) {
1123 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001124 ArrayList<View> children = mTempViewList;
1125 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001126 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001127 parentGroup.addChildrenForAccessibility(children);
1128 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001129 for (int i = 0; i < childCount; i++) {
1130 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1131 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001132 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001133 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001134 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Qasid Ahmad Sadiq834787a2019-01-18 00:01:54 -08001135 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001136 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001137 AccessibilityNodeProvider provider =
1138 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001139 if (provider == null) {
1140 info = child.createAccessibilityNodeInfo();
1141 } else {
1142 info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001143 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001144 }
1145 if (info != null) {
1146 outInfos.add(info);
1147 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001148 }
1149 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001150 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001151 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001152 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001153 }
1154 }
1155
1156 private void prefetchDescendantsOfRealNode(View root,
1157 List<AccessibilityNodeInfo> outInfos) {
1158 if (!(root instanceof ViewGroup)) {
1159 return;
1160 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001161 HashMap<View, AccessibilityNodeInfo> addedChildren =
1162 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001163 ArrayList<View> children = mTempViewList;
1164 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001165 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001166 root.addChildrenForAccessibility(children);
1167 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001168 for (int i = 0; i < childCount; i++) {
1169 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1170 return;
1171 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001172 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001173 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001174 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1175 if (provider == null) {
1176 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1177 if (info != null) {
1178 outInfos.add(info);
1179 addedChildren.put(child, null);
1180 }
1181 } else {
1182 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001183 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001184 if (info != null) {
1185 outInfos.add(info);
1186 addedChildren.put(child, info);
1187 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001188 }
1189 }
1190 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001191 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001192 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001193 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001194 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1195 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1196 View addedChild = entry.getKey();
1197 AccessibilityNodeInfo virtualRoot = entry.getValue();
1198 if (virtualRoot == null) {
1199 prefetchDescendantsOfRealNode(addedChild, outInfos);
1200 } else {
1201 AccessibilityNodeProvider provider =
1202 addedChild.getAccessibilityNodeProvider();
1203 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1204 }
1205 }
1206 }
1207 }
1208
1209 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1210 View providerHost, AccessibilityNodeProvider provider,
1211 List<AccessibilityNodeInfo> outInfos) {
Svet Ganov75e58162016-02-19 16:29:24 -08001212 final int initialResultSize = outInfos.size();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001213 long parentNodeId = root.getParentNodeId();
1214 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001215 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001216 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1217 return;
1218 }
1219 final int virtualDescendantId =
1220 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001221 if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001222 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001223 final AccessibilityNodeInfo parent;
Phil Weaverf00cd142017-03-03 13:44:00 -08001224 parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
Alan Viverette84feea112014-11-04 15:59:55 -08001225 if (parent == null) {
Svet Ganov75e58162016-02-19 16:29:24 -08001226 // Going up the parent relation we found a null predecessor,
1227 // so remove these disconnected nodes form the result.
1228 final int currentResultSize = outInfos.size();
1229 for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1230 outInfos.remove(i);
1231 }
Alan Viverette84feea112014-11-04 15:59:55 -08001232 // Couldn't obtain the parent, which means we have a
1233 // disconnected sub-tree. Abort prefetch immediately.
1234 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001235 }
Alan Viverette84feea112014-11-04 15:59:55 -08001236 outInfos.add(parent);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001237 parentNodeId = parent.getParentNodeId();
1238 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1239 parentNodeId);
1240 } else {
1241 prefetchPredecessorsOfRealNode(providerHost, outInfos);
1242 return;
1243 }
1244 }
1245 }
1246
1247 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1248 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1249 final long parentNodeId = current.getParentNodeId();
1250 final int parentAccessibilityViewId =
1251 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1252 final int parentVirtualDescendantId =
1253 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001254 if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001255 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
Phil Weaverf00cd142017-03-03 13:44:00 -08001256 final AccessibilityNodeInfo parent =
1257 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001258 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -08001259 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001260 for (int i = 0; i < childCount; i++) {
1261 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1262 return;
1263 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001264 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001265 if (childNodeId != current.getSourceNodeId()) {
1266 final int childVirtualDescendantId =
1267 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1268 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1269 childVirtualDescendantId);
1270 if (child != null) {
1271 outInfos.add(child);
1272 }
1273 }
1274 }
1275 }
1276 } else {
1277 prefetchSiblingsOfRealNode(providerHost, outInfos);
1278 }
1279 }
1280
1281 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1282 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001283 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -08001284 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001285 for (int i = 0; i < childCount; i++) {
1286 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1287 return;
1288 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001289 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001290 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1291 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1292 if (child != null) {
1293 outInfos.add(child);
1294 }
1295 }
1296 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1297 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1298 for (int i = 0; i < addedChildCount; i++) {
1299 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1300 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1301 }
1302 }
1303 }
1304 }
1305
1306 private class PrivateHandler extends Handler {
Phil Weaverc2e28932016-12-08 12:29:25 -08001307 private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
1308 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1309 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1310 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
1311 private static final int MSG_FIND_FOCUS = 5;
1312 private static final int MSG_FOCUS_SEARCH = 6;
Phil Weaver08cccc12017-02-28 09:55:31 -08001313 private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;
1314 private static final int MSG_APP_PREPARATION_FINISHED = 8;
1315 private static final int MSG_APP_PREPARATION_TIMEOUT = 9;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001316
Rhed Jao23813d92018-12-18 21:30:52 +08001317 // Uses FIRST_NO_ACCESSIBILITY_CALLBACK_MSG for messages that don't need to call back
1318 // results to interrogating client.
1319 private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100;
1320 private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS =
1321 FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1;
1322
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001323 public PrivateHandler(Looper looper) {
1324 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001325 }
1326
1327 @Override
1328 public String getMessageName(Message message) {
1329 final int type = message.what;
1330 switch (type) {
1331 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1332 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
Svet Ganov7498efd2014-09-03 21:33:00 -07001333 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1334 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1335 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1336 return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1337 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1338 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001339 case MSG_FIND_FOCUS:
1340 return "MSG_FIND_FOCUS";
1341 case MSG_FOCUS_SEARCH:
1342 return "MSG_FOCUS_SEARCH";
Phil Weaver08cccc12017-02-28 09:55:31 -08001343 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:
1344 return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";
1345 case MSG_APP_PREPARATION_FINISHED:
1346 return "MSG_APP_PREPARATION_FINISHED";
1347 case MSG_APP_PREPARATION_TIMEOUT:
1348 return "MSG_APP_PREPARATION_TIMEOUT";
Rhed Jao23813d92018-12-18 21:30:52 +08001349 case MSG_CLEAR_ACCESSIBILITY_FOCUS:
1350 return "MSG_CLEAR_ACCESSIBILITY_FOCUS";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001351 default:
1352 throw new IllegalArgumentException("Unknown message type: " + type);
1353 }
1354 }
1355
1356 @Override
1357 public void handleMessage(Message message) {
1358 final int type = message.what;
1359 switch (type) {
Svet Ganov7498efd2014-09-03 21:33:00 -07001360 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001361 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1362 } break;
1363 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
George Mount41725de2015-04-09 08:23:05 -07001364 performAccessibilityActionUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001365 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001366 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001367 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001368 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001369 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001370 findAccessibilityNodeInfosByTextUiThread(message);
1371 } break;
1372 case MSG_FIND_FOCUS: {
1373 findFocusUiThread(message);
1374 } break;
1375 case MSG_FOCUS_SEARCH: {
1376 focusSearchUiThread(message);
1377 } break;
Phil Weaver08cccc12017-02-28 09:55:31 -08001378 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
1379 prepareForExtraDataRequestUiThread(message);
1380 } break;
1381 case MSG_APP_PREPARATION_FINISHED: {
1382 requestPreparerDoneUiThread(message);
1383 } break;
1384 case MSG_APP_PREPARATION_TIMEOUT: {
1385 requestPreparerTimeoutUiThread();
1386 } break;
Rhed Jao23813d92018-12-18 21:30:52 +08001387 case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
1388 clearAccessibilityFocusUiThread();
1389 } break;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001390 default:
1391 throw new IllegalArgumentException("Unknown message type: " + type);
1392 }
1393 }
Rhed Jao23813d92018-12-18 21:30:52 +08001394
1395 boolean hasAccessibilityCallback(Message message) {
1396 return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false;
1397 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001398 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001399
1400 private final class AddNodeInfosForViewId implements Predicate<View> {
1401 private int mViewId = View.NO_ID;
1402 private List<AccessibilityNodeInfo> mInfos;
1403
1404 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1405 mViewId = viewId;
1406 mInfos = infos;
1407 }
1408
1409 public void reset() {
1410 mViewId = View.NO_ID;
1411 mInfos = null;
1412 }
1413
1414 @Override
Paul Duffinca4964c2017-02-07 15:04:10 +00001415 public boolean test(View view) {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001416 if (view.getId() == mViewId && isShown(view)) {
1417 mInfos.add(view.createAccessibilityNodeInfo());
1418 }
1419 return false;
1420 }
1421 }
Phil Weaver08cccc12017-02-28 09:55:31 -08001422
1423 private static final class MessageHolder {
1424 final Message mMessage;
1425 final int mInterrogatingPid;
1426 final long mInterrogatingTid;
1427
1428 MessageHolder(Message message, int interrogatingPid, long interrogatingTid) {
1429 mMessage = message;
1430 mInterrogatingPid = interrogatingPid;
1431 mInterrogatingTid = interrogatingTid;
1432 }
1433 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001434}