AccessibilityServiceInfo.java revision 23be199171bedb05e81864f8941492af066fa667
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
17package android.accessibilityservice;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.content.pm.PackageManager.NameNotFoundException;
23import android.content.pm.ResolveInfo;
24import android.content.pm.ServiceInfo;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.content.res.XmlResourceParser;
28import android.os.Build;
29import android.os.Parcel;
30import android.os.Parcelable;
31import android.util.AttributeSet;
32import android.util.SparseArray;
33import android.util.TypedValue;
34import android.util.Xml;
35import android.view.View;
36import android.view.accessibility.AccessibilityEvent;
37import android.view.accessibility.AccessibilityNodeInfo;
38
39import org.xmlpull.v1.XmlPullParser;
40import org.xmlpull.v1.XmlPullParserException;
41
42import com.android.internal.R;
43
44import java.io.IOException;
45import java.util.ArrayList;
46import java.util.Collections;
47import 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 */
77public 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 {
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));
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));
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));
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     * The event types an {@link AccessibilityService} is interested in.
288     * <p>
289     *   <strong>Can be dynamically set at runtime.</strong>
290     * </p>
291     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
292     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
293     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
294     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
295     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
296     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
297     * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
298     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
299     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
300     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
301     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
302     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
303     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
304     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
305     */
306    public int eventTypes;
307
308    /**
309     * The package names an {@link AccessibilityService} is interested in. Setting
310     * to <code>null</code> is equivalent to all packages.
311     * <p>
312     *   <strong>Can be dynamically set at runtime.</strong>
313     * </p>
314     */
315    public String[] packageNames;
316
317    /**
318     * The feedback type an {@link AccessibilityService} provides.
319     * <p>
320     *   <strong>Can be dynamically set at runtime.</strong>
321     * </p>
322     * @see #FEEDBACK_AUDIBLE
323     * @see #FEEDBACK_GENERIC
324     * @see #FEEDBACK_HAPTIC
325     * @see #FEEDBACK_SPOKEN
326     * @see #FEEDBACK_VISUAL
327     * @see #FEEDBACK_BRAILLE
328     */
329    public int feedbackType;
330
331    /**
332     * The timeout after the most recent event of a given type before an
333     * {@link AccessibilityService} is notified.
334     * <p>
335     *   <strong>Can be dynamically set at runtime.</strong>.
336     * </p>
337     * <p>
338     * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
339     *       events to the client too frequently since this is accomplished via an expensive
340     *       interprocess call. One can think of the timeout as a criteria to determine when
341     *       event generation has settled down.
342     */
343    public long notificationTimeout;
344
345    /**
346     * This field represents a set of flags used for configuring an
347     * {@link AccessibilityService}.
348     * <p>
349     *   <strong>Can be dynamically set at runtime.</strong>
350     * </p>
351     * @see #DEFAULT
352     * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
353     * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
354     * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
355     * @see #FLAG_REQUEST_FILTER_KEY_EVENTS
356     * @see #FLAG_REPORT_VIEW_IDS
357     */
358    public int flags;
359
360    /**
361     * The unique string Id to identify the accessibility service.
362     */
363    private String mId;
364
365    /**
366     * The Service that implements this accessibility service component.
367     */
368    private ResolveInfo mResolveInfo;
369
370    /**
371     * The accessibility service setting activity's name, used by the system
372     * settings to launch the setting activity of this accessibility service.
373     */
374    private String mSettingsActivityName;
375
376    /**
377     * Bit mask with capabilities of this service.
378     */
379    private int mCapabilities;
380
381    /**
382     * Resource id of the description of the accessibility service.
383     */
384    private int mDescriptionResId;
385
386    /**
387     * Non localized description of the accessibility service.
388     */
389    private String mNonLocalizedDescription;
390
391    /**
392     * Creates a new instance.
393     */
394    public AccessibilityServiceInfo() {
395        /* do nothing */
396    }
397
398    /**
399     * Creates a new instance.
400     *
401     * @param resolveInfo The service resolve info.
402     * @param context Context for accessing resources.
403     * @throws XmlPullParserException If a XML parsing error occurs.
404     * @throws IOException If a XML parsing error occurs.
405     *
406     * @hide
407     */
408    public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
409            throws XmlPullParserException, IOException {
410        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
411        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
412        mResolveInfo = resolveInfo;
413
414        XmlResourceParser parser = null;
415
416        try {
417            PackageManager packageManager = context.getPackageManager();
418            parser = serviceInfo.loadXmlMetaData(packageManager,
419                    AccessibilityService.SERVICE_META_DATA);
420            if (parser == null) {
421                return;
422            }
423
424            int type = 0;
425            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
426                type = parser.next();
427            }
428
429            String nodeName = parser.getName();
430            if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
431                throw new XmlPullParserException( "Meta-data does not start with"
432                        + TAG_ACCESSIBILITY_SERVICE + " tag");
433            }
434
435            AttributeSet allAttributes = Xml.asAttributeSet(parser);
436            Resources resources = packageManager.getResourcesForApplication(
437                    serviceInfo.applicationInfo);
438            TypedArray asAttributes = resources.obtainAttributes(allAttributes,
439                    com.android.internal.R.styleable.AccessibilityService);
440            eventTypes = asAttributes.getInt(
441                    com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
442                    0);
443            String packageNamez = asAttributes.getString(
444                    com.android.internal.R.styleable.AccessibilityService_packageNames);
445            if (packageNamez != null) {
446                packageNames = packageNamez.split("(\\s)*,(\\s)*");
447            }
448            feedbackType = asAttributes.getInt(
449                    com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
450                    0);
451            notificationTimeout = asAttributes.getInt(
452                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
453                    0);
454            flags = asAttributes.getInt(
455                    com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
456            mSettingsActivityName = asAttributes.getString(
457                    com.android.internal.R.styleable.AccessibilityService_settingsActivity);
458            if (asAttributes.getBoolean(com.android.internal.R.styleable
459                    .AccessibilityService_canRetrieveWindowContent, false)) {
460                mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
461            }
462            if (asAttributes.getBoolean(com.android.internal.R.styleable
463                    .AccessibilityService_canRequestTouchExplorationMode, false)) {
464                mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION;
465            }
466            if (asAttributes.getBoolean(com.android.internal.R.styleable
467                        .AccessibilityService_canRequestEnhancedWebAccessibility, false)) {
468                    mCapabilities |= CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY;
469            }
470            if (asAttributes.getBoolean(com.android.internal.R.styleable
471                    .AccessibilityService_canRequestFilterKeyEvents, false)) {
472                mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
473            }
474            TypedValue peekedValue = asAttributes.peekValue(
475                    com.android.internal.R.styleable.AccessibilityService_description);
476            if (peekedValue != null) {
477                mDescriptionResId = peekedValue.resourceId;
478                CharSequence nonLocalizedDescription = peekedValue.coerceToString();
479                if (nonLocalizedDescription != null) {
480                    mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
481                }
482            }
483            asAttributes.recycle();
484        } catch (NameNotFoundException e) {
485            throw new XmlPullParserException( "Unable to create context for: "
486                    + serviceInfo.packageName);
487        } finally {
488            if (parser != null) {
489                parser.close();
490            }
491        }
492    }
493
494    /**
495     * Updates the properties that an AccessibilitySerivice can change dynamically.
496     *
497     * @param other The info from which to update the properties.
498     *
499     * @hide
500     */
501    public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
502        eventTypes = other.eventTypes;
503        packageNames = other.packageNames;
504        feedbackType = other.feedbackType;
505        notificationTimeout = other.notificationTimeout;
506        flags = other.flags;
507    }
508
509    /**
510     * @hide
511     */
512    public void setComponentName(ComponentName component) {
513        mId = component.flattenToShortString();
514    }
515
516    /**
517     * The accessibility service id.
518     * <p>
519     *   <strong>Generated by the system.</strong>
520     * </p>
521     * @return The id.
522     */
523    public String getId() {
524        return mId;
525    }
526
527    /**
528     * The service {@link ResolveInfo}.
529     * <p>
530     *   <strong>Generated by the system.</strong>
531     * </p>
532     * @return The info.
533     */
534    public ResolveInfo getResolveInfo() {
535        return mResolveInfo;
536    }
537
538    /**
539     * The settings activity name.
540     * <p>
541     *    <strong>Statically set from
542     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
543     * </p>
544     * @return The settings activity name.
545     */
546    public String getSettingsActivityName() {
547        return mSettingsActivityName;
548    }
549
550    /**
551     * Whether this service can retrieve the current window's content.
552     * <p>
553     *    <strong>Statically set from
554     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
555     * </p>
556     * @return True if window content can be retrieved.
557     *
558     * @deprecated Use {@link #getCapabilities()}.
559     */
560    public boolean getCanRetrieveWindowContent() {
561        return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
562    }
563
564    /**
565     * Returns the bit mask of capabilities this accessibility service has such as
566     * being able to retrieve the active window content, etc.
567     *
568     * @return The capability bit mask.
569     *
570     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
571     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
572     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
573     * @see #CAPABILITY_FILTER_KEY_EVENTS
574     */
575    public int getCapabilities() {
576        return mCapabilities;
577    }
578
579    /**
580     * Sets the bit mask of capabilities this accessibility service has such as
581     * being able to retrieve the active window content, etc.
582     *
583     * @param capabilities The capability bit mask.
584     *
585     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
586     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
587     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
588     * @see #CAPABILITY_FILTER_KEY_EVENTS
589     *
590     * @hide
591     */
592    public void setCapabilities(int capabilities) {
593        mCapabilities = capabilities;
594    }
595
596    /**
597     * Gets the non-localized description of the accessibility service.
598     * <p>
599     *    <strong>Statically set from
600     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
601     * </p>
602     * @return The description.
603     *
604     * @deprecated Use {@link #loadDescription(PackageManager)}.
605     */
606    public String getDescription() {
607        return mNonLocalizedDescription;
608    }
609
610    /**
611     * The localized description of the accessibility service.
612     * <p>
613     *    <strong>Statically set from
614     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
615     * </p>
616     * @return The localized description.
617     */
618    public String loadDescription(PackageManager packageManager) {
619        if (mDescriptionResId == 0) {
620            return mNonLocalizedDescription;
621        }
622        ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
623        CharSequence description = packageManager.getText(serviceInfo.packageName,
624                mDescriptionResId, serviceInfo.applicationInfo);
625        if (description != null) {
626            return description.toString().trim();
627        }
628        return null;
629    }
630
631    /**
632     * {@inheritDoc}
633     */
634    public int describeContents() {
635        return 0;
636    }
637
638    public void writeToParcel(Parcel parcel, int flagz) {
639        parcel.writeInt(eventTypes);
640        parcel.writeStringArray(packageNames);
641        parcel.writeInt(feedbackType);
642        parcel.writeLong(notificationTimeout);
643        parcel.writeInt(flags);
644        parcel.writeString(mId);
645        parcel.writeParcelable(mResolveInfo, 0);
646        parcel.writeString(mSettingsActivityName);
647        parcel.writeInt(mCapabilities);
648        parcel.writeInt(mDescriptionResId);
649        parcel.writeString(mNonLocalizedDescription);
650    }
651
652    private void initFromParcel(Parcel parcel) {
653        eventTypes = parcel.readInt();
654        packageNames = parcel.readStringArray();
655        feedbackType = parcel.readInt();
656        notificationTimeout = parcel.readLong();
657        flags = parcel.readInt();
658        mId = parcel.readString();
659        mResolveInfo = parcel.readParcelable(null);
660        mSettingsActivityName = parcel.readString();
661        mCapabilities = parcel.readInt();
662        mDescriptionResId = parcel.readInt();
663        mNonLocalizedDescription = parcel.readString();
664    }
665
666    @Override
667    public int hashCode() {
668        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
669    }
670
671    @Override
672    public boolean equals(Object obj) {
673        if (this == obj) {
674            return true;
675        }
676        if (obj == null) {
677            return false;
678        }
679        if (getClass() != obj.getClass()) {
680            return false;
681        }
682        AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj;
683        if (mId == null) {
684            if (other.mId != null) {
685                return false;
686            }
687        } else if (!mId.equals(other.mId)) {
688            return false;
689        }
690        return true;
691    }
692
693    @Override
694    public String toString() {
695        StringBuilder stringBuilder = new StringBuilder();
696        appendEventTypes(stringBuilder, eventTypes);
697        stringBuilder.append(", ");
698        appendPackageNames(stringBuilder, packageNames);
699        stringBuilder.append(", ");
700        appendFeedbackTypes(stringBuilder, feedbackType);
701        stringBuilder.append(", ");
702        stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
703        stringBuilder.append(", ");
704        appendFlags(stringBuilder, flags);
705        stringBuilder.append(", ");
706        stringBuilder.append("id: ").append(mId);
707        stringBuilder.append(", ");
708        stringBuilder.append("resolveInfo: ").append(mResolveInfo);
709        stringBuilder.append(", ");
710        stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
711        stringBuilder.append(", ");
712        appendCapabilities(stringBuilder, mCapabilities);
713        return stringBuilder.toString();
714    }
715
716    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
717        stringBuilder.append("feedbackTypes:");
718        stringBuilder.append("[");
719        while (feedbackTypes != 0) {
720            final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
721            stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
722            feedbackTypes &= ~feedbackTypeBit;
723            if (feedbackTypes != 0) {
724                stringBuilder.append(", ");
725            }
726        }
727        stringBuilder.append("]");
728    }
729
730    private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
731        stringBuilder.append("packageNames:");
732        stringBuilder.append("[");
733        if (packageNames != null) {
734            final int packageNameCount = packageNames.length;
735            for (int i = 0; i < packageNameCount; i++) {
736                stringBuilder.append(packageNames[i]);
737                if (i < packageNameCount - 1) {
738                    stringBuilder.append(", ");
739                }
740            }
741        }
742        stringBuilder.append("]");
743    }
744
745    private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
746        stringBuilder.append("eventTypes:");
747        stringBuilder.append("[");
748        while (eventTypes != 0) {
749            final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
750            stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
751            eventTypes &= ~eventTypeBit;
752            if (eventTypes != 0) {
753                stringBuilder.append(", ");
754            }
755        }
756        stringBuilder.append("]");
757    }
758
759    private static void appendFlags(StringBuilder stringBuilder, int flags) {
760        stringBuilder.append("flags:");
761        stringBuilder.append("[");
762        while (flags != 0) {
763            final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
764            stringBuilder.append(flagToString(flagBit));
765            flags &= ~flagBit;
766            if (flags != 0) {
767                stringBuilder.append(", ");
768            }
769        }
770        stringBuilder.append("]");
771    }
772
773    private static void appendCapabilities(StringBuilder stringBuilder, int capabilities) {
774        stringBuilder.append("capabilities:");
775        stringBuilder.append("[");
776        while (capabilities != 0) {
777            final int capabilityBit = (1 << Integer.numberOfTrailingZeros(capabilities));
778            stringBuilder.append(capabilityToString(capabilityBit));
779            capabilities &= ~capabilityBit;
780            if (capabilities != 0) {
781                stringBuilder.append(", ");
782            }
783        }
784        stringBuilder.append("]");
785    }
786
787    /**
788     * Returns the string representation of a feedback type. For example,
789     * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
790     *
791     * @param feedbackType The feedback type.
792     * @return The string representation.
793     */
794    public static String feedbackTypeToString(int feedbackType) {
795        StringBuilder builder = new StringBuilder();
796        builder.append("[");
797        while (feedbackType != 0) {
798            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
799            feedbackType &= ~feedbackTypeFlag;
800            switch (feedbackTypeFlag) {
801                case FEEDBACK_AUDIBLE:
802                    if (builder.length() > 1) {
803                        builder.append(", ");
804                    }
805                    builder.append("FEEDBACK_AUDIBLE");
806                    break;
807                case FEEDBACK_HAPTIC:
808                    if (builder.length() > 1) {
809                        builder.append(", ");
810                    }
811                    builder.append("FEEDBACK_HAPTIC");
812                    break;
813                case FEEDBACK_GENERIC:
814                    if (builder.length() > 1) {
815                        builder.append(", ");
816                    }
817                    builder.append("FEEDBACK_GENERIC");
818                    break;
819                case FEEDBACK_SPOKEN:
820                    if (builder.length() > 1) {
821                        builder.append(", ");
822                    }
823                    builder.append("FEEDBACK_SPOKEN");
824                    break;
825                case FEEDBACK_VISUAL:
826                    if (builder.length() > 1) {
827                        builder.append(", ");
828                    }
829                    builder.append("FEEDBACK_VISUAL");
830                    break;
831                case FEEDBACK_BRAILLE:
832                    if (builder.length() > 1) {
833                        builder.append(", ");
834                    }
835                    builder.append("FEEDBACK_BRAILLE");
836                    break;
837            }
838        }
839        builder.append("]");
840        return builder.toString();
841    }
842
843    /**
844     * Returns the string representation of a flag. For example,
845     * {@link #DEFAULT} is represented by the string DEFAULT.
846     *
847     * @param flag The flag.
848     * @return The string representation.
849     */
850    public static String flagToString(int flag) {
851        switch (flag) {
852            case DEFAULT:
853                return "DEFAULT";
854            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
855                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
856            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
857                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
858            case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
859                return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
860            case FLAG_REPORT_VIEW_IDS:
861                return "FLAG_REPORT_VIEW_IDS";
862            case FLAG_REQUEST_FILTER_KEY_EVENTS:
863                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
864            default:
865                return null;
866        }
867    }
868
869    /**
870     * Returns the string representation of a capability. For example,
871     * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
872     * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
873     *
874     * @param capability The capability.
875     * @return The string representation.
876     */
877    public static String capabilityToString(int capability) {
878        switch (capability) {
879            case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
880                return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
881            case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
882                return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
883            case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
884                return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
885            case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS:
886                return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
887            default:
888                return "UNKNOWN";
889        }
890    }
891
892    /**
893     * @hide
894     * @return The list of {@link CapabilityInfo} objects.
895     */
896    public List<CapabilityInfo> getCapabilityInfos() {
897        if (mCapabilities == 0) {
898            return Collections.emptyList();
899        }
900        int capabilities = mCapabilities;
901        List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>();
902        while (capabilities != 0) {
903            final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities);
904            capabilities &= ~capabilityBit;
905            CapabilityInfo capabilityInfo = sAvailableCapabilityInfos.get(capabilityBit);
906            if (capabilityInfo != null) {
907                capabilityInfos.add(capabilityInfo);
908            }
909        }
910        return capabilityInfos;
911    }
912
913    /**
914     * @hide
915     */
916    public static final class CapabilityInfo {
917        public final int capability;
918        public final int titleResId;
919        public final int descResId;
920
921        public CapabilityInfo(int capability, int titleResId, int descResId) {
922            this.capability = capability;
923            this.titleResId = titleResId;
924            this.descResId = descResId;
925        }
926    }
927
928    /**
929     * @see Parcelable.Creator
930     */
931    public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
932            new Parcelable.Creator<AccessibilityServiceInfo>() {
933        public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
934            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
935            info.initFromParcel(parcel);
936            return info;
937        }
938
939        public AccessibilityServiceInfo[] newArray(int size) {
940            return new AccessibilityServiceInfo[size];
941        }
942    };
943}
944