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.Parcel;
     29 import android.os.Parcelable;
     30 import android.util.AttributeSet;
     31 import android.util.Xml;
     32 import android.view.accessibility.AccessibilityEvent;
     33 
     34 import org.xmlpull.v1.XmlPullParser;
     35 import org.xmlpull.v1.XmlPullParserException;
     36 
     37 import java.io.IOException;
     38 
     39 /**
     40  * This class describes an {@link AccessibilityService}. The system notifies an
     41  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
     42  * according to the information encapsulated in this class.
     43  *
     44  * @see AccessibilityService
     45  * @see android.view.accessibility.AccessibilityEvent
     46  * @see android.view.accessibility.AccessibilityManager
     47  */
     48 public class AccessibilityServiceInfo implements Parcelable {
     49 
     50     private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service";
     51 
     52     /**
     53      * Denotes spoken feedback.
     54      */
     55     public static final int FEEDBACK_SPOKEN = 0x0000001;
     56 
     57     /**
     58      * Denotes haptic feedback.
     59      */
     60     public static final int FEEDBACK_HAPTIC =  0x0000002;
     61 
     62     /**
     63      * Denotes audible (not spoken) feedback.
     64      */
     65     public static final int FEEDBACK_AUDIBLE = 0x0000004;
     66 
     67     /**
     68      * Denotes visual feedback.
     69      */
     70     public static final int FEEDBACK_VISUAL = 0x0000008;
     71 
     72     /**
     73      * Denotes generic feedback.
     74      */
     75     public static final int FEEDBACK_GENERIC = 0x0000010;
     76 
     77     /**
     78      * Mask for all feedback types.
     79      *
     80      * @see #FEEDBACK_SPOKEN
     81      * @see #FEEDBACK_HAPTIC
     82      * @see #FEEDBACK_AUDIBLE
     83      * @see #FEEDBACK_VISUAL
     84      * @see #FEEDBACK_GENERIC
     85      */
     86     public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
     87 
     88     /**
     89      * If an {@link AccessibilityService} is the default for a given type.
     90      * Default service is invoked only if no package specific one exists. In case of
     91      * more than one package specific service only the earlier registered is notified.
     92      */
     93     public static final int DEFAULT = 0x0000001;
     94 
     95     /**
     96      * The event types an {@link AccessibilityService} is interested in.
     97      * <p>
     98      *   <strong>Can be dynamically set at runtime.</strong>
     99      * </p>
    100      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
    101      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
    102      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
    103      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
    104      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
    105      * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
    106      * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
    107      * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
    108      * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
    109      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
    110      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
    111      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
    112      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
    113      * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
    114      */
    115     public int eventTypes;
    116 
    117     /**
    118      * The package names an {@link AccessibilityService} is interested in. Setting
    119      * to <code>null</code> is equivalent to all packages.
    120      * <p>
    121      *   <strong>Can be dynamically set at runtime.</strong>
    122      * </p>
    123      */
    124     public String[] packageNames;
    125 
    126     /**
    127      * The feedback type an {@link AccessibilityService} provides.
    128      * <p>
    129      *   <strong>Can be dynamically set at runtime.</strong>
    130      * </p>
    131      * @see #FEEDBACK_AUDIBLE
    132      * @see #FEEDBACK_GENERIC
    133      * @see #FEEDBACK_HAPTIC
    134      * @see #FEEDBACK_SPOKEN
    135      * @see #FEEDBACK_VISUAL
    136      */
    137     public int feedbackType;
    138 
    139     /**
    140      * The timeout after the most recent event of a given type before an
    141      * {@link AccessibilityService} is notified.
    142      * <p>
    143      *   <strong>Can be dynamically set at runtime.</strong>.
    144      * </p>
    145      * <p>
    146      * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
    147      *       events to the client too frequently since this is accomplished via an expensive
    148      *       interprocess call. One can think of the timeout as a criteria to determine when
    149      *       event generation has settled down.
    150      */
    151     public long notificationTimeout;
    152 
    153     /**
    154      * This field represents a set of flags used for configuring an
    155      * {@link AccessibilityService}.
    156      * <p>
    157      *   <strong>Can be dynamically set at runtime.</strong>
    158      * </p>
    159      * @see #DEFAULT
    160      */
    161     public int flags;
    162 
    163     /**
    164      * The unique string Id to identify the accessibility service.
    165      */
    166     private String mId;
    167 
    168     /**
    169      * The Service that implements this accessibility service component.
    170      */
    171     private ResolveInfo mResolveInfo;
    172 
    173     /**
    174      * The accessibility service setting activity's name, used by the system
    175      * settings to launch the setting activity of this accessibility service.
    176      */
    177     private String mSettingsActivityName;
    178 
    179     /**
    180      * Flag whether this accessibility service can retrieve window content.
    181      */
    182     private boolean mCanRetrieveWindowContent;
    183 
    184     /**
    185      * Description of the accessibility service.
    186      */
    187     private String mDescription;
    188 
    189     /**
    190      * Creates a new instance.
    191      */
    192     public AccessibilityServiceInfo() {
    193         /* do nothing */
    194     }
    195 
    196     /**
    197      * Creates a new instance.
    198      *
    199      * @param resolveInfo The service resolve info.
    200      * @param context Context for accessing resources.
    201      * @throws XmlPullParserException If a XML parsing error occurs.
    202      * @throws IOException If a XML parsing error occurs.
    203      *
    204      * @hide
    205      */
    206     public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
    207             throws XmlPullParserException, IOException {
    208         ServiceInfo serviceInfo = resolveInfo.serviceInfo;
    209         mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
    210         mResolveInfo = resolveInfo;
    211 
    212         XmlResourceParser parser = null;
    213 
    214         try {
    215             PackageManager packageManager = context.getPackageManager();
    216             parser = serviceInfo.loadXmlMetaData(packageManager,
    217                     AccessibilityService.SERVICE_META_DATA);
    218             if (parser == null) {
    219                 return;
    220             }
    221 
    222             int type = 0;
    223             while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
    224                 type = parser.next();
    225             }
    226 
    227             String nodeName = parser.getName();
    228             if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
    229                 throw new XmlPullParserException( "Meta-data does not start with"
    230                         + TAG_ACCESSIBILITY_SERVICE + " tag");
    231             }
    232 
    233             AttributeSet allAttributes = Xml.asAttributeSet(parser);
    234             Resources resources = packageManager.getResourcesForApplication(
    235                     serviceInfo.applicationInfo);
    236             TypedArray asAttributes = resources.obtainAttributes(allAttributes,
    237                     com.android.internal.R.styleable.AccessibilityService);
    238             eventTypes = asAttributes.getInt(
    239                     com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
    240                     0);
    241             String packageNamez = asAttributes.getString(
    242                     com.android.internal.R.styleable.AccessibilityService_packageNames);
    243             if (packageNamez != null) {
    244                 packageNames = packageNamez.split("(\\s)*,(\\s)*");
    245             }
    246             feedbackType = asAttributes.getInt(
    247                     com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
    248                     0);
    249             notificationTimeout = asAttributes.getInt(
    250                     com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
    251                     0);
    252             flags = asAttributes.getInt(
    253                     com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
    254             mSettingsActivityName = asAttributes.getString(
    255                     com.android.internal.R.styleable.AccessibilityService_settingsActivity);
    256             mCanRetrieveWindowContent = asAttributes.getBoolean(
    257                     com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent,
    258                     false);
    259             mDescription = asAttributes.getString(
    260                     com.android.internal.R.styleable.AccessibilityService_description);
    261             asAttributes.recycle();
    262         } catch (NameNotFoundException e) {
    263             throw new XmlPullParserException( "Unable to create context for: "
    264                     + serviceInfo.packageName);
    265         } finally {
    266             if (parser != null) {
    267                 parser.close();
    268             }
    269         }
    270     }
    271 
    272     /**
    273      * Updates the properties that an AccessibilitySerivice can change dynamically.
    274      *
    275      * @param other The info from which to update the properties.
    276      *
    277      * @hide
    278      */
    279     public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
    280         eventTypes = other.eventTypes;
    281         packageNames = other.packageNames;
    282         feedbackType = other.feedbackType;
    283         notificationTimeout = other.notificationTimeout;
    284         flags = other.flags;
    285     }
    286 
    287     /**
    288      * The accessibility service id.
    289      * <p>
    290      *   <strong>Generated by the system.</strong>
    291      * </p>
    292      * @return The id.
    293      */
    294     public String getId() {
    295         return mId;
    296     }
    297 
    298     /**
    299      * The service {@link ResolveInfo}.
    300      * <p>
    301      *   <strong>Generated by the system.</strong>
    302      * </p>
    303      * @return The info.
    304      */
    305     public ResolveInfo getResolveInfo() {
    306         return mResolveInfo;
    307     }
    308 
    309     /**
    310      * The settings activity name.
    311      * <p>
    312      *    <strong>Statically set from
    313      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
    314      * </p>
    315      * @return The settings activity name.
    316      */
    317     public String getSettingsActivityName() {
    318         return mSettingsActivityName;
    319     }
    320 
    321     /**
    322      * Whether this service can retrieve the current window's content.
    323      * <p>
    324      *    <strong>Statically set from
    325      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
    326      * </p>
    327      * @return True window content can be retrieved.
    328      */
    329     public boolean getCanRetrieveWindowContent() {
    330         return mCanRetrieveWindowContent;
    331     }
    332 
    333     /**
    334      * Description of the accessibility service.
    335      * <p>
    336      *    <strong>Statically set from
    337      *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
    338      * </p>
    339      * @return The description.
    340      */
    341     public String getDescription() {
    342         return mDescription;
    343     }
    344 
    345     /**
    346      * {@inheritDoc}
    347      */
    348     public int describeContents() {
    349         return 0;
    350     }
    351 
    352     public void writeToParcel(Parcel parcel, int flagz) {
    353         parcel.writeInt(eventTypes);
    354         parcel.writeStringArray(packageNames);
    355         parcel.writeInt(feedbackType);
    356         parcel.writeLong(notificationTimeout);
    357         parcel.writeInt(flags);
    358         parcel.writeString(mId);
    359         parcel.writeParcelable(mResolveInfo, 0);
    360         parcel.writeString(mSettingsActivityName);
    361         parcel.writeInt(mCanRetrieveWindowContent ? 1 : 0);
    362         parcel.writeString(mDescription);
    363     }
    364 
    365     private void initFromParcel(Parcel parcel) {
    366         eventTypes = parcel.readInt();
    367         packageNames = parcel.readStringArray();
    368         feedbackType = parcel.readInt();
    369         notificationTimeout = parcel.readLong();
    370         flags = parcel.readInt();
    371         mId = parcel.readString();
    372         mResolveInfo = parcel.readParcelable(null);
    373         mSettingsActivityName = parcel.readString();
    374         mCanRetrieveWindowContent = (parcel.readInt() == 1);
    375         mDescription = parcel.readString();
    376     }
    377 
    378     @Override
    379     public String toString() {
    380         StringBuilder stringBuilder = new StringBuilder();
    381         appendEventTypes(stringBuilder, eventTypes);
    382         stringBuilder.append(", ");
    383         appendPackageNames(stringBuilder, packageNames);
    384         stringBuilder.append(", ");
    385         appendFeedbackTypes(stringBuilder, feedbackType);
    386         stringBuilder.append(", ");
    387         stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
    388         stringBuilder.append(", ");
    389         appendFlags(stringBuilder, flags);
    390         stringBuilder.append(", ");
    391         stringBuilder.append("id: ").append(mId);
    392         stringBuilder.append(", ");
    393         stringBuilder.append("resolveInfo: ").append(mResolveInfo);
    394         stringBuilder.append(", ");
    395         stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
    396         stringBuilder.append(", ");
    397         stringBuilder.append("retrieveScreenContent: ").append(mCanRetrieveWindowContent);
    398         return stringBuilder.toString();
    399     }
    400 
    401     private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
    402         stringBuilder.append("feedbackTypes:");
    403         stringBuilder.append("[");
    404         while (feedbackTypes != 0) {
    405             final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
    406             stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
    407             feedbackTypes &= ~feedbackTypeBit;
    408             if (feedbackTypes != 0) {
    409                 stringBuilder.append(", ");
    410             }
    411         }
    412         stringBuilder.append("]");
    413     }
    414 
    415     private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
    416         stringBuilder.append("packageNames:");
    417         stringBuilder.append("[");
    418         if (packageNames != null) {
    419             final int packageNameCount = packageNames.length;
    420             for (int i = 0; i < packageNameCount; i++) {
    421                 stringBuilder.append(packageNames[i]);
    422                 if (i < packageNameCount - 1) {
    423                     stringBuilder.append(", ");
    424                 }
    425             }
    426         }
    427         stringBuilder.append("]");
    428     }
    429 
    430     private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
    431         stringBuilder.append("eventTypes:");
    432         stringBuilder.append("[");
    433         while (eventTypes != 0) {
    434             final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
    435             stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
    436             eventTypes &= ~eventTypeBit;
    437             if (eventTypes != 0) {
    438                 stringBuilder.append(", ");
    439             }
    440         }
    441         stringBuilder.append("]");
    442     }
    443 
    444     private static void appendFlags(StringBuilder stringBuilder, int flags) {
    445         stringBuilder.append("flags:");
    446         stringBuilder.append("[");
    447         while (flags != 0) {
    448             final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
    449             stringBuilder.append(flagToString(flagBit));
    450             flags &= ~flagBit;
    451             if (flags != 0) {
    452                 stringBuilder.append(", ");
    453             }
    454         }
    455         stringBuilder.append("]");
    456     }
    457 
    458     /**
    459      * Returns the string representation of a feedback type. For example,
    460      * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
    461      *
    462      * @param feedbackType The feedback type.
    463      * @return The string representation.
    464      */
    465     public static String feedbackTypeToString(int feedbackType) {
    466         StringBuilder builder = new StringBuilder();
    467         builder.append("[");
    468         while (feedbackType > 0) {
    469             final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
    470             feedbackType &= ~feedbackTypeFlag;
    471             if (builder.length() > 1) {
    472                 builder.append(", ");
    473             }
    474             switch (feedbackTypeFlag) {
    475                 case FEEDBACK_AUDIBLE:
    476                     builder.append("FEEDBACK_AUDIBLE");
    477                     break;
    478                 case FEEDBACK_HAPTIC:
    479                     builder.append("FEEDBACK_HAPTIC");
    480                     break;
    481                 case FEEDBACK_GENERIC:
    482                     builder.append("FEEDBACK_GENERIC");
    483                     break;
    484                 case FEEDBACK_SPOKEN:
    485                     builder.append("FEEDBACK_SPOKEN");
    486                     break;
    487                 case FEEDBACK_VISUAL:
    488                     builder.append("FEEDBACK_VISUAL");
    489                     break;
    490             }
    491         }
    492         builder.append("]");
    493         return builder.toString();
    494     }
    495 
    496     /**
    497      * Returns the string representation of a flag. For example,
    498      * {@link #DEFAULT} is represented by the string DEFAULT.
    499      *
    500      * @param flag The flag.
    501      * @return The string representation.
    502      */
    503     public static String flagToString(int flag) {
    504         switch (flag) {
    505             case DEFAULT:
    506                 return "DEFAULT";
    507             default:
    508                 return null;
    509         }
    510     }
    511 
    512     /**
    513      * @see Parcelable.Creator
    514      */
    515     public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
    516             new Parcelable.Creator<AccessibilityServiceInfo>() {
    517         public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
    518             AccessibilityServiceInfo info = new AccessibilityServiceInfo();
    519             info.initFromParcel(parcel);
    520             return info;
    521         }
    522 
    523         public AccessibilityServiceInfo[] newArray(int size) {
    524             return new AccessibilityServiceInfo[size];
    525         }
    526     };
    527 }
    528