1 /*
2  * Copyright (C) 2009 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 android.accessibilityservice;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.content.pm.ResolveInfo;
24 import android.content.pm.ServiceInfo;
25 import android.content.res.Resources;
26 import android.content.res.TypedArray;
27 import android.content.res.XmlResourceParser;
28 import android.os.Build;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.util.AttributeSet;
32 import android.util.SparseArray;
33 import android.util.TypedValue;
34 import android.util.Xml;
35 import android.view.View;
36 import android.view.accessibility.AccessibilityEvent;
37 import android.view.accessibility.AccessibilityNodeInfo;
38 
39 import org.xmlpull.v1.XmlPullParser;
40 import org.xmlpull.v1.XmlPullParserException;
41 
42 import com.android.internal.R;
43 
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 
49 /**
50  * This class describes an {@link AccessibilityService}. The system notifies an
51  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
52  * according to the information encapsulated in this class.
53  *
54  * <div class="special reference">
55  * <h3>Developer Guides</h3>
56  * <p>For more information about creating AccessibilityServices, read the
57  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
58  * developer guide.</p>
59  * </div>
60  *
61  * @attr ref android.R.styleable#AccessibilityService_accessibilityEventTypes
62  * @attr ref android.R.styleable#AccessibilityService_accessibilityFeedbackType
63  * @attr ref android.R.styleable#AccessibilityService_accessibilityFlags
64  * @attr ref android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
65  * @attr ref android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
66  * @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
67  * @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent
68  * @attr ref android.R.styleable#AccessibilityService_description
69  * @attr ref android.R.styleable#AccessibilityService_notificationTimeout
70  * @attr ref android.R.styleable#AccessibilityService_packageNames
71  * @attr ref android.R.styleable#AccessibilityService_settingsActivity
72  *
73  * @see AccessibilityService
74  * @see android.view.accessibility.AccessibilityEvent
75  * @see android.view.accessibility.AccessibilityManager
76  */
77 public class AccessibilityServiceInfo implements Parcelable {
78 
79     private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service";
80 
81     /**
82      * Capability: This accessibility service can retrieve the active window content.
83      * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
84      */
85     public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001;
86 
87     /**
88      * Capability: This accessibility service can request touch exploration mode in which
89      * touched items are spoken aloud and the UI can be explored via gestures.
90      * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
91      */
92     public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002;
93 
94     /**
95      * Capability: This accessibility service can request enhanced web accessibility
96      * enhancements. For example, installing scripts to make app content more accessible.
97      * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
98      */
99     public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004;
100 
101     /**
102      * Capability: This accessibility service can request to filter the key event stream.
103      * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
104      */
105     public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 0x00000008;
106 
107     private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos =
108             new SparseArray<CapabilityInfo>();
109     static {
sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT, new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT, R.string.capability_title_canRetrieveWindowContent, R.string.capability_desc_canRetrieveWindowContent))110         sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
111                 new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
112                         R.string.capability_title_canRetrieveWindowContent,
113                         R.string.capability_desc_canRetrieveWindowContent));
sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION, new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION, R.string.capability_title_canRequestTouchExploration, R.string.capability_desc_canRequestTouchExploration))114         sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
115                 new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
116                         R.string.capability_title_canRequestTouchExploration,
117                         R.string.capability_desc_canRequestTouchExploration));
sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, new CapabilityInfo(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, R.string.capability_title_canRequestEnhancedWebAccessibility, R.string.capability_desc_canRequestEnhancedWebAccessibility))118         sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
119                 new CapabilityInfo(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
120                         R.string.capability_title_canRequestEnhancedWebAccessibility,
121                         R.string.capability_desc_canRequestEnhancedWebAccessibility));
sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS, new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS, R.string.capability_title_canRequestFilterKeyEvents, R.string.capability_desc_canRequestFilterKeyEvents))122         sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
123                 new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
124                         R.string.capability_title_canRequestFilterKeyEvents,
125                         R.string.capability_desc_canRequestFilterKeyEvents));
126     }
127 
128     /**
129      * Denotes spoken feedback.
130      */
131     public static final int FEEDBACK_SPOKEN = 0x0000001;
132 
133     /**
134      * Denotes haptic feedback.
135      */
136     public static final int FEEDBACK_HAPTIC =  0x0000002;
137 
138     /**
139      * Denotes audible (not spoken) feedback.
140      */
141     public static final int FEEDBACK_AUDIBLE = 0x0000004;
142 
143     /**
144      * Denotes visual feedback.
145      */
146     public static final int FEEDBACK_VISUAL = 0x0000008;
147 
148     /**
149      * Denotes generic feedback.
150      */
151     public static final int FEEDBACK_GENERIC = 0x0000010;
152 
153     /**
154      * Denotes braille feedback.
155      */
156     public static final int FEEDBACK_BRAILLE = 0x0000020;
157 
158     /**
159      * Mask for all feedback types.
160      *
161      * @see #FEEDBACK_SPOKEN
162      * @see #FEEDBACK_HAPTIC
163      * @see #FEEDBACK_AUDIBLE
164      * @see #FEEDBACK_VISUAL
165      * @see #FEEDBACK_GENERIC
166      * @see #FEEDBACK_BRAILLE
167      */
168     public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
169 
170     /**
171      * If an {@link AccessibilityService} is the default for a given type.
172      * Default service is invoked only if no package specific one exists. In case of
173      * more than one package specific service only the earlier registered is notified.
174      */
175     public static final int DEFAULT = 0x0000001;
176 
177     /**
178      * If this flag is set the system will regard views that are not important
179      * for accessibility in addition to the ones that are important for accessibility.
180      * That is, views that are marked as not important for accessibility via
181      * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} or
182      * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are
183      * marked as potentially important for accessibility via
184      * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
185      * that are not important for accessibility, are reported while querying the window
186      * content and also the accessibility service will receive accessibility events from
187      * them.
188      * <p>
189      * <strong>Note:</strong> For accessibility services targeting API version
190      * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
191      * set for the system to regard views that are not important for accessibility. For
192      * accessibility services targeting API version lower than
193      * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
194      * regarded for accessibility purposes.
195      * </p>
196      * <p>
197      * Usually views not important for accessibility are layout managers that do not
198      * react to user actions, do not draw any content, and do not have any special
199      * semantics in the context of the screen content. For example, a three by three
200      * grid can be implemented as three horizontal linear layouts and one vertical,
201      * or three vertical linear layouts and one horizontal, or one grid layout, etc.
202      * In this context the actual layout mangers used to achieve the grid configuration
203      * are not important, rather it is important that there are nine evenly distributed
204      * elements.
205      * </p>
206      */
207     public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
208 
209     /**
210      * This flag requests that the system gets into touch exploration mode.
211      * In this mode a single finger moving on the screen behaves as a mouse
212      * pointer hovering over the user interface. The system will also detect
213      * certain gestures performed on the touch screen and notify this service.
214      * The system will enable touch exploration mode if there is at least one
215      * accessibility service that has this flag set. Hence, clearing this
216      * flag does not guarantee that the device will not be in touch exploration
217      * mode since there may be another enabled service that requested it.
218      * <p>
219      * For accessibility services targeting API version higher than
220      * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
221      * this flag have to declare this capability in their meta-data by setting
222      * the attribute {@link android.R.attr#canRequestTouchExplorationMode
223      * canRequestTouchExplorationMode} to true, otherwise this flag will
224      * be ignored. For how to declare the meta-data of a service refer to
225      * {@value AccessibilityService#SERVICE_META_DATA}.
226      * </p>
227      * <p>
228      * Services targeting API version equal to or lower than
229      * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
230      * the first time they are run, if this flag is specified, a dialog is
231      * shown to the user to confirm enabling explore by touch.
232      * </p>
233      * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
234      */
235     public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
236 
237     /**
238      * This flag requests from the system to enable web accessibility enhancing
239      * extensions. Such extensions aim to provide improved accessibility support
240      * for content presented in a {@link android.webkit.WebView}. An example of such
241      * an extension is injecting JavaScript from a secure source. The system will enable
242      * enhanced web accessibility if there is at least one accessibility service
243      * that has this flag set. Hence, clearing this flag does not guarantee that the
244      * device will not have enhanced web accessibility enabled since there may be
245      * another enabled service that requested it.
246      * <p>
247      * Services that want to set this flag have to declare this capability
248      * in their meta-data by setting the attribute {@link android.R.attr
249      * #canRequestEnhancedWebAccessibility canRequestEnhancedWebAccessibility} to
250      * true, otherwise this flag will be ignored. For how to declare the meta-data
251      * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
252      * </p>
253      * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
254      */
255     public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
256 
257     /**
258      * This flag requests that the {@link AccessibilityNodeInfo}s obtained
259      * by an {@link AccessibilityService} contain the id of the source view.
260      * The source view id will be a fully qualified resource name of the
261      * form "package:id/name", for example "foo.bar:id/my_list", and it is
262      * useful for UI test automation. This flag is not set by default.
263      */
264     public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
265 
266     /**
267      * This flag requests from the system to filter key events. If this flag
268      * is set the accessibility service will receive the key events before
269      * applications allowing it implement global shortcuts. Setting this flag
270      * does not guarantee that this service will filter key events since only
271      * one service can do so at any given time. This avoids user confusion due
272      * to behavior change in case different key filtering services are enabled.
273      * If there is already another key filtering service enabled, this one will
274      * not receive key events.
275      * <p>
276      * Services that want to set this flag have to declare this capability
277      * in their meta-data by setting the attribute {@link android.R.attr
278      * #canRequestFilterKeyEvents canRequestFilterKeyEvents} to true,
279      * otherwise this flag will be ignored. For how to declare the meta-data
280      * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
281      * </p>
282      * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
283      */
284     public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
285 
286     /**
287      * This flag indicates to the system that the accessibility service wants
288      * to access content of all interactive windows. An interactive window is a
289      * window that has input focus or can be touched by a sighted user when explore
290      * by touch is not enabled. If this flag is not set your service will not receive
291      * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED}
292      * events, calling AccessibilityService{@link AccessibilityService#getWindows()
293      * AccessibilityService.getWindows()} will return an empty list, and {@link
294      * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will
295      * return null.
296      * <p>
297      * Services that want to set this flag have to declare the capability
298      * to retrieve window content in their meta-data by setting the attribute
299      * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to
300      * true, otherwise this flag will be ignored. For how to declare the meta-data
301      * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
302      * </p>
303      * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
304      */
305     public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;
306 
307     /**
308      * The event types an {@link AccessibilityService} is interested in.
309      * <p>
310      *   <strong>Can be dynamically set at runtime.</strong>
311      * </p>
312      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
313      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
314      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
315      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
316      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
317      * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
318      * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
319      * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
320      * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
321      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
322      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
323      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
324      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
325      * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
326      * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START
327      * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END
328      * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT
329      * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START
330      * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END
331      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED
332      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
333      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
334      * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED
335      */
336     public int eventTypes;
337 
338     /**
339      * The package names an {@link AccessibilityService} is interested in. Setting
340      * to <code>null</code> is equivalent to all packages.
341      * <p>
342      *   <strong>Can be dynamically set at runtime.</strong>
343      * </p>
344      */
345     public String[] packageNames;
346 
347     /**
348      * The feedback type an {@link AccessibilityService} provides.
349      * <p>
350      *   <strong>Can be dynamically set at runtime.</strong>
351      * </p>
352      * @see #FEEDBACK_AUDIBLE
353      * @see #FEEDBACK_GENERIC
354      * @see #FEEDBACK_HAPTIC
355      * @see #FEEDBACK_SPOKEN
356      * @see #FEEDBACK_VISUAL
357      * @see #FEEDBACK_BRAILLE
358      */
359     public int feedbackType;
360 
361     /**
362      * The timeout after the most recent event of a given type before an
363      * {@link AccessibilityService} is notified.
364      * <p>
365      *   <strong>Can be dynamically set at runtime.</strong>.
366      * </p>
367      * <p>
368      * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
369      *       events to the client too frequently since this is accomplished via an expensive
370      *       interprocess call. One can think of the timeout as a criteria to determine when
371      *       event generation has settled down.
372      */
373     public long notificationTimeout;
374 
375     /**
376      * This field represents a set of flags used for configuring an
377      * {@link AccessibilityService}.
378      * <p>
379      *   <strong>Can be dynamically set at runtime.</strong>
380      * </p>
381      * @see #DEFAULT
382      * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
383      * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
384      * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
385      * @see #FLAG_REQUEST_FILTER_KEY_EVENTS
386      * @see #FLAG_REPORT_VIEW_IDS
387      * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
388      */
389     public int flags;
390 
391     /**
392      * The unique string Id to identify the accessibility service.
393      */
394     private String mId;
395 
396     /**
397      * The Service that implements this accessibility service component.
398      */
399     private ResolveInfo mResolveInfo;
400 
401     /**
402      * The accessibility service setting activity's name, used by the system
403      * settings to launch the setting activity of this accessibility service.
404      */
405     private String mSettingsActivityName;
406 
407     /**
408      * Bit mask with capabilities of this service.
409      */
410     private int mCapabilities;
411 
412     /**
413      * Resource id of the description of the accessibility service.
414      */
415     private int mDescriptionResId;
416 
417     /**
418      * Non localized description of the accessibility service.
419      */
420     private String mNonLocalizedDescription;
421 
422     /**
423      * Creates a new instance.
424      */
AccessibilityServiceInfo()425     public AccessibilityServiceInfo() {
426         /* do nothing */
427     }
428 
429     /**
430      * Creates a new instance.
431      *
432      * @param resolveInfo The service resolve info.
433      * @param context Context for accessing resources.
434      * @throws XmlPullParserException If a XML parsing error occurs.
435      * @throws IOException If a XML parsing error occurs.
436      *
437      * @hide
438      */
AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)439     public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
440             throws XmlPullParserException, IOException {
441         ServiceInfo serviceInfo = resolveInfo.serviceInfo;
442         mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
443         mResolveInfo = resolveInfo;
444 
445         XmlResourceParser parser = null;
446 
447         try {
448             PackageManager packageManager = context.getPackageManager();
449             parser = serviceInfo.loadXmlMetaData(packageManager,
450                     AccessibilityService.SERVICE_META_DATA);
451             if (parser == null) {
452                 return;
453             }
454 
455             int type = 0;
456             while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
457                 type = parser.next();
458             }
459 
460             String nodeName = parser.getName();
461             if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
462                 throw new XmlPullParserException( "Meta-data does not start with"
463                         + TAG_ACCESSIBILITY_SERVICE + " tag");
464             }
465 
466             AttributeSet allAttributes = Xml.asAttributeSet(parser);
467             Resources resources = packageManager.getResourcesForApplication(
468                     serviceInfo.applicationInfo);
469             TypedArray asAttributes = resources.obtainAttributes(allAttributes,
470                     com.android.internal.R.styleable.AccessibilityService);
471             eventTypes = asAttributes.getInt(
472                     com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
473                     0);
474             String packageNamez = asAttributes.getString(
475                     com.android.internal.R.styleable.AccessibilityService_packageNames);
476             if (packageNamez != null) {
477                 packageNames = packageNamez.split("(\\s)*,(\\s)*");
478             }
479             feedbackType = asAttributes.getInt(
480                     com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
481                     0);
482             notificationTimeout = asAttributes.getInt(
483                     com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
484                     0);
485             flags = asAttributes.getInt(
486                     com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
487             mSettingsActivityName = asAttributes.getString(
488                     com.android.internal.R.styleable.AccessibilityService_settingsActivity);
489             if (asAttributes.getBoolean(com.android.internal.R.styleable
490                     .AccessibilityService_canRetrieveWindowContent, false)) {
491                 mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
492             }
493             if (asAttributes.getBoolean(com.android.internal.R.styleable
494                     .AccessibilityService_canRequestTouchExplorationMode, false)) {
495                 mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION;
496             }
497             if (asAttributes.getBoolean(com.android.internal.R.styleable
498                         .AccessibilityService_canRequestEnhancedWebAccessibility, false)) {
499                     mCapabilities |= CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY;
500             }
501             if (asAttributes.getBoolean(com.android.internal.R.styleable
502                     .AccessibilityService_canRequestFilterKeyEvents, false)) {
503                 mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
504             }
505             TypedValue peekedValue = asAttributes.peekValue(
506                     com.android.internal.R.styleable.AccessibilityService_description);
507             if (peekedValue != null) {
508                 mDescriptionResId = peekedValue.resourceId;
509                 CharSequence nonLocalizedDescription = peekedValue.coerceToString();
510                 if (nonLocalizedDescription != null) {
511                     mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
512                 }
513             }
514             asAttributes.recycle();
515         } catch (NameNotFoundException e) {
516             throw new XmlPullParserException( "Unable to create context for: "
517                     + serviceInfo.packageName);
518         } finally {
519             if (parser != null) {
520                 parser.close();
521             }
522         }
523     }
524 
525     /**
526      * Updates the properties that an AccessibilitySerivice can change dynamically.
527      *
528      * @param other The info from which to update the properties.
529      *
530      * @hide
531      */
updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other)532     public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
533         eventTypes = other.eventTypes;
534         packageNames = other.packageNames;
535         feedbackType = other.feedbackType;
536         notificationTimeout = other.notificationTimeout;
537         flags = other.flags;
538     }
539 
540     /**
541      * @hide
542      */
setComponentName(ComponentName component)543     public void setComponentName(ComponentName component) {
544         mId = component.flattenToShortString();
545     }
546 
547     /**
548      * The accessibility service id.
549      * <p>
550      *   <strong>Generated by the system.</strong>
551      * </p>
552      * @return The id.
553      */
getId()554     public String getId() {
555         return mId;
556     }
557 
558     /**
559      * The service {@link ResolveInfo}.
560      * <p>
561      *   <strong>Generated by the system.</strong>
562      * </p>
563      * @return The info.
564      */
getResolveInfo()565     public ResolveInfo getResolveInfo() {
566         return mResolveInfo;
567     }
568 
569     /**
570      * The settings activity name.
571      * <p>
572      *    <strong>Statically set from
573      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
574      * </p>
575      * @return The settings activity name.
576      */
getSettingsActivityName()577     public String getSettingsActivityName() {
578         return mSettingsActivityName;
579     }
580 
581     /**
582      * Whether this service can retrieve the current window's content.
583      * <p>
584      *    <strong>Statically set from
585      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
586      * </p>
587      * @return True if window content can be retrieved.
588      *
589      * @deprecated Use {@link #getCapabilities()}.
590      */
getCanRetrieveWindowContent()591     public boolean getCanRetrieveWindowContent() {
592         return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
593     }
594 
595     /**
596      * Returns the bit mask of capabilities this accessibility service has such as
597      * being able to retrieve the active window content, etc.
598      *
599      * @return The capability bit mask.
600      *
601      * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
602      * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
603      * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
604      * @see #CAPABILITY_FILTER_KEY_EVENTS
605      */
getCapabilities()606     public int getCapabilities() {
607         return mCapabilities;
608     }
609 
610     /**
611      * Sets the bit mask of capabilities this accessibility service has such as
612      * being able to retrieve the active window content, etc.
613      *
614      * @param capabilities The capability bit mask.
615      *
616      * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
617      * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
618      * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
619      * @see #CAPABILITY_FILTER_KEY_EVENTS
620      *
621      * @hide
622      */
setCapabilities(int capabilities)623     public void setCapabilities(int capabilities) {
624         mCapabilities = capabilities;
625     }
626 
627     /**
628      * Gets the non-localized description of the accessibility service.
629      * <p>
630      *    <strong>Statically set from
631      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
632      * </p>
633      * @return The description.
634      *
635      * @deprecated Use {@link #loadDescription(PackageManager)}.
636      */
getDescription()637     public String getDescription() {
638         return mNonLocalizedDescription;
639     }
640 
641     /**
642      * The localized description of the accessibility service.
643      * <p>
644      *    <strong>Statically set from
645      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
646      * </p>
647      * @return The localized description.
648      */
loadDescription(PackageManager packageManager)649     public String loadDescription(PackageManager packageManager) {
650         if (mDescriptionResId == 0) {
651             return mNonLocalizedDescription;
652         }
653         ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
654         CharSequence description = packageManager.getText(serviceInfo.packageName,
655                 mDescriptionResId, serviceInfo.applicationInfo);
656         if (description != null) {
657             return description.toString().trim();
658         }
659         return null;
660     }
661 
662     /**
663      * {@inheritDoc}
664      */
describeContents()665     public int describeContents() {
666         return 0;
667     }
668 
writeToParcel(Parcel parcel, int flagz)669     public void writeToParcel(Parcel parcel, int flagz) {
670         parcel.writeInt(eventTypes);
671         parcel.writeStringArray(packageNames);
672         parcel.writeInt(feedbackType);
673         parcel.writeLong(notificationTimeout);
674         parcel.writeInt(flags);
675         parcel.writeString(mId);
676         parcel.writeParcelable(mResolveInfo, 0);
677         parcel.writeString(mSettingsActivityName);
678         parcel.writeInt(mCapabilities);
679         parcel.writeInt(mDescriptionResId);
680         parcel.writeString(mNonLocalizedDescription);
681     }
682 
initFromParcel(Parcel parcel)683     private void initFromParcel(Parcel parcel) {
684         eventTypes = parcel.readInt();
685         packageNames = parcel.readStringArray();
686         feedbackType = parcel.readInt();
687         notificationTimeout = parcel.readLong();
688         flags = parcel.readInt();
689         mId = parcel.readString();
690         mResolveInfo = parcel.readParcelable(null);
691         mSettingsActivityName = parcel.readString();
692         mCapabilities = parcel.readInt();
693         mDescriptionResId = parcel.readInt();
694         mNonLocalizedDescription = parcel.readString();
695     }
696 
697     @Override
hashCode()698     public int hashCode() {
699         return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
700     }
701 
702     @Override
equals(Object obj)703     public boolean equals(Object obj) {
704         if (this == obj) {
705             return true;
706         }
707         if (obj == null) {
708             return false;
709         }
710         if (getClass() != obj.getClass()) {
711             return false;
712         }
713         AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj;
714         if (mId == null) {
715             if (other.mId != null) {
716                 return false;
717             }
718         } else if (!mId.equals(other.mId)) {
719             return false;
720         }
721         return true;
722     }
723 
724     @Override
toString()725     public String toString() {
726         StringBuilder stringBuilder = new StringBuilder();
727         appendEventTypes(stringBuilder, eventTypes);
728         stringBuilder.append(", ");
729         appendPackageNames(stringBuilder, packageNames);
730         stringBuilder.append(", ");
731         appendFeedbackTypes(stringBuilder, feedbackType);
732         stringBuilder.append(", ");
733         stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
734         stringBuilder.append(", ");
735         appendFlags(stringBuilder, flags);
736         stringBuilder.append(", ");
737         stringBuilder.append("id: ").append(mId);
738         stringBuilder.append(", ");
739         stringBuilder.append("resolveInfo: ").append(mResolveInfo);
740         stringBuilder.append(", ");
741         stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
742         stringBuilder.append(", ");
743         appendCapabilities(stringBuilder, mCapabilities);
744         return stringBuilder.toString();
745     }
746 
appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes)747     private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
748         stringBuilder.append("feedbackTypes:");
749         stringBuilder.append("[");
750         while (feedbackTypes != 0) {
751             final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
752             stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
753             feedbackTypes &= ~feedbackTypeBit;
754             if (feedbackTypes != 0) {
755                 stringBuilder.append(", ");
756             }
757         }
758         stringBuilder.append("]");
759     }
760 
appendPackageNames(StringBuilder stringBuilder, String[] packageNames)761     private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
762         stringBuilder.append("packageNames:");
763         stringBuilder.append("[");
764         if (packageNames != null) {
765             final int packageNameCount = packageNames.length;
766             for (int i = 0; i < packageNameCount; i++) {
767                 stringBuilder.append(packageNames[i]);
768                 if (i < packageNameCount - 1) {
769                     stringBuilder.append(", ");
770                 }
771             }
772         }
773         stringBuilder.append("]");
774     }
775 
appendEventTypes(StringBuilder stringBuilder, int eventTypes)776     private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
777         stringBuilder.append("eventTypes:");
778         stringBuilder.append("[");
779         while (eventTypes != 0) {
780             final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
781             stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
782             eventTypes &= ~eventTypeBit;
783             if (eventTypes != 0) {
784                 stringBuilder.append(", ");
785             }
786         }
787         stringBuilder.append("]");
788     }
789 
appendFlags(StringBuilder stringBuilder, int flags)790     private static void appendFlags(StringBuilder stringBuilder, int flags) {
791         stringBuilder.append("flags:");
792         stringBuilder.append("[");
793         while (flags != 0) {
794             final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
795             stringBuilder.append(flagToString(flagBit));
796             flags &= ~flagBit;
797             if (flags != 0) {
798                 stringBuilder.append(", ");
799             }
800         }
801         stringBuilder.append("]");
802     }
803 
appendCapabilities(StringBuilder stringBuilder, int capabilities)804     private static void appendCapabilities(StringBuilder stringBuilder, int capabilities) {
805         stringBuilder.append("capabilities:");
806         stringBuilder.append("[");
807         while (capabilities != 0) {
808             final int capabilityBit = (1 << Integer.numberOfTrailingZeros(capabilities));
809             stringBuilder.append(capabilityToString(capabilityBit));
810             capabilities &= ~capabilityBit;
811             if (capabilities != 0) {
812                 stringBuilder.append(", ");
813             }
814         }
815         stringBuilder.append("]");
816     }
817 
818     /**
819      * Returns the string representation of a feedback type. For example,
820      * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
821      *
822      * @param feedbackType The feedback type.
823      * @return The string representation.
824      */
feedbackTypeToString(int feedbackType)825     public static String feedbackTypeToString(int feedbackType) {
826         StringBuilder builder = new StringBuilder();
827         builder.append("[");
828         while (feedbackType != 0) {
829             final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
830             feedbackType &= ~feedbackTypeFlag;
831             switch (feedbackTypeFlag) {
832                 case FEEDBACK_AUDIBLE:
833                     if (builder.length() > 1) {
834                         builder.append(", ");
835                     }
836                     builder.append("FEEDBACK_AUDIBLE");
837                     break;
838                 case FEEDBACK_HAPTIC:
839                     if (builder.length() > 1) {
840                         builder.append(", ");
841                     }
842                     builder.append("FEEDBACK_HAPTIC");
843                     break;
844                 case FEEDBACK_GENERIC:
845                     if (builder.length() > 1) {
846                         builder.append(", ");
847                     }
848                     builder.append("FEEDBACK_GENERIC");
849                     break;
850                 case FEEDBACK_SPOKEN:
851                     if (builder.length() > 1) {
852                         builder.append(", ");
853                     }
854                     builder.append("FEEDBACK_SPOKEN");
855                     break;
856                 case FEEDBACK_VISUAL:
857                     if (builder.length() > 1) {
858                         builder.append(", ");
859                     }
860                     builder.append("FEEDBACK_VISUAL");
861                     break;
862                 case FEEDBACK_BRAILLE:
863                     if (builder.length() > 1) {
864                         builder.append(", ");
865                     }
866                     builder.append("FEEDBACK_BRAILLE");
867                     break;
868             }
869         }
870         builder.append("]");
871         return builder.toString();
872     }
873 
874     /**
875      * Returns the string representation of a flag. For example,
876      * {@link #DEFAULT} is represented by the string DEFAULT.
877      *
878      * @param flag The flag.
879      * @return The string representation.
880      */
flagToString(int flag)881     public static String flagToString(int flag) {
882         switch (flag) {
883             case DEFAULT:
884                 return "DEFAULT";
885             case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
886                 return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
887             case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
888                 return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
889             case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
890                 return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
891             case FLAG_REPORT_VIEW_IDS:
892                 return "FLAG_REPORT_VIEW_IDS";
893             case FLAG_REQUEST_FILTER_KEY_EVENTS:
894                 return "FLAG_REQUEST_FILTER_KEY_EVENTS";
895             case FLAG_RETRIEVE_INTERACTIVE_WINDOWS:
896                 return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
897             default:
898                 return null;
899         }
900     }
901 
902     /**
903      * Returns the string representation of a capability. For example,
904      * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
905      * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
906      *
907      * @param capability The capability.
908      * @return The string representation.
909      */
capabilityToString(int capability)910     public static String capabilityToString(int capability) {
911         switch (capability) {
912             case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
913                 return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
914             case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
915                 return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
916             case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
917                 return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
918             case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS:
919                 return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
920             default:
921                 return "UNKNOWN";
922         }
923     }
924 
925     /**
926      * @hide
927      * @return The list of {@link CapabilityInfo} objects.
928      */
getCapabilityInfos()929     public List<CapabilityInfo> getCapabilityInfos() {
930         if (mCapabilities == 0) {
931             return Collections.emptyList();
932         }
933         int capabilities = mCapabilities;
934         List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>();
935         while (capabilities != 0) {
936             final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities);
937             capabilities &= ~capabilityBit;
938             CapabilityInfo capabilityInfo = sAvailableCapabilityInfos.get(capabilityBit);
939             if (capabilityInfo != null) {
940                 capabilityInfos.add(capabilityInfo);
941             }
942         }
943         return capabilityInfos;
944     }
945 
946     /**
947      * @hide
948      */
949     public static final class CapabilityInfo {
950         public final int capability;
951         public final int titleResId;
952         public final int descResId;
953 
CapabilityInfo(int capability, int titleResId, int descResId)954         public CapabilityInfo(int capability, int titleResId, int descResId) {
955             this.capability = capability;
956             this.titleResId = titleResId;
957             this.descResId = descResId;
958         }
959     }
960 
961     /**
962      * @see Parcelable.Creator
963      */
964     public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
965             new Parcelable.Creator<AccessibilityServiceInfo>() {
966         public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
967             AccessibilityServiceInfo info = new AccessibilityServiceInfo();
968             info.initFromParcel(parcel);
969             return info;
970         }
971 
972         public AccessibilityServiceInfo[] newArray(int size) {
973             return new AccessibilityServiceInfo[size];
974         }
975     };
976 }
977