1 /*
2  * Copyright (C) 2019 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_BY_ADMIN;
20 import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
21 import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
22 
23 import android.Manifest;
24 import android.accessibilityservice.AccessibilityService;
25 import android.accessibilityservice.AccessibilityServiceInfo;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.AppOpsManager;
29 import android.app.role.RoleManager;
30 import android.appwidget.AppWidgetManagerInternal;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManagerInternal;
35 import android.content.pm.ResolveInfo;
36 import android.content.pm.ServiceInfo;
37 import android.content.pm.UserInfo;
38 import android.os.Binder;
39 import android.os.IBinder;
40 import android.os.Process;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.util.ArraySet;
44 import android.util.Slog;
45 import android.util.SparseBooleanArray;
46 import android.view.accessibility.AccessibilityEvent;
47 import android.view.inputmethod.InputMethodInfo;
48 
49 import com.android.internal.util.ArrayUtils;
50 import com.android.server.inputmethod.InputMethodManagerInternal;
51 
52 import libcore.util.EmptyArray;
53 
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.Set;
57 
58 /**
59  * This class provides APIs of accessibility security policies for accessibility manager
60  * to grant accessibility capabilities or events access right to accessibility services. And also
61  * monitors the current bound accessibility services to prompt permission warnings for
62  * not accessibility-categorized ones.
63  */
64 public class AccessibilitySecurityPolicy {
65     private static final int OWN_PROCESS_ID = android.os.Process.myPid();
66     private static final String LOG_TAG = "AccessibilitySecurityPolicy";
67 
68     private static final int KEEP_SOURCE_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED
69             | AccessibilityEvent.TYPE_VIEW_FOCUSED
70             | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
71             | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
72             | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
73             | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
74             | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
75             | AccessibilityEvent.TYPE_WINDOWS_CHANGED
76             | AccessibilityEvent.TYPE_VIEW_SELECTED
77             | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
78             | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
79             | AccessibilityEvent.TYPE_VIEW_SCROLLED
80             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
81             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
82             | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
83             | AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL;
84 
85     /**
86      * Methods that should find their way into separate modules, but are still in AMS
87      * TODO (b/111889696): Refactoring UserState to AccessibilityUserManager.
88      */
89     public interface AccessibilityUserManager {
90         /**
91          * Returns current userId maintained in accessibility manager service
92          */
getCurrentUserIdLocked()93         int getCurrentUserIdLocked();
94         // TODO: Should include resolveProfileParentLocked, but that was already in SecurityPolicy
95 
96         // TODO(b/255426725): temporary hack; see comment on A11YMS.mVisibleBgUserIds
97         /**
98          * Returns the {@link android.os.UserManager#getVisibleUsers() visible users}.
99          */
getVisibleUserIdsLocked()100         @Nullable SparseBooleanArray getVisibleUserIdsLocked();
101     }
102 
103     private final Context mContext;
104     private final PackageManager mPackageManager;
105     private final PackageManagerInternal mPackageManagerInternal;
106     private final UserManager mUserManager;
107     private final AppOpsManager mAppOpsManager;
108     private final AccessibilityUserManager mAccessibilityUserManager;
109     private final PolicyWarningUIController mPolicyWarningUIController;
110     /** All bound accessibility services which don't belong to accessibility category. */
111     private final ArraySet<ComponentName> mNonA11yCategoryServices = new ArraySet<>();
112 
113     private AppWidgetManagerInternal mAppWidgetService;
114     private AccessibilityWindowManager mAccessibilityWindowManager;
115     private int mCurrentUserId = UserHandle.USER_NULL;
116     private boolean mSendNonA11yToolNotificationEnabled = false;
117 
118     /**
119      * Constructor for AccessibilityManagerService.
120      */
AccessibilitySecurityPolicy(PolicyWarningUIController policyWarningUIController, @NonNull Context context, @NonNull AccessibilityUserManager a11yUserManager, @NonNull PackageManagerInternal packageManagerInternal)121     public AccessibilitySecurityPolicy(PolicyWarningUIController policyWarningUIController,
122             @NonNull Context context,
123             @NonNull AccessibilityUserManager a11yUserManager,
124             @NonNull PackageManagerInternal packageManagerInternal) {
125         mContext = context;
126         mAccessibilityUserManager = a11yUserManager;
127         mPackageManager = mContext.getPackageManager();
128         mPackageManagerInternal = packageManagerInternal;
129         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
130         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
131         mPolicyWarningUIController = policyWarningUIController;
132     }
133 
134     /**
135      * Enables sending the notification for non-AccessibilityTool services with the given state.
136      *
137      */
setSendingNonA11yToolNotificationLocked(boolean enable)138     public void setSendingNonA11yToolNotificationLocked(boolean enable) {
139         if (enable == mSendNonA11yToolNotificationEnabled) {
140             return;
141         }
142 
143         mSendNonA11yToolNotificationEnabled = enable;
144         mPolicyWarningUIController.enableSendingNonA11yToolNotification(enable);
145         if (enable) {
146             for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
147                 final ComponentName service = mNonA11yCategoryServices.valueAt(i);
148                 mPolicyWarningUIController.onNonA11yCategoryServiceBound(mCurrentUserId, service);
149             }
150         }
151     }
152 
153     /**
154      * Setup AccessibilityWindowManager. This isn't part of the constructor because the
155      * window manager and security policy both call each other.
156      */
setAccessibilityWindowManager(@onNull AccessibilityWindowManager awm)157     public void setAccessibilityWindowManager(@NonNull AccessibilityWindowManager awm) {
158         mAccessibilityWindowManager = awm;
159     }
160 
161     /**
162      * Setup AppWidgetManger during boot phase.
163      */
setAppWidgetManager(@onNull AppWidgetManagerInternal appWidgetManager)164     public void setAppWidgetManager(@NonNull AppWidgetManagerInternal appWidgetManager) {
165         mAppWidgetService = appWidgetManager;
166     }
167 
168     /**
169      * Check if an accessibility event can be dispatched. Events should be dispatched only if they
170      * are dispatched from items that services can see.
171      *
172      * @param userId The userId to check
173      * @param event The event to check
174      * @return {@code true} if the event can be dispatched
175      */
canDispatchAccessibilityEventLocked(int userId, @NonNull AccessibilityEvent event)176     public boolean canDispatchAccessibilityEventLocked(int userId,
177             @NonNull AccessibilityEvent event) {
178         final int eventType = event.getEventType();
179         switch (eventType) {
180             // All events that are for changes in a global window
181             // state should *always* be dispatched.
182             case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
183             case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
184             case AccessibilityEvent.TYPE_ANNOUNCEMENT:
185                 // All events generated by the user touching the
186                 // screen should *always* be dispatched.
187             case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
188             case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
189             case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
190             case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
191             case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
192             case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
193             case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
194             case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
195                 // Also always dispatch the event that assist is reading context.
196             case AccessibilityEvent.TYPE_ASSIST_READING_CONTEXT:
197                 // Also windows changing should always be dispatched.
198             case AccessibilityEvent.TYPE_WINDOWS_CHANGED: {
199                 return true;
200             }
201             // All events for changes in window content should be
202             // dispatched *only* if this window is one of the windows
203             // the accessibility layer reports which are windows
204             // that a sighted user can touch.
205             default: {
206                 return isRetrievalAllowingWindowLocked(userId, event.getWindowId());
207             }
208         }
209     }
210 
211     /**
212      * Find a valid package name for an app to expose to accessibility
213      *
214      * @param packageName The package name the app wants to expose
215      * @param appId The app's id
216      * @param userId The app's user id
217      * @param pid The app's process pid that requested this
218      * @return A package name that is valid to report
219      */
220     @Nullable
resolveValidReportedPackageLocked( @ullable CharSequence packageName, int appId, int userId, int pid)221     public String resolveValidReportedPackageLocked(
222             @Nullable CharSequence packageName, int appId, int userId, int pid) {
223         // Okay to pass no package
224         if (packageName == null) {
225             return null;
226         }
227         // The system gets to pass any package
228         if (appId == Process.SYSTEM_UID) {
229             return packageName.toString();
230         }
231         // Passing a package in your UID is fine
232         final String packageNameStr = packageName.toString();
233         final int resolvedUid = UserHandle.getUid(userId, appId);
234         if (isValidPackageForUid(packageNameStr, resolvedUid)) {
235             return packageName.toString();
236         }
237         // Appwidget hosts get to pass packages for widgets they host
238         if (mAppWidgetService != null && ArrayUtils.contains(mAppWidgetService
239                 .getHostedWidgetPackages(resolvedUid), packageNameStr)) {
240             return packageName.toString();
241         }
242         // If app has the targeted permission to act as another package
243         if (mContext.checkPermission(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY,
244                 pid, resolvedUid) == PackageManager.PERMISSION_GRANTED) {
245             return packageName.toString();
246         }
247         // Otherwise, set the package to the first one in the UID
248         final String[] packageNames = mPackageManager.getPackagesForUid(resolvedUid);
249         if (ArrayUtils.isEmpty(packageNames)) {
250             return null;
251         }
252         // Okay, the caller reported a package it does not have access to.
253         // Instead of crashing the caller for better backwards compatibility
254         // we report the first package in the UID. Since most of the time apps
255         // don't use shared user id, this will yield correct results and for
256         // the edge case of using a shared user id we may report the wrong
257         // package but this is fine since first, this is a cheating app and
258         // second there is no way to get the correct package anyway.
259         return packageNames[0];
260     }
261 
262     /**
263      * Get the packages that are valid for a uid. In some situations, like app widgets, there
264      * could be several valid packages
265      *
266      * @param targetPackage A package that is known to be valid for this id
267      * @param targetUid The whose packages should be checked
268      * @return An array of all valid package names. An empty array means any package is OK
269      */
270     @NonNull
computeValidReportedPackages( @onNull String targetPackage, int targetUid)271     public String[] computeValidReportedPackages(
272             @NonNull String targetPackage, int targetUid) {
273         if (UserHandle.getAppId(targetUid) == Process.SYSTEM_UID) {
274             // Empty array means any package is Okay
275             return EmptyArray.STRING;
276         }
277         // IMPORTANT: The target package is already vetted to be in the target UID
278         String[] uidPackages = new String[]{targetPackage};
279         // Appwidget hosts get to pass packages for widgets they host
280         if (mAppWidgetService != null) {
281             final ArraySet<String> widgetPackages = mAppWidgetService
282                     .getHostedWidgetPackages(targetUid);
283             if (widgetPackages != null && !widgetPackages.isEmpty()) {
284                 final String[] validPackages = new String[uidPackages.length
285                         + widgetPackages.size()];
286                 System.arraycopy(uidPackages, 0, validPackages, 0, uidPackages.length);
287                 final int widgetPackageCount = widgetPackages.size();
288                 for (int i = 0; i < widgetPackageCount; i++) {
289                     validPackages[uidPackages.length + i] = widgetPackages.valueAt(i);
290                 }
291                 return validPackages;
292             }
293         }
294         return uidPackages;
295     }
296 
297     /**
298      * Reset the event source for events that should not carry one
299      *
300      * @param event The event potentially to modify
301      */
updateEventSourceLocked(@onNull AccessibilityEvent event)302     public void updateEventSourceLocked(@NonNull AccessibilityEvent event) {
303         if ((event.getEventType() & KEEP_SOURCE_EVENT_TYPES) == 0) {
304             event.setSource(null);
305         }
306     }
307 
308     /**
309      * Check if a service can have access to a window
310      *
311      * @param userId The id of the user running the service
312      * @param service The service requesting access
313      * @param windowId The window it wants access to
314      *
315      * @return Whether ot not the service may retrieve info from the window
316      */
canGetAccessibilityNodeInfoLocked(int userId, @NonNull AbstractAccessibilityServiceConnection service, int windowId)317     public boolean canGetAccessibilityNodeInfoLocked(int userId,
318             @NonNull AbstractAccessibilityServiceConnection service, int windowId) {
319         return canRetrieveWindowContentLocked(service)
320                 && isRetrievalAllowingWindowLocked(userId, windowId);
321     }
322 
323     /**
324      * Check if a service can have access the list of windows
325      *
326      * @param service The service requesting access
327      *
328      * @return Whether ot not the service may retrieve the window list
329      */
canRetrieveWindowsLocked( @onNull AbstractAccessibilityServiceConnection service)330     public boolean canRetrieveWindowsLocked(
331             @NonNull AbstractAccessibilityServiceConnection service) {
332         return canRetrieveWindowContentLocked(service) && service.mRetrieveInteractiveWindows;
333     }
334 
335     /**
336      * Check if a service can have access the content of windows on the screen
337      *
338      * @param service The service requesting access
339      *
340      * @return Whether ot not the service may retrieve the content
341      */
canRetrieveWindowContentLocked( @onNull AbstractAccessibilityServiceConnection service)342     public boolean canRetrieveWindowContentLocked(
343             @NonNull AbstractAccessibilityServiceConnection service) {
344         return (service.getCapabilities()
345                 & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
346     }
347 
348     /**
349      * Check if a service can control magnification
350      *
351      * @param service The service requesting access
352      *
353      * @return Whether ot not the service may control magnification
354      */
canControlMagnification( @onNull AbstractAccessibilityServiceConnection service)355     public boolean canControlMagnification(
356             @NonNull AbstractAccessibilityServiceConnection service) {
357         return (service.getCapabilities()
358                 & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
359     }
360 
361     /**
362      * Check if a service can perform gestures
363      *
364      * @param service The service requesting access
365      *
366      * @return Whether ot not the service may perform gestures
367      */
canPerformGestures(@onNull AccessibilityServiceConnection service)368     public boolean canPerformGestures(@NonNull AccessibilityServiceConnection service) {
369         return (service.getCapabilities()
370                 & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
371     }
372 
373     /**
374      * Check if a service can capture gestures from the fingerprint sensor
375      *
376      * @param service The service requesting access
377      *
378      * @return Whether ot not the service may capture gestures from the fingerprint sensor
379      */
canCaptureFingerprintGestures(@onNull AccessibilityServiceConnection service)380     public boolean canCaptureFingerprintGestures(@NonNull AccessibilityServiceConnection service) {
381         return (service.getCapabilities()
382                 & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES) != 0;
383     }
384 
385     /**
386      * Checks if a service can take screenshot.
387      *
388      * @param service The service requesting access
389      *
390      * @return Whether ot not the service may take screenshot
391      */
canTakeScreenshotLocked( @onNull AbstractAccessibilityServiceConnection service)392     public boolean canTakeScreenshotLocked(
393             @NonNull AbstractAccessibilityServiceConnection service) {
394         return (service.getCapabilities()
395                 & AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT) != 0;
396     }
397 
398     /**
399      * Check whether the input method can be enabled or disabled by the accessibility service.
400      *
401      * @param imeId The id of the input method.
402      * @param service The accessibility service connection.
403      * @return Whether the input method can be enabled/disabled or the reason why it can't be
404      *         enabled/disabled.
405      * @throws SecurityException if the input method is not in the same package as the service.
406      */
407     @AccessibilityService.SoftKeyboardController.EnableImeResult
canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service)408     int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service)
409             throws SecurityException {
410         final String servicePackageName = service.getComponentName().getPackageName();
411         final int callingUserId = UserHandle.getCallingUserId();
412 
413         InputMethodInfo inputMethodInfo = null;
414         List<InputMethodInfo> inputMethodInfoList =
415                 InputMethodManagerInternal.get().getInputMethodListAsUser(callingUserId);
416         if (inputMethodInfoList != null) {
417             for (InputMethodInfo info : inputMethodInfoList) {
418                 if (info.getId().equals(imeId)) {
419                     inputMethodInfo = info;
420                     break;
421                 }
422             }
423         }
424 
425         if (inputMethodInfo == null
426                 || !inputMethodInfo.getPackageName().equals(servicePackageName)) {
427             throw new SecurityException("The input method is in a different package with the "
428                     + "accessibility service");
429         }
430 
431         // TODO(b/207697949, b/208872785): Add cts test for managed device.
432         //  Use RestrictedLockUtilsInternal in AccessibilitySecurityPolicy
433         if (RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(
434                 mContext, inputMethodInfo.getPackageName(), callingUserId) != null) {
435             return ENABLE_IME_FAIL_BY_ADMIN;
436         }
437 
438         return ENABLE_IME_SUCCESS;
439     }
440 
441     /**
442      * Returns the parent userId of the profile according to the specified userId.
443      *
444      * @param userId The userId to check
445      * @return the parent userId of the profile, or self if no parent exist
446      */
resolveProfileParentLocked(int userId)447     public int resolveProfileParentLocked(int userId) {
448         if (userId != mAccessibilityUserManager.getCurrentUserIdLocked()) {
449             final long identity = Binder.clearCallingIdentity();
450             try {
451                 UserInfo parent = mUserManager.getProfileParent(userId);
452                 if (parent != null) {
453                     return parent.getUserHandle().getIdentifier();
454                 }
455             } finally {
456                 Binder.restoreCallingIdentity(identity);
457             }
458         }
459         return userId;
460     }
461 
462     /**
463      * Returns the parent userId of the profile according to the specified userId. Enforcing
464      * permissions check if specified userId is not caller's userId.
465      *
466      * @param userId The userId to check
467      * @return the parent userId of the profile, or self if no parent exist
468      * @throws SecurityException if caller cannot interact across users
469      * @throws IllegalArgumentException if specified invalid userId
470      */
resolveCallingUserIdEnforcingPermissionsLocked(int userId)471     public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
472         final int callingUid = Binder.getCallingUid();
473         final int currentUserId = mAccessibilityUserManager.getCurrentUserIdLocked();
474         if (callingUid == 0
475                 || callingUid == Process.SYSTEM_UID
476                 || callingUid == Process.SHELL_UID) {
477             if (userId == UserHandle.USER_CURRENT
478                     || userId == UserHandle.USER_CURRENT_OR_SELF) {
479                 return currentUserId;
480             }
481             return resolveProfileParentLocked(userId);
482         }
483         final int callingUserId = UserHandle.getUserId(callingUid);
484         if (callingUserId == userId) {
485             return resolveProfileParentLocked(userId);
486         }
487         final int callingUserParentId = resolveProfileParentLocked(callingUserId);
488         if (callingUserParentId == currentUserId && (userId == UserHandle.USER_CURRENT
489                 || userId == UserHandle.USER_CURRENT_OR_SELF)) {
490             return currentUserId;
491         }
492         if (!hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
493                 && !hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
494             throw new SecurityException("Call from user " + callingUserId + " as user "
495                     + userId + " without permission INTERACT_ACROSS_USERS or "
496                     + "INTERACT_ACROSS_USERS_FULL not allowed.");
497         }
498         if (userId == UserHandle.USER_CURRENT
499                 || userId == UserHandle.USER_CURRENT_OR_SELF) {
500             return currentUserId;
501         }
502         return resolveProfileParentLocked(userId);
503     }
504 
505     /**
506      * Returns false if caller is not SYSTEM and SHELL, and tried to interact across users.
507      *
508      * @param userId The userId to interact.
509      * @return false if caller cannot interact across users.
510      */
isCallerInteractingAcrossUsers(int userId)511     public boolean isCallerInteractingAcrossUsers(int userId) {
512         final int callingUid = Binder.getCallingUid();
513         return (Binder.getCallingPid() == android.os.Process.myPid()
514                 || callingUid == Process.SHELL_UID
515                 || userId == UserHandle.USER_CURRENT
516                 || userId == UserHandle.USER_CURRENT_OR_SELF);
517     }
518 
isValidPackageForUid(String packageName, int uid)519     private boolean isValidPackageForUid(String packageName, int uid) {
520         final long token = Binder.clearCallingIdentity();
521         try {
522             // Since we treat calls from a profile as if made by its parent, using
523             // MATCH_ANY_USER to query the uid of the given package name.
524             return mPackageManagerInternal.isSameApp(packageName, PackageManager.MATCH_ANY_USER,
525                     uid, UserHandle.getUserId(uid));
526         } finally {
527             Binder.restoreCallingIdentity(token);
528         }
529     }
530 
isRetrievalAllowingWindowLocked(int userId, int windowId)531     private boolean isRetrievalAllowingWindowLocked(int userId, int windowId) {
532         // The system gets to interact with any window it wants.
533         if (Binder.getCallingUid() == Process.SYSTEM_UID) {
534             return true;
535         }
536         if (Binder.getCallingUid() == Process.SHELL_UID) {
537             if (!isShellAllowedToRetrieveWindowLocked(userId, windowId)) {
538                 return false;
539             }
540         }
541         if (mAccessibilityWindowManager.resolveParentWindowIdLocked(windowId)
542                 == mAccessibilityWindowManager.getActiveWindowId(userId)) {
543             return true;
544         }
545         return mAccessibilityWindowManager.findA11yWindowInfoByIdLocked(windowId) != null;
546     }
547 
isShellAllowedToRetrieveWindowLocked(int userId, int windowId)548     private boolean isShellAllowedToRetrieveWindowLocked(int userId, int windowId) {
549         final long token = Binder.clearCallingIdentity();
550         try {
551             IBinder windowToken = mAccessibilityWindowManager
552                     .getWindowTokenForUserAndWindowIdLocked(userId, windowId);
553             if (windowToken == null) {
554                 return false;
555             }
556             int windowOwnerUserId = mAccessibilityWindowManager.getWindowOwnerUserId(windowToken);
557             if (windowOwnerUserId == UserHandle.USER_NULL) {
558                 return false;
559             }
560             return !mUserManager.hasUserRestriction(
561                     UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(windowOwnerUserId));
562         } finally {
563             Binder.restoreCallingIdentity(token);
564         }
565     }
566 
567     /**
568      * Enforcing permission check to caller.
569      *
570      * @param permission The permission to check
571      * @param function The function name to check
572      */
enforceCallingPermission(@onNull String permission, @Nullable String function)573     public void enforceCallingPermission(@NonNull String permission, @Nullable String function) {
574         if (OWN_PROCESS_ID == Binder.getCallingPid()) {
575             return;
576         }
577         if (!hasPermission(permission)) {
578             throw new SecurityException("You do not have " + permission
579                     + " required to call " + function + " from pid="
580                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
581         }
582     }
583 
584     /**
585      * Permission check to caller.
586      *
587      * @param permission The permission to check
588      * @return true if caller has permission
589      */
hasPermission(@onNull String permission)590     public boolean hasPermission(@NonNull String permission) {
591         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
592     }
593 
594     /**
595      * Checks if accessibility service could register into the system.
596      *
597      * @param serviceInfo The ServiceInfo
598      * @return True if it could register into the system
599      */
canRegisterService(@onNull ServiceInfo serviceInfo)600     public boolean canRegisterService(@NonNull ServiceInfo serviceInfo) {
601         if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
602                 serviceInfo.permission)) {
603             Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
604                     serviceInfo.packageName, serviceInfo.name).flattenToShortString()
605                     + ": it does not require the permission "
606                     + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
607             return false;
608         }
609 
610         if ((serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
611             Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
612                     serviceInfo.packageName, serviceInfo.name).flattenToShortString()
613                     + ": the service is the external one and doesn't allow to register as "
614                     + "an accessibility service ");
615             return false;
616         }
617 
618         int servicePackageUid = serviceInfo.applicationInfo.uid;
619         if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
620                 servicePackageUid, serviceInfo.packageName, null, null)
621                 != AppOpsManager.MODE_ALLOWED) {
622             Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
623                     serviceInfo.packageName, serviceInfo.name).flattenToShortString()
624                     + ": disallowed by AppOps");
625             return false;
626         }
627 
628         return true;
629     }
630 
631     /**
632      * Checks if accessibility service could execute accessibility operations.
633      *
634      * @param service The accessibility service connection
635      * @return True if it could execute accessibility operations
636      */
checkAccessibilityAccess(AbstractAccessibilityServiceConnection service)637     public boolean checkAccessibilityAccess(AbstractAccessibilityServiceConnection service) {
638         final String packageName = service.getComponentName().getPackageName();
639         final ResolveInfo resolveInfo = service.getServiceInfo().getResolveInfo();
640 
641         if (resolveInfo == null) {
642             // For InteractionBridge, UiAutomation, and Proxy.
643             return true;
644         }
645 
646         final int servicePackageUid = resolveInfo.serviceInfo.applicationInfo.uid;
647         final int callingPid = Binder.getCallingPid();
648         final long identityToken = Binder.clearCallingIdentity();
649         final String attributionTag = service.getAttributionTag();
650         try {
651             // For the caller is system, just block the data to a11y services.
652             if (OWN_PROCESS_ID == callingPid) {
653                 return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
654                         servicePackageUid, packageName, attributionTag, null)
655                         == AppOpsManager.MODE_ALLOWED;
656             }
657 
658             return mAppOpsManager.noteOp(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
659                     servicePackageUid, packageName, attributionTag, null)
660                     == AppOpsManager.MODE_ALLOWED;
661         } finally {
662             Binder.restoreCallingIdentity(identityToken);
663         }
664     }
665 
666     /**
667      * Enforcing permission check to IPC caller or grant it if it's not through IPC.
668      *
669      * @param permission The permission to check
670      */
enforceCallingOrSelfPermission(@onNull String permission)671     public void enforceCallingOrSelfPermission(@NonNull String permission) {
672         if (mContext.checkCallingOrSelfPermission(permission)
673                 != PackageManager.PERMISSION_GRANTED) {
674             throw new SecurityException("Caller does not hold permission "
675                     + permission);
676         }
677     }
678 
679     /**
680      * Throws a SecurityException if the caller has neither the MANAGE_ACCESSIBILITY permission nor
681      * the COMPANION_DEVICE_APP_STREAMING role.
682      */
checkForAccessibilityPermissionOrRole()683     public void checkForAccessibilityPermissionOrRole() {
684         final boolean canManageAccessibility =
685                 mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
686                         == PackageManager.PERMISSION_GRANTED;
687         if (canManageAccessibility) {
688             return;
689         }
690         final int callingUid = Binder.getCallingUid();
691         final long identity = Binder.clearCallingIdentity();
692         try {
693             final RoleManager roleManager = mContext.getSystemService(RoleManager.class);
694             if (roleManager != null) {
695                 final List<String> holders = roleManager.getRoleHoldersAsUser(
696                         DEVICE_PROFILE_APP_STREAMING, UserHandle.getUserHandleForUid(callingUid));
697                 final String[] packageNames = mPackageManager.getPackagesForUid(callingUid);
698                 if (packageNames != null) {
699                     for (String packageName : packageNames) {
700                         if (holders.contains(packageName)) {
701                             return;
702                         }
703                     }
704                 }
705             }
706             throw new SecurityException(
707                     "Cannot register a proxy for a device without the "
708                             + "android.app.role.COMPANION_DEVICE_APP_STREAMING role or the"
709                             + " MANAGE_ACCESSIBILITY permission.");
710         } finally {
711             Binder.restoreCallingIdentity(identity);
712         }
713     }
714 
715     /**
716      * Called after a service was bound or unbound. Checks the current bound accessibility
717      * services and updates alarms.
718      *
719      * @param userId        The user id
720      * @param boundServices The bound services
721      */
onBoundServicesChangedLocked(int userId, ArrayList<AccessibilityServiceConnection> boundServices)722     public void onBoundServicesChangedLocked(int userId,
723             ArrayList<AccessibilityServiceConnection> boundServices) {
724         if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) {
725             return;
726         }
727 
728         ArraySet<ComponentName> tempNonA11yCategoryServices = new ArraySet<>();
729         for (int i = 0; i < boundServices.size(); i++) {
730             final AccessibilityServiceInfo a11yServiceInfo = boundServices.get(
731                     i).getServiceInfo();
732             final ComponentName service = a11yServiceInfo.getComponentName().clone();
733             if (!a11yServiceInfo.isAccessibilityTool()) {
734                 tempNonA11yCategoryServices.add(service);
735                 if (mNonA11yCategoryServices.contains(service)) {
736                     mNonA11yCategoryServices.remove(service);
737                 } else {
738                     if (mSendNonA11yToolNotificationEnabled) {
739                         mPolicyWarningUIController.onNonA11yCategoryServiceBound(userId, service);
740                     }
741                 }
742             }
743         }
744 
745         for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
746             final ComponentName service = mNonA11yCategoryServices.valueAt(i);
747             mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(userId, service);
748         }
749         mNonA11yCategoryServices.clear();
750         mNonA11yCategoryServices.addAll(tempNonA11yCategoryServices);
751     }
752 
753     /**
754      * Called after switching to another user. Resets data and cancels old alarms after
755      * switching to another user.
756      *
757      * @param userId          The user id
758      * @param enabledServices The enabled services
759      */
onSwitchUserLocked(int userId, Set<ComponentName> enabledServices)760     public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) {
761         if (mCurrentUserId == userId) {
762             return;
763         }
764         mPolicyWarningUIController.onSwitchUser(userId,
765                 new ArraySet<>(enabledServices));
766 
767         for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
768             mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(mCurrentUserId,
769                     mNonA11yCategoryServices.valueAt(i));
770         }
771         mNonA11yCategoryServices.clear();
772         mCurrentUserId = userId;
773     }
774 
775     /**
776      * Called after the enabled accessibility services changed.
777      *
778      * @param userId          The user id
779      * @param enabledServices The enabled services
780      */
onEnabledServicesChangedLocked(int userId, Set<ComponentName> enabledServices)781     public void onEnabledServicesChangedLocked(int userId, Set<ComponentName> enabledServices) {
782         if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) {
783             return;
784         }
785         mPolicyWarningUIController.onEnabledServicesChanged(userId,
786                 new ArraySet<>(enabledServices));
787     }
788 }
789