AccessibilityServiceInfo.java revision 447d94684ee73046d769649d8247aacd581bd6e3
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.TypedValue;
33import android.util.Xml;
34import android.view.View;
35import android.view.accessibility.AccessibilityEvent;
36import android.view.accessibility.AccessibilityNodeInfo;
37
38import org.xmlpull.v1.XmlPullParser;
39import org.xmlpull.v1.XmlPullParserException;
40
41import java.io.IOException;
42
43/**
44 * This class describes an {@link AccessibilityService}. The system notifies an
45 * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
46 * according to the information encapsulated in this class.
47 *
48 * <div class="special reference">
49 * <h3>Developer Guides</h3>
50 * <p>For more information about creating AccessibilityServices, read the
51 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
52 * developer guide.</p>
53 * </div>
54 *
55 * @see AccessibilityService
56 * @see android.view.accessibility.AccessibilityEvent
57 * @see android.view.accessibility.AccessibilityManager
58 */
59public class AccessibilityServiceInfo implements Parcelable {
60
61    private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service";
62
63    /**
64     * Denotes spoken feedback.
65     */
66    public static final int FEEDBACK_SPOKEN = 0x0000001;
67
68    /**
69     * Denotes haptic feedback.
70     */
71    public static final int FEEDBACK_HAPTIC =  0x0000002;
72
73    /**
74     * Denotes audible (not spoken) feedback.
75     */
76    public static final int FEEDBACK_AUDIBLE = 0x0000004;
77
78    /**
79     * Denotes visual feedback.
80     */
81    public static final int FEEDBACK_VISUAL = 0x0000008;
82
83    /**
84     * Denotes generic feedback.
85     */
86    public static final int FEEDBACK_GENERIC = 0x0000010;
87
88    /**
89     * Denotes braille feedback.
90     */
91    public static final int FEEDBACK_BRAILLE = 0x0000020;
92
93    /**
94     * Mask for all feedback types.
95     *
96     * @see #FEEDBACK_SPOKEN
97     * @see #FEEDBACK_HAPTIC
98     * @see #FEEDBACK_AUDIBLE
99     * @see #FEEDBACK_VISUAL
100     * @see #FEEDBACK_GENERIC
101     * @see #FEEDBACK_BRAILLE
102     */
103    public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
104
105    /**
106     * If an {@link AccessibilityService} is the default for a given type.
107     * Default service is invoked only if no package specific one exists. In case of
108     * more than one package specific service only the earlier registered is notified.
109     */
110    public static final int DEFAULT = 0x0000001;
111
112    /**
113     * If this flag is set the system will regard views that are not important
114     * for accessibility in addition to the ones that are important for accessibility.
115     * That is, views that are marked as not important for accessibility via
116     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} and views that are marked as
117     * potentially important for accessibility via
118     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
119     * that are not important for accessibility, are both reported while querying the
120     * window content and also the accessibility service will receive accessibility events
121     * from them.
122     * <p>
123     * <strong>Note:</strong> For accessibility services targeting API version
124     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
125     * set for the system to regard views that are not important for accessibility. For
126     * accessibility services targeting API version lower than
127     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
128     * regarded for accessibility purposes.
129     * </p>
130     * <p>
131     * Usually views not important for accessibility are layout managers that do not
132     * react to user actions, do not draw any content, and do not have any special
133     * semantics in the context of the screen content. For example, a three by three
134     * grid can be implemented as three horizontal linear layouts and one vertical,
135     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
136     * In this context the actual layout mangers used to achieve the grid configuration
137     * are not important, rather it is important that there are nine evenly distributed
138     * elements.
139     * </p>
140     */
141    public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
142
143    /**
144     * This flag requests that the system gets into touch exploration mode.
145     * In this mode a single finger moving on the screen behaves as a mouse
146     * pointer hovering over the user interface. The system will also detect
147     * certain gestures performed on the touch screen and notify this service.
148     * The system will enable touch exploration mode if there is at least one
149     * accessibility service that has this flag set. Hence, clearing this
150     * flag does not guarantee that the device will not be in touch exploration
151     * mode since there may be another enabled service that requested it.
152     * <p>
153     * For accessibility services targeting API version higher than
154     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
155     * this flag have to request the
156     * {@link android.Manifest.permission#CAN_REQUEST_TOUCH_EXPLORATION_MODE}
157     * permission or the flag will be ignored.
158     * </p>
159     * <p>
160     * Services targeting API version equal to or lower than
161     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
162     * the first time they are run, if this flag is specified, a dialog is
163     * shown to the user to confirm enabling explore by touch.
164     * </p>
165     */
166    public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
167
168    /**
169     * This flag requests from the system to enable web accessibility enhancing
170     * extensions. Such extensions aim to provide improved accessibility support
171     * for content presented in a {@link android.webkit.WebView}. An example of such
172     * an extension is injecting JavaScript from Google. The system will enable
173     * enhanced web accessibility if there is at least one accessibility service
174     * that has this flag set. Hence, clearing this flag does not guarantee that the
175     * device will not have enhanced web accessibility enabled since there may be
176     * another enabled service that requested it.
177     * <p>
178     * Clients that want to set this flag have to request the
179     * {@link android.Manifest.permission#CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY}
180     * permission or the flag will be ignored.
181     * </p>
182     */
183    public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
184
185    /**
186     * This flag requests that the {@link AccessibilityNodeInfo}s obtained
187     * by an {@link AccessibilityService} contain the id of the source view.
188     * The source view id will be a fully qualified resource name of the
189     * form "package:id/name", for example "foo.bar:id/my_list", and it is
190     * useful for UI test automation. This flag is not set by default.
191     */
192    public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
193
194    /**
195     * The event types an {@link AccessibilityService} is interested in.
196     * <p>
197     *   <strong>Can be dynamically set at runtime.</strong>
198     * </p>
199     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
200     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
201     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
202     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
203     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
204     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
205     * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
206     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
207     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
208     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
209     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
210     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
211     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
212     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
213     */
214    public int eventTypes;
215
216    /**
217     * The package names an {@link AccessibilityService} is interested in. Setting
218     * to <code>null</code> is equivalent to all packages.
219     * <p>
220     *   <strong>Can be dynamically set at runtime.</strong>
221     * </p>
222     */
223    public String[] packageNames;
224
225    /**
226     * The feedback type an {@link AccessibilityService} provides.
227     * <p>
228     *   <strong>Can be dynamically set at runtime.</strong>
229     * </p>
230     * @see #FEEDBACK_AUDIBLE
231     * @see #FEEDBACK_GENERIC
232     * @see #FEEDBACK_HAPTIC
233     * @see #FEEDBACK_SPOKEN
234     * @see #FEEDBACK_VISUAL
235     * @see #FEEDBACK_BRAILLE
236     */
237    public int feedbackType;
238
239    /**
240     * The timeout after the most recent event of a given type before an
241     * {@link AccessibilityService} is notified.
242     * <p>
243     *   <strong>Can be dynamically set at runtime.</strong>.
244     * </p>
245     * <p>
246     * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
247     *       events to the client too frequently since this is accomplished via an expensive
248     *       interprocess call. One can think of the timeout as a criteria to determine when
249     *       event generation has settled down.
250     */
251    public long notificationTimeout;
252
253    /**
254     * This field represents a set of flags used for configuring an
255     * {@link AccessibilityService}.
256     * <p>
257     *   <strong>Can be dynamically set at runtime.</strong>
258     * </p>
259     * @see #DEFAULT
260     * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
261     * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
262     */
263    public int flags;
264
265    /**
266     * The unique string Id to identify the accessibility service.
267     */
268    private String mId;
269
270    /**
271     * The Service that implements this accessibility service component.
272     */
273    private ResolveInfo mResolveInfo;
274
275    /**
276     * The accessibility service setting activity's name, used by the system
277     * settings to launch the setting activity of this accessibility service.
278     */
279    private String mSettingsActivityName;
280
281    /**
282     * Flag whether this accessibility service can retrieve window content.
283     */
284    private boolean mCanRetrieveWindowContent;
285
286    /**
287     * Resource id of the description of the accessibility service.
288     */
289    private int mDescriptionResId;
290
291    /**
292     * Non localized description of the accessibility service.
293     */
294    private String mNonLocalizedDescription;
295
296    /**
297     * Creates a new instance.
298     */
299    public AccessibilityServiceInfo() {
300        /* do nothing */
301    }
302
303    /**
304     * Creates a new instance.
305     *
306     * @param resolveInfo The service resolve info.
307     * @param context Context for accessing resources.
308     * @throws XmlPullParserException If a XML parsing error occurs.
309     * @throws IOException If a XML parsing error occurs.
310     *
311     * @hide
312     */
313    public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
314            throws XmlPullParserException, IOException {
315        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
316        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
317        mResolveInfo = resolveInfo;
318
319        XmlResourceParser parser = null;
320
321        try {
322            PackageManager packageManager = context.getPackageManager();
323            parser = serviceInfo.loadXmlMetaData(packageManager,
324                    AccessibilityService.SERVICE_META_DATA);
325            if (parser == null) {
326                return;
327            }
328
329            int type = 0;
330            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
331                type = parser.next();
332            }
333
334            String nodeName = parser.getName();
335            if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
336                throw new XmlPullParserException( "Meta-data does not start with"
337                        + TAG_ACCESSIBILITY_SERVICE + " tag");
338            }
339
340            AttributeSet allAttributes = Xml.asAttributeSet(parser);
341            Resources resources = packageManager.getResourcesForApplication(
342                    serviceInfo.applicationInfo);
343            TypedArray asAttributes = resources.obtainAttributes(allAttributes,
344                    com.android.internal.R.styleable.AccessibilityService);
345            eventTypes = asAttributes.getInt(
346                    com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
347                    0);
348            String packageNamez = asAttributes.getString(
349                    com.android.internal.R.styleable.AccessibilityService_packageNames);
350            if (packageNamez != null) {
351                packageNames = packageNamez.split("(\\s)*,(\\s)*");
352            }
353            feedbackType = asAttributes.getInt(
354                    com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
355                    0);
356            notificationTimeout = asAttributes.getInt(
357                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
358                    0);
359            flags = asAttributes.getInt(
360                    com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
361            mSettingsActivityName = asAttributes.getString(
362                    com.android.internal.R.styleable.AccessibilityService_settingsActivity);
363            mCanRetrieveWindowContent = asAttributes.getBoolean(
364                    com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent,
365                    false);
366            TypedValue peekedValue = asAttributes.peekValue(
367                    com.android.internal.R.styleable.AccessibilityService_description);
368            if (peekedValue != null) {
369                mDescriptionResId = peekedValue.resourceId;
370                CharSequence nonLocalizedDescription = peekedValue.coerceToString();
371                if (nonLocalizedDescription != null) {
372                    mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
373                }
374            }
375            asAttributes.recycle();
376        } catch (NameNotFoundException e) {
377            throw new XmlPullParserException( "Unable to create context for: "
378                    + serviceInfo.packageName);
379        } finally {
380            if (parser != null) {
381                parser.close();
382            }
383        }
384    }
385
386    /**
387     * Updates the properties that an AccessibilitySerivice can change dynamically.
388     *
389     * @param other The info from which to update the properties.
390     *
391     * @hide
392     */
393    public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
394        eventTypes = other.eventTypes;
395        packageNames = other.packageNames;
396        feedbackType = other.feedbackType;
397        notificationTimeout = other.notificationTimeout;
398        flags = other.flags;
399    }
400
401    /**
402     * The accessibility service id.
403     * <p>
404     *   <strong>Generated by the system.</strong>
405     * </p>
406     * @return The id.
407     */
408    public String getId() {
409        return mId;
410    }
411
412    /**
413     * The service {@link ResolveInfo}.
414     * <p>
415     *   <strong>Generated by the system.</strong>
416     * </p>
417     * @return The info.
418     */
419    public ResolveInfo getResolveInfo() {
420        return mResolveInfo;
421    }
422
423    /**
424     * The settings activity name.
425     * <p>
426     *    <strong>Statically set from
427     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
428     * </p>
429     * @return The settings activity name.
430     */
431    public String getSettingsActivityName() {
432        return mSettingsActivityName;
433    }
434
435    /**
436     * Whether this service can retrieve the current window's content.
437     * <p>
438     *    <strong>Statically set from
439     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
440     * </p>
441     * @return True if window content can be retrieved.
442     */
443    public boolean getCanRetrieveWindowContent() {
444        return mCanRetrieveWindowContent;
445    }
446
447    /**
448     * Gets the non-localized description of the accessibility service.
449     * <p>
450     *    <strong>Statically set from
451     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
452     * </p>
453     * @return The description.
454     *
455     * @deprecated Use {@link #loadDescription(PackageManager)}.
456     */
457    public String getDescription() {
458        return mNonLocalizedDescription;
459    }
460
461    /**
462     * The localized description of the accessibility service.
463     * <p>
464     *    <strong>Statically set from
465     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
466     * </p>
467     * @return The localized description.
468     */
469    public String loadDescription(PackageManager packageManager) {
470        if (mDescriptionResId == 0) {
471            return mNonLocalizedDescription;
472        }
473        ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
474        CharSequence description = packageManager.getText(serviceInfo.packageName,
475                mDescriptionResId, serviceInfo.applicationInfo);
476        if (description != null) {
477            return description.toString().trim();
478        }
479        return null;
480    }
481
482    /**
483     * {@inheritDoc}
484     */
485    public int describeContents() {
486        return 0;
487    }
488
489    public void writeToParcel(Parcel parcel, int flagz) {
490        parcel.writeInt(eventTypes);
491        parcel.writeStringArray(packageNames);
492        parcel.writeInt(feedbackType);
493        parcel.writeLong(notificationTimeout);
494        parcel.writeInt(flags);
495        parcel.writeString(mId);
496        parcel.writeParcelable(mResolveInfo, 0);
497        parcel.writeString(mSettingsActivityName);
498        parcel.writeInt(mCanRetrieveWindowContent ? 1 : 0);
499        parcel.writeInt(mDescriptionResId);
500        parcel.writeString(mNonLocalizedDescription);
501    }
502
503    private void initFromParcel(Parcel parcel) {
504        eventTypes = parcel.readInt();
505        packageNames = parcel.readStringArray();
506        feedbackType = parcel.readInt();
507        notificationTimeout = parcel.readLong();
508        flags = parcel.readInt();
509        mId = parcel.readString();
510        mResolveInfo = parcel.readParcelable(null);
511        mSettingsActivityName = parcel.readString();
512        mCanRetrieveWindowContent = (parcel.readInt() == 1);
513        mDescriptionResId = parcel.readInt();
514        mNonLocalizedDescription = parcel.readString();
515    }
516
517    @Override
518    public String toString() {
519        StringBuilder stringBuilder = new StringBuilder();
520        appendEventTypes(stringBuilder, eventTypes);
521        stringBuilder.append(", ");
522        appendPackageNames(stringBuilder, packageNames);
523        stringBuilder.append(", ");
524        appendFeedbackTypes(stringBuilder, feedbackType);
525        stringBuilder.append(", ");
526        stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
527        stringBuilder.append(", ");
528        appendFlags(stringBuilder, flags);
529        stringBuilder.append(", ");
530        stringBuilder.append("id: ").append(mId);
531        stringBuilder.append(", ");
532        stringBuilder.append("resolveInfo: ").append(mResolveInfo);
533        stringBuilder.append(", ");
534        stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
535        stringBuilder.append(", ");
536        stringBuilder.append("retrieveScreenContent: ").append(mCanRetrieveWindowContent);
537        return stringBuilder.toString();
538    }
539
540    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
541        stringBuilder.append("feedbackTypes:");
542        stringBuilder.append("[");
543        while (feedbackTypes != 0) {
544            final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
545            stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
546            feedbackTypes &= ~feedbackTypeBit;
547            if (feedbackTypes != 0) {
548                stringBuilder.append(", ");
549            }
550        }
551        stringBuilder.append("]");
552    }
553
554    private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
555        stringBuilder.append("packageNames:");
556        stringBuilder.append("[");
557        if (packageNames != null) {
558            final int packageNameCount = packageNames.length;
559            for (int i = 0; i < packageNameCount; i++) {
560                stringBuilder.append(packageNames[i]);
561                if (i < packageNameCount - 1) {
562                    stringBuilder.append(", ");
563                }
564            }
565        }
566        stringBuilder.append("]");
567    }
568
569    private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
570        stringBuilder.append("eventTypes:");
571        stringBuilder.append("[");
572        while (eventTypes != 0) {
573            final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
574            stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
575            eventTypes &= ~eventTypeBit;
576            if (eventTypes != 0) {
577                stringBuilder.append(", ");
578            }
579        }
580        stringBuilder.append("]");
581    }
582
583    private static void appendFlags(StringBuilder stringBuilder, int flags) {
584        stringBuilder.append("flags:");
585        stringBuilder.append("[");
586        while (flags != 0) {
587            final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
588            stringBuilder.append(flagToString(flagBit));
589            flags &= ~flagBit;
590            if (flags != 0) {
591                stringBuilder.append(", ");
592            }
593        }
594        stringBuilder.append("]");
595    }
596
597    /**
598     * Returns the string representation of a feedback type. For example,
599     * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
600     *
601     * @param feedbackType The feedback type.
602     * @return The string representation.
603     */
604    public static String feedbackTypeToString(int feedbackType) {
605        StringBuilder builder = new StringBuilder();
606        builder.append("[");
607        while (feedbackType != 0) {
608            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
609            feedbackType &= ~feedbackTypeFlag;
610            switch (feedbackTypeFlag) {
611                case FEEDBACK_AUDIBLE:
612                    if (builder.length() > 1) {
613                        builder.append(", ");
614                    }
615                    builder.append("FEEDBACK_AUDIBLE");
616                    break;
617                case FEEDBACK_HAPTIC:
618                    if (builder.length() > 1) {
619                        builder.append(", ");
620                    }
621                    builder.append("FEEDBACK_HAPTIC");
622                    break;
623                case FEEDBACK_GENERIC:
624                    if (builder.length() > 1) {
625                        builder.append(", ");
626                    }
627                    builder.append("FEEDBACK_GENERIC");
628                    break;
629                case FEEDBACK_SPOKEN:
630                    if (builder.length() > 1) {
631                        builder.append(", ");
632                    }
633                    builder.append("FEEDBACK_SPOKEN");
634                    break;
635                case FEEDBACK_VISUAL:
636                    if (builder.length() > 1) {
637                        builder.append(", ");
638                    }
639                    builder.append("FEEDBACK_VISUAL");
640                    break;
641                case FEEDBACK_BRAILLE:
642                    if (builder.length() > 1) {
643                        builder.append(", ");
644                    }
645                    builder.append("FEEDBACK_BRAILLE");
646                    break;
647            }
648        }
649        builder.append("]");
650        return builder.toString();
651    }
652
653    /**
654     * Returns the string representation of a flag. For example,
655     * {@link #DEFAULT} is represented by the string DEFAULT.
656     *
657     * @param flag The flag.
658     * @return The string representation.
659     */
660    public static String flagToString(int flag) {
661        switch (flag) {
662            case DEFAULT:
663                return "DEFAULT";
664            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
665                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
666            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
667                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
668            default:
669                return null;
670        }
671    }
672
673    /**
674     * @see Parcelable.Creator
675     */
676    public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
677            new Parcelable.Creator<AccessibilityServiceInfo>() {
678        public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
679            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
680            info.initFromParcel(parcel);
681            return info;
682        }
683
684        public AccessibilityServiceInfo[] newArray(int size) {
685            return new AccessibilityServiceInfo[size];
686        }
687    };
688}
689