1 /*
2  ** Copyright 2017, The Android Open Source Project
3  **
4  ** Licensed under the Apache License, Version 2.0 (the "License");
5  ** you may not use this file except in compliance with the License.
6  ** You may obtain a copy of the License at
7  **
8  **     http://www.apache.org/licenses/LICENSE-2.0
9  **
10  ** Unless required by applicable law or agreed to in writing, software
11  ** distributed under the License is distributed on an "AS IS" BASIS,
12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  ** See the License for the specific language governing permissions and
14  ** limitations under the License.
15  */
16 
17 package com.android.server.accessibility;
18 
19 import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
20 import static android.view.Display.DEFAULT_DISPLAY;
21 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
22 
23 import android.accessibilityservice.AccessibilityServiceInfo;
24 import android.accessibilityservice.IAccessibilityServiceClient;
25 import android.accessibilityservice.IAccessibilityServiceConnection;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.PendingIntent;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.ServiceConnection;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ParceledListSlice;
35 import android.graphics.Region;
36 import android.os.Binder;
37 import android.os.Build;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.Looper;
42 import android.os.Message;
43 import android.os.RemoteException;
44 import android.util.Slog;
45 import android.util.SparseArray;
46 import android.view.KeyEvent;
47 import android.view.MagnificationSpec;
48 import android.view.View;
49 import android.view.accessibility.AccessibilityCache;
50 import android.view.accessibility.AccessibilityEvent;
51 import android.view.accessibility.AccessibilityNodeInfo;
52 import android.view.accessibility.AccessibilityWindowInfo;
53 import android.view.accessibility.IAccessibilityInteractionConnection;
54 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
55 
56 import com.android.internal.annotations.GuardedBy;
57 import com.android.internal.os.SomeArgs;
58 import com.android.internal.util.DumpUtils;
59 import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection;
60 import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy;
61 import com.android.server.wm.WindowManagerInternal;
62 
63 import java.io.FileDescriptor;
64 import java.io.PrintWriter;
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.HashSet;
68 import java.util.List;
69 import java.util.Set;
70 
71 /**
72  * This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
73  * It is responsible for behavior common to both types of clients.
74  */
75 abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub
76         implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter,
77         FingerprintGestureDispatcher.FingerprintGestureClient {
78     private static final boolean DEBUG = false;
79     private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
80 
81     protected final Context mContext;
82     protected final SystemSupport mSystemSupport;
83     private final WindowManagerInternal mWindowManagerService;
84     private final GlobalActionPerformer mGlobalActionPerformer;
85 
86     // Handler for scheduling method invocations on the main thread.
87     public final InvocationHandler mInvocationHandler;
88 
89     final int mId;
90 
91     protected final AccessibilityServiceInfo mAccessibilityServiceInfo;
92 
93     // Lock must match the one used by AccessibilityManagerService
94     protected final Object mLock;
95 
96     protected final SecurityPolicy mSecurityPolicy;
97 
98     // The service that's bound to this instance. Whenever this value is non-null, this
99     // object is registered as a death recipient
100     IBinder mService;
101 
102     IAccessibilityServiceClient mServiceInterface;
103 
104     int mEventTypes;
105 
106     int mFeedbackType;
107 
108     Set<String> mPackageNames = new HashSet<>();
109 
110     boolean mIsDefault;
111 
112     boolean mRequestTouchExplorationMode;
113 
114     boolean mRequestFilterKeyEvents;
115 
116     boolean mRetrieveInteractiveWindows;
117 
118     boolean mCaptureFingerprintGestures;
119 
120     boolean mRequestAccessibilityButton;
121 
122     boolean mReceivedAccessibilityButtonCallbackSinceBind;
123 
124     boolean mLastAccessibilityButtonCallbackState;
125 
126     int mFetchFlags;
127 
128     long mNotificationTimeout;
129 
130     final ComponentName mComponentName;
131 
132     // the events pending events to be dispatched to this service
133     final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>();
134 
135     /** Whether this service relies on its {@link AccessibilityCache} being up to date */
136     boolean mUsesAccessibilityCache = false;
137 
138     // Handler only for dispatching accessibility events since we use event
139     // types as message types allowing us to remove messages per event type.
140     public Handler mEventDispatchHandler;
141 
142     final IBinder mOverlayWindowToken = new Binder();
143 
144 
145     public interface SystemSupport {
146         /**
147          * @return The current dispatcher for key events
148          */
getKeyEventDispatcher()149         @NonNull KeyEventDispatcher getKeyEventDispatcher();
150 
151         /**
152          * @param windowId The id of the window of interest
153          * @return The magnification spec for the window, or {@code null} if none is available
154          */
getCompatibleMagnificationSpecLocked(int windowId)155         @Nullable MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId);
156 
157         /**
158          * @return The current injector of motion events, if one exists
159          */
getMotionEventInjectorLocked()160         @Nullable MotionEventInjector getMotionEventInjectorLocked();
161 
162         /**
163          * @return The current dispatcher for fingerprint gestures, if one exists
164          */
getFingerprintGestureDispatcher()165         @Nullable FingerprintGestureDispatcher getFingerprintGestureDispatcher();
166 
167         /**
168          * @return The magnification controller
169          */
getMagnificationController()170         @NonNull MagnificationController getMagnificationController();
171 
172         /**
173          * Resolve a connection wrapper for a window id
174          *
175          * @param windowId The id of the window of interest
176          *
177          * @return a connection to the window
178          */
getConnectionLocked(int windowId)179         RemoteAccessibilityConnection getConnectionLocked(int windowId);
180 
181         /**
182          * Perform the specified accessibility action
183          *
184          * @param resolvedWindowId The window ID
185          * [Other parameters match the method on IAccessibilityServiceConnection]
186          *
187          * @return Whether or not the action could be sent to the app process
188          */
performAccessibilityAction(int resolvedWindowId, long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags, long interrogatingTid)189         boolean performAccessibilityAction(int resolvedWindowId,
190                 long accessibilityNodeId, int action, Bundle arguments, int interactionId,
191                 IAccessibilityInteractionConnectionCallback callback, int fetchFlags,
192                 long interrogatingTid);
193 
194         /**
195          * Replace the interaction callback if needed, for example if the window is in picture-
196          * in-picture mode and needs its nodes replaced.
197          *
198          * @param originalCallback The callback we were planning to use
199          * @param resolvedWindowId The ID of the window we're calling
200          * @param interactionId The id for the original callback
201          * @param interrogatingPid Process ID of requester
202          * @param interrogatingTid Thread ID of requester
203          *
204          * @return The callback to use, which may be the original one.
205          */
replaceCallbackIfNeeded( IAccessibilityInteractionConnectionCallback originalCallback, int resolvedWindowId, int interactionId, int interrogatingPid, long interrogatingTid)206         @NonNull IAccessibilityInteractionConnectionCallback replaceCallbackIfNeeded(
207                 IAccessibilityInteractionConnectionCallback originalCallback,
208                 int resolvedWindowId, int interactionId, int interrogatingPid,
209                 long interrogatingTid);
210 
211         /**
212          * Request that the system make sure windows are available to interrogate
213          */
ensureWindowsAvailableTimed()214         void ensureWindowsAvailableTimed();
215 
216         /**
217          * Called back to notify system that the client has changed
218          * @param serviceInfoChanged True if the service's AccessibilityServiceInfo changed.
219          */
onClientChangeLocked(boolean serviceInfoChanged)220         void onClientChangeLocked(boolean serviceInfoChanged);
221 
getCurrentUserIdLocked()222         int getCurrentUserIdLocked();
223 
isAccessibilityButtonShown()224         boolean isAccessibilityButtonShown();
225 
226         /**
227          * Persists the component names in the specified setting in a
228          * colon separated fashion.
229          *
230          * @param settingName The setting name.
231          * @param componentNames The component names.
232          * @param userId The user id to persist the setting for.
233          */
persistComponentNamesToSettingLocked(String settingName, Set<ComponentName> componentNames, int userId)234         void persistComponentNamesToSettingLocked(String settingName,
235                 Set<ComponentName> componentNames, int userId);
236 
237         /* This is exactly PendingIntent.getActivity, separated out for testability */
getPendingIntentActivity(Context context, int requestCode, Intent intent, int flags)238         PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
239                 int flags);
240     }
241 
AbstractAccessibilityServiceConnection(Context context, ComponentName componentName, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport, WindowManagerInternal windowManagerInternal, GlobalActionPerformer globalActionPerfomer)242     public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
243             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
244             Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport,
245             WindowManagerInternal windowManagerInternal,
246             GlobalActionPerformer globalActionPerfomer) {
247         mContext = context;
248         mWindowManagerService = windowManagerInternal;
249         mId = id;
250         mComponentName = componentName;
251         mAccessibilityServiceInfo = accessibilityServiceInfo;
252         mLock = lock;
253         mSecurityPolicy = securityPolicy;
254         mGlobalActionPerformer = globalActionPerfomer;
255         mSystemSupport = systemSupport;
256         mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
257         mEventDispatchHandler = new Handler(mainHandler.getLooper()) {
258             @Override
259             public void handleMessage(Message message) {
260                 final int eventType =  message.what;
261                 AccessibilityEvent event = (AccessibilityEvent) message.obj;
262                 boolean serviceWantsEvent = message.arg1 != 0;
263                 notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);
264             }
265         };
266         setDynamicallyConfigurableProperties(accessibilityServiceInfo);
267     }
268 
269     @Override
onKeyEvent(KeyEvent keyEvent, int sequenceNumber)270     public boolean onKeyEvent(KeyEvent keyEvent, int sequenceNumber) {
271         if (!mRequestFilterKeyEvents || (mServiceInterface == null)) {
272             return false;
273         }
274         if((mAccessibilityServiceInfo.getCapabilities()
275                 & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) {
276             return false;
277         }
278         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
279             return false;
280         }
281         try {
282             mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
283         } catch (RemoteException e) {
284             return false;
285         }
286         return true;
287     }
288 
setDynamicallyConfigurableProperties(AccessibilityServiceInfo info)289     public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
290         mEventTypes = info.eventTypes;
291         mFeedbackType = info.feedbackType;
292         String[] packageNames = info.packageNames;
293         if (packageNames != null) {
294             mPackageNames.addAll(Arrays.asList(packageNames));
295         }
296         mNotificationTimeout = info.notificationTimeout;
297         mIsDefault = (info.flags & DEFAULT) != 0;
298 
299         if (supportsFlagForNotImportantViews(info)) {
300             if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
301                 mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
302             } else {
303                 mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
304             }
305         }
306 
307         if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
308             mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
309         } else {
310             mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
311         }
312 
313         mRequestTouchExplorationMode = (info.flags
314                 & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
315         mRequestFilterKeyEvents = (info.flags
316                 & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
317         mRetrieveInteractiveWindows = (info.flags
318                 & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
319         mCaptureFingerprintGestures = (info.flags
320                 & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
321         mRequestAccessibilityButton = (info.flags
322                 & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
323     }
324 
supportsFlagForNotImportantViews(AccessibilityServiceInfo info)325     protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
326         return info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
327                 >= Build.VERSION_CODES.JELLY_BEAN;
328     }
329 
canReceiveEventsLocked()330     public boolean canReceiveEventsLocked() {
331         return (mEventTypes != 0 && mFeedbackType != 0 && mService != null);
332     }
333 
334     @Override
setOnKeyEventResult(boolean handled, int sequence)335     public void setOnKeyEventResult(boolean handled, int sequence) {
336         mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
337     }
338 
339     @Override
getServiceInfo()340     public AccessibilityServiceInfo getServiceInfo() {
341         synchronized (mLock) {
342             return mAccessibilityServiceInfo;
343         }
344     }
345 
getCapabilities()346     public int getCapabilities() {
347         return mAccessibilityServiceInfo.getCapabilities();
348     }
349 
getRelevantEventTypes()350     int getRelevantEventTypes() {
351         return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK
352                 : AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) | mEventTypes;
353     }
354 
355     @Override
setServiceInfo(AccessibilityServiceInfo info)356     public void setServiceInfo(AccessibilityServiceInfo info) {
357         final long identity = Binder.clearCallingIdentity();
358         try {
359             synchronized (mLock) {
360                 // If the XML manifest had data to configure the service its info
361                 // should be already set. In such a case update only the dynamically
362                 // configurable properties.
363                 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
364                 if (oldInfo != null) {
365                     oldInfo.updateDynamicallyConfigurableProperties(info);
366                     setDynamicallyConfigurableProperties(oldInfo);
367                 } else {
368                     setDynamicallyConfigurableProperties(info);
369                 }
370                 mSystemSupport.onClientChangeLocked(true);
371             }
372         } finally {
373             Binder.restoreCallingIdentity(identity);
374         }
375     }
376 
isCalledForCurrentUserLocked()377     protected abstract boolean isCalledForCurrentUserLocked();
378 
379     @Override
getWindows()380     public List<AccessibilityWindowInfo> getWindows() {
381         mSystemSupport.ensureWindowsAvailableTimed();
382         synchronized (mLock) {
383             if (!isCalledForCurrentUserLocked()) {
384                 return null;
385             }
386             final boolean permissionGranted =
387                     mSecurityPolicy.canRetrieveWindowsLocked(this);
388             if (!permissionGranted) {
389                 return null;
390             }
391             if (mSecurityPolicy.mWindows == null) {
392                 return null;
393             }
394             if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
395                 return null;
396             }
397             List<AccessibilityWindowInfo> windows = new ArrayList<>();
398             final int windowCount = mSecurityPolicy.mWindows.size();
399             for (int i = 0; i < windowCount; i++) {
400                 AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i);
401                 AccessibilityWindowInfo windowClone =
402                         AccessibilityWindowInfo.obtain(window);
403                 windowClone.setConnectionId(mId);
404                 windows.add(windowClone);
405             }
406             return windows;
407         }
408     }
409 
410     @Override
getWindow(int windowId)411     public AccessibilityWindowInfo getWindow(int windowId) {
412         mSystemSupport.ensureWindowsAvailableTimed();
413         synchronized (mLock) {
414             if (!isCalledForCurrentUserLocked()) {
415                 return null;
416             }
417             final boolean permissionGranted =
418                     mSecurityPolicy.canRetrieveWindowsLocked(this);
419             if (!permissionGranted) {
420                 return null;
421             }
422             if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
423                 return null;
424             }
425             AccessibilityWindowInfo window = mSecurityPolicy.findA11yWindowInfoById(windowId);
426             if (window != null) {
427                 AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
428                 windowClone.setConnectionId(mId);
429                 return windowClone;
430             }
431             return null;
432         }
433     }
434 
435     @Override
findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewIdResName, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)436     public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
437             long accessibilityNodeId, String viewIdResName, int interactionId,
438             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
439             throws RemoteException {
440         final int resolvedWindowId;
441         RemoteAccessibilityConnection connection;
442         Region partialInteractiveRegion = Region.obtain();
443         MagnificationSpec spec;
444         synchronized (mLock) {
445             mUsesAccessibilityCache = true;
446             if (!isCalledForCurrentUserLocked()) {
447                 return null;
448             }
449             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
450             final boolean permissionGranted =
451                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
452             if (!permissionGranted) {
453                 return null;
454             } else {
455                 connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
456                 if (connection == null) {
457                     return null;
458                 }
459             }
460             if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
461                     resolvedWindowId, partialInteractiveRegion)) {
462                 partialInteractiveRegion.recycle();
463                 partialInteractiveRegion = null;
464             }
465             spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
466         }
467         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
468             return null;
469         }
470         final int interrogatingPid = Binder.getCallingPid();
471         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
472                 interrogatingPid, interrogatingTid);
473         final long identityToken = Binder.clearCallingIdentity();
474         try {
475             connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
476                     viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
477                     interrogatingPid, interrogatingTid, spec);
478             return mSecurityPolicy.computeValidReportedPackages(
479                     connection.getPackageName(), connection.getUid());
480         } catch (RemoteException re) {
481             if (DEBUG) {
482                 Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
483             }
484         } finally {
485             Binder.restoreCallingIdentity(identityToken);
486             // Recycle if passed to another process.
487             if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
488                 partialInteractiveRegion.recycle();
489             }
490         }
491         return null;
492     }
493 
494     @Override
findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)495     public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId,
496             long accessibilityNodeId, String text, int interactionId,
497             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
498             throws RemoteException {
499         final int resolvedWindowId;
500         RemoteAccessibilityConnection connection;
501         Region partialInteractiveRegion = Region.obtain();
502         MagnificationSpec spec;
503         synchronized (mLock) {
504             mUsesAccessibilityCache = true;
505             if (!isCalledForCurrentUserLocked()) {
506                 return null;
507             }
508             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
509             final boolean permissionGranted =
510                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
511             if (!permissionGranted) {
512                 return null;
513             } else {
514                 connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
515                 if (connection == null) {
516                     return null;
517                 }
518             }
519             if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
520                     resolvedWindowId, partialInteractiveRegion)) {
521                 partialInteractiveRegion.recycle();
522                 partialInteractiveRegion = null;
523             }
524             spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
525         }
526         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
527             return null;
528         }
529         final int interrogatingPid = Binder.getCallingPid();
530         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
531                 interrogatingPid, interrogatingTid);
532         final long identityToken = Binder.clearCallingIdentity();
533         try {
534             connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
535                     text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
536                     interrogatingPid, interrogatingTid, spec);
537             return mSecurityPolicy.computeValidReportedPackages(
538                     connection.getPackageName(), connection.getUid());
539         } catch (RemoteException re) {
540             if (DEBUG) {
541                 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
542             }
543         } finally {
544             Binder.restoreCallingIdentity(identityToken);
545             // Recycle if passed to another process.
546             if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
547                 partialInteractiveRegion.recycle();
548             }
549         }
550         return null;
551     }
552 
553     @Override
findAccessibilityNodeInfoByAccessibilityId( int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid, Bundle arguments)554     public String[] findAccessibilityNodeInfoByAccessibilityId(
555             int accessibilityWindowId, long accessibilityNodeId, int interactionId,
556             IAccessibilityInteractionConnectionCallback callback, int flags,
557             long interrogatingTid, Bundle arguments) throws RemoteException {
558         final int resolvedWindowId;
559         RemoteAccessibilityConnection connection;
560         Region partialInteractiveRegion = Region.obtain();
561         MagnificationSpec spec;
562         synchronized (mLock) {
563             mUsesAccessibilityCache = true;
564             if (!isCalledForCurrentUserLocked()) {
565                 return null;
566             }
567             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
568             final boolean permissionGranted =
569                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
570             if (!permissionGranted) {
571                 return null;
572             } else {
573                 connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
574                 if (connection == null) {
575                     return null;
576                 }
577             }
578             if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
579                     resolvedWindowId, partialInteractiveRegion)) {
580                 partialInteractiveRegion.recycle();
581                 partialInteractiveRegion = null;
582             }
583             spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
584         }
585         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
586             return null;
587         }
588         final int interrogatingPid = Binder.getCallingPid();
589         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
590                 interrogatingPid, interrogatingTid);
591         final long identityToken = Binder.clearCallingIdentity();
592         try {
593             connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
594                     accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
595                     mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments);
596             return mSecurityPolicy.computeValidReportedPackages(
597                     connection.getPackageName(), connection.getUid());
598         } catch (RemoteException re) {
599             if (DEBUG) {
600                 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
601             }
602         } finally {
603             Binder.restoreCallingIdentity(identityToken);
604             // Recycle if passed to another process.
605             if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
606                 partialInteractiveRegion.recycle();
607             }
608         }
609         return null;
610     }
611 
612     @Override
findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)613     public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId,
614             int focusType, int interactionId,
615             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
616             throws RemoteException {
617         final int resolvedWindowId;
618         RemoteAccessibilityConnection connection;
619         Region partialInteractiveRegion = Region.obtain();
620         MagnificationSpec spec;
621         synchronized (mLock) {
622             if (!isCalledForCurrentUserLocked()) {
623                 return null;
624             }
625             resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
626                     accessibilityWindowId, focusType);
627             final boolean permissionGranted =
628                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
629             if (!permissionGranted) {
630                 return null;
631             } else {
632                 connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
633                 if (connection == null) {
634                     return null;
635                 }
636             }
637             if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
638                     resolvedWindowId, partialInteractiveRegion)) {
639                 partialInteractiveRegion.recycle();
640                 partialInteractiveRegion = null;
641             }
642             spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
643         }
644         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
645             return null;
646         }
647         final int interrogatingPid = Binder.getCallingPid();
648         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
649                 interrogatingPid, interrogatingTid);
650         final long identityToken = Binder.clearCallingIdentity();
651         try {
652             connection.getRemote().findFocus(accessibilityNodeId, focusType,
653                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
654                     interrogatingPid, interrogatingTid, spec);
655             return mSecurityPolicy.computeValidReportedPackages(
656                     connection.getPackageName(), connection.getUid());
657         } catch (RemoteException re) {
658             if (DEBUG) {
659                 Slog.e(LOG_TAG, "Error calling findFocus()");
660             }
661         } finally {
662             Binder.restoreCallingIdentity(identityToken);
663             // Recycle if passed to another process.
664             if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
665                 partialInteractiveRegion.recycle();
666             }
667         }
668         return null;
669     }
670 
671     @Override
focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)672     public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId,
673             int direction, int interactionId,
674             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
675             throws RemoteException {
676         final int resolvedWindowId;
677         RemoteAccessibilityConnection connection;
678         Region partialInteractiveRegion = Region.obtain();
679         MagnificationSpec spec;
680         synchronized (mLock) {
681             if (!isCalledForCurrentUserLocked()) {
682                 return null;
683             }
684             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
685             final boolean permissionGranted =
686                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
687             if (!permissionGranted) {
688                 return null;
689             } else {
690                 connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
691                 if (connection == null) {
692                     return null;
693                 }
694             }
695             if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
696                     resolvedWindowId, partialInteractiveRegion)) {
697                 partialInteractiveRegion.recycle();
698                 partialInteractiveRegion = null;
699             }
700             spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
701         }
702         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
703             return null;
704         }
705         final int interrogatingPid = Binder.getCallingPid();
706         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
707                 interrogatingPid, interrogatingTid);
708         final long identityToken = Binder.clearCallingIdentity();
709         try {
710             connection.getRemote().focusSearch(accessibilityNodeId, direction,
711                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
712                     interrogatingPid, interrogatingTid, spec);
713             return mSecurityPolicy.computeValidReportedPackages(
714                     connection.getPackageName(), connection.getUid());
715         } catch (RemoteException re) {
716             if (DEBUG) {
717                 Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
718             }
719         } finally {
720             Binder.restoreCallingIdentity(identityToken);
721             // Recycle if passed to another process.
722             if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
723                 partialInteractiveRegion.recycle();
724             }
725         }
726         return null;
727     }
728 
729     @Override
sendGesture(int sequence, ParceledListSlice gestureSteps)730     public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
731     }
732 
733     @Override
performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)734     public boolean performAccessibilityAction(int accessibilityWindowId,
735             long accessibilityNodeId, int action, Bundle arguments, int interactionId,
736             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
737             throws RemoteException {
738         final int resolvedWindowId;
739         IAccessibilityInteractionConnection connection = null;
740         synchronized (mLock) {
741             if (!isCalledForCurrentUserLocked()) {
742                 return false;
743             }
744             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
745             if (!mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId)) {
746                 return false;
747             }
748         }
749         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
750             return false;
751         }
752         boolean returnValue =
753                 mSystemSupport.performAccessibilityAction(resolvedWindowId, accessibilityNodeId,
754                 action, arguments, interactionId, callback, mFetchFlags, interrogatingTid);
755         return returnValue;
756     }
757 
758     @Override
performGlobalAction(int action)759     public boolean performGlobalAction(int action) {
760         synchronized (mLock) {
761             if (!isCalledForCurrentUserLocked()) {
762                 return false;
763             }
764         }
765         return mGlobalActionPerformer.performGlobalAction(action);
766     }
767 
768     @Override
isFingerprintGestureDetectionAvailable()769     public boolean isFingerprintGestureDetectionAvailable() {
770         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
771             return false;
772         }
773         if (isCapturingFingerprintGestures()) {
774             FingerprintGestureDispatcher dispatcher =
775                     mSystemSupport.getFingerprintGestureDispatcher();
776             return (dispatcher != null) && dispatcher.isFingerprintGestureDetectionAvailable();
777         }
778         return false;
779     }
780 
781     @Override
getMagnificationScale(int displayId)782     public float getMagnificationScale(int displayId) {
783         synchronized (mLock) {
784             if (!isCalledForCurrentUserLocked()) {
785                 return 1.0f;
786             }
787         }
788         final long identity = Binder.clearCallingIdentity();
789         try {
790             return mSystemSupport.getMagnificationController().getScale(displayId);
791         } finally {
792             Binder.restoreCallingIdentity(identity);
793         }
794     }
795 
796     @Override
getMagnificationRegion(int displayId)797     public Region getMagnificationRegion(int displayId) {
798         synchronized (mLock) {
799             final Region region = Region.obtain();
800             if (!isCalledForCurrentUserLocked()) {
801                 return region;
802             }
803             MagnificationController magnificationController =
804                     mSystemSupport.getMagnificationController();
805             boolean registeredJustForThisCall =
806                     registerMagnificationIfNeeded(displayId, magnificationController);
807             final long identity = Binder.clearCallingIdentity();
808             try {
809                 magnificationController.getMagnificationRegion(displayId, region);
810                 return region;
811             } finally {
812                 Binder.restoreCallingIdentity(identity);
813                 if (registeredJustForThisCall) {
814                     magnificationController.unregister(displayId);
815                 }
816             }
817         }
818     }
819 
820     @Override
getMagnificationCenterX(int displayId)821     public float getMagnificationCenterX(int displayId) {
822         synchronized (mLock) {
823             if (!isCalledForCurrentUserLocked()) {
824                 return 0.0f;
825             }
826             MagnificationController magnificationController =
827                     mSystemSupport.getMagnificationController();
828             boolean registeredJustForThisCall =
829                     registerMagnificationIfNeeded(displayId, magnificationController);
830             final long identity = Binder.clearCallingIdentity();
831             try {
832                 return magnificationController.getCenterX(displayId);
833             } finally {
834                 Binder.restoreCallingIdentity(identity);
835                 if (registeredJustForThisCall) {
836                     magnificationController.unregister(displayId);
837                 }
838             }
839         }
840     }
841 
842     @Override
getMagnificationCenterY(int displayId)843     public float getMagnificationCenterY(int displayId) {
844         synchronized (mLock) {
845             if (!isCalledForCurrentUserLocked()) {
846                 return 0.0f;
847             }
848             MagnificationController magnificationController =
849                     mSystemSupport.getMagnificationController();
850             boolean registeredJustForThisCall =
851                     registerMagnificationIfNeeded(displayId, magnificationController);
852             final long identity = Binder.clearCallingIdentity();
853             try {
854                 return magnificationController.getCenterY(displayId);
855             } finally {
856                 Binder.restoreCallingIdentity(identity);
857                 if (registeredJustForThisCall) {
858                     magnificationController.unregister(displayId);
859                 }
860             }
861         }
862     }
863 
registerMagnificationIfNeeded(int displayId, MagnificationController magnificationController)864     private boolean registerMagnificationIfNeeded(int displayId,
865             MagnificationController magnificationController) {
866         if (!magnificationController.isRegistered(displayId)
867                 && mSecurityPolicy.canControlMagnification(this)) {
868             magnificationController.register(displayId);
869             return true;
870         }
871         return false;
872     }
873 
874     @Override
resetMagnification(int displayId, boolean animate)875     public boolean resetMagnification(int displayId, boolean animate) {
876         synchronized (mLock) {
877             if (!isCalledForCurrentUserLocked()) {
878                 return false;
879             }
880             if (!mSecurityPolicy.canControlMagnification(this)) {
881                 return false;
882             }
883         }
884         final long identity = Binder.clearCallingIdentity();
885         try {
886             MagnificationController magnificationController =
887                     mSystemSupport.getMagnificationController();
888             return (magnificationController.reset(displayId, animate)
889                     || !magnificationController.isMagnifying(displayId));
890         } finally {
891             Binder.restoreCallingIdentity(identity);
892         }
893     }
894 
895     @Override
setMagnificationScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate)896     public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
897             float centerY, boolean animate) {
898         synchronized (mLock) {
899             if (!isCalledForCurrentUserLocked()) {
900                 return false;
901             }
902             if (!mSecurityPolicy.canControlMagnification(this)) {
903                 return false;
904             }
905             final long identity = Binder.clearCallingIdentity();
906             try {
907                 MagnificationController magnificationController =
908                         mSystemSupport.getMagnificationController();
909                 if (!magnificationController.isRegistered(displayId)) {
910                     magnificationController.register(displayId);
911                 }
912                 return magnificationController
913                         .setScaleAndCenter(displayId, scale, centerX, centerY, animate, mId);
914             } finally {
915                 Binder.restoreCallingIdentity(identity);
916             }
917         }
918     }
919 
920     @Override
setMagnificationCallbackEnabled(int displayId, boolean enabled)921     public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
922         mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
923     }
924 
isMagnificationCallbackEnabled(int displayId)925     public boolean isMagnificationCallbackEnabled(int displayId) {
926         return mInvocationHandler.isMagnificationCallbackEnabled(displayId);
927     }
928 
929     @Override
setSoftKeyboardCallbackEnabled(boolean enabled)930     public void setSoftKeyboardCallbackEnabled(boolean enabled) {
931         mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
932     }
933 
934     @Override
dump(FileDescriptor fd, final PrintWriter pw, String[] args)935     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
936         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
937         synchronized (mLock) {
938             pw.append("Service[label=" + mAccessibilityServiceInfo.getResolveInfo()
939                     .loadLabel(mContext.getPackageManager()));
940             pw.append(", feedbackType"
941                     + AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType));
942             pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities());
943             pw.append(", eventTypes="
944                     + AccessibilityEvent.eventTypeToString(mEventTypes));
945             pw.append(", notificationTimeout=" + mNotificationTimeout);
946             pw.append("]");
947         }
948     }
949 
onAdded()950     public void onAdded() {
951         final long identity = Binder.clearCallingIdentity();
952         try {
953             mWindowManagerService.addWindowToken(mOverlayWindowToken,
954                     TYPE_ACCESSIBILITY_OVERLAY, DEFAULT_DISPLAY);
955         } finally {
956             Binder.restoreCallingIdentity(identity);
957         }
958     }
959 
onRemoved()960     public void onRemoved() {
961         final long identity = Binder.clearCallingIdentity();
962         try {
963             mWindowManagerService.removeWindowToken(mOverlayWindowToken, true, DEFAULT_DISPLAY);
964         } finally {
965             Binder.restoreCallingIdentity(identity);
966         }
967     }
968 
resetLocked()969     public void resetLocked() {
970         mSystemSupport.getKeyEventDispatcher().flush(this);
971         try {
972             // Clear the proxy in the other process so this
973             // IAccessibilityServiceConnection can be garbage collected.
974             if (mServiceInterface != null) {
975                 mServiceInterface.init(null, mId, null);
976             }
977         } catch (RemoteException re) {
978                 /* ignore */
979         }
980         if (mService != null) {
981             mService.unlinkToDeath(this, 0);
982             mService = null;
983         }
984 
985         mServiceInterface = null;
986         mReceivedAccessibilityButtonCallbackSinceBind = false;
987     }
988 
isConnectedLocked()989     public boolean isConnectedLocked() {
990         return (mService != null);
991     }
992 
notifyAccessibilityEvent(AccessibilityEvent event)993     public void notifyAccessibilityEvent(AccessibilityEvent event) {
994         synchronized (mLock) {
995             final int eventType = event.getEventType();
996 
997             final boolean serviceWantsEvent = wantsEventLocked(event);
998             final boolean requiredForCacheConsistency = mUsesAccessibilityCache
999                     && ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0);
1000             if (!serviceWantsEvent && !requiredForCacheConsistency) {
1001                 return;
1002             }
1003 
1004             if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
1005                 return;
1006             }
1007             // Make a copy since during dispatch it is possible the event to
1008             // be modified to remove its source if the receiving service does
1009             // not have permission to access the window content.
1010             AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
1011             Message message;
1012             if ((mNotificationTimeout > 0)
1013                     && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
1014                 // Allow at most one pending event
1015                 final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
1016                 mPendingEvents.put(eventType, newEvent);
1017                 if (oldEvent != null) {
1018                     mEventDispatchHandler.removeMessages(eventType);
1019                     oldEvent.recycle();
1020                 }
1021                 message = mEventDispatchHandler.obtainMessage(eventType);
1022             } else {
1023                 // Send all messages, bypassing mPendingEvents
1024                 message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
1025             }
1026             message.arg1 = serviceWantsEvent ? 1 : 0;
1027 
1028             mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
1029         }
1030     }
1031 
1032     /**
1033      * Determines if given event can be dispatched to a service based on the package of the
1034      * event source. Specifically, a service is notified if it is interested in events from the
1035      * package.
1036      *
1037      * @param event The event.
1038      * @return True if the listener should be notified, false otherwise.
1039      */
wantsEventLocked(AccessibilityEvent event)1040     private boolean wantsEventLocked(AccessibilityEvent event) {
1041 
1042         if (!canReceiveEventsLocked()) {
1043             return false;
1044         }
1045 
1046         if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
1047                 && !event.isImportantForAccessibility()
1048                 && (mFetchFlags & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
1049             return false;
1050         }
1051 
1052         int eventType = event.getEventType();
1053         if ((mEventTypes & eventType) != eventType) {
1054             return false;
1055         }
1056 
1057         Set<String> packageNames = mPackageNames;
1058         String packageName = (event.getPackageName() != null)
1059                 ? event.getPackageName().toString() : null;
1060 
1061         return (packageNames.isEmpty() || packageNames.contains(packageName));
1062     }
1063 
1064     /**
1065      * Notifies an accessibility service client for a scheduled event given the event type.
1066      *
1067      * @param eventType The type of the event to dispatch.
1068      */
notifyAccessibilityEventInternal( int eventType, AccessibilityEvent event, boolean serviceWantsEvent)1069     private void notifyAccessibilityEventInternal(
1070             int eventType,
1071             AccessibilityEvent event,
1072             boolean serviceWantsEvent) {
1073         IAccessibilityServiceClient listener;
1074 
1075         synchronized (mLock) {
1076             listener = mServiceInterface;
1077 
1078             // If the service died/was disabled while the message for dispatching
1079             // the accessibility event was propagating the listener may be null.
1080             if (listener == null) {
1081                 return;
1082             }
1083 
1084             // There are two ways we notify for events, throttled AND non-throttled. If we
1085             // are not throttling, then messages come with events, which we handle with
1086             // minimal fuss.
1087             if (event == null) {
1088                 // We are throttling events, so we'll send the event for this type in
1089                 // mPendingEvents as long as it it's null. It can only null due to a race
1090                 // condition:
1091                 //
1092                 //   1) A binder thread calls notifyAccessibilityServiceDelayedLocked
1093                 //      which posts a message for dispatching an event and stores the event
1094                 //      in mPendingEvents.
1095                 //   2) The message is pulled from the queue by the handler on the service
1096                 //      thread and this method is just about to acquire the lock.
1097                 //   3) Another binder thread acquires the lock in notifyAccessibilityEvent
1098                 //   4) notifyAccessibilityEvent recycles the event that this method was about
1099                 //      to process, replaces it with a new one, and posts a second message
1100                 //   5) This method grabs the new event, processes it, and removes it from
1101                 //      mPendingEvents
1102                 //   6) The second message dispatched in (4) arrives, but the event has been
1103                 //      remvoved in (5).
1104                 event = mPendingEvents.get(eventType);
1105                 if (event == null) {
1106                     return;
1107                 }
1108                 mPendingEvents.remove(eventType);
1109             }
1110             if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
1111                 event.setConnectionId(mId);
1112             } else {
1113                 event.setSource((View) null);
1114             }
1115             event.setSealed(true);
1116         }
1117 
1118         try {
1119             listener.onAccessibilityEvent(event, serviceWantsEvent);
1120             if (DEBUG) {
1121                 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
1122             }
1123         } catch (RemoteException re) {
1124             Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
1125         } finally {
1126             event.recycle();
1127         }
1128     }
1129 
notifyGesture(int gestureId)1130     public void notifyGesture(int gestureId) {
1131         mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
1132                 gestureId, 0).sendToTarget();
1133     }
1134 
notifyClearAccessibilityNodeInfoCache()1135     public void notifyClearAccessibilityNodeInfoCache() {
1136         mInvocationHandler.sendEmptyMessage(
1137                 InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
1138     }
1139 
notifyMagnificationChangedLocked(int displayId, @NonNull Region region, float scale, float centerX, float centerY)1140     public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
1141             float scale, float centerX, float centerY) {
1142         mInvocationHandler
1143                 .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
1144     }
1145 
notifySoftKeyboardShowModeChangedLocked(int showState)1146     public void notifySoftKeyboardShowModeChangedLocked(int showState) {
1147         mInvocationHandler.notifySoftKeyboardShowModeChangedLocked(showState);
1148     }
1149 
notifyAccessibilityButtonClickedLocked()1150     public void notifyAccessibilityButtonClickedLocked() {
1151         mInvocationHandler.notifyAccessibilityButtonClickedLocked();
1152     }
1153 
notifyAccessibilityButtonAvailabilityChangedLocked(boolean available)1154     public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
1155         mInvocationHandler.notifyAccessibilityButtonAvailabilityChangedLocked(available);
1156     }
1157 
1158     /**
1159      * Called by the invocation handler to notify the service that the
1160      * state of magnification has changed.
1161      */
notifyMagnificationChangedInternal(int displayId, @NonNull Region region, float scale, float centerX, float centerY)1162     private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
1163             float scale, float centerX, float centerY) {
1164         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
1165         if (listener != null) {
1166             try {
1167                 listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
1168             } catch (RemoteException re) {
1169                 Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
1170             }
1171         }
1172     }
1173 
1174     /**
1175      * Called by the invocation handler to notify the service that the state of the soft
1176      * keyboard show mode has changed.
1177      */
notifySoftKeyboardShowModeChangedInternal(int showState)1178     private void notifySoftKeyboardShowModeChangedInternal(int showState) {
1179         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
1180         if (listener != null) {
1181             try {
1182                 listener.onSoftKeyboardShowModeChanged(showState);
1183             } catch (RemoteException re) {
1184                 Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService,
1185                         re);
1186             }
1187         }
1188     }
1189 
notifyAccessibilityButtonClickedInternal()1190     private void notifyAccessibilityButtonClickedInternal() {
1191         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
1192         if (listener != null) {
1193             try {
1194                 listener.onAccessibilityButtonClicked();
1195             } catch (RemoteException re) {
1196                 Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re);
1197             }
1198         }
1199     }
1200 
notifyAccessibilityButtonAvailabilityChangedInternal(boolean available)1201     private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) {
1202         // Only notify the service if it's not been notified or the state has changed
1203         if (mReceivedAccessibilityButtonCallbackSinceBind
1204                 && (mLastAccessibilityButtonCallbackState == available)) {
1205             return;
1206         }
1207         mReceivedAccessibilityButtonCallbackSinceBind = true;
1208         mLastAccessibilityButtonCallbackState = available;
1209         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
1210         if (listener != null) {
1211             try {
1212                 listener.onAccessibilityButtonAvailabilityChanged(available);
1213             } catch (RemoteException re) {
1214                 Slog.e(LOG_TAG,
1215                         "Error sending accessibility button availability change to " + mService,
1216                         re);
1217             }
1218         }
1219     }
1220 
notifyGestureInternal(int gestureId)1221     private void notifyGestureInternal(int gestureId) {
1222         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
1223         if (listener != null) {
1224             try {
1225                 listener.onGesture(gestureId);
1226             } catch (RemoteException re) {
1227                 Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
1228                         + " to " + mService, re);
1229             }
1230         }
1231     }
1232 
notifyClearAccessibilityCacheInternal()1233     private void notifyClearAccessibilityCacheInternal() {
1234         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
1235         if (listener != null) {
1236             try {
1237                 listener.clearAccessibilityCache();
1238             } catch (RemoteException re) {
1239                 Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
1240                         + " to be cleared.", re);
1241             }
1242         }
1243     }
1244 
getServiceInterfaceSafely()1245     private IAccessibilityServiceClient getServiceInterfaceSafely() {
1246         synchronized (mLock) {
1247             return mServiceInterface;
1248         }
1249     }
1250 
resolveAccessibilityWindowIdLocked(int accessibilityWindowId)1251     private int resolveAccessibilityWindowIdLocked(int accessibilityWindowId) {
1252         if (accessibilityWindowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) {
1253             return mSecurityPolicy.getActiveWindowId();
1254         }
1255         return accessibilityWindowId;
1256     }
1257 
resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType)1258     private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
1259         if (windowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) {
1260             return mSecurityPolicy.mActiveWindowId;
1261         }
1262         if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) {
1263             if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
1264                 return mSecurityPolicy.mFocusedWindowId;
1265             } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
1266                 return mSecurityPolicy.mAccessibilityFocusedWindowId;
1267             }
1268         }
1269         return windowId;
1270     }
1271 
getComponentName()1272     public ComponentName getComponentName() {
1273         return mComponentName;
1274     }
1275 
1276     private final class InvocationHandler extends Handler {
1277         public static final int MSG_ON_GESTURE = 1;
1278         public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
1279 
1280         private static final int MSG_ON_MAGNIFICATION_CHANGED = 5;
1281         private static final int MSG_ON_SOFT_KEYBOARD_STATE_CHANGED = 6;
1282         private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7;
1283         private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8;
1284 
1285         /** List of magnification callback states, mapping from displayId -> Boolean */
1286         @GuardedBy("mlock")
1287         private final SparseArray<Boolean> mMagnificationCallbackState = new SparseArray<>(0);
1288         private boolean mIsSoftKeyboardCallbackEnabled = false;
1289 
InvocationHandler(Looper looper)1290         public InvocationHandler(Looper looper) {
1291             super(looper, null, true);
1292         }
1293 
1294         @Override
handleMessage(Message message)1295         public void handleMessage(Message message) {
1296             final int type = message.what;
1297             switch (type) {
1298                 case MSG_ON_GESTURE: {
1299                     final int gestureId = message.arg1;
1300                     notifyGestureInternal(gestureId);
1301                 } break;
1302 
1303                 case MSG_CLEAR_ACCESSIBILITY_CACHE: {
1304                     notifyClearAccessibilityCacheInternal();
1305                 } break;
1306 
1307                 case MSG_ON_MAGNIFICATION_CHANGED: {
1308                     final SomeArgs args = (SomeArgs) message.obj;
1309                     final Region region = (Region) args.arg1;
1310                     final float scale = (float) args.arg2;
1311                     final float centerX = (float) args.arg3;
1312                     final float centerY = (float) args.arg4;
1313                     final int displayId = args.argi1;
1314                     notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY);
1315                     args.recycle();
1316                 } break;
1317 
1318                 case MSG_ON_SOFT_KEYBOARD_STATE_CHANGED: {
1319                     final int showState = (int) message.arg1;
1320                     notifySoftKeyboardShowModeChangedInternal(showState);
1321                 } break;
1322 
1323                 case MSG_ON_ACCESSIBILITY_BUTTON_CLICKED: {
1324                     notifyAccessibilityButtonClickedInternal();
1325                 } break;
1326 
1327                 case MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: {
1328                     final boolean available = (message.arg1 != 0);
1329                     notifyAccessibilityButtonAvailabilityChangedInternal(available);
1330                 } break;
1331 
1332                 default: {
1333                     throw new IllegalArgumentException("Unknown message: " + type);
1334                 }
1335             }
1336         }
1337 
notifyMagnificationChangedLocked(int displayId, @NonNull Region region, float scale, float centerX, float centerY)1338         public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
1339                 float scale, float centerX, float centerY) {
1340             synchronized (mLock) {
1341                 if (mMagnificationCallbackState.get(displayId) == null) {
1342                     return;
1343                 }
1344             }
1345 
1346             final SomeArgs args = SomeArgs.obtain();
1347             args.arg1 = region;
1348             args.arg2 = scale;
1349             args.arg3 = centerX;
1350             args.arg4 = centerY;
1351             args.argi1 = displayId;
1352 
1353             final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
1354             msg.sendToTarget();
1355         }
1356 
setMagnificationCallbackEnabled(int displayId, boolean enabled)1357         public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
1358             synchronized (mLock) {
1359                 if (enabled) {
1360                     mMagnificationCallbackState.put(displayId, true);
1361                 } else {
1362                     mMagnificationCallbackState.remove(displayId);
1363                 }
1364             }
1365         }
1366 
isMagnificationCallbackEnabled(int displayId)1367         public boolean isMagnificationCallbackEnabled(int displayId) {
1368             synchronized (mLock) {
1369                 return mMagnificationCallbackState.get(displayId) != null;
1370             }
1371         }
1372 
notifySoftKeyboardShowModeChangedLocked(int showState)1373         public void notifySoftKeyboardShowModeChangedLocked(int showState) {
1374             if (!mIsSoftKeyboardCallbackEnabled) {
1375                 return;
1376             }
1377 
1378             final Message msg = obtainMessage(MSG_ON_SOFT_KEYBOARD_STATE_CHANGED, showState, 0);
1379             msg.sendToTarget();
1380         }
1381 
setSoftKeyboardCallbackEnabled(boolean enabled)1382         public void setSoftKeyboardCallbackEnabled(boolean enabled) {
1383             mIsSoftKeyboardCallbackEnabled = enabled;
1384         }
1385 
notifyAccessibilityButtonClickedLocked()1386         public void notifyAccessibilityButtonClickedLocked() {
1387             final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_CLICKED);
1388             msg.sendToTarget();
1389         }
1390 
notifyAccessibilityButtonAvailabilityChangedLocked(boolean available)1391         public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
1392             final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED,
1393                     (available ? 1 : 0), 0);
1394             msg.sendToTarget();
1395         }
1396     }
1397 }
1398