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