• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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