AccessibilityServiceInfo.java revision 00d17f7aab54ef055d35da603dab5a7bae751876
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.accessibilityservice;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.content.pm.PackageManager.NameNotFoundException;
23import android.content.pm.ResolveInfo;
24import android.content.pm.ServiceInfo;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.content.res.XmlResourceParser;
28import android.os.Build;
29import android.os.Parcel;
30import android.os.Parcelable;
31import android.util.AttributeSet;
32import android.util.SparseArray;
33import android.util.TypedValue;
34import android.util.Xml;
35import android.view.View;
36import android.view.accessibility.AccessibilityEvent;
37import android.view.accessibility.AccessibilityNodeInfo;
38
39import org.xmlpull.v1.XmlPullParser;
40import org.xmlpull.v1.XmlPullParserException;
41
42import com.android.internal.R;
43
44import java.io.IOException;
45import java.util.ArrayList;
46import java.util.Collections;
47import java.util.List;
48
49/**
50 * This class describes an {@link AccessibilityService}. The system notifies an
51 * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
52 * according to the information encapsulated in this class.
53 *
54 * <div class="special reference">
55 * <h3>Developer Guides</h3>
56 * <p>For more information about creating AccessibilityServices, read the
57 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
58 * developer guide.</p>
59 * </div>
60 *
61 * @attr ref android.R.styleable#AccessibilityService_accessibilityEventTypes
62 * @attr ref android.R.styleable#AccessibilityService_accessibilityFeedbackType
63 * @attr ref android.R.styleable#AccessibilityService_accessibilityFlags
64 * @attr ref android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
65 * @attr ref android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
66 * @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
67 * @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent
68 * @attr ref android.R.styleable#AccessibilityService_description
69 * @attr ref android.R.styleable#AccessibilityService_notificationTimeout
70 * @attr ref android.R.styleable#AccessibilityService_packageNames
71 * @attr ref android.R.styleable#AccessibilityService_settingsActivity
72 *
73 * @see AccessibilityService
74 * @see android.view.accessibility.AccessibilityEvent
75 * @see android.view.accessibility.AccessibilityManager
76 */
77public class AccessibilityServiceInfo implements Parcelable {
78
79    private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service";
80
81    /**
82     * Capability: This accessibility service can retrieve the active window content.
83     * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
84     */
85    public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001;
86
87    /**
88     * Capability: This accessibility service can request touch exploration mode in which
89     * touched items are spoken aloud and the UI can be explored via gestures.
90     * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
91     */
92    public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002;
93
94    /**
95     * Capability: This accessibility service can request enhanced web accessibility
96     * enhancements. For example, installing scripts to make app content more accessible.
97     * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
98     */
99    public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004;
100
101    /**
102     * Capability: This accessibility service can request to filter the key event stream.
103     * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
104     */
105    public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 0x00000008;
106
107    private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos =
108            new SparseArray<CapabilityInfo>();
109    static {
110        sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
111                new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
112                        R.string.capability_title_canRetrieveWindowContent,
113                        R.string.capability_desc_canRetrieveWindowContent));
114        sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
115                new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
116                        R.string.capability_title_canRequestTouchExploration,
117                        R.string.capability_desc_canRequestTouchExploration));
118        sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
119                new CapabilityInfo(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
120                        R.string.capability_title_canRequestEnhancedWebAccessibility,
121                        R.string.capability_desc_canRequestEnhancedWebAccessibility));
122        sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
123                new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
124                        R.string.capability_title_canRequestFilterKeyEvents,
125                        R.string.capability_desc_canRequestFilterKeyEvents));
126    }
127
128    /**
129     * Denotes spoken feedback.
130     */
131    public static final int FEEDBACK_SPOKEN = 0x0000001;
132
133    /**
134     * Denotes haptic feedback.
135     */
136    public static final int FEEDBACK_HAPTIC =  0x0000002;
137
138    /**
139     * Denotes audible (not spoken) feedback.
140     */
141    public static final int FEEDBACK_AUDIBLE = 0x0000004;
142
143    /**
144     * Denotes visual feedback.
145     */
146    public static final int FEEDBACK_VISUAL = 0x0000008;
147
148    /**
149     * Denotes generic feedback.
150     */
151    public static final int FEEDBACK_GENERIC = 0x0000010;
152
153    /**
154     * Denotes braille feedback.
155     */
156    public static final int FEEDBACK_BRAILLE = 0x0000020;
157
158    /**
159     * Mask for all feedback types.
160     *
161     * @see #FEEDBACK_SPOKEN
162     * @see #FEEDBACK_HAPTIC
163     * @see #FEEDBACK_AUDIBLE
164     * @see #FEEDBACK_VISUAL
165     * @see #FEEDBACK_GENERIC
166     * @see #FEEDBACK_BRAILLE
167     */
168    public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
169
170    /**
171     * If an {@link AccessibilityService} is the default for a given type.
172     * Default service is invoked only if no package specific one exists. In case of
173     * more than one package specific service only the earlier registered is notified.
174     */
175    public static final int DEFAULT = 0x0000001;
176
177    /**
178     * If this flag is set the system will regard views that are not important
179     * for accessibility in addition to the ones that are important for accessibility.
180     * That is, views that are marked as not important for accessibility via
181     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} and views that are marked as
182     * potentially important for accessibility via
183     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
184     * that are not important for accessibility, are both reported while querying the
185     * window content and also the accessibility service will receive accessibility events
186     * from them.
187     * <p>
188     * <strong>Note:</strong> For accessibility services targeting API version
189     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
190     * set for the system to regard views that are not important for accessibility. For
191     * accessibility services targeting API version lower than
192     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
193     * regarded for accessibility purposes.
194     * </p>
195     * <p>
196     * Usually views not important for accessibility are layout managers that do not
197     * react to user actions, do not draw any content, and do not have any special
198     * semantics in the context of the screen content. For example, a three by three
199     * grid can be implemented as three horizontal linear layouts and one vertical,
200     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
201     * In this context the actual layout mangers used to achieve the grid configuration
202     * are not important, rather it is important that there are nine evenly distributed
203     * elements.
204     * </p>
205     */
206    public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
207
208    /**
209     * This flag requests that the system gets into touch exploration mode.
210     * In this mode a single finger moving on the screen behaves as a mouse
211     * pointer hovering over the user interface. The system will also detect
212     * certain gestures performed on the touch screen and notify this service.
213     * The system will enable touch exploration mode if there is at least one
214     * accessibility service that has this flag set. Hence, clearing this
215     * flag does not guarantee that the device will not be in touch exploration
216     * mode since there may be another enabled service that requested it.
217     * <p>
218     * For accessibility services targeting API version higher than
219     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
220     * this flag have to declare this capability in their meta-data by setting
221     * the attribute {@link android.R.attr#canRequestTouchExplorationMode
222     * canRequestTouchExplorationMode} to true, otherwise this flag will
223     * be ignored. For how to declare the meta-data of a service refer to
224     * {@value AccessibilityService#SERVICE_META_DATA}.
225     * </p>
226     * <p>
227     * Services targeting API version equal to or lower than
228     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
229     * the first time they are run, if this flag is specified, a dialog is
230     * shown to the user to confirm enabling explore by touch.
231     * </p>
232     * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
233     */
234    public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
235
236    /**
237     * This flag requests from the system to enable web accessibility enhancing
238     * extensions. Such extensions aim to provide improved accessibility support
239     * for content presented in a {@link android.webkit.WebView}. An example of such
240     * an extension is injecting JavaScript from a secure source. The system will enable
241     * enhanced web accessibility if there is at least one accessibility service
242     * that has this flag set. Hence, clearing this flag does not guarantee that the
243     * device will not have enhanced web accessibility enabled since there may be
244     * another enabled service that requested it.
245     * <p>
246     * Services that want to set this flag have to declare this capability
247     * in their meta-data by setting the attribute {@link android.R.attr
248     * #canRequestEnhancedWebAccessibility canRequestEnhancedWebAccessibility} to
249     * true, otherwise this flag will be ignored. For how to declare the meta-data
250     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
251     * </p>
252     * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
253     */
254    public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
255
256    /**
257     * This flag requests that the {@link AccessibilityNodeInfo}s obtained
258     * by an {@link AccessibilityService} contain the id of the source view.
259     * The source view id will be a fully qualified resource name of the
260     * form "package:id/name", for example "foo.bar:id/my_list", and it is
261     * useful for UI test automation. This flag is not set by default.
262     */
263    public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
264
265    /**
266     * This flag requests from the system to filter key events. If this flag
267     * is set the accessibility service will receive the key events before
268     * applications allowing it implement global shortcuts. Setting this flag
269     * does not guarantee that this service will filter key events since only
270     * one service can do so at any given time. This avoids user confusion due
271     * to behavior change in case different key filtering services are enabled.
272     * If there is already another key filtering service enabled, this one will
273     * not receive key events.
274     * <p>
275     * Services that want to set this flag have to declare this capability
276     * in their meta-data by setting the attribute {@link android.R.attr
277     * #canRequestFilterKeyEvents canRequestFilterKeyEvents} to true,
278     * otherwise this flag will be ignored. For how to declare the meta-data
279     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
280     * </p>
281     * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
282     */
283    public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
284
285    /**
286     * The event types an {@link AccessibilityService} is interested in.
287     * <p>
288     *   <strong>Can be dynamically set at runtime.</strong>
289     * </p>
290     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
291     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
292     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
293     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
294     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
295     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
296     * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
297     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
298     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
299     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
300     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
301     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
302     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
303     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
304     */
305    public int eventTypes;
306
307    /**
308     * The package names an {@link AccessibilityService} is interested in. Setting
309     * to <code>null</code> is equivalent to all packages.
310     * <p>
311     *   <strong>Can be dynamically set at runtime.</strong>
312     * </p>
313     */
314    public String[] packageNames;
315
316    /**
317     * The feedback type an {@link AccessibilityService} provides.
318     * <p>
319     *   <strong>Can be dynamically set at runtime.</strong>
320     * </p>
321     * @see #FEEDBACK_AUDIBLE
322     * @see #FEEDBACK_GENERIC
323     * @see #FEEDBACK_HAPTIC
324     * @see #FEEDBACK_SPOKEN
325     * @see #FEEDBACK_VISUAL
326     * @see #FEEDBACK_BRAILLE
327     */
328    public int feedbackType;
329
330    /**
331     * The timeout after the most recent event of a given type before an
332     * {@link AccessibilityService} is notified.
333     * <p>
334     *   <strong>Can be dynamically set at runtime.</strong>.
335     * </p>
336     * <p>
337     * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
338     *       events to the client too frequently since this is accomplished via an expensive
339     *       interprocess call. One can think of the timeout as a criteria to determine when
340     *       event generation has settled down.
341     */
342    public long notificationTimeout;
343
344    /**
345     * This field represents a set of flags used for configuring an
346     * {@link AccessibilityService}.
347     * <p>
348     *   <strong>Can be dynamically set at runtime.</strong>
349     * </p>
350     * @see #DEFAULT
351     * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
352     * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
353     * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
354     * @see #FLAG_REQUEST_FILTER_KEY_EVENTS
355     * @see #FLAG_REPORT_VIEW_IDS
356     */
357    public int flags;
358
359    /**
360     * The unique string Id to identify the accessibility service.
361     */
362    private String mId;
363
364    /**
365     * The Service that implements this accessibility service component.
366     */
367    private ResolveInfo mResolveInfo;
368
369    /**
370     * The accessibility service setting activity's name, used by the system
371     * settings to launch the setting activity of this accessibility service.
372     */
373    private String mSettingsActivityName;
374
375    /**
376     * Bit mask with capabilities of this service.
377     */
378    private int mCapabilities;
379
380    /**
381     * Resource id of the description of the accessibility service.
382     */
383    private int mDescriptionResId;
384
385    /**
386     * Non localized description of the accessibility service.
387     */
388    private String mNonLocalizedDescription;
389
390    /**
391     * Creates a new instance.
392     */
393    public AccessibilityServiceInfo() {
394        /* do nothing */
395    }
396
397    /**
398     * Creates a new instance.
399     *
400     * @param resolveInfo The service resolve info.
401     * @param context Context for accessing resources.
402     * @throws XmlPullParserException If a XML parsing error occurs.
403     * @throws IOException If a XML parsing error occurs.
404     *
405     * @hide
406     */
407    public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
408            throws XmlPullParserException, IOException {
409        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
410        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
411        mResolveInfo = resolveInfo;
412
413        XmlResourceParser parser = null;
414
415        try {
416            PackageManager packageManager = context.getPackageManager();
417            parser = serviceInfo.loadXmlMetaData(packageManager,
418                    AccessibilityService.SERVICE_META_DATA);
419            if (parser == null) {
420                return;
421            }
422
423            int type = 0;
424            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
425                type = parser.next();
426            }
427
428            String nodeName = parser.getName();
429            if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
430                throw new XmlPullParserException( "Meta-data does not start with"
431                        + TAG_ACCESSIBILITY_SERVICE + " tag");
432            }
433
434            AttributeSet allAttributes = Xml.asAttributeSet(parser);
435            Resources resources = packageManager.getResourcesForApplication(
436                    serviceInfo.applicationInfo);
437            TypedArray asAttributes = resources.obtainAttributes(allAttributes,
438                    com.android.internal.R.styleable.AccessibilityService);
439            eventTypes = asAttributes.getInt(
440                    com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
441                    0);
442            String packageNamez = asAttributes.getString(
443                    com.android.internal.R.styleable.AccessibilityService_packageNames);
444            if (packageNamez != null) {
445                packageNames = packageNamez.split("(\\s)*,(\\s)*");
446            }
447            feedbackType = asAttributes.getInt(
448                    com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
449                    0);
450            notificationTimeout = asAttributes.getInt(
451                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
452                    0);
453            flags = asAttributes.getInt(
454                    com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
455            mSettingsActivityName = asAttributes.getString(
456                    com.android.internal.R.styleable.AccessibilityService_settingsActivity);
457            if (asAttributes.getBoolean(com.android.internal.R.styleable
458                    .AccessibilityService_canRetrieveWindowContent, false)) {
459                mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
460            }
461            if (asAttributes.getBoolean(com.android.internal.R.styleable
462                    .AccessibilityService_canRequestTouchExplorationMode, false)) {
463                mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION;
464            }
465            if (asAttributes.getBoolean(com.android.internal.R.styleable
466                        .AccessibilityService_canRequestEnhancedWebAccessibility, false)) {
467                    mCapabilities |= CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY;
468            }
469            if (asAttributes.getBoolean(com.android.internal.R.styleable
470                    .AccessibilityService_canRequestFilterKeyEvents, false)) {
471                mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
472            }
473            TypedValue peekedValue = asAttributes.peekValue(
474                    com.android.internal.R.styleable.AccessibilityService_description);
475            if (peekedValue != null) {
476                mDescriptionResId = peekedValue.resourceId;
477                CharSequence nonLocalizedDescription = peekedValue.coerceToString();
478                if (nonLocalizedDescription != null) {
479                    mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
480                }
481            }
482            asAttributes.recycle();
483        } catch (NameNotFoundException e) {
484            throw new XmlPullParserException( "Unable to create context for: "
485                    + serviceInfo.packageName);
486        } finally {
487            if (parser != null) {
488                parser.close();
489            }
490        }
491    }
492
493    /**
494     * Updates the properties that an AccessibilitySerivice can change dynamically.
495     *
496     * @param other The info from which to update the properties.
497     *
498     * @hide
499     */
500    public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
501        eventTypes = other.eventTypes;
502        packageNames = other.packageNames;
503        feedbackType = other.feedbackType;
504        notificationTimeout = other.notificationTimeout;
505        flags = other.flags;
506    }
507
508    /**
509     * @hide
510     */
511    public void setComponentName(ComponentName component) {
512        mId = component.flattenToShortString();
513    }
514
515    /**
516     * The accessibility service id.
517     * <p>
518     *   <strong>Generated by the system.</strong>
519     * </p>
520     * @return The id.
521     */
522    public String getId() {
523        return mId;
524    }
525
526    /**
527     * The service {@link ResolveInfo}.
528     * <p>
529     *   <strong>Generated by the system.</strong>
530     * </p>
531     * @return The info.
532     */
533    public ResolveInfo getResolveInfo() {
534        return mResolveInfo;
535    }
536
537    /**
538     * The settings activity name.
539     * <p>
540     *    <strong>Statically set from
541     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
542     * </p>
543     * @return The settings activity name.
544     */
545    public String getSettingsActivityName() {
546        return mSettingsActivityName;
547    }
548
549    /**
550     * Whether this service can retrieve the current window's content.
551     * <p>
552     *    <strong>Statically set from
553     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
554     * </p>
555     * @return True if window content can be retrieved.
556     *
557     * @deprecated Use {@link #getCapabilities()}.
558     */
559    public boolean getCanRetrieveWindowContent() {
560        return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
561    }
562
563    /**
564     * Returns the bit mask of capabilities this accessibility service has such as
565     * being able to retrieve the active window content, etc.
566     *
567     * @return The capability bit mask.
568     *
569     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
570     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
571     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
572     * @see #CAPABILITY_FILTER_KEY_EVENTS
573     */
574    public int getCapabilities() {
575        return mCapabilities;
576    }
577
578    /**
579     * Sets the bit mask of capabilities this accessibility service has such as
580     * being able to retrieve the active window content, etc.
581     *
582     * @param capabilities The capability bit mask.
583     *
584     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
585     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
586     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
587     * @see #CAPABILITY_FILTER_KEY_EVENTS
588     *
589     * @hide
590     */
591    public void setCapabilities(int capabilities) {
592        mCapabilities = capabilities;
593    }
594
595    /**
596     * Gets the non-localized description of the accessibility service.
597     * <p>
598     *    <strong>Statically set from
599     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
600     * </p>
601     * @return The description.
602     *
603     * @deprecated Use {@link #loadDescription(PackageManager)}.
604     */
605    public String getDescription() {
606        return mNonLocalizedDescription;
607    }
608
609    /**
610     * The localized description of the accessibility service.
611     * <p>
612     *    <strong>Statically set from
613     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
614     * </p>
615     * @return The localized description.
616     */
617    public String loadDescription(PackageManager packageManager) {
618        if (mDescriptionResId == 0) {
619            return mNonLocalizedDescription;
620        }
621        ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
622        CharSequence description = packageManager.getText(serviceInfo.packageName,
623                mDescriptionResId, serviceInfo.applicationInfo);
624        if (description != null) {
625            return description.toString().trim();
626        }
627        return null;
628    }
629
630    /**
631     * {@inheritDoc}
632     */
633    public int describeContents() {
634        return 0;
635    }
636
637    public void writeToParcel(Parcel parcel, int flagz) {
638        parcel.writeInt(eventTypes);
639        parcel.writeStringArray(packageNames);
640        parcel.writeInt(feedbackType);
641        parcel.writeLong(notificationTimeout);
642        parcel.writeInt(flags);
643        parcel.writeString(mId);
644        parcel.writeParcelable(mResolveInfo, 0);
645        parcel.writeString(mSettingsActivityName);
646        parcel.writeInt(mCapabilities);
647        parcel.writeInt(mDescriptionResId);
648        parcel.writeString(mNonLocalizedDescription);
649    }
650
651    private void initFromParcel(Parcel parcel) {
652        eventTypes = parcel.readInt();
653        packageNames = parcel.readStringArray();
654        feedbackType = parcel.readInt();
655        notificationTimeout = parcel.readLong();
656        flags = parcel.readInt();
657        mId = parcel.readString();
658        mResolveInfo = parcel.readParcelable(null);
659        mSettingsActivityName = parcel.readString();
660        mCapabilities = parcel.readInt();
661        mDescriptionResId = parcel.readInt();
662        mNonLocalizedDescription = parcel.readString();
663    }
664
665    @Override
666    public int hashCode() {
667        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
668    }
669
670    @Override
671    public boolean equals(Object obj) {
672        if (this == obj) {
673            return true;
674        }
675        if (obj == null) {
676            return false;
677        }
678        if (getClass() != obj.getClass()) {
679            return false;
680        }
681        AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj;
682        if (mId == null) {
683            if (other.mId != null) {
684                return false;
685            }
686        } else if (!mId.equals(other.mId)) {
687            return false;
688        }
689        return true;
690    }
691
692    @Override
693    public String toString() {
694        StringBuilder stringBuilder = new StringBuilder();
695        appendEventTypes(stringBuilder, eventTypes);
696        stringBuilder.append(", ");
697        appendPackageNames(stringBuilder, packageNames);
698        stringBuilder.append(", ");
699        appendFeedbackTypes(stringBuilder, feedbackType);
700        stringBuilder.append(", ");
701        stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
702        stringBuilder.append(", ");
703        appendFlags(stringBuilder, flags);
704        stringBuilder.append(", ");
705        stringBuilder.append("id: ").append(mId);
706        stringBuilder.append(", ");
707        stringBuilder.append("resolveInfo: ").append(mResolveInfo);
708        stringBuilder.append(", ");
709        stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
710        stringBuilder.append(", ");
711        appendCapabilities(stringBuilder, mCapabilities);
712        return stringBuilder.toString();
713    }
714
715    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
716        stringBuilder.append("feedbackTypes:");
717        stringBuilder.append("[");
718        while (feedbackTypes != 0) {
719            final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
720            stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
721            feedbackTypes &= ~feedbackTypeBit;
722            if (feedbackTypes != 0) {
723                stringBuilder.append(", ");
724            }
725        }
726        stringBuilder.append("]");
727    }
728
729    private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
730        stringBuilder.append("packageNames:");
731        stringBuilder.append("[");
732        if (packageNames != null) {
733            final int packageNameCount = packageNames.length;
734            for (int i = 0; i < packageNameCount; i++) {
735                stringBuilder.append(packageNames[i]);
736                if (i < packageNameCount - 1) {
737                    stringBuilder.append(", ");
738                }
739            }
740        }
741        stringBuilder.append("]");
742    }
743
744    private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
745        stringBuilder.append("eventTypes:");
746        stringBuilder.append("[");
747        while (eventTypes != 0) {
748            final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
749            stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
750            eventTypes &= ~eventTypeBit;
751            if (eventTypes != 0) {
752                stringBuilder.append(", ");
753            }
754        }
755        stringBuilder.append("]");
756    }
757
758    private static void appendFlags(StringBuilder stringBuilder, int flags) {
759        stringBuilder.append("flags:");
760        stringBuilder.append("[");
761        while (flags != 0) {
762            final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
763            stringBuilder.append(flagToString(flagBit));
764            flags &= ~flagBit;
765            if (flags != 0) {
766                stringBuilder.append(", ");
767            }
768        }
769        stringBuilder.append("]");
770    }
771
772    private static void appendCapabilities(StringBuilder stringBuilder, int capabilities) {
773        stringBuilder.append("capabilities:");
774        stringBuilder.append("[");
775        while (capabilities != 0) {
776            final int capabilityBit = (1 << Integer.numberOfTrailingZeros(capabilities));
777            stringBuilder.append(capabilityToString(capabilityBit));
778            capabilities &= ~capabilityBit;
779            if (capabilities != 0) {
780                stringBuilder.append(", ");
781            }
782        }
783        stringBuilder.append("]");
784    }
785
786    /**
787     * Returns the string representation of a feedback type. For example,
788     * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
789     *
790     * @param feedbackType The feedback type.
791     * @return The string representation.
792     */
793    public static String feedbackTypeToString(int feedbackType) {
794        StringBuilder builder = new StringBuilder();
795        builder.append("[");
796        while (feedbackType != 0) {
797            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
798            feedbackType &= ~feedbackTypeFlag;
799            switch (feedbackTypeFlag) {
800                case FEEDBACK_AUDIBLE:
801                    if (builder.length() > 1) {
802                        builder.append(", ");
803                    }
804                    builder.append("FEEDBACK_AUDIBLE");
805                    break;
806                case FEEDBACK_HAPTIC:
807                    if (builder.length() > 1) {
808                        builder.append(", ");
809                    }
810                    builder.append("FEEDBACK_HAPTIC");
811                    break;
812                case FEEDBACK_GENERIC:
813                    if (builder.length() > 1) {
814                        builder.append(", ");
815                    }
816                    builder.append("FEEDBACK_GENERIC");
817                    break;
818                case FEEDBACK_SPOKEN:
819                    if (builder.length() > 1) {
820                        builder.append(", ");
821                    }
822                    builder.append("FEEDBACK_SPOKEN");
823                    break;
824                case FEEDBACK_VISUAL:
825                    if (builder.length() > 1) {
826                        builder.append(", ");
827                    }
828                    builder.append("FEEDBACK_VISUAL");
829                    break;
830                case FEEDBACK_BRAILLE:
831                    if (builder.length() > 1) {
832                        builder.append(", ");
833                    }
834                    builder.append("FEEDBACK_BRAILLE");
835                    break;
836            }
837        }
838        builder.append("]");
839        return builder.toString();
840    }
841
842    /**
843     * Returns the string representation of a flag. For example,
844     * {@link #DEFAULT} is represented by the string DEFAULT.
845     *
846     * @param flag The flag.
847     * @return The string representation.
848     */
849    public static String flagToString(int flag) {
850        switch (flag) {
851            case DEFAULT:
852                return "DEFAULT";
853            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
854                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
855            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
856                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
857            case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
858                return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
859            case FLAG_REPORT_VIEW_IDS:
860                return "FLAG_REPORT_VIEW_IDS";
861            case FLAG_REQUEST_FILTER_KEY_EVENTS:
862                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
863            default:
864                return null;
865        }
866    }
867
868    /**
869     * Returns the string representation of a capability. For example,
870     * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
871     * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
872     *
873     * @param capability The capability.
874     * @return The string representation.
875     */
876    public static String capabilityToString(int capability) {
877        switch (capability) {
878            case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
879                return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
880            case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
881                return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
882            case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
883                return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
884            case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS:
885                return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
886            default:
887                return "UNKNOWN";
888        }
889    }
890
891    /**
892     * @hide
893     * @return The list of {@link CapabilityInfo} objects.
894     */
895    public List<CapabilityInfo> getCapabilityInfos() {
896        if (mCapabilities == 0) {
897            return Collections.emptyList();
898        }
899        int capabilities = mCapabilities;
900        List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>();
901        while (capabilities != 0) {
902            final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities);
903            capabilities &= ~capabilityBit;
904            CapabilityInfo capabilityInfo = sAvailableCapabilityInfos.get(capabilityBit);
905            if (capabilityInfo != null) {
906                capabilityInfos.add(capabilityInfo);
907            }
908        }
909        return capabilityInfos;
910    }
911
912    /**
913     * @hide
914     */
915    public static final class CapabilityInfo {
916        public final int capability;
917        public final int titleResId;
918        public final int descResId;
919
920        public CapabilityInfo(int capability, int titleResId, int descResId) {
921            this.capability = capability;
922            this.titleResId = titleResId;
923            this.descResId = descResId;
924        }
925    }
926
927    /**
928     * @see Parcelable.Creator
929     */
930    public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
931            new Parcelable.Creator<AccessibilityServiceInfo>() {
932        public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
933            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
934            info.initFromParcel(parcel);
935            return info;
936        }
937
938        public AccessibilityServiceInfo[] newArray(int size) {
939            return new AccessibilityServiceInfo[size];
940        }
941    };
942}
943