AccessibilityServiceInfo.java revision db7da0eb8b7d515c168d5b410764e24c9a0f9431
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 * @see AccessibilityService
62 * @see android.view.accessibility.AccessibilityEvent
63 * @see android.view.accessibility.AccessibilityManager
64 */
65public 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 {
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));
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));
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));
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     */
374    public AccessibilityServiceInfo() {
375        /* do nothing */
376    }
377
378    /**
379     * Creates a new instance.
380     *
381     * @param isAutomation Whether this is a test automation service.
382     *
383     * @hide
384     */
385    public AccessibilityServiceInfo(boolean isAutomation) {
386        // Automation service can do anything.
387        if (isAutomation) {
388            mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
389                    | CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
390                    | CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
391                    | CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
392        }
393    }
394
395    /**
396     * Creates a new instance.
397     *
398     * @param resolveInfo The service resolve info.
399     * @param context Context for accessing resources.
400     * @throws XmlPullParserException If a XML parsing error occurs.
401     * @throws IOException If a XML parsing error occurs.
402     *
403     * @hide
404     */
405    public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
406            throws XmlPullParserException, IOException {
407        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
408        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
409        mResolveInfo = resolveInfo;
410
411        XmlResourceParser parser = null;
412
413        try {
414            PackageManager packageManager = context.getPackageManager();
415            parser = serviceInfo.loadXmlMetaData(packageManager,
416                    AccessibilityService.SERVICE_META_DATA);
417            if (parser == null) {
418                return;
419            }
420
421            int type = 0;
422            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
423                type = parser.next();
424            }
425
426            String nodeName = parser.getName();
427            if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
428                throw new XmlPullParserException( "Meta-data does not start with"
429                        + TAG_ACCESSIBILITY_SERVICE + " tag");
430            }
431
432            AttributeSet allAttributes = Xml.asAttributeSet(parser);
433            Resources resources = packageManager.getResourcesForApplication(
434                    serviceInfo.applicationInfo);
435            TypedArray asAttributes = resources.obtainAttributes(allAttributes,
436                    com.android.internal.R.styleable.AccessibilityService);
437            eventTypes = asAttributes.getInt(
438                    com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
439                    0);
440            String packageNamez = asAttributes.getString(
441                    com.android.internal.R.styleable.AccessibilityService_packageNames);
442            if (packageNamez != null) {
443                packageNames = packageNamez.split("(\\s)*,(\\s)*");
444            }
445            feedbackType = asAttributes.getInt(
446                    com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
447                    0);
448            notificationTimeout = asAttributes.getInt(
449                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
450                    0);
451            flags = asAttributes.getInt(
452                    com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
453            mSettingsActivityName = asAttributes.getString(
454                    com.android.internal.R.styleable.AccessibilityService_settingsActivity);
455            if (asAttributes.getBoolean(com.android.internal.R.styleable
456                    .AccessibilityService_canRetrieveWindowContent, false)) {
457                mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
458            }
459            if (asAttributes.getBoolean(com.android.internal.R.styleable
460                    .AccessibilityService_canRequestTouchExplorationMode, false)) {
461                mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION;
462            }
463            if (asAttributes.getBoolean(com.android.internal.R.styleable
464                    .AccessibilityService_canRequestEnhancedWebAccessibility, false)) {
465                mCapabilities |= CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY;
466            }
467            if (asAttributes.getBoolean(com.android.internal.R.styleable
468                    .AccessibilityService_canRequestFilterKeyEvents, false)) {
469                mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
470            }
471            TypedValue peekedValue = asAttributes.peekValue(
472                    com.android.internal.R.styleable.AccessibilityService_description);
473            if (peekedValue != null) {
474                mDescriptionResId = peekedValue.resourceId;
475                CharSequence nonLocalizedDescription = peekedValue.coerceToString();
476                if (nonLocalizedDescription != null) {
477                    mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
478                }
479            }
480            asAttributes.recycle();
481        } catch (NameNotFoundException e) {
482            throw new XmlPullParserException( "Unable to create context for: "
483                    + serviceInfo.packageName);
484        } finally {
485            if (parser != null) {
486                parser.close();
487            }
488        }
489    }
490
491    /**
492     * Updates the properties that an AccessibilitySerivice can change dynamically.
493     *
494     * @param other The info from which to update the properties.
495     *
496     * @hide
497     */
498    public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
499        eventTypes = other.eventTypes;
500        packageNames = other.packageNames;
501        feedbackType = other.feedbackType;
502        notificationTimeout = other.notificationTimeout;
503        flags = other.flags;
504    }
505
506    /**
507     * @hide
508     */
509    public void setComponentName(ComponentName component) {
510        mId = component.flattenToShortString();
511    }
512
513    /**
514     * The accessibility service id.
515     * <p>
516     *   <strong>Generated by the system.</strong>
517     * </p>
518     * @return The id.
519     */
520    public String getId() {
521        return mId;
522    }
523
524    /**
525     * The service {@link ResolveInfo}.
526     * <p>
527     *   <strong>Generated by the system.</strong>
528     * </p>
529     * @return The info.
530     */
531    public ResolveInfo getResolveInfo() {
532        return mResolveInfo;
533    }
534
535    /**
536     * The settings activity name.
537     * <p>
538     *    <strong>Statically set from
539     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
540     * </p>
541     * @return The settings activity name.
542     */
543    public String getSettingsActivityName() {
544        return mSettingsActivityName;
545    }
546
547    /**
548     * Whether this service can retrieve the current window's content.
549     * <p>
550     *    <strong>Statically set from
551     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
552     * </p>
553     * @return True if window content can be retrieved.
554     *
555     * @deprecated Use {@link #getCapabilities()}.
556     */
557    public boolean getCanRetrieveWindowContent() {
558        return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
559    }
560
561    /**
562     * Returns the bit mask of capabilities this accessibility service has such as
563     * being able to retrieve the active window content, etc.
564     *
565     * @return The capability bit mask.
566     *
567     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
568     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
569     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
570     * @see #CAPABILITY_FILTER_KEY_EVENTS
571     */
572    public int getCapabilities() {
573        return mCapabilities;
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     */
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     */
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     */
614    public int describeContents() {
615        return 0;
616    }
617
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
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
647    public int hashCode() {
648        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
649    }
650
651    @Override
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
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
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
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
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
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
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     */
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     */
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     */
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     */
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
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