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.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
20 import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
21 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
22 
23 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
24 
25 import android.Manifest;
26 import android.accessibilityservice.AccessibilityService;
27 import android.accessibilityservice.AccessibilityServiceInfo;
28 import android.accessibilityservice.AccessibilityTrace;
29 import android.accessibilityservice.BrailleDisplayController;
30 import android.accessibilityservice.IAccessibilityServiceClient;
31 import android.accessibilityservice.IBrailleDisplayController;
32 import android.accessibilityservice.TouchInteractionController;
33 import android.annotation.EnforcePermission;
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.annotation.RequiresNoPermission;
37 import android.annotation.UserIdInt;
38 import android.app.PendingIntent;
39 import android.bluetooth.BluetoothAdapter;
40 import android.bluetooth.BluetoothDevice;
41 import android.bluetooth.BluetoothManager;
42 import android.content.ComponentName;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.pm.ParceledListSlice;
46 import android.hardware.usb.UsbDevice;
47 import android.hardware.usb.UsbManager;
48 import android.os.Binder;
49 import android.os.Bundle;
50 import android.os.Handler;
51 import android.os.IBinder;
52 import android.os.Message;
53 import android.os.Process;
54 import android.os.RemoteException;
55 import android.os.Trace;
56 import android.os.UserHandle;
57 import android.provider.Settings;
58 import android.text.TextUtils;
59 import android.util.Slog;
60 import android.view.Display;
61 import android.view.MotionEvent;
62 
63 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
64 import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
65 import com.android.server.inputmethod.InputMethodManagerInternal;
66 import com.android.server.wm.ActivityTaskManagerInternal;
67 import com.android.server.wm.WindowManagerInternal;
68 
69 import java.lang.ref.WeakReference;
70 import java.util.List;
71 import java.util.Objects;
72 import java.util.Set;
73 
74 /**
75  * This class represents an accessibility service. It stores all per service
76  * data required for the service management, provides API for starting/stopping the
77  * service and is responsible for adding/removing the service in the data structures
78  * for service management. The class also exposes configuration interface that is
79  * passed to the service it represents as soon it is bound. It also serves as the
80  * connection for the service.
81  */
82 class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
83     private static final String LOG_TAG = "AccessibilityServiceConnection";
84 
85     /*
86      Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
87      lists of bound and binding services. These are freed on user changes, but just in case it
88      somehow gets lost the weak reference will let the memory get GCed.
89 
90      Having the reference be null when being called is a very bad sign, but we check the condition.
91     */
92     final WeakReference<AccessibilityUserState> mUserStateWeakReference;
93     @UserIdInt
94     final int mUserId;
95     final Intent mIntent;
96     final ActivityTaskManagerInternal mActivityTaskManagerService;
97 
98     private BrailleDisplayConnection mBrailleDisplayConnection;
99     private List<Bundle> mTestBrailleDisplays = null;
100 
101     private final Handler mMainHandler;
102 
103     private static final class AccessibilityInputMethodSessionCallback
104             extends IAccessibilityInputMethodSessionCallback.Stub {
105         @UserIdInt
106         private final int mUserId;
107 
AccessibilityInputMethodSessionCallback(@serIdInt int userId)108         AccessibilityInputMethodSessionCallback(@UserIdInt int userId) {
109             mUserId = userId;
110         }
111 
112         @RequiresNoPermission
113         @Override
sessionCreated(IAccessibilityInputMethodSession session, int id)114         public void sessionCreated(IAccessibilityInputMethodSession session, int id) {
115             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ASC.sessionCreated");
116             final long ident = Binder.clearCallingIdentity();
117             try {
118                 InputMethodManagerInternal.get()
119                         .onSessionForAccessibilityCreated(id, session, mUserId);
120             } finally {
121                 Binder.restoreCallingIdentity(ident);
122             }
123             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
124         }
125     }
126 
AccessibilityServiceConnection(@ullable AccessibilityUserState userState, Context context, ComponentName componentName, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal, SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm, ActivityTaskManagerInternal activityTaskManagerService)127     AccessibilityServiceConnection(@Nullable AccessibilityUserState userState,
128             Context context, ComponentName componentName,
129             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
130             Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
131             AccessibilityTrace trace, WindowManagerInternal windowManagerInternal,
132             SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm,
133             ActivityTaskManagerInternal activityTaskManagerService) {
134         super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
135                 securityPolicy, systemSupport, trace, windowManagerInternal, systemActionPerfomer,
136                 awm);
137         mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
138         // the user ID doesn't matter when userState is null, because it is null only when this is a
139         // ProxyAccessibilityServiceConnection, for which it never creates an IME session and uses
140         // the user ID.
141         mUserId = userState == null ? UserHandle.USER_NULL : userState.mUserId;
142         mIntent = new Intent().setComponent(mComponentName);
143         mMainHandler = mainHandler;
144         mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
145                 com.android.internal.R.string.accessibility_binding_label);
146         mActivityTaskManagerService = activityTaskManagerService;
147         final long identity = Binder.clearCallingIdentity();
148         try {
149             mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, mSystemSupport.getPendingIntentActivity(
150                     mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
151                     PendingIntent.FLAG_IMMUTABLE));
152         } finally {
153             Binder.restoreCallingIdentity(identity);
154         }
155     }
156 
bindLocked()157     public void bindLocked() {
158         AccessibilityUserState userState = mUserStateWeakReference.get();
159         if (userState == null) return;
160         final long identity = Binder.clearCallingIdentity();
161         try {
162             int flags = Context.BIND_AUTO_CREATE
163                     | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
164                     | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
165                     | Context.BIND_INCLUDE_CAPABILITIES;
166             if (userState.getBindInstantServiceAllowedLocked()) {
167                 flags |= Context.BIND_ALLOW_INSTANT;
168             }
169             if (mService == null && mContext.bindServiceAsUser(
170                     mIntent, this, flags, new UserHandle(userState.mUserId))) {
171                 userState.getBindingServicesLocked().add(mComponentName);
172             }
173         } finally {
174             Binder.restoreCallingIdentity(identity);
175         }
176         mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
177                 mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
178                 userState.mUserId);
179     }
180 
unbindLocked()181     public void unbindLocked() {
182         if (requestImeApis()) {
183             mSystemSupport.unbindImeLocked(this);
184         }
185         mContext.unbindService(this);
186         AccessibilityUserState userState = mUserStateWeakReference.get();
187         if (userState == null) return;
188         userState.removeServiceLocked(this);
189         mSystemSupport.getMagnificationProcessor().resetAllIfNeeded(mId);
190         mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
191                 userState.mUserId);
192         resetLocked();
193     }
194 
canRetrieveInteractiveWindowsLocked()195     public boolean canRetrieveInteractiveWindowsLocked() {
196         return mSecurityPolicy.canRetrieveWindowContentLocked(this) && mRetrieveInteractiveWindows;
197     }
198 
199     @RequiresNoPermission
200     @Override
disableSelf()201     public void disableSelf() {
202         if (svcConnTracingEnabled()) {
203             logTraceSvcConn("disableSelf", "");
204         }
205         synchronized (mLock) {
206             AccessibilityUserState userState = mUserStateWeakReference.get();
207             if (userState == null) return;
208             if (userState.getEnabledServicesLocked().remove(mComponentName)) {
209                 final long identity = Binder.clearCallingIdentity();
210                 try {
211                     mSystemSupport.persistComponentNamesToSettingLocked(
212                             Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
213                             userState.getEnabledServicesLocked(), userState.mUserId);
214 
215                     mSystemSupport.onClientChangeLocked(false);
216                 } finally {
217                     Binder.restoreCallingIdentity(identity);
218                 }
219             }
220         }
221     }
222 
223     @Override
onServiceConnected(ComponentName componentName, IBinder service)224     public void onServiceConnected(ComponentName componentName, IBinder service) {
225         AccessibilityUserState userState = mUserStateWeakReference.get();
226         if (userState != null) {
227             addWindowTokensForAllDisplays();
228         }
229         synchronized (mLock) {
230             if (mService != service) {
231                 if (mService != null) {
232                     mService.unlinkToDeath(this, 0);
233                 }
234                 mService = service;
235                 try {
236                     mService.linkToDeath(this, 0);
237                 } catch (RemoteException re) {
238                     Slog.e(LOG_TAG, "Failed registering death link");
239                     binderDied();
240                     return;
241                 }
242             }
243             mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
244             if (userState == null) return;
245             userState.addServiceLocked(this);
246             mSystemSupport.onClientChangeLocked(false);
247             // Initialize the service on the main handler after we're done setting up for
248             // the new configuration (for example, initializing the input filter).
249             mMainHandler.sendMessage(obtainMessage(
250                     AccessibilityServiceConnection::initializeService, this));
251             if (requestImeApis()) {
252                 mSystemSupport.requestImeLocked(this);
253             }
254         }
255     }
256 
257     @RequiresNoPermission
258     @Override
getServiceInfo()259     public AccessibilityServiceInfo getServiceInfo() {
260         return mAccessibilityServiceInfo;
261     }
262 
initializeService()263     private void initializeService() {
264         IAccessibilityServiceClient serviceInterface = null;
265         synchronized (mLock) {
266             AccessibilityUserState userState = mUserStateWeakReference.get();
267             if (userState == null) return;
268             final Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
269             final Set<ComponentName> crashedServices = userState.getCrashedServicesLocked();
270             if (bindingServices.contains(mComponentName)
271                     || crashedServices.contains(mComponentName)) {
272                 bindingServices.remove(mComponentName);
273                 crashedServices.remove(mComponentName);
274                 mAccessibilityServiceInfo.crashed = false;
275                 serviceInterface = mServiceInterface;
276             }
277             // There's a chance that service is removed from enabled_accessibility_services setting
278             // key, but skip unbinding because of it's in binding state. Unbinds it if it's
279             // not in enabled service list.
280             if (serviceInterface != null
281                     && !userState.getEnabledServicesLocked().contains(mComponentName)) {
282                 mSystemSupport.onClientChangeLocked(false);
283                 return;
284             }
285         }
286         if (serviceInterface == null) {
287             binderDied();
288             return;
289         }
290         try {
291             if (svcClientTracingEnabled()) {
292                 logTraceSvcClient("init",
293                         this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
294             }
295             serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
296         } catch (RemoteException re) {
297             Slog.w(LOG_TAG, "Error while setting connection for service: "
298                     + serviceInterface, re);
299             binderDied();
300         }
301     }
302 
303     @Override
onServiceDisconnected(ComponentName componentName)304     public void onServiceDisconnected(ComponentName componentName) {
305         binderDied();
306         AccessibilityUserState userState = mUserStateWeakReference.get();
307         if (userState != null) {
308             mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
309                     userState.mUserId);
310         }
311     }
312 
313     @Override
hasRightsToCurrentUserLocked()314     protected boolean hasRightsToCurrentUserLocked() {
315         // We treat calls from a profile as if made by its parent as profiles
316         // share the accessibility state of the parent. The call below
317         // performs the current profile parent resolution.
318         final int callingUid = Binder.getCallingUid();
319         if (callingUid == Process.ROOT_UID
320                 || callingUid == Process.SYSTEM_UID
321                 || callingUid == Process.SHELL_UID) {
322             return true;
323         }
324         if (mSecurityPolicy.resolveProfileParentLocked(UserHandle.getUserId(callingUid))
325                 == mSystemSupport.getCurrentUserIdLocked()) {
326             return true;
327         }
328         if (mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
329                 || mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
330             return true;
331         }
332         return false;
333     }
334 
335     @RequiresNoPermission
336     @Override
setSoftKeyboardShowMode(int showMode)337     public boolean setSoftKeyboardShowMode(int showMode) {
338         if (svcConnTracingEnabled()) {
339             logTraceSvcConn("setSoftKeyboardShowMode", "showMode=" + showMode);
340         }
341         synchronized (mLock) {
342             if (!hasRightsToCurrentUserLocked()) {
343                 return false;
344             }
345             final AccessibilityUserState userState = mUserStateWeakReference.get();
346             if (userState == null) return false;
347 
348             final long identity = Binder.clearCallingIdentity();
349             try {
350                 return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
351             } finally {
352                 Binder.restoreCallingIdentity(identity);
353             }
354         }
355     }
356 
357     @RequiresNoPermission
358     @Override
getSoftKeyboardShowMode()359     public int getSoftKeyboardShowMode() {
360         if (svcConnTracingEnabled()) {
361             logTraceSvcConn("getSoftKeyboardShowMode", "");
362         }
363         final AccessibilityUserState userState = mUserStateWeakReference.get();
364         final long identity = Binder.clearCallingIdentity();
365         try {
366             return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
367         } finally {
368             Binder.restoreCallingIdentity(identity);
369         }
370     }
371 
372     @RequiresNoPermission
373     @Override
switchToInputMethod(String imeId)374     public boolean switchToInputMethod(String imeId) {
375         if (svcConnTracingEnabled()) {
376             logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
377         }
378         synchronized (mLock) {
379             if (!hasRightsToCurrentUserLocked()) {
380                 return false;
381             }
382         }
383         final boolean result;
384         final int callingUserId = UserHandle.getCallingUserId();
385         final long identity = Binder.clearCallingIdentity();
386         try {
387             result = InputMethodManagerInternal.get().switchToInputMethod(imeId, callingUserId);
388         } finally {
389             Binder.restoreCallingIdentity(identity);
390         }
391         return result;
392     }
393 
394     @RequiresNoPermission
395     @Override
396     @AccessibilityService.SoftKeyboardController.EnableImeResult
setInputMethodEnabled(String imeId, boolean enabled)397     public int setInputMethodEnabled(String imeId, boolean enabled) throws SecurityException {
398         if (svcConnTracingEnabled()) {
399             logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
400         }
401         synchronized (mLock) {
402             if (!hasRightsToCurrentUserLocked()) {
403                 return ENABLE_IME_FAIL_UNKNOWN;
404             }
405         }
406 
407         final int callingUserId = UserHandle.getCallingUserId();
408         final InputMethodManagerInternal inputMethodManagerInternal =
409                 InputMethodManagerInternal.get();
410 
411         final @AccessibilityService.SoftKeyboardController.EnableImeResult int checkResult;
412         final long identity = Binder.clearCallingIdentity();
413         try {
414             synchronized (mLock) {
415                 checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this);
416             }
417             if (checkResult != ENABLE_IME_SUCCESS) {
418                 return checkResult;
419             }
420             if (inputMethodManagerInternal.setInputMethodEnabled(imeId,
421                         enabled, callingUserId)) {
422                 return ENABLE_IME_SUCCESS;
423             }
424         } finally {
425             Binder.restoreCallingIdentity(identity);
426         }
427         return ENABLE_IME_FAIL_UNKNOWN;
428     }
429 
430     @RequiresNoPermission
431     @Override
isAccessibilityButtonAvailable()432     public boolean isAccessibilityButtonAvailable() {
433         if (svcConnTracingEnabled()) {
434             logTraceSvcConn("isAccessibilityButtonAvailable", "");
435         }
436         synchronized (mLock) {
437             if (!hasRightsToCurrentUserLocked()) {
438                 return false;
439             }
440 
441             final long identity = Binder.clearCallingIdentity();
442             try {
443                 AccessibilityUserState userState = mUserStateWeakReference.get();
444                 return (userState != null) && isAccessibilityButtonAvailableLocked(userState);
445             } finally {
446                 Binder.restoreCallingIdentity(identity);
447             }
448         }
449     }
450 
451     @Override
binderDied()452     public void binderDied() {
453         synchronized (mLock) {
454             // It is possible that this service's package was force stopped during
455             // whose handling the death recipient is unlinked and still get a call
456             // on binderDied since the call was made before we unlink but was
457             // waiting on the lock we held during the force stop handling.
458             if (!isConnectedLocked()) {
459                 return;
460             }
461             if (requestImeApis()) {
462                 mSystemSupport.unbindImeLocked(this);
463             }
464             mAccessibilityServiceInfo.crashed = true;
465             AccessibilityUserState userState = mUserStateWeakReference.get();
466             if (userState != null) {
467                 userState.serviceDisconnectedLocked(this);
468             }
469             resetLocked();
470             mSystemSupport.getMagnificationProcessor().resetAllIfNeeded(mId);
471             mSystemSupport.onClientChangeLocked(false);
472         }
473     }
474 
475     @Override
resetLocked()476     public void resetLocked() {
477         super.resetLocked();
478         if (android.view.accessibility.Flags.brailleDisplayHid()) {
479             if (mBrailleDisplayConnection != null) {
480                 mBrailleDisplayConnection.disconnect();
481             }
482         }
483     }
484 
isAccessibilityButtonAvailableLocked(AccessibilityUserState userState)485     public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
486         // If the service does not request the accessibility button, it isn't available
487         if (!mRequestAccessibilityButton) {
488             return false;
489         }
490         // If the accessibility button isn't currently shown, it cannot be available to services
491         if (!mSystemSupport.isAccessibilityButtonShown()) {
492             return false;
493         }
494         return true;
495     }
496 
497     @Override
isCapturingFingerprintGestures()498     public boolean isCapturingFingerprintGestures() {
499         return (mServiceInterface != null)
500                 && mSecurityPolicy.canCaptureFingerprintGestures(this)
501                 && mCaptureFingerprintGestures;
502     }
503 
504     @Override
onFingerprintGestureDetectionActiveChanged(boolean active)505     public void onFingerprintGestureDetectionActiveChanged(boolean active) {
506         if (!isCapturingFingerprintGestures()) {
507             return;
508         }
509         IAccessibilityServiceClient serviceInterface;
510         synchronized (mLock) {
511             serviceInterface = mServiceInterface;
512         }
513         if (serviceInterface != null) {
514             try {
515                 if (svcClientTracingEnabled()) {
516                     logTraceSvcClient(
517                             "onFingerprintCapturingGesturesChanged", String.valueOf(active));
518                 }
519                 mServiceInterface.onFingerprintCapturingGesturesChanged(active);
520             } catch (RemoteException e) {
521             }
522         }
523     }
524 
525     @Override
onFingerprintGesture(int gesture)526     public void onFingerprintGesture(int gesture) {
527         if (!isCapturingFingerprintGestures()) {
528             return;
529         }
530         IAccessibilityServiceClient serviceInterface;
531         synchronized (mLock) {
532             serviceInterface = mServiceInterface;
533         }
534         if (serviceInterface != null) {
535             try {
536                 if (svcClientTracingEnabled()) {
537                     logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture));
538                 }
539                 mServiceInterface.onFingerprintGesture(gesture);
540             } catch (RemoteException e) {
541             }
542         }
543     }
544 
545     @RequiresNoPermission
546     @Override
dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId)547     public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
548         synchronized (mLock) {
549             if (mServiceInterface != null && mSecurityPolicy.canPerformGestures(this)) {
550                 final long identity = Binder.clearCallingIdentity();
551                 try {
552                     MotionEventInjector motionEventInjector =
553                             mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
554                     if (wmTracingEnabled()) {
555                         logTraceWM("isTouchOrFaketouchDevice", "");
556                     }
557                     if (motionEventInjector != null
558                             && mWindowManagerService.isTouchOrFaketouchDevice()) {
559                         motionEventInjector.injectEvents(
560                                 gestureSteps.getList(), mServiceInterface, sequence, displayId);
561                     } else {
562                         try {
563                             if (svcClientTracingEnabled()) {
564                                 logTraceSvcClient("onPerformGestureResult", sequence + ", false");
565                             }
566                             mServiceInterface.onPerformGestureResult(sequence, false);
567                         } catch (RemoteException re) {
568                             Slog.e(LOG_TAG, "Error sending motion event injection failure to "
569                                     + mServiceInterface, re);
570                         }
571                     }
572                 } finally {
573                     Binder.restoreCallingIdentity(identity);
574                 }
575 
576             }
577         }
578     }
579 
580     @RequiresNoPermission
581     @Override
setFocusAppearance(int strokeWidth, int color)582     public void setFocusAppearance(int strokeWidth, int color) {
583         AccessibilityUserState userState = mUserStateWeakReference.get();
584         if (userState == null) {
585             return;
586         }
587 
588         synchronized (mLock) {
589             if (!hasRightsToCurrentUserLocked()) {
590                 return;
591             }
592 
593             if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
594                 return;
595             }
596 
597             if (userState.getFocusStrokeWidthLocked() == strokeWidth
598                     && userState.getFocusColorLocked() == color) {
599                 return;
600             }
601 
602             final long identity = Binder.clearCallingIdentity();
603             try {
604                 // Sets the appearance data in the A11yUserState.
605                 userState.setFocusAppearanceLocked(strokeWidth, color);
606                 // Updates the appearance data in the A11yManager.
607                 mSystemSupport.onClientChangeLocked(false);
608             } finally {
609                 Binder.restoreCallingIdentity(identity);
610             }
611         }
612     }
613 
notifyMotionEvent(MotionEvent event)614     public void notifyMotionEvent(MotionEvent event) {
615         final Message msg = obtainMessage(
616                 AccessibilityServiceConnection::notifyMotionEventInternal,
617                 AccessibilityServiceConnection.this, event);
618         mMainHandler.sendMessage(msg);
619     }
620 
notifyTouchState(int displayId, int state)621     public void notifyTouchState(int displayId, int state) {
622         final Message msg = obtainMessage(
623                 AccessibilityServiceConnection::notifyTouchStateInternal,
624                 AccessibilityServiceConnection.this, displayId, state);
625         mMainHandler.sendMessage(msg);
626     }
627 
requestImeApis()628     public boolean requestImeApis() {
629         return mRequestImeApis;
630     }
631 
632     @Override
createImeSessionInternal()633     protected void createImeSessionInternal() {
634         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
635         if (listener != null) {
636             try {
637                 if (svcClientTracingEnabled()) {
638                     logTraceSvcClient("createImeSession", "");
639                 }
640                 AccessibilityInputMethodSessionCallback
641                         callback = new AccessibilityInputMethodSessionCallback(mUserId);
642                 listener.createImeSession(callback);
643             } catch (RemoteException re) {
644                 Slog.e(LOG_TAG,
645                         "Error requesting IME session from " + mService, re);
646             }
647         }
648     }
649 
notifyMotionEventInternal(MotionEvent event)650     private void notifyMotionEventInternal(MotionEvent event) {
651         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
652         if (listener != null) {
653             try {
654                 if (mTrace.isA11yTracingEnabled()) {
655                     logTraceSvcClient(".onMotionEvent ",
656                             event.toString());
657                 }
658                 listener.onMotionEvent(event);
659             } catch (RemoteException re) {
660                 Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
661             }
662         }
663     }
664 
notifyTouchStateInternal(int displayId, int state)665     private void notifyTouchStateInternal(int displayId, int state) {
666         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
667         if (listener != null) {
668             try {
669                 if (mTrace.isA11yTracingEnabled()) {
670                     logTraceSvcClient(".onTouchStateChanged ",
671                             TouchInteractionController.stateToString(state));
672                 }
673                 listener.onTouchStateChanged(displayId, state);
674             } catch (RemoteException re) {
675                 Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
676             }
677         }
678     }
679 
checkAccessibilityAccessLocked()680     private void checkAccessibilityAccessLocked() {
681         if (!hasRightsToCurrentUserLocked()
682                 || !mSecurityPolicy.checkAccessibilityAccess(this)) {
683             throw new SecurityException("Caller does not have accessibility access");
684         }
685     }
686 
687     /**
688      * Sets up a BrailleDisplayConnection interface for the requested Bluetooth-connected
689      * Braille display.
690      *
691      * @param bluetoothAddress The address from
692      *                         {@link android.bluetooth.BluetoothDevice#getAddress()}.
693      */
694     @Override
695     @EnforcePermission(Manifest.permission.BLUETOOTH_CONNECT)
connectBluetoothBrailleDisplay( @onNull String bluetoothAddress, @NonNull IBrailleDisplayController controller)696     public void connectBluetoothBrailleDisplay(
697             @NonNull String bluetoothAddress, @NonNull IBrailleDisplayController controller) {
698         connectBluetoothBrailleDisplay_enforcePermission();
699         if (!android.view.accessibility.Flags.brailleDisplayHid()) {
700             throw new IllegalStateException("Flag BRAILLE_DISPLAY_HID not enabled");
701         }
702         Objects.requireNonNull(bluetoothAddress);
703         Objects.requireNonNull(controller);
704         if (!BluetoothAdapter.checkBluetoothAddress(bluetoothAddress)) {
705             throw new IllegalArgumentException(
706                     bluetoothAddress + " is not a valid Bluetooth address");
707         }
708         final BluetoothManager bluetoothManager =
709                 mContext.getSystemService(BluetoothManager.class);
710         final String bluetoothDeviceName = bluetoothManager == null ? null :
711                 bluetoothManager.getAdapter().getBondedDevices().stream()
712                         .filter(device -> device.getAddress().equalsIgnoreCase(bluetoothAddress))
713                         .map(BluetoothDevice::getName)
714                         .findFirst().orElse(null);
715         synchronized (mLock) {
716             checkAccessibilityAccessLocked();
717             if (mBrailleDisplayConnection != null) {
718                 throw new IllegalStateException(
719                         "This service already has a connected Braille display");
720             }
721             BrailleDisplayConnection connection = new BrailleDisplayConnection(mLock, this);
722             if (mTestBrailleDisplays != null) {
723                 connection.setTestData(mTestBrailleDisplays);
724             }
725             connection.connectLocked(
726                     bluetoothAddress,
727                     bluetoothDeviceName,
728                     BrailleDisplayConnection.BUS_BLUETOOTH,
729                     controller);
730         }
731     }
732 
733     /**
734      * Sets up a BrailleDisplayConnection interface for the requested USB-connected
735      * Braille display.
736      *
737      * <p>The caller package must already have USB permission for this {@link UsbDevice}.
738      */
739     @RequiresNoPermission
740     @Override
741     @NonNull
connectUsbBrailleDisplay(@onNull UsbDevice usbDevice, @NonNull IBrailleDisplayController controller)742     public void connectUsbBrailleDisplay(@NonNull UsbDevice usbDevice,
743             @NonNull IBrailleDisplayController controller) {
744         if (!android.view.accessibility.Flags.brailleDisplayHid()) {
745             throw new IllegalStateException("Flag BRAILLE_DISPLAY_HID not enabled");
746         }
747         Objects.requireNonNull(usbDevice);
748         Objects.requireNonNull(controller);
749         final UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
750         final String usbSerialNumber;
751         final int uid = Binder.getCallingUid();
752         final int pid = Binder.getCallingPid();
753         final long identity = Binder.clearCallingIdentity();
754         try {
755             if (usbManager == null || !usbManager.hasPermission(
756                     usbDevice, mComponentName.getPackageName(), /*pid=*/ pid, /*uid=*/ uid)) {
757                 throw new SecurityException(
758                         "Caller does not have permission to access this UsbDevice");
759             }
760             usbSerialNumber = usbDevice.getSerialNumber();
761             if (TextUtils.isEmpty(usbSerialNumber)) {
762                 // If the UsbDevice does not report a serial number for locating the HIDRAW
763                 // node then notify connection error ERROR_BRAILLE_DISPLAY_NOT_FOUND.
764                 try {
765                     controller.onConnectionFailed(BrailleDisplayController.BrailleDisplayCallback
766                             .FLAG_ERROR_BRAILLE_DISPLAY_NOT_FOUND);
767                 } catch (RemoteException e) {
768                     Slog.e(LOG_TAG, "Error calling onConnectionFailed", e);
769                 }
770                 return;
771             }
772         } finally {
773             Binder.restoreCallingIdentity(identity);
774         }
775         synchronized (mLock) {
776             checkAccessibilityAccessLocked();
777             if (mBrailleDisplayConnection != null) {
778                 throw new IllegalStateException(
779                         "This service already has a connected Braille display");
780             }
781             BrailleDisplayConnection connection = new BrailleDisplayConnection(mLock, this);
782             if (mTestBrailleDisplays != null) {
783                 connection.setTestData(mTestBrailleDisplays);
784             }
785             connection.connectLocked(
786                     usbSerialNumber,
787                     usbDevice.getProductName(),
788                     BrailleDisplayConnection.BUS_USB,
789                     controller);
790         }
791     }
792 
793     @Override
794     @EnforcePermission(Manifest.permission.MANAGE_ACCESSIBILITY)
setTestBrailleDisplayData(List<Bundle> brailleDisplays)795     public void setTestBrailleDisplayData(List<Bundle> brailleDisplays) {
796         setTestBrailleDisplayData_enforcePermission();
797         // Enforce that this TestApi is only called by trusted (test) callers.
798         mTestBrailleDisplays = brailleDisplays;
799     }
800 
onBrailleDisplayConnectedLocked(BrailleDisplayConnection connection)801     void onBrailleDisplayConnectedLocked(BrailleDisplayConnection connection) {
802         mBrailleDisplayConnection = connection;
803     }
804 
805     // Reset state when the BrailleDisplayConnection object disconnects itself.
onBrailleDisplayDisconnectedLocked()806     void onBrailleDisplayDisconnectedLocked() {
807         mBrailleDisplayConnection = null;
808     }
809 }
810