blob: 45fa5614abb71e46d8ee50b25f9bbadc9b2a1054 [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;
Phil Weaver193520e2016-12-13 09:39:06 -080035import android.text.style.AccessibilityClickableSpan;
36import android.text.style.ClickableSpan;
Svetoslav8e3feb12014-02-24 13:46:47 -080037import android.util.LongSparseArray;
Phil Weaver08cccc12017-02-28 09:55:31 -080038import android.util.Slog;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070039import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070040import android.view.accessibility.AccessibilityInteractionClient;
Phil Weaver08cccc12017-02-28 09:55:31 -080041import android.view.accessibility.AccessibilityManager;
Svetoslav Ganov42138042012-03-20 11:51:39 -070042import android.view.accessibility.AccessibilityNodeInfo;
43import android.view.accessibility.AccessibilityNodeProvider;
Phil Weaver08cccc12017-02-28 09:55:31 -080044import android.view.accessibility.AccessibilityRequestPreparer;
Svetoslav Ganov42138042012-03-20 11:51:39 -070045import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
46
Phil Weaver193520e2016-12-13 09:39:06 -080047import com.android.internal.R;
Phil Weaver08cccc12017-02-28 09:55:31 -080048import com.android.internal.annotations.GuardedBy;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070049import com.android.internal.os.SomeArgs;
50
Svetoslav Ganov42138042012-03-20 11:51:39 -070051import java.util.ArrayList;
52import java.util.HashMap;
Svetoslav8e3feb12014-02-24 13:46:47 -080053import java.util.HashSet;
54import java.util.LinkedList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070055import java.util.List;
56import java.util.Map;
Svetoslav8e3feb12014-02-24 13:46:47 -080057import java.util.Queue;
Paul Duffinca4964c2017-02-07 15:04:10 +000058import java.util.function.Predicate;
Svetoslav Ganov42138042012-03-20 11:51:39 -070059
60/**
61 * Class for managing accessibility interactions initiated from the system
62 * and targeting the view hierarchy. A *ClientThread method is to be
63 * called from the interaction connection ViewAncestor gives the system to
64 * talk to it and a corresponding *UiThread method that is executed on the
65 * UI thread.
66 */
67final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070068
Phil Weaver08cccc12017-02-28 09:55:31 -080069 private static final String LOG_TAG = "AccessibilityInteractionController";
70
71 // Debugging flag
Svetoslavaaa11422014-03-28 13:31:13 -070072 private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
Svetoslav8e3feb12014-02-24 13:46:47 -080073
Phil Weaver08cccc12017-02-28 09:55:31 -080074 // Constants for readability
75 private static final boolean IGNORE_REQUEST_PREPARERS = true;
76 private static final boolean CONSIDER_REQUEST_PREPARERS = false;
77
78 // If an app holds off accessibility for longer than this, the hold-off is canceled to prevent
79 // accessibility from hanging
80 private static final long REQUEST_PREPARER_TIMEOUT_MS = 500;
81
Svetoslav Ganov80943d82013-01-02 10:25:37 -080082 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070083 new ArrayList<AccessibilityNodeInfo>();
84
Phil Weaver08cccc12017-02-28 09:55:31 -080085 private final Object mLock = new Object();
86
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070087 private final Handler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070088
89 private final ViewRootImpl mViewRootImpl;
90
91 private final AccessibilityNodePrefetcher mPrefetcher;
92
Svetoslav Ganov749e7962012-04-19 17:13:46 -070093 private final long mMyLooperThreadId;
94
95 private final int mMyProcessId;
96
Phil Weaver08cccc12017-02-28 09:55:31 -080097 private final AccessibilityManager mA11yManager;
98
Svetoslav Ganov30ac6452012-06-01 09:10:25 -070099 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
100
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700101 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700102 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700103 private final Rect mTempRect1 = new Rect();
104 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700105
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800106 private AddNodeInfosForViewId mAddNodeInfosForViewId;
107
Phil Weaver08cccc12017-02-28 09:55:31 -0800108 @GuardedBy("mLock")
109 private int mNumActiveRequestPreparers;
110 @GuardedBy("mLock")
111 private List<MessageHolder> mMessagesWaitingForRequestPreparer;
112 @GuardedBy("mLock")
113 private int mActiveRequestPreparerId;
114
Svetoslav Ganov42138042012-03-20 11:51:39 -0700115 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700116 Looper looper = viewRootImpl.mHandler.getLooper();
117 mMyLooperThreadId = looper.getThread().getId();
118 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700119 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700120 mViewRootImpl = viewRootImpl;
121 mPrefetcher = new AccessibilityNodePrefetcher();
Phil Weaver08cccc12017-02-28 09:55:31 -0800122 mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700123 }
124
Phil Weaver08cccc12017-02-28 09:55:31 -0800125 private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
126 boolean ignoreRequestPreparers) {
127 if (ignoreRequestPreparers
128 || !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {
129 // If the interrogation is performed by the same thread as the main UI
130 // thread in this process, set the message as a static reference so
131 // after this call completes the same thread but in the interrogating
132 // client can handle the message to generate the result.
133 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
134 AccessibilityInteractionClient.getInstanceForThread(
135 interrogatingTid).setSameThreadMessage(message);
136 } else {
137 mHandler.sendMessage(message);
138 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800139 }
140 }
141
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700142 private boolean isShown(View view) {
143 // The first two checks are made also made by isShown() which
144 // however traverses the tree up to the parent to catch that.
145 // Therefore, we do some fail fast check to minimize the up
146 // tree traversal.
147 return (view.mAttachInfo != null
148 && view.mAttachInfo.mWindowVisibility == View.VISIBLE
149 && view.isShown());
150 }
151
Svetoslav Ganov42138042012-03-20 11:51:39 -0700152 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav9ae9ed22014-09-02 16:36:35 -0700153 long accessibilityNodeId, Region interactiveRegion, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700154 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Phil Weaverc2e28932016-12-08 12:29:25 -0800155 long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
Phil Weaver08cccc12017-02-28 09:55:31 -0800156 final Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700157 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700158 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700159
Phil Weaver08cccc12017-02-28 09:55:31 -0800160 final SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700161 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
162 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
163 args.argi3 = interactionId;
164 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700165 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700166 args.arg3 = interactiveRegion;
Phil Weaverc2e28932016-12-08 12:29:25 -0800167 args.arg4 = arguments;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700168 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700169
Phil Weaver08cccc12017-02-28 09:55:31 -0800170 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
171 }
172
173 /**
174 * Check if this message needs to be held off while the app prepares to meet either this
175 * request, or a request ahead of it.
176 *
177 * @param originalMessage The message to be processed
178 * @param callingPid The calling process id
179 * @param callingTid The calling thread id
180 *
181 * @return {@code true} if the message is held off and will be processed later, {@code false} if
182 * the message should be posted.
183 */
184 private boolean holdOffMessageIfNeeded(
185 Message originalMessage, int callingPid, long callingTid) {
186 synchronized (mLock) {
187 // If a request is already pending, queue this request for when it's finished
188 if (mNumActiveRequestPreparers != 0) {
189 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
190 return true;
191 }
192
193 // Currently the only message that can hold things off is findByA11yId with extra data.
194 if (originalMessage.what
195 != PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID) {
196 return false;
197 }
198 SomeArgs originalMessageArgs = (SomeArgs) originalMessage.obj;
199 Bundle requestArguments = (Bundle) originalMessageArgs.arg4;
200 if (requestArguments == null) {
201 return false;
202 }
203
204 // If nothing it registered for this view, nothing to do
205 int accessibilityViewId = originalMessageArgs.argi1;
206 final List<AccessibilityRequestPreparer> preparers =
207 mA11yManager.getRequestPreparersForAccessibilityId(accessibilityViewId);
208 if (preparers == null) {
209 return false;
210 }
211
212 // If the bundle doesn't request the extra data, nothing to do
213 final String extraDataKey = requestArguments.getString(EXTRA_DATA_REQUESTED_KEY);
214 if (extraDataKey == null) {
215 return false;
216 }
217
218 // Send the request to the AccessibilityRequestPreparers on the UI thread
219 mNumActiveRequestPreparers = preparers.size();
220 for (int i = 0; i < preparers.size(); i++) {
221 final Message requestPreparerMessage = mHandler.obtainMessage(
222 PrivateHandler.MSG_PREPARE_FOR_EXTRA_DATA_REQUEST);
223 final SomeArgs requestPreparerArgs = SomeArgs.obtain();
224 // virtualDescendentId
225 requestPreparerArgs.argi1 =
226 (originalMessageArgs.argi2 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
227 ? AccessibilityNodeProvider.HOST_VIEW_ID : originalMessageArgs.argi2;
228 requestPreparerArgs.arg1 = preparers.get(i);
229 requestPreparerArgs.arg2 = extraDataKey;
230 requestPreparerArgs.arg3 = requestArguments;
231 Message preparationFinishedMessage = mHandler.obtainMessage(
232 PrivateHandler.MSG_APP_PREPARATION_FINISHED);
233 preparationFinishedMessage.arg1 = ++mActiveRequestPreparerId;
234 requestPreparerArgs.arg4 = preparationFinishedMessage;
235
236 requestPreparerMessage.obj = requestPreparerArgs;
237 scheduleMessage(requestPreparerMessage, callingPid, callingTid,
238 IGNORE_REQUEST_PREPARERS);
239 mHandler.obtainMessage(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
240 mHandler.sendEmptyMessageDelayed(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT,
241 REQUEST_PREPARER_TIMEOUT_MS);
242 }
243
244 // Set the initial request aside
245 queueMessageToHandleOncePrepared(originalMessage, callingPid, callingTid);
246 return true;
247 }
248 }
249
250 private void prepareForExtraDataRequestUiThread(Message message) {
251 SomeArgs args = (SomeArgs) message.obj;
252 final int virtualDescendantId = args.argi1;
253 final AccessibilityRequestPreparer preparer = (AccessibilityRequestPreparer) args.arg1;
254 final String extraDataKey = (String) args.arg2;
255 final Bundle requestArguments = (Bundle) args.arg3;
256 final Message preparationFinishedMessage = (Message) args.arg4;
257
258 preparer.onPrepareExtraData(virtualDescendantId, extraDataKey,
259 requestArguments, preparationFinishedMessage);
260 }
261
262 private void queueMessageToHandleOncePrepared(Message message, int interrogatingPid,
263 long interrogatingTid) {
264 if (mMessagesWaitingForRequestPreparer == null) {
265 mMessagesWaitingForRequestPreparer = new ArrayList<>(1);
266 }
267 MessageHolder messageHolder =
268 new MessageHolder(message, interrogatingPid, interrogatingTid);
269 mMessagesWaitingForRequestPreparer.add(messageHolder);
270 }
271
272 private void requestPreparerDoneUiThread(Message message) {
273 synchronized (mLock) {
274 if (message.arg1 != mActiveRequestPreparerId) {
275 Slog.e(LOG_TAG, "Surprising AccessibilityRequestPreparer callback (likely late)");
276 return;
277 }
278 mNumActiveRequestPreparers--;
279 if (mNumActiveRequestPreparers <= 0) {
280 mHandler.removeMessages(PrivateHandler.MSG_APP_PREPARATION_TIMEOUT);
281 scheduleAllMessagesWaitingForRequestPreparerLocked();
282 }
283 }
284 }
285
286 private void requestPreparerTimeoutUiThread() {
287 synchronized (mLock) {
288 Slog.e(LOG_TAG, "AccessibilityRequestPreparer timed out");
289 scheduleAllMessagesWaitingForRequestPreparerLocked();
290 }
291 }
292
293 @GuardedBy("mLock")
294 private void scheduleAllMessagesWaitingForRequestPreparerLocked() {
295 int numMessages = mMessagesWaitingForRequestPreparer.size();
296 for (int i = 0; i < numMessages; i++) {
297 MessageHolder request = mMessagesWaitingForRequestPreparer.get(i);
298 scheduleMessage(request.mMessage, request.mInterrogatingPid,
299 request.mInterrogatingTid,
300 (i == 0) /* the app is ready for the first request */);
301 }
302 mMessagesWaitingForRequestPreparer.clear();
303 mNumActiveRequestPreparers = 0; // Just to be safe - should be unnecessary
304 mActiveRequestPreparerId = -1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700305 }
306
307 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
308 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700309
Svetoslav Ganov42138042012-03-20 11:51:39 -0700310 SomeArgs args = (SomeArgs) message.obj;
311 final int accessibilityViewId = args.argi1;
312 final int virtualDescendantId = args.argi2;
313 final int interactionId = args.argi3;
314 final IAccessibilityInteractionConnectionCallback callback =
315 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700316 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700317 final Region interactiveRegion = (Region) args.arg3;
Phil Weaverc2e28932016-12-08 12:29:25 -0800318 final Bundle arguments = (Bundle) args.arg4;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700319
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700320 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700321
Svetoslav Ganov42138042012-03-20 11:51:39 -0700322 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
323 infos.clear();
324 try {
325 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
326 return;
327 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800328 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700329 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800330 if (accessibilityViewId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700331 root = mViewRootImpl.mView;
332 } else {
333 root = findViewByAccessibilityId(accessibilityViewId);
334 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700335 if (root != null && isShown(root)) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800336 mPrefetcher.prefetchAccessibilityNodeInfos(
337 root, virtualDescendantId, flags, infos, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700338 }
339 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800340 updateInfosForViewportAndReturnFindNodeResult(
341 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700342 }
343 }
344
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800345 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700346 String viewId, Region interactiveRegion, int interactionId,
347 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
348 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700349 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700350 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700351 message.arg1 = flags;
352 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700353
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700354 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800355 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700356 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700357 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800358 args.arg3 = viewId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700359 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700360 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700361
Phil Weaver08cccc12017-02-28 09:55:31 -0800362 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700363 }
364
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800365 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700366 final int flags = message.arg1;
367 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700368
Svetoslav Ganov42138042012-03-20 11:51:39 -0700369 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800370 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700371 final IAccessibilityInteractionConnectionCallback callback =
372 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700373 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800374 final String viewId = (String) args.arg3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700375 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700376 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700377
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800378 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
379 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700380 try {
381 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
382 return;
383 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800384 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700385 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800386 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700387 root = findViewByAccessibilityId(accessibilityViewId);
388 } else {
389 root = mViewRootImpl.mView;
390 }
391 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;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700451 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800452 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700453 root = findViewByAccessibilityId(accessibilityViewId);
454 } else {
455 root = mViewRootImpl.mView;
456 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700457 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700458 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
459 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800460 infos = provider.findAccessibilityNodeInfosByText(text,
461 virtualDescendantId);
462 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700463 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700464 foundViews.clear();
465 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
466 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
467 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
468 if (!foundViews.isEmpty()) {
469 infos = mTempAccessibilityNodeInfoList;
470 infos.clear();
471 final int viewCount = foundViews.size();
472 for (int i = 0; i < viewCount; i++) {
473 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700474 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700475 provider = foundView.getAccessibilityNodeProvider();
476 if (provider != null) {
477 List<AccessibilityNodeInfo> infosFromProvider =
478 provider.findAccessibilityNodeInfosByText(text,
Svetoslav8e3feb12014-02-24 13:46:47 -0800479 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700480 if (infosFromProvider != null) {
481 infos.addAll(infosFromProvider);
482 }
483 } else {
484 infos.add(foundView.createAccessibilityNodeInfo());
485 }
486 }
487 }
488 }
489 }
490 }
491 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800492 updateInfosForViewportAndReturnFindNodeResult(
493 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700494 }
495 }
496
Svetoslav9ae9ed22014-09-02 16:36:35 -0700497 public void findFocusClientThread(long accessibilityNodeId, int focusType,
498 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800499 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700500 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700501 Message message = mHandler.obtainMessage();
502 message.what = PrivateHandler.MSG_FIND_FOCUS;
503 message.arg1 = flags;
504 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700505
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700506 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700507 args.argi1 = interactionId;
508 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
509 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
510 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700511 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700512 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700513
Svetoslav Ganov42138042012-03-20 11:51:39 -0700514 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700515
Phil Weaver08cccc12017-02-28 09:55:31 -0800516 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700517 }
518
519 private void findFocusUiThread(Message message) {
520 final int flags = message.arg1;
521 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700522
Svetoslav Ganov42138042012-03-20 11:51:39 -0700523 SomeArgs args = (SomeArgs) message.obj;
524 final int interactionId = args.argi1;
525 final int accessibilityViewId = args.argi2;
526 final int virtualDescendantId = args.argi3;
527 final IAccessibilityInteractionConnectionCallback callback =
528 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700529 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700530 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700531 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700532
Svetoslav Ganov42138042012-03-20 11:51:39 -0700533 AccessibilityNodeInfo focused = null;
534 try {
535 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
536 return;
537 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800538 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700539 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800540 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700541 root = findViewByAccessibilityId(accessibilityViewId);
542 } else {
543 root = mViewRootImpl.mView;
544 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700545 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700546 switch (focusType) {
547 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
548 View host = mViewRootImpl.mAccessibilityFocusedHost;
549 // If there is no accessibility focus host or it is not a descendant
550 // of the root from which to start the search, then the search failed.
551 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
552 break;
553 }
Svetoslav6254f482013-06-04 17:22:14 -0700554 // The focused view not shown, we failed.
555 if (!isShown(host)) {
556 break;
557 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700558 // If the host has a provider ask this provider to search for the
559 // focus instead fetching all provider nodes to do the search here.
560 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
561 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700562 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
563 focused = AccessibilityNodeInfo.obtain(
564 mViewRootImpl.mAccessibilityFocusedVirtualView);
565 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800566 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700567 focused = host.createAccessibilityNodeInfo();
568 }
569 } break;
570 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700571 View target = root.findFocus();
Alan Viverette2e1e0812013-09-30 13:45:55 -0700572 if (target == null || !isShown(target)) {
573 break;
574 }
575 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
576 if (provider != null) {
577 focused = provider.findFocus(focusType);
578 }
579 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700580 focused = target.createAccessibilityNodeInfo();
581 }
582 } break;
583 default:
584 throw new IllegalArgumentException("Unknown focus type: " + focusType);
585 }
586 }
587 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800588 updateInfoForViewportAndReturnFindNodeResult(
589 focused, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700590 }
591 }
592
Svetoslav9ae9ed22014-09-02 16:36:35 -0700593 public void focusSearchClientThread(long accessibilityNodeId, int direction,
594 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800595 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700596 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700597 Message message = mHandler.obtainMessage();
598 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
599 message.arg1 = flags;
600 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700601
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700602 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700603 args.argi2 = direction;
604 args.argi3 = interactionId;
605 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700606 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700607 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700608
Svetoslav Ganov42138042012-03-20 11:51:39 -0700609 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700610
Phil Weaver08cccc12017-02-28 09:55:31 -0800611 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700612 }
613
614 private void focusSearchUiThread(Message message) {
615 final int flags = message.arg1;
616 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700617
Svetoslav Ganov42138042012-03-20 11:51:39 -0700618 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700619 final int direction = args.argi2;
620 final int interactionId = args.argi3;
621 final IAccessibilityInteractionConnectionCallback callback =
622 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700623 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700624 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700625
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700626 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700627
Svetoslav Ganov42138042012-03-20 11:51:39 -0700628 AccessibilityNodeInfo next = null;
629 try {
630 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
631 return;
632 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800633 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700634 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800635 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700636 root = findViewByAccessibilityId(accessibilityViewId);
637 } else {
638 root = mViewRootImpl.mView;
639 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700640 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700641 View nextView = root.focusSearch(direction);
642 if (nextView != null) {
643 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700644 }
645 }
646 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800647 updateInfoForViewportAndReturnFindNodeResult(
648 next, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700649 }
650 }
651
652 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700653 Bundle arguments, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800654 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700655 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700656 Message message = mHandler.obtainMessage();
657 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
658 message.arg1 = flags;
659 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700660
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700661 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700662 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
663 args.argi2 = action;
664 args.argi3 = interactionId;
665 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700666 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700667
Svetoslav Ganov42138042012-03-20 11:51:39 -0700668 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700669
Phil Weaver08cccc12017-02-28 09:55:31 -0800670 scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700671 }
672
George Mount41725de2015-04-09 08:23:05 -0700673 private void performAccessibilityActionUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700674 final int flags = message.arg1;
675 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700676
Svetoslav Ganov42138042012-03-20 11:51:39 -0700677 SomeArgs args = (SomeArgs) message.obj;
678 final int virtualDescendantId = args.argi1;
679 final int action = args.argi2;
680 final int interactionId = args.argi3;
681 final IAccessibilityInteractionConnectionCallback callback =
682 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700683 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700684
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700685 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700686
Svetoslav Ganov42138042012-03-20 11:51:39 -0700687 boolean succeeded = false;
688 try {
George Mount41725de2015-04-09 08:23:05 -0700689 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
690 mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700691 return;
692 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800693 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700694 View target = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800695 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700696 target = findViewByAccessibilityId(accessibilityViewId);
697 } else {
698 target = mViewRootImpl.mView;
699 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700700 if (target != null && isShown(target)) {
Phil Weaver193520e2016-12-13 09:39:06 -0800701 if (action == R.id.accessibilityActionClickOnClickableSpan) {
702 // Handle this hidden action separately
703 succeeded = handleClickableSpanActionUiThread(
704 target, virtualDescendantId, arguments);
705 } else {
706 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
707 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800708 succeeded = provider.performAction(virtualDescendantId, action,
709 arguments);
710 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800711 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav8e3feb12014-02-24 13:46:47 -0800712 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700713 }
714 }
715 } finally {
716 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800717 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700718 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
719 } catch (RemoteException re) {
720 /* ignore - the other side will time out */
721 }
722 }
723 }
724
725 private View findViewByAccessibilityId(int accessibilityId) {
726 View root = mViewRootImpl.mView;
727 if (root == null) {
728 return null;
729 }
730 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700731 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700732 return null;
733 }
734 return foundView;
735 }
736
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700737 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
738 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700739 if (infos == null) {
740 return;
741 }
742 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700743 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700744 final int infoCount = infos.size();
745 for (int i = 0; i < infoCount; i++) {
746 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700747 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700748 }
749 }
750 }
751
Svetoslav9ae9ed22014-09-02 16:36:35 -0700752 private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
753 Region interactiveRegion) {
754 if (interactiveRegion == null || infos == null) {
755 return;
756 }
757 final int infoCount = infos.size();
758 for (int i = 0; i < infoCount; i++) {
759 AccessibilityNodeInfo info = infos.get(i);
760 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
761 }
762 }
763
764 private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
765 Region interactiveRegion) {
766 if (interactiveRegion == null || info == null) {
767 return;
768 }
769 Rect boundsInScreen = mTempRect;
770 info.getBoundsInScreen(boundsInScreen);
771 if (interactiveRegion.quickReject(boundsInScreen)) {
772 info.setVisibleToUser(false);
773 }
774 }
775
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700776 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
777 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700778 if (info == null) {
779 return;
780 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700781
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700782 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700783 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
784 return;
785 }
786
787 Rect boundsInParent = mTempRect;
788 Rect boundsInScreen = mTempRect1;
789
790 info.getBoundsInParent(boundsInParent);
791 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700792 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700793 boundsInParent.scale(applicationScale);
794 boundsInScreen.scale(applicationScale);
795 }
796 if (spec != null) {
797 boundsInParent.scale(spec.scale);
798 // boundsInParent must not be offset.
799 boundsInScreen.scale(spec.scale);
800 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
801 }
802 info.setBoundsInParent(boundsInParent);
803 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700804
Phil Weaverc2e28932016-12-08 12:29:25 -0800805 // Scale text locations if they are present
806 if (info.hasExtras()) {
807 Bundle extras = info.getExtras();
808 Parcelable[] textLocations =
809 extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
810 if (textLocations != null) {
811 for (int i = 0; i < textLocations.length; i++) {
812 // Unchecked cast - an app that puts other objects in this bundle with this
813 // key will crash.
814 RectF textLocation = ((RectF) textLocations[i]);
815 textLocation.scale(applicationScale);
816 if (spec != null) {
817 textLocation.scale(spec.scale);
818 textLocation.offset(spec.offsetX, spec.offsetY);
819 }
820 }
821 }
822 }
823
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700824 if (spec != null) {
825 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
826 if (attachInfo.mDisplay == null) {
827 return;
828 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700829
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700830 final float scale = attachInfo.mApplicationScale * spec.scale;
831
832 Rect visibleWinFrame = mTempRect1;
833 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
834 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
835 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
836 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
837
838 attachInfo.mDisplay.getRealSize(mTempPoint);
839 final int displayWidth = mTempPoint.x;
840 final int displayHeight = mTempPoint.y;
841
842 Rect visibleDisplayFrame = mTempRect2;
843 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
844
Doris Liu9607fbe2015-05-28 17:17:28 -0700845 if (!visibleWinFrame.intersect(visibleDisplayFrame)) {
846 // If there's no intersection with display, set visibleWinFrame empty.
847 visibleDisplayFrame.setEmpty();
848 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700849
850 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
851 boundsInScreen.right, boundsInScreen.bottom)) {
852 info.setVisibleToUser(false);
853 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700854 }
855 }
856
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700857 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
858 MagnificationSpec spec) {
859 return (appScale != 1.0f || (spec != null && !spec.isNop()));
860 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700861
Phil Weaverc2e28932016-12-08 12:29:25 -0800862 private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
863 IAccessibilityInteractionConnectionCallback callback, int interactionId,
864 MagnificationSpec spec, Region interactiveRegion) {
865 try {
866 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
867 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
868 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
869 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Phil Weaverf00cd142017-03-03 13:44:00 -0800870 if (infos != null) {
871 infos.clear();
872 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800873 } catch (RemoteException re) {
874 /* ignore - the other side will time out */
875 } finally {
876 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
877 }
878 }
879
880 private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
881 IAccessibilityInteractionConnectionCallback callback, int interactionId,
882 MagnificationSpec spec, Region interactiveRegion) {
883 try {
884 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
885 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
886 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
887 callback.setFindAccessibilityNodeInfoResult(info, interactionId);
888 } catch (RemoteException re) {
889 /* ignore - the other side will time out */
890 } finally {
891 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
892 }
893 }
894
895 private void recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region) {
896 if (android.os.Process.myPid() != Binder.getCallingPid()) {
897 // Specs are cached in the system process and obtained from a pool when read from
898 // a parcel, so only recycle the spec if called from another process.
899 if (spec != null) {
900 spec.recycle();
901 }
902 } else {
903 // Regions are obtained in the system process and instantiated when read from
904 // a parcel, so only recycle the region if caled from the same process.
905 if (region != null) {
906 region.recycle();
907 }
908 }
909 }
910
Phil Weaver193520e2016-12-13 09:39:06 -0800911 private boolean handleClickableSpanActionUiThread(
912 View view, int virtualDescendantId, Bundle arguments) {
913 Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
914 if (!(span instanceof AccessibilityClickableSpan)) {
915 return false;
916 }
917
918 // Find the original ClickableSpan if it's still on the screen
919 AccessibilityNodeInfo infoWithSpan = null;
920 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
921 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800922 infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
923 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800924 infoWithSpan = view.createAccessibilityNodeInfo();
925 }
926 if (infoWithSpan == null) {
927 return false;
928 }
929
930 // Click on the corresponding span
931 ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
932 infoWithSpan.getOriginalText());
933 if (clickableSpan != null) {
934 clickableSpan.onClick(view);
935 return true;
936 }
937 return false;
938 }
939
Svetoslav Ganov42138042012-03-20 11:51:39 -0700940 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700941 * This class encapsulates a prefetching strategy for the accessibility APIs for
942 * querying window content. It is responsible to prefetch a batch of
943 * AccessibilityNodeInfos in addition to the one for a requested node.
944 */
945 private class AccessibilityNodePrefetcher {
946
947 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
948
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700949 private final ArrayList<View> mTempViewList = new ArrayList<View>();
950
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800951 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Phil Weaverc2e28932016-12-08 12:29:25 -0800952 List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700953 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
Phil Weaverc2e28932016-12-08 12:29:25 -0800954 // Determine if we'll be populating extra data
955 final String extraDataRequested = (arguments == null) ? null
Phil Weaver08cccc12017-02-28 09:55:31 -0800956 : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700957 if (provider == null) {
958 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
959 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800960 if (extraDataRequested != null) {
961 view.addExtraDataToAccessibilityNodeInfo(
962 root, extraDataRequested, arguments);
963 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700964 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800965 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700966 prefetchPredecessorsOfRealNode(view, outInfos);
967 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800968 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700969 prefetchSiblingsOfRealNode(view, outInfos);
970 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800971 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700972 prefetchDescendantsOfRealNode(view, outInfos);
973 }
974 }
975 } else {
Phil Weaverf00cd142017-03-03 13:44:00 -0800976 final AccessibilityNodeInfo root =
977 provider.createAccessibilityNodeInfo(virtualViewId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700978 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800979 if (extraDataRequested != null) {
980 provider.addExtraDataToAccessibilityNodeInfo(
Phil Weaverf00cd142017-03-03 13:44:00 -0800981 virtualViewId, root, extraDataRequested, arguments);
Phil Weaverc2e28932016-12-08 12:29:25 -0800982 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700983 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800984 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700985 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
986 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800987 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700988 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
989 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800990 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700991 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
992 }
993 }
994 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800995 if (ENFORCE_NODE_TREE_CONSISTENT) {
996 enforceNodeTreeConsistent(outInfos);
997 }
998 }
999
1000 private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
1001 LongSparseArray<AccessibilityNodeInfo> nodeMap =
1002 new LongSparseArray<AccessibilityNodeInfo>();
1003 final int nodeCount = nodes.size();
1004 for (int i = 0; i < nodeCount; i++) {
1005 AccessibilityNodeInfo node = nodes.get(i);
1006 nodeMap.put(node.getSourceNodeId(), node);
1007 }
1008
1009 // If the nodes are a tree it does not matter from
1010 // which node we start to search for the root.
1011 AccessibilityNodeInfo root = nodeMap.valueAt(0);
1012 AccessibilityNodeInfo parent = root;
1013 while (parent != null) {
1014 root = parent;
1015 parent = nodeMap.get(parent.getParentNodeId());
1016 }
1017
1018 // Traverse the tree and do some checks.
1019 AccessibilityNodeInfo accessFocus = null;
1020 AccessibilityNodeInfo inputFocus = null;
1021 HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
1022 Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
1023 fringe.add(root);
1024
1025 while (!fringe.isEmpty()) {
1026 AccessibilityNodeInfo current = fringe.poll();
1027
1028 // Check for duplicates
1029 if (!seen.add(current)) {
1030 throw new IllegalStateException("Duplicate node: "
1031 + current + " in window:"
1032 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1033 }
1034
1035 // Check for one accessibility focus.
1036 if (current.isAccessibilityFocused()) {
1037 if (accessFocus != null) {
1038 throw new IllegalStateException("Duplicate accessibility focus:"
1039 + current
1040 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1041 } else {
1042 accessFocus = current;
1043 }
1044 }
1045
1046 // Check for one input focus.
1047 if (current.isFocused()) {
1048 if (inputFocus != null) {
1049 throw new IllegalStateException("Duplicate input focus: "
1050 + current + " in window:"
1051 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
1052 } else {
1053 inputFocus = current;
1054 }
1055 }
1056
1057 final int childCount = current.getChildCount();
1058 for (int j = 0; j < childCount; j++) {
1059 final long childId = current.getChildId(j);
1060 final AccessibilityNodeInfo child = nodeMap.get(childId);
1061 if (child != null) {
1062 fringe.add(child);
1063 }
1064 }
1065 }
1066
1067 // Check for disconnected nodes.
1068 for (int j = nodeMap.size() - 1; j >= 0; j--) {
1069 AccessibilityNodeInfo info = nodeMap.valueAt(j);
1070 if (!seen.contains(info)) {
1071 throw new IllegalStateException("Disconnected node: " + info);
1072 }
1073 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001074 }
1075
1076 private void prefetchPredecessorsOfRealNode(View view,
1077 List<AccessibilityNodeInfo> outInfos) {
1078 ViewParent parent = view.getParentForAccessibility();
1079 while (parent instanceof View
1080 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1081 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001082 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
1083 if (info != null) {
1084 outInfos.add(info);
1085 }
1086 parent = parent.getParentForAccessibility();
1087 }
1088 }
1089
1090 private void prefetchSiblingsOfRealNode(View current,
1091 List<AccessibilityNodeInfo> outInfos) {
1092 ViewParent parent = current.getParentForAccessibility();
1093 if (parent instanceof ViewGroup) {
1094 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001095 ArrayList<View> children = mTempViewList;
1096 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001097 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001098 parentGroup.addChildrenForAccessibility(children);
1099 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001100 for (int i = 0; i < childCount; i++) {
1101 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1102 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001103 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001104 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001105 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001106 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001107 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001108 AccessibilityNodeProvider provider =
1109 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001110 if (provider == null) {
1111 info = child.createAccessibilityNodeInfo();
1112 } else {
1113 info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001114 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001115 }
1116 if (info != null) {
1117 outInfos.add(info);
1118 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001119 }
1120 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001121 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001122 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001123 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001124 }
1125 }
1126
1127 private void prefetchDescendantsOfRealNode(View root,
1128 List<AccessibilityNodeInfo> outInfos) {
1129 if (!(root instanceof ViewGroup)) {
1130 return;
1131 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001132 HashMap<View, AccessibilityNodeInfo> addedChildren =
1133 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001134 ArrayList<View> children = mTempViewList;
1135 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001136 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001137 root.addChildrenForAccessibility(children);
1138 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001139 for (int i = 0; i < childCount; i++) {
1140 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1141 return;
1142 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001143 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001144 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001145 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1146 if (provider == null) {
1147 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1148 if (info != null) {
1149 outInfos.add(info);
1150 addedChildren.put(child, null);
1151 }
1152 } else {
1153 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001154 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001155 if (info != null) {
1156 outInfos.add(info);
1157 addedChildren.put(child, info);
1158 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001159 }
1160 }
1161 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001162 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001163 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001164 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001165 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1166 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1167 View addedChild = entry.getKey();
1168 AccessibilityNodeInfo virtualRoot = entry.getValue();
1169 if (virtualRoot == null) {
1170 prefetchDescendantsOfRealNode(addedChild, outInfos);
1171 } else {
1172 AccessibilityNodeProvider provider =
1173 addedChild.getAccessibilityNodeProvider();
1174 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1175 }
1176 }
1177 }
1178 }
1179
1180 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1181 View providerHost, AccessibilityNodeProvider provider,
1182 List<AccessibilityNodeInfo> outInfos) {
Svet Ganov75e58162016-02-19 16:29:24 -08001183 final int initialResultSize = outInfos.size();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001184 long parentNodeId = root.getParentNodeId();
1185 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001186 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001187 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1188 return;
1189 }
1190 final int virtualDescendantId =
1191 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001192 if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001193 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001194 final AccessibilityNodeInfo parent;
Phil Weaverf00cd142017-03-03 13:44:00 -08001195 parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
Alan Viverette84feea112014-11-04 15:59:55 -08001196 if (parent == null) {
Svet Ganov75e58162016-02-19 16:29:24 -08001197 // Going up the parent relation we found a null predecessor,
1198 // so remove these disconnected nodes form the result.
1199 final int currentResultSize = outInfos.size();
1200 for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1201 outInfos.remove(i);
1202 }
Alan Viverette84feea112014-11-04 15:59:55 -08001203 // Couldn't obtain the parent, which means we have a
1204 // disconnected sub-tree. Abort prefetch immediately.
1205 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001206 }
Alan Viverette84feea112014-11-04 15:59:55 -08001207 outInfos.add(parent);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001208 parentNodeId = parent.getParentNodeId();
1209 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1210 parentNodeId);
1211 } else {
1212 prefetchPredecessorsOfRealNode(providerHost, outInfos);
1213 return;
1214 }
1215 }
1216 }
1217
1218 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1219 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1220 final long parentNodeId = current.getParentNodeId();
1221 final int parentAccessibilityViewId =
1222 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1223 final int parentVirtualDescendantId =
1224 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001225 if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001226 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
Phil Weaverf00cd142017-03-03 13:44:00 -08001227 final AccessibilityNodeInfo parent =
1228 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001229 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -08001230 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001231 for (int i = 0; i < childCount; i++) {
1232 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1233 return;
1234 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001235 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001236 if (childNodeId != current.getSourceNodeId()) {
1237 final int childVirtualDescendantId =
1238 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1239 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1240 childVirtualDescendantId);
1241 if (child != null) {
1242 outInfos.add(child);
1243 }
1244 }
1245 }
1246 }
1247 } else {
1248 prefetchSiblingsOfRealNode(providerHost, outInfos);
1249 }
1250 }
1251
1252 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1253 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001254 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -08001255 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001256 for (int i = 0; i < childCount; i++) {
1257 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1258 return;
1259 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001260 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001261 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1262 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1263 if (child != null) {
1264 outInfos.add(child);
1265 }
1266 }
1267 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1268 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1269 for (int i = 0; i < addedChildCount; i++) {
1270 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1271 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1272 }
1273 }
1274 }
1275 }
1276
1277 private class PrivateHandler extends Handler {
Phil Weaverc2e28932016-12-08 12:29:25 -08001278 private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
1279 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1280 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1281 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
1282 private static final int MSG_FIND_FOCUS = 5;
1283 private static final int MSG_FOCUS_SEARCH = 6;
Phil Weaver08cccc12017-02-28 09:55:31 -08001284 private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;
1285 private static final int MSG_APP_PREPARATION_FINISHED = 8;
1286 private static final int MSG_APP_PREPARATION_TIMEOUT = 9;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001287
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001288 public PrivateHandler(Looper looper) {
1289 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001290 }
1291
1292 @Override
1293 public String getMessageName(Message message) {
1294 final int type = message.what;
1295 switch (type) {
1296 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1297 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
Svet Ganov7498efd2014-09-03 21:33:00 -07001298 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1299 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1300 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1301 return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1302 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1303 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001304 case MSG_FIND_FOCUS:
1305 return "MSG_FIND_FOCUS";
1306 case MSG_FOCUS_SEARCH:
1307 return "MSG_FOCUS_SEARCH";
Phil Weaver08cccc12017-02-28 09:55:31 -08001308 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:
1309 return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";
1310 case MSG_APP_PREPARATION_FINISHED:
1311 return "MSG_APP_PREPARATION_FINISHED";
1312 case MSG_APP_PREPARATION_TIMEOUT:
1313 return "MSG_APP_PREPARATION_TIMEOUT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001314 default:
1315 throw new IllegalArgumentException("Unknown message type: " + type);
1316 }
1317 }
1318
1319 @Override
1320 public void handleMessage(Message message) {
1321 final int type = message.what;
1322 switch (type) {
Svet Ganov7498efd2014-09-03 21:33:00 -07001323 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001324 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1325 } break;
1326 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
George Mount41725de2015-04-09 08:23:05 -07001327 performAccessibilityActionUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001328 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001329 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001330 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001331 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001332 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001333 findAccessibilityNodeInfosByTextUiThread(message);
1334 } break;
1335 case MSG_FIND_FOCUS: {
1336 findFocusUiThread(message);
1337 } break;
1338 case MSG_FOCUS_SEARCH: {
1339 focusSearchUiThread(message);
1340 } break;
Phil Weaver08cccc12017-02-28 09:55:31 -08001341 case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
1342 prepareForExtraDataRequestUiThread(message);
1343 } break;
1344 case MSG_APP_PREPARATION_FINISHED: {
1345 requestPreparerDoneUiThread(message);
1346 } break;
1347 case MSG_APP_PREPARATION_TIMEOUT: {
1348 requestPreparerTimeoutUiThread();
1349 } break;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001350 default:
1351 throw new IllegalArgumentException("Unknown message type: " + type);
1352 }
1353 }
1354 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001355
1356 private final class AddNodeInfosForViewId implements Predicate<View> {
1357 private int mViewId = View.NO_ID;
1358 private List<AccessibilityNodeInfo> mInfos;
1359
1360 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1361 mViewId = viewId;
1362 mInfos = infos;
1363 }
1364
1365 public void reset() {
1366 mViewId = View.NO_ID;
1367 mInfos = null;
1368 }
1369
1370 @Override
Paul Duffinca4964c2017-02-07 15:04:10 +00001371 public boolean test(View view) {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001372 if (view.getId() == mViewId && isShown(view)) {
1373 mInfos.add(view.createAccessibilityNodeInfo());
1374 }
1375 return false;
1376 }
1377 }
Phil Weaver08cccc12017-02-28 09:55:31 -08001378
1379 private static final class MessageHolder {
1380 final Message mMessage;
1381 final int mInterrogatingPid;
1382 final long mInterrogatingTid;
1383
1384 MessageHolder(Message message, int interrogatingPid, long interrogatingTid) {
1385 mMessage = message;
1386 mInterrogatingPid = interrogatingPid;
1387 mInterrogatingTid = interrogatingTid;
1388 }
1389 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001390}