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