1 /* 2 * Copyright (C) 2020 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.internal.accessibility.dialog; 18 19 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; 20 import static com.android.internal.accessibility.AccessibilityShortcutController.AUTOCLICK_COMPONENT_NAME; 21 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; 22 import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; 23 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; 24 import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME; 25 import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME; 26 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; 27 import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; 28 import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained; 29 import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE; 30 31 import android.accessibilityservice.AccessibilityServiceInfo; 32 import android.accessibilityservice.AccessibilityShortcutInfo; 33 import android.annotation.NonNull; 34 import android.app.ActivityManager; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.os.Build; 38 import android.os.UserHandle; 39 import android.provider.Settings; 40 import android.view.accessibility.AccessibilityManager; 41 42 import com.android.internal.R; 43 import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; 44 import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; 45 import com.android.internal.annotations.VisibleForTesting; 46 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.List; 50 51 /** 52 * Collection of utilities for accessibility target. 53 */ 54 public final class AccessibilityTargetHelper { AccessibilityTargetHelper()55 private AccessibilityTargetHelper() {} 56 57 /** 58 * Returns list of {@link AccessibilityTarget} of assigned accessibility shortcuts from 59 * {@link AccessibilityManager#getAccessibilityShortcutTargets} including accessibility 60 * feature's package name, component id, etc. 61 * 62 * @param context The context of the application. 63 * @param shortcutType The shortcut type. 64 * @return The list of {@link AccessibilityTarget}. 65 * @hide 66 */ getTargets(Context context, @UserShortcutType int shortcutType)67 public static List<AccessibilityTarget> getTargets(Context context, 68 @UserShortcutType int shortcutType) { 69 // List all accessibility target 70 final List<AccessibilityTarget> installedTargets = getInstalledTargets(context, 71 shortcutType); 72 73 // List accessibility shortcut target 74 final AccessibilityManager am = (AccessibilityManager) context.getSystemService( 75 Context.ACCESSIBILITY_SERVICE); 76 final List<String> assignedTargets = am.getAccessibilityShortcutTargets(shortcutType); 77 78 // Get the list of accessibility shortcut target in all accessibility target 79 final List<AccessibilityTarget> results = new ArrayList<>(); 80 for (String assignedTarget : assignedTargets) { 81 for (AccessibilityTarget installedTarget : installedTargets) { 82 if (!MAGNIFICATION_CONTROLLER_NAME.contentEquals(assignedTarget)) { 83 final ComponentName assignedTargetComponentName = 84 ComponentName.unflattenFromString(assignedTarget); 85 final ComponentName targetComponentName = ComponentName.unflattenFromString( 86 installedTarget.getId()); 87 if (assignedTargetComponentName.equals(targetComponentName)) { 88 results.add(installedTarget); 89 continue; 90 } 91 } 92 if (assignedTarget.contentEquals(installedTarget.getId())) { 93 results.add(installedTarget); 94 } 95 } 96 } 97 return results; 98 } 99 100 /** 101 * Returns list of {@link AccessibilityTarget} of the installed accessibility service, 102 * accessibility activity, and allowlisting feature including accessibility feature's package 103 * name, component id, etc. 104 * 105 * @param context The context of the application. 106 * @param shortcutType The shortcut type. 107 * @return The list of {@link AccessibilityTarget}. 108 * @hide 109 */ getInstalledTargets(Context context, @UserShortcutType int shortcutType)110 public static List<AccessibilityTarget> getInstalledTargets(Context context, 111 @UserShortcutType int shortcutType) { 112 final List<AccessibilityTarget> targets = new ArrayList<>(); 113 targets.addAll(getAccessibilityServiceTargets(context, shortcutType)); 114 targets.addAll(getAccessibilityActivityTargets(context, shortcutType)); 115 targets.addAll(getAllowListingFeatureTargets(context, shortcutType)); 116 117 return targets; 118 } 119 getAccessibilityServiceTargets(Context context, @UserShortcutType int shortcutType)120 private static List<AccessibilityTarget> getAccessibilityServiceTargets(Context context, 121 @UserShortcutType int shortcutType) { 122 final AccessibilityManager am = (AccessibilityManager) context.getSystemService( 123 Context.ACCESSIBILITY_SERVICE); 124 final List<AccessibilityServiceInfo> installedServices = 125 am.getInstalledAccessibilityServiceList(); 126 if (installedServices == null) { 127 return Collections.emptyList(); 128 } 129 130 final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size()); 131 for (AccessibilityServiceInfo info : installedServices) { 132 if (isValidServiceTarget(info, shortcutType)) { 133 targets.add(createAccessibilityServiceTarget(context, shortcutType, info)); 134 } 135 } 136 137 return targets; 138 } 139 140 /** 141 * Check for maintaining compatibility on prior versions. 142 * Determines if a given service should be accumulated in a list of installed services. 143 * @param info service info to check. 144 * @param shortcutType type of shortcut to accumulate a list for. 145 * @return {@code true} if the service should be added (always true past version Q), 146 * otherwise {@code false}. 147 */ 148 @VisibleForTesting isValidServiceTarget( AccessibilityServiceInfo info, @UserShortcutType int shortcutType)149 public static boolean isValidServiceTarget( 150 AccessibilityServiceInfo info, @UserShortcutType int shortcutType) { 151 final boolean hasRequestAccessibilityButtonFlag = 152 (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; 153 return (info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion 154 > Build.VERSION_CODES.Q) || hasRequestAccessibilityButtonFlag 155 || shortcutType != SOFTWARE; 156 } 157 getAccessibilityActivityTargets(Context context, @UserShortcutType int shortcutType)158 private static List<AccessibilityTarget> getAccessibilityActivityTargets(Context context, 159 @UserShortcutType int shortcutType) { 160 final AccessibilityManager am = (AccessibilityManager) context.getSystemService( 161 Context.ACCESSIBILITY_SERVICE); 162 final List<AccessibilityShortcutInfo> installedServices = 163 am.getInstalledAccessibilityShortcutListAsUser(context, 164 ActivityManager.getCurrentUser()); 165 if (installedServices == null) { 166 return Collections.emptyList(); 167 } 168 169 final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size()); 170 for (AccessibilityShortcutInfo info : installedServices) { 171 targets.add(new AccessibilityActivityTarget(context, shortcutType, info)); 172 } 173 174 return targets; 175 } 176 getAllowListingFeatureTargets(Context context, @UserShortcutType int shortcutType)177 private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context, 178 @UserShortcutType int shortcutType) { 179 final List<AccessibilityTarget> targets = new ArrayList<>(); 180 final int uid = context.getApplicationInfo().uid; 181 182 final InvisibleToggleAllowListingFeatureTarget magnification = 183 new InvisibleToggleAllowListingFeatureTarget(context, 184 shortcutType, 185 isShortcutContained( 186 context, shortcutType, MAGNIFICATION_CONTROLLER_NAME), 187 MAGNIFICATION_CONTROLLER_NAME, 188 uid, 189 context.getString(R.string.accessibility_magnification_chooser_text), 190 context.getDrawable(R.drawable.ic_accessibility_magnification), 191 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); 192 targets.add(magnification); 193 194 final ToggleAllowListingFeatureTarget daltonizer = 195 new ToggleAllowListingFeatureTarget(context, 196 shortcutType, 197 isShortcutContained(context, shortcutType, 198 DALTONIZER_COMPONENT_NAME.flattenToString()), 199 DALTONIZER_COMPONENT_NAME.flattenToString(), 200 uid, 201 context.getString(R.string.color_correction_feature_name), 202 context.getDrawable(R.drawable.ic_accessibility_color_correction), 203 Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); 204 targets.add(daltonizer); 205 206 final ToggleAllowListingFeatureTarget colorInversion = 207 new ToggleAllowListingFeatureTarget(context, 208 shortcutType, 209 isShortcutContained(context, shortcutType, 210 COLOR_INVERSION_COMPONENT_NAME.flattenToString()), 211 COLOR_INVERSION_COMPONENT_NAME.flattenToString(), 212 uid, 213 context.getString(R.string.color_inversion_feature_name), 214 context.getDrawable(R.drawable.ic_accessibility_color_inversion), 215 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); 216 targets.add(colorInversion); 217 218 final ToggleAllowListingFeatureTarget autoclick = 219 new ToggleAllowListingFeatureTarget(context, 220 shortcutType, 221 isShortcutContained(context, shortcutType, 222 AUTOCLICK_COMPONENT_NAME.flattenToString()), 223 AUTOCLICK_COMPONENT_NAME.flattenToString(), 224 uid, 225 context.getString(R.string.autoclick_feature_name), 226 context.getDrawable(R.drawable.ic_accessibility_autoclick), 227 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED); 228 targets.add(autoclick); 229 230 if (SUPPORT_ONE_HANDED_MODE) { 231 final ToggleAllowListingFeatureTarget oneHandedMode = 232 new ToggleAllowListingFeatureTarget(context, 233 shortcutType, 234 isShortcutContained(context, shortcutType, 235 ONE_HANDED_COMPONENT_NAME.flattenToString()), 236 ONE_HANDED_COMPONENT_NAME.flattenToString(), 237 uid, 238 context.getString(R.string.one_handed_mode_feature_name), 239 context.getDrawable(R.drawable.ic_accessibility_one_handed), 240 Settings.Secure.ONE_HANDED_MODE_ACTIVATED); 241 targets.add(oneHandedMode); 242 } 243 244 final ToggleAllowListingFeatureTarget reduceBrightColors = 245 new ToggleAllowListingFeatureTarget(context, 246 shortcutType, 247 isShortcutContained(context, shortcutType, 248 REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()), 249 REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(), 250 uid, 251 context.getString(R.string.reduce_bright_colors_feature_name), 252 context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors), 253 Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED); 254 targets.add(reduceBrightColors); 255 256 final InvisibleToggleAllowListingFeatureTarget hearingAids = 257 new InvisibleToggleAllowListingFeatureTarget(context, 258 shortcutType, 259 isShortcutContained(context, shortcutType, 260 ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()), 261 ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString(), 262 uid, 263 context.getString(R.string.hearing_aids_feature_name), 264 context.getDrawable(R.drawable.ic_accessibility_hearing_aid), 265 /* key= */ null); 266 targets.add(hearingAids); 267 268 return targets; 269 } 270 createAccessibilityServiceTarget(Context context, @UserShortcutType int shortcutType, @NonNull AccessibilityServiceInfo info)271 private static AccessibilityTarget createAccessibilityServiceTarget(Context context, 272 @UserShortcutType int shortcutType, @NonNull AccessibilityServiceInfo info) { 273 switch (getAccessibilityServiceFragmentType(info)) { 274 case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE: 275 return new VolumeShortcutToggleAccessibilityServiceTarget(context, shortcutType, 276 info); 277 case AccessibilityFragmentType.INVISIBLE_TOGGLE: 278 return new InvisibleToggleAccessibilityServiceTarget(context, shortcutType, info); 279 case AccessibilityFragmentType.TOGGLE: 280 return new ToggleAccessibilityServiceTarget(context, shortcutType, info); 281 default: 282 throw new IllegalStateException("Unexpected fragment type"); 283 } 284 } 285 286 /** 287 * Determines if the{@link AccessibilityTarget} is allowed. 288 */ isAccessibilityTargetAllowed(Context context, String packageName, int uid)289 public static boolean isAccessibilityTargetAllowed(Context context, String packageName, 290 int uid) { 291 final AccessibilityManager am = context.getSystemService(AccessibilityManager.class); 292 return am.isAccessibilityTargetAllowed(packageName, uid, UserHandle.myUserId()); 293 } 294 295 /** 296 * Sends restricted dialog intent if the accessibility target is disallowed. 297 */ sendRestrictedDialogIntent(Context context, String packageName, int uid)298 public static boolean sendRestrictedDialogIntent(Context context, String packageName, int uid) { 299 final AccessibilityManager am = context.getSystemService(AccessibilityManager.class); 300 return am.sendRestrictedDialogIntent(packageName, uid, UserHandle.myUserId()); 301 } 302 } 303