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