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