AccessibilityServiceInfo.java revision f7174e87b6007000777b0124de9cef70d8618788
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} or
182     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are
183     * marked as potentially important for accessibility via
184     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
185     * that are not important for accessibility, are reported while querying the window
186     * content and also the accessibility service will receive accessibility events from
187     * them.
188     * <p>
189     * <strong>Note:</strong> For accessibility services targeting API version
190     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
191     * set for the system to regard views that are not important for accessibility. For
192     * accessibility services targeting API version lower than
193     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
194     * regarded for accessibility purposes.
195     * </p>
196     * <p>
197     * Usually views not important for accessibility are layout managers that do not
198     * react to user actions, do not draw any content, and do not have any special
199     * semantics in the context of the screen content. For example, a three by three
200     * grid can be implemented as three horizontal linear layouts and one vertical,
201     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
202     * In this context the actual layout mangers used to achieve the grid configuration
203     * are not important, rather it is important that there are nine evenly distributed
204     * elements.
205     * </p>
206     */
207    public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
208
209    /**
210     * This flag requests that the system gets into touch exploration mode.
211     * In this mode a single finger moving on the screen behaves as a mouse
212     * pointer hovering over the user interface. The system will also detect
213     * certain gestures performed on the touch screen and notify this service.
214     * The system will enable touch exploration mode if there is at least one
215     * accessibility service that has this flag set. Hence, clearing this
216     * flag does not guarantee that the device will not be in touch exploration
217     * mode since there may be another enabled service that requested it.
218     * <p>
219     * For accessibility services targeting API version higher than
220     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
221     * this flag have to declare this capability in their meta-data by setting
222     * the attribute {@link android.R.attr#canRequestTouchExplorationMode
223     * canRequestTouchExplorationMode} to true, otherwise this flag will
224     * be ignored. For how to declare the meta-data of a service refer to
225     * {@value AccessibilityService#SERVICE_META_DATA}.
226     * </p>
227     * <p>
228     * Services targeting API version equal to or lower than
229     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
230     * the first time they are run, if this flag is specified, a dialog is
231     * shown to the user to confirm enabling explore by touch.
232     * </p>
233     * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
234     */
235    public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
236
237    /**
238     * This flag requests from the system to enable web accessibility enhancing
239     * extensions. Such extensions aim to provide improved accessibility support
240     * for content presented in a {@link android.webkit.WebView}. An example of such
241     * an extension is injecting JavaScript from a secure source. The system will enable
242     * enhanced web accessibility if there is at least one accessibility service
243     * that has this flag set. Hence, clearing this flag does not guarantee that the
244     * device will not have enhanced web accessibility enabled since there may be
245     * another enabled service that requested it.
246     * <p>
247     * Services that want to set this flag have to declare this capability
248     * in their meta-data by setting the attribute {@link android.R.attr
249     * #canRequestEnhancedWebAccessibility canRequestEnhancedWebAccessibility} to
250     * true, otherwise this flag will be ignored. For how to declare the meta-data
251     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
252     * </p>
253     * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility
254     */
255    public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
256
257    /**
258     * This flag requests that the {@link AccessibilityNodeInfo}s obtained
259     * by an {@link AccessibilityService} contain the id of the source view.
260     * The source view id will be a fully qualified resource name of the
261     * form "package:id/name", for example "foo.bar:id/my_list", and it is
262     * useful for UI test automation. This flag is not set by default.
263     */
264    public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
265
266    /**
267     * This flag requests from the system to filter key events. If this flag
268     * is set the accessibility service will receive the key events before
269     * applications allowing it implement global shortcuts. Setting this flag
270     * does not guarantee that this service will filter key events since only
271     * one service can do so at any given time. This avoids user confusion due
272     * to behavior change in case different key filtering services are enabled.
273     * If there is already another key filtering service enabled, this one will
274     * not receive key events.
275     * <p>
276     * Services that want to set this flag have to declare this capability
277     * in their meta-data by setting the attribute {@link android.R.attr
278     * #canRequestFilterKeyEvents canRequestFilterKeyEvents} to true,
279     * otherwise this flag will be ignored. For how to declare the meta-data
280     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
281     * </p>
282     * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
283     */
284    public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
285
286    /**
287     * This flag indicates to the system that the accessibility service wants
288     * to access content of all interactive windows. An interactive window is a
289     * window that has input focus or can be touched by a sighted user when explore
290     * by touch is not enabled. If this flag is not set your service will not receive
291     * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED}
292     * events, calling AccessibilityService{@link AccessibilityService#getWindows()
293     * AccessibilityService.getWindows()} will return an empty list, and {@link
294     * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will
295     * return null.
296     * <p>
297     * Services that want to set this flag have to declare the capability
298     * to retrieve window content in their meta-data by setting the attribute
299     * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to
300     * true, otherwise this flag will be ignored. For how to declare the meta-data
301     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
302     * </p>
303     * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
304     */
305    public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;
306
307    /**
308     * The event types an {@link AccessibilityService} is interested in.
309     * <p>
310     *   <strong>Can be dynamically set at runtime.</strong>
311     * </p>
312     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
313     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
314     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
315     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
316     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
317     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
318     * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
319     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
320     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
321     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
322     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
323     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
324     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
325     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
326     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START
327     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END
328     * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT
329     * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START
330     * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END
331     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED
332     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
333     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
334     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED
335     */
336    public int eventTypes;
337
338    /**
339     * The package names an {@link AccessibilityService} is interested in. Setting
340     * to <code>null</code> is equivalent to all packages.
341     * <p>
342     *   <strong>Can be dynamically set at runtime.</strong>
343     * </p>
344     */
345    public String[] packageNames;
346
347    /**
348     * The feedback type an {@link AccessibilityService} provides.
349     * <p>
350     *   <strong>Can be dynamically set at runtime.</strong>
351     * </p>
352     * @see #FEEDBACK_AUDIBLE
353     * @see #FEEDBACK_GENERIC
354     * @see #FEEDBACK_HAPTIC
355     * @see #FEEDBACK_SPOKEN
356     * @see #FEEDBACK_VISUAL
357     * @see #FEEDBACK_BRAILLE
358     */
359    public int feedbackType;
360
361    /**
362     * The timeout after the most recent event of a given type before an
363     * {@link AccessibilityService} is notified.
364     * <p>
365     *   <strong>Can be dynamically set at runtime.</strong>.
366     * </p>
367     * <p>
368     * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
369     *       events to the client too frequently since this is accomplished via an expensive
370     *       interprocess call. One can think of the timeout as a criteria to determine when
371     *       event generation has settled down.
372     */
373    public long notificationTimeout;
374
375    /**
376     * This field represents a set of flags used for configuring an
377     * {@link AccessibilityService}.
378     * <p>
379     *   <strong>Can be dynamically set at runtime.</strong>
380     * </p>
381     * @see #DEFAULT
382     * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
383     * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
384     * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
385     * @see #FLAG_REQUEST_FILTER_KEY_EVENTS
386     * @see #FLAG_REPORT_VIEW_IDS
387     * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
388     */
389    public int flags;
390
391    /**
392     * The unique string Id to identify the accessibility service.
393     */
394    private String mId;
395
396    /**
397     * The Service that implements this accessibility service component.
398     */
399    private ResolveInfo mResolveInfo;
400
401    /**
402     * The accessibility service setting activity's name, used by the system
403     * settings to launch the setting activity of this accessibility service.
404     */
405    private String mSettingsActivityName;
406
407    /**
408     * Bit mask with capabilities of this service.
409     */
410    private int mCapabilities;
411
412    /**
413     * Resource id of the description of the accessibility service.
414     */
415    private int mDescriptionResId;
416
417    /**
418     * Non localized description of the accessibility service.
419     */
420    private String mNonLocalizedDescription;
421
422    /**
423     * Creates a new instance.
424     */
425    public AccessibilityServiceInfo() {
426        /* do nothing */
427    }
428
429    /**
430     * Creates a new instance.
431     *
432     * @param resolveInfo The service resolve info.
433     * @param context Context for accessing resources.
434     * @throws XmlPullParserException If a XML parsing error occurs.
435     * @throws IOException If a XML parsing error occurs.
436     *
437     * @hide
438     */
439    public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
440            throws XmlPullParserException, IOException {
441        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
442        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
443        mResolveInfo = resolveInfo;
444
445        XmlResourceParser parser = null;
446
447        try {
448            PackageManager packageManager = context.getPackageManager();
449            parser = serviceInfo.loadXmlMetaData(packageManager,
450                    AccessibilityService.SERVICE_META_DATA);
451            if (parser == null) {
452                return;
453            }
454
455            int type = 0;
456            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
457                type = parser.next();
458            }
459
460            String nodeName = parser.getName();
461            if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
462                throw new XmlPullParserException( "Meta-data does not start with"
463                        + TAG_ACCESSIBILITY_SERVICE + " tag");
464            }
465
466            AttributeSet allAttributes = Xml.asAttributeSet(parser);
467            Resources resources = packageManager.getResourcesForApplication(
468                    serviceInfo.applicationInfo);
469            TypedArray asAttributes = resources.obtainAttributes(allAttributes,
470                    com.android.internal.R.styleable.AccessibilityService);
471            eventTypes = asAttributes.getInt(
472                    com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
473                    0);
474            String packageNamez = asAttributes.getString(
475                    com.android.internal.R.styleable.AccessibilityService_packageNames);
476            if (packageNamez != null) {
477                packageNames = packageNamez.split("(\\s)*,(\\s)*");
478            }
479            feedbackType = asAttributes.getInt(
480                    com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
481                    0);
482            notificationTimeout = asAttributes.getInt(
483                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
484                    0);
485            flags = asAttributes.getInt(
486                    com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
487            mSettingsActivityName = asAttributes.getString(
488                    com.android.internal.R.styleable.AccessibilityService_settingsActivity);
489            if (asAttributes.getBoolean(com.android.internal.R.styleable
490                    .AccessibilityService_canRetrieveWindowContent, false)) {
491                mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
492            }
493            if (asAttributes.getBoolean(com.android.internal.R.styleable
494                    .AccessibilityService_canRequestTouchExplorationMode, false)) {
495                mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION;
496            }
497            if (asAttributes.getBoolean(com.android.internal.R.styleable
498                        .AccessibilityService_canRequestEnhancedWebAccessibility, false)) {
499                    mCapabilities |= CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY;
500            }
501            if (asAttributes.getBoolean(com.android.internal.R.styleable
502                    .AccessibilityService_canRequestFilterKeyEvents, false)) {
503                mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
504            }
505            TypedValue peekedValue = asAttributes.peekValue(
506                    com.android.internal.R.styleable.AccessibilityService_description);
507            if (peekedValue != null) {
508                mDescriptionResId = peekedValue.resourceId;
509                CharSequence nonLocalizedDescription = peekedValue.coerceToString();
510                if (nonLocalizedDescription != null) {
511                    mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
512                }
513            }
514            asAttributes.recycle();
515        } catch (NameNotFoundException e) {
516            throw new XmlPullParserException( "Unable to create context for: "
517                    + serviceInfo.packageName);
518        } finally {
519            if (parser != null) {
520                parser.close();
521            }
522        }
523    }
524
525    /**
526     * Updates the properties that an AccessibilitySerivice can change dynamically.
527     *
528     * @param other The info from which to update the properties.
529     *
530     * @hide
531     */
532    public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
533        eventTypes = other.eventTypes;
534        packageNames = other.packageNames;
535        feedbackType = other.feedbackType;
536        notificationTimeout = other.notificationTimeout;
537        flags = other.flags;
538    }
539
540    /**
541     * @hide
542     */
543    public void setComponentName(ComponentName component) {
544        mId = component.flattenToShortString();
545    }
546
547    /**
548     * The accessibility service id.
549     * <p>
550     *   <strong>Generated by the system.</strong>
551     * </p>
552     * @return The id.
553     */
554    public String getId() {
555        return mId;
556    }
557
558    /**
559     * The service {@link ResolveInfo}.
560     * <p>
561     *   <strong>Generated by the system.</strong>
562     * </p>
563     * @return The info.
564     */
565    public ResolveInfo getResolveInfo() {
566        return mResolveInfo;
567    }
568
569    /**
570     * The settings activity name.
571     * <p>
572     *    <strong>Statically set from
573     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
574     * </p>
575     * @return The settings activity name.
576     */
577    public String getSettingsActivityName() {
578        return mSettingsActivityName;
579    }
580
581    /**
582     * Whether this service can retrieve the current window's content.
583     * <p>
584     *    <strong>Statically set from
585     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
586     * </p>
587     * @return True if window content can be retrieved.
588     *
589     * @deprecated Use {@link #getCapabilities()}.
590     */
591    public boolean getCanRetrieveWindowContent() {
592        return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
593    }
594
595    /**
596     * Returns the bit mask of capabilities this accessibility service has such as
597     * being able to retrieve the active window content, etc.
598     *
599     * @return The capability bit mask.
600     *
601     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
602     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
603     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
604     * @see #CAPABILITY_FILTER_KEY_EVENTS
605     */
606    public int getCapabilities() {
607        return mCapabilities;
608    }
609
610    /**
611     * Sets the bit mask of capabilities this accessibility service has such as
612     * being able to retrieve the active window content, etc.
613     *
614     * @param capabilities The capability bit mask.
615     *
616     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
617     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
618     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
619     * @see #CAPABILITY_FILTER_KEY_EVENTS
620     *
621     * @hide
622     */
623    public void setCapabilities(int capabilities) {
624        mCapabilities = capabilities;
625    }
626
627    /**
628     * Gets the non-localized description of the accessibility service.
629     * <p>
630     *    <strong>Statically set from
631     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
632     * </p>
633     * @return The description.
634     *
635     * @deprecated Use {@link #loadDescription(PackageManager)}.
636     */
637    public String getDescription() {
638        return mNonLocalizedDescription;
639    }
640
641    /**
642     * The localized description of the accessibility service.
643     * <p>
644     *    <strong>Statically set from
645     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
646     * </p>
647     * @return The localized description.
648     */
649    public String loadDescription(PackageManager packageManager) {
650        if (mDescriptionResId == 0) {
651            return mNonLocalizedDescription;
652        }
653        ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
654        CharSequence description = packageManager.getText(serviceInfo.packageName,
655                mDescriptionResId, serviceInfo.applicationInfo);
656        if (description != null) {
657            return description.toString().trim();
658        }
659        return null;
660    }
661
662    /**
663     * {@inheritDoc}
664     */
665    public int describeContents() {
666        return 0;
667    }
668
669    public void writeToParcel(Parcel parcel, int flagz) {
670        parcel.writeInt(eventTypes);
671        parcel.writeStringArray(packageNames);
672        parcel.writeInt(feedbackType);
673        parcel.writeLong(notificationTimeout);
674        parcel.writeInt(flags);
675        parcel.writeString(mId);
676        parcel.writeParcelable(mResolveInfo, 0);
677        parcel.writeString(mSettingsActivityName);
678        parcel.writeInt(mCapabilities);
679        parcel.writeInt(mDescriptionResId);
680        parcel.writeString(mNonLocalizedDescription);
681    }
682
683    private void initFromParcel(Parcel parcel) {
684        eventTypes = parcel.readInt();
685        packageNames = parcel.readStringArray();
686        feedbackType = parcel.readInt();
687        notificationTimeout = parcel.readLong();
688        flags = parcel.readInt();
689        mId = parcel.readString();
690        mResolveInfo = parcel.readParcelable(null);
691        mSettingsActivityName = parcel.readString();
692        mCapabilities = parcel.readInt();
693        mDescriptionResId = parcel.readInt();
694        mNonLocalizedDescription = parcel.readString();
695    }
696
697    @Override
698    public int hashCode() {
699        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
700    }
701
702    @Override
703    public boolean equals(Object obj) {
704        if (this == obj) {
705            return true;
706        }
707        if (obj == null) {
708            return false;
709        }
710        if (getClass() != obj.getClass()) {
711            return false;
712        }
713        AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj;
714        if (mId == null) {
715            if (other.mId != null) {
716                return false;
717            }
718        } else if (!mId.equals(other.mId)) {
719            return false;
720        }
721        return true;
722    }
723
724    @Override
725    public String toString() {
726        StringBuilder stringBuilder = new StringBuilder();
727        appendEventTypes(stringBuilder, eventTypes);
728        stringBuilder.append(", ");
729        appendPackageNames(stringBuilder, packageNames);
730        stringBuilder.append(", ");
731        appendFeedbackTypes(stringBuilder, feedbackType);
732        stringBuilder.append(", ");
733        stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
734        stringBuilder.append(", ");
735        appendFlags(stringBuilder, flags);
736        stringBuilder.append(", ");
737        stringBuilder.append("id: ").append(mId);
738        stringBuilder.append(", ");
739        stringBuilder.append("resolveInfo: ").append(mResolveInfo);
740        stringBuilder.append(", ");
741        stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
742        stringBuilder.append(", ");
743        appendCapabilities(stringBuilder, mCapabilities);
744        return stringBuilder.toString();
745    }
746
747    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
748        stringBuilder.append("feedbackTypes:");
749        stringBuilder.append("[");
750        while (feedbackTypes != 0) {
751            final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
752            stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
753            feedbackTypes &= ~feedbackTypeBit;
754            if (feedbackTypes != 0) {
755                stringBuilder.append(", ");
756            }
757        }
758        stringBuilder.append("]");
759    }
760
761    private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
762        stringBuilder.append("packageNames:");
763        stringBuilder.append("[");
764        if (packageNames != null) {
765            final int packageNameCount = packageNames.length;
766            for (int i = 0; i < packageNameCount; i++) {
767                stringBuilder.append(packageNames[i]);
768                if (i < packageNameCount - 1) {
769                    stringBuilder.append(", ");
770                }
771            }
772        }
773        stringBuilder.append("]");
774    }
775
776    private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
777        stringBuilder.append("eventTypes:");
778        stringBuilder.append("[");
779        while (eventTypes != 0) {
780            final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
781            stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
782            eventTypes &= ~eventTypeBit;
783            if (eventTypes != 0) {
784                stringBuilder.append(", ");
785            }
786        }
787        stringBuilder.append("]");
788    }
789
790    private static void appendFlags(StringBuilder stringBuilder, int flags) {
791        stringBuilder.append("flags:");
792        stringBuilder.append("[");
793        while (flags != 0) {
794            final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
795            stringBuilder.append(flagToString(flagBit));
796            flags &= ~flagBit;
797            if (flags != 0) {
798                stringBuilder.append(", ");
799            }
800        }
801        stringBuilder.append("]");
802    }
803
804    private static void appendCapabilities(StringBuilder stringBuilder, int capabilities) {
805        stringBuilder.append("capabilities:");
806        stringBuilder.append("[");
807        while (capabilities != 0) {
808            final int capabilityBit = (1 << Integer.numberOfTrailingZeros(capabilities));
809            stringBuilder.append(capabilityToString(capabilityBit));
810            capabilities &= ~capabilityBit;
811            if (capabilities != 0) {
812                stringBuilder.append(", ");
813            }
814        }
815        stringBuilder.append("]");
816    }
817
818    /**
819     * Returns the string representation of a feedback type. For example,
820     * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
821     *
822     * @param feedbackType The feedback type.
823     * @return The string representation.
824     */
825    public static String feedbackTypeToString(int feedbackType) {
826        StringBuilder builder = new StringBuilder();
827        builder.append("[");
828        while (feedbackType != 0) {
829            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
830            feedbackType &= ~feedbackTypeFlag;
831            switch (feedbackTypeFlag) {
832                case FEEDBACK_AUDIBLE:
833                    if (builder.length() > 1) {
834                        builder.append(", ");
835                    }
836                    builder.append("FEEDBACK_AUDIBLE");
837                    break;
838                case FEEDBACK_HAPTIC:
839                    if (builder.length() > 1) {
840                        builder.append(", ");
841                    }
842                    builder.append("FEEDBACK_HAPTIC");
843                    break;
844                case FEEDBACK_GENERIC:
845                    if (builder.length() > 1) {
846                        builder.append(", ");
847                    }
848                    builder.append("FEEDBACK_GENERIC");
849                    break;
850                case FEEDBACK_SPOKEN:
851                    if (builder.length() > 1) {
852                        builder.append(", ");
853                    }
854                    builder.append("FEEDBACK_SPOKEN");
855                    break;
856                case FEEDBACK_VISUAL:
857                    if (builder.length() > 1) {
858                        builder.append(", ");
859                    }
860                    builder.append("FEEDBACK_VISUAL");
861                    break;
862                case FEEDBACK_BRAILLE:
863                    if (builder.length() > 1) {
864                        builder.append(", ");
865                    }
866                    builder.append("FEEDBACK_BRAILLE");
867                    break;
868            }
869        }
870        builder.append("]");
871        return builder.toString();
872    }
873
874    /**
875     * Returns the string representation of a flag. For example,
876     * {@link #DEFAULT} is represented by the string DEFAULT.
877     *
878     * @param flag The flag.
879     * @return The string representation.
880     */
881    public static String flagToString(int flag) {
882        switch (flag) {
883            case DEFAULT:
884                return "DEFAULT";
885            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
886                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
887            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
888                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
889            case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
890                return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
891            case FLAG_REPORT_VIEW_IDS:
892                return "FLAG_REPORT_VIEW_IDS";
893            case FLAG_REQUEST_FILTER_KEY_EVENTS:
894                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
895            case FLAG_RETRIEVE_INTERACTIVE_WINDOWS:
896                return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
897            default:
898                return null;
899        }
900    }
901
902    /**
903     * Returns the string representation of a capability. For example,
904     * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
905     * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
906     *
907     * @param capability The capability.
908     * @return The string representation.
909     */
910    public static String capabilityToString(int capability) {
911        switch (capability) {
912            case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
913                return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
914            case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
915                return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
916            case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
917                return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
918            case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS:
919                return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
920            default:
921                return "UNKNOWN";
922        }
923    }
924
925    /**
926     * @hide
927     * @return The list of {@link CapabilityInfo} objects.
928     */
929    public List<CapabilityInfo> getCapabilityInfos() {
930        if (mCapabilities == 0) {
931            return Collections.emptyList();
932        }
933        int capabilities = mCapabilities;
934        List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>();
935        while (capabilities != 0) {
936            final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities);
937            capabilities &= ~capabilityBit;
938            CapabilityInfo capabilityInfo = sAvailableCapabilityInfos.get(capabilityBit);
939            if (capabilityInfo != null) {
940                capabilityInfos.add(capabilityInfo);
941            }
942        }
943        return capabilityInfos;
944    }
945
946    /**
947     * @hide
948     */
949    public static final class CapabilityInfo {
950        public final int capability;
951        public final int titleResId;
952        public final int descResId;
953
954        public CapabilityInfo(int capability, int titleResId, int descResId) {
955            this.capability = capability;
956            this.titleResId = titleResId;
957            this.descResId = descResId;
958        }
959    }
960
961    /**
962     * @see Parcelable.Creator
963     */
964    public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
965            new Parcelable.Creator<AccessibilityServiceInfo>() {
966        public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
967            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
968            info.initFromParcel(parcel);
969            return info;
970        }
971
972        public AccessibilityServiceInfo[] newArray(int size) {
973            return new AccessibilityServiceInfo[size];
974        }
975    };
976}
977