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