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