AccessibilityServiceInfo.java revision 35bfedeaba724aeadc6f6c890269cb6bf7ef42f5
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.Parcel;
29import android.os.Parcelable;
30import android.util.AttributeSet;
31import android.util.Xml;
32import android.view.accessibility.AccessibilityEvent;
33
34import org.xmlpull.v1.XmlPullParser;
35import org.xmlpull.v1.XmlPullParserException;
36
37import java.io.IOException;
38
39/**
40 * This class describes an {@link AccessibilityService}. The system notifies an
41 * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
42 * according to the information encapsulated in this class.
43 *
44 * @see AccessibilityService
45 * @see android.view.accessibility.AccessibilityEvent
46 * @see android.view.accessibility.AccessibilityManager
47 */
48public class AccessibilityServiceInfo implements Parcelable {
49
50    private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service";
51
52    /**
53     * Denotes spoken feedback.
54     */
55    public static final int FEEDBACK_SPOKEN = 0x0000001;
56
57    /**
58     * Denotes haptic feedback.
59     */
60    public static final int FEEDBACK_HAPTIC =  0x0000002;
61
62    /**
63     * Denotes audible (not spoken) feedback.
64     */
65    public static final int FEEDBACK_AUDIBLE = 0x0000004;
66
67    /**
68     * Denotes visual feedback.
69     */
70    public static final int FEEDBACK_VISUAL = 0x0000008;
71
72    /**
73     * Denotes generic feedback.
74     */
75    public static final int FEEDBACK_GENERIC = 0x0000010;
76
77    /**
78     * If an {@link AccessibilityService} is the default for a given type.
79     * Default service is invoked only if no package specific one exists. In case of
80     * more than one package specific service only the earlier registered is notified.
81     */
82    public static final int DEFAULT = 0x0000001;
83
84    /**
85     * The event types an {@link AccessibilityService} is interested in.
86     * <p>
87     *   <strong>Can be dynamically set at runtime.</strong>
88     * </p>
89     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
90     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
91     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
92     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
93     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
94     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
95     * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
96     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START
97     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END
98     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER
99     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT
100     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
101     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
102     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
103     */
104    public int eventTypes;
105
106    /**
107     * The package names an {@link AccessibilityService} is interested in. Setting
108     * to <code>null</code> is equivalent to all packages.
109     * <p>
110     *   <strong>Can be dynamically set at runtime.</strong>
111     * </p>
112     */
113    public String[] packageNames;
114
115    /**
116     * The feedback type an {@link AccessibilityService} provides.
117     * <p>
118     *   <strong>Can be dynamically set at runtime.</strong>
119     * </p>
120     * @see #FEEDBACK_AUDIBLE
121     * @see #FEEDBACK_GENERIC
122     * @see #FEEDBACK_HAPTIC
123     * @see #FEEDBACK_SPOKEN
124     * @see #FEEDBACK_VISUAL
125     */
126    public int feedbackType;
127
128    /**
129     * The timeout after the most recent event of a given type before an
130     * {@link AccessibilityService} is notified.
131     * <p>
132     *   <strong>Can be dynamically set at runtime.</strong>.
133     * </p>
134     * <p>
135     * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
136     *       events to the client too frequently since this is accomplished via an expensive
137     *       interprocess call. One can think of the timeout as a criteria to determine when
138     *       event generation has settled down.
139     */
140    public long notificationTimeout;
141
142    /**
143     * This field represents a set of flags used for configuring an
144     * {@link AccessibilityService}.
145     * <p>
146     *   <strong>Can be dynamically set at runtime.</strong>
147     * </p>
148     * @see #DEFAULT
149     */
150    public int flags;
151
152    /**
153     * The unique string Id to identify the accessibility service.
154     */
155    private String mId;
156
157    /**
158     * The Service that implements this accessibility service component.
159     */
160    private ResolveInfo mResolveInfo;
161
162    /**
163     * The accessibility service setting activity's name, used by the system
164     * settings to launch the setting activity of this accessibility service.
165     */
166    private String mSettingsActivityName;
167
168    /**
169     * Flag whether this accessibility service can retrieve window content.
170     */
171    private boolean mCanRetrieveWindowContent;
172
173    /**
174     * Description of the accessibility service.
175     */
176    private String mDescription;
177
178    /**
179     * Creates a new instance.
180     */
181    public AccessibilityServiceInfo() {
182        /* do nothing */
183    }
184
185    /**
186     * Creates a new instance.
187     *
188     * @param resolveInfo The service resolve info.
189     * @param context Context for accessing resources.
190     * @throws XmlPullParserException If a XML parsing error occurs.
191     * @throws IOException If a XML parsing error occurs.
192     *
193     * @hide
194     */
195    public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
196            throws XmlPullParserException, IOException {
197        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
198        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
199        mResolveInfo = resolveInfo;
200
201        XmlResourceParser parser = null;
202
203        try {
204            PackageManager packageManager = context.getPackageManager();
205            parser = serviceInfo.loadXmlMetaData(packageManager,
206                    AccessibilityService.SERVICE_META_DATA);
207            if (parser == null) {
208                return;
209            }
210
211            int type = 0;
212            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
213                type = parser.next();
214            }
215
216            String nodeName = parser.getName();
217            if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) {
218                throw new XmlPullParserException( "Meta-data does not start with"
219                        + TAG_ACCESSIBILITY_SERVICE + " tag");
220            }
221
222            AttributeSet allAttributes = Xml.asAttributeSet(parser);
223            Resources resources = packageManager.getResourcesForApplication(
224                    serviceInfo.applicationInfo);
225            TypedArray asAttributes = resources.obtainAttributes(allAttributes,
226                    com.android.internal.R.styleable.AccessibilityService);
227            eventTypes = asAttributes.getInt(
228                    com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes,
229                    0);
230            String packageNamez = asAttributes.getString(
231                    com.android.internal.R.styleable.AccessibilityService_packageNames);
232            if (packageNamez != null) {
233                packageNames = packageNamez.split("(\\s)*,(\\s)*");
234            }
235            feedbackType = asAttributes.getInt(
236                    com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
237                    0);
238            notificationTimeout = asAttributes.getInt(
239                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
240                    0);
241            flags = asAttributes.getInt(
242                    com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
243            mSettingsActivityName = asAttributes.getString(
244                    com.android.internal.R.styleable.AccessibilityService_settingsActivity);
245            mCanRetrieveWindowContent = asAttributes.getBoolean(
246                    com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent,
247                    false);
248            mDescription = asAttributes.getString(
249                    com.android.internal.R.styleable.AccessibilityService_description);
250            asAttributes.recycle();
251        } catch (NameNotFoundException e) {
252            throw new XmlPullParserException( "Unable to create context for: "
253                    + serviceInfo.packageName);
254        } finally {
255            if (parser != null) {
256                parser.close();
257            }
258        }
259    }
260
261    /**
262     * Updates the properties that an AccessibilitySerivice can change dynamically.
263     *
264     * @param other The info from which to update the properties.
265     *
266     * @hide
267     */
268    public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) {
269        eventTypes = other.eventTypes;
270        packageNames = other.packageNames;
271        feedbackType = other.feedbackType;
272        notificationTimeout = other.notificationTimeout;
273        flags = other.flags;
274    }
275
276    /**
277     * The accessibility service id.
278     * <p>
279     *   <strong>Generated by the system.</strong>
280     * </p>
281     * @return The id.
282     */
283    public String getId() {
284        return mId;
285    }
286
287    /**
288     * The service {@link ResolveInfo}.
289     * <p>
290     *   <strong>Generated by the system.</strong>
291     * </p>
292     * @return The info.
293     */
294    public ResolveInfo getResolveInfo() {
295        return mResolveInfo;
296    }
297
298    /**
299     * The settings activity name.
300     * <p>
301     *    <strong>Statically set from
302     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
303     * </p>
304     * @return The settings activity name.
305     */
306    public String getSettingsActivityName() {
307        return mSettingsActivityName;
308    }
309
310    /**
311     * Whether this service can retrieve the current window's content.
312     * <p>
313     *    <strong>Statically set from
314     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
315     * </p>
316     * @return True window content can be retrieved.
317     */
318    public boolean getCanRetrieveWindowContent() {
319        return mCanRetrieveWindowContent;
320    }
321
322    /**
323     * Description of the accessibility service.
324     * <p>
325     *    <strong>Statically set from
326     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
327     * </p>
328     * @return The description.
329     */
330    public String getDescription() {
331        return mDescription;
332    }
333
334    /**
335     * {@inheritDoc}
336     */
337    public int describeContents() {
338        return 0;
339    }
340
341    public void writeToParcel(Parcel parcel, int flagz) {
342        parcel.writeInt(eventTypes);
343        parcel.writeStringArray(packageNames);
344        parcel.writeInt(feedbackType);
345        parcel.writeLong(notificationTimeout);
346        parcel.writeInt(flags);
347        parcel.writeString(mId);
348        parcel.writeParcelable(mResolveInfo, 0);
349        parcel.writeString(mSettingsActivityName);
350        parcel.writeInt(mCanRetrieveWindowContent ? 1 : 0);
351        parcel.writeString(mDescription);
352    }
353
354    private void initFromParcel(Parcel parcel) {
355        eventTypes = parcel.readInt();
356        packageNames = parcel.readStringArray();
357        feedbackType = parcel.readInt();
358        notificationTimeout = parcel.readLong();
359        flags = parcel.readInt();
360        mId = parcel.readString();
361        mResolveInfo = parcel.readParcelable(null);
362        mSettingsActivityName = parcel.readString();
363        mCanRetrieveWindowContent = (parcel.readInt() == 1);
364        mDescription = parcel.readString();
365    }
366
367    @Override
368    public String toString() {
369        StringBuilder stringBuilder = new StringBuilder();
370        appendEventTypes(stringBuilder, eventTypes);
371        stringBuilder.append(", ");
372        appendPackageNames(stringBuilder, packageNames);
373        stringBuilder.append(", ");
374        appendFeedbackTypes(stringBuilder, feedbackType);
375        stringBuilder.append(", ");
376        stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
377        stringBuilder.append(", ");
378        appendFlags(stringBuilder, flags);
379        stringBuilder.append(", ");
380        stringBuilder.append("id: ").append(mId);
381        stringBuilder.append(", ");
382        stringBuilder.append("resolveInfo: ").append(mResolveInfo);
383        stringBuilder.append(", ");
384        stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
385        stringBuilder.append(", ");
386        stringBuilder.append("retrieveScreenContent: ").append(mCanRetrieveWindowContent);
387        return stringBuilder.toString();
388    }
389
390    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
391        stringBuilder.append("feedbackTypes:");
392        stringBuilder.append("[");
393        while (feedbackTypes != 0) {
394            final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes));
395            stringBuilder.append(feedbackTypeToString(feedbackTypeBit));
396            feedbackTypes &= ~feedbackTypeBit;
397            if (feedbackTypes != 0) {
398                stringBuilder.append(", ");
399            }
400        }
401        stringBuilder.append("]");
402    }
403
404    private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) {
405        stringBuilder.append("packageNames:");
406        stringBuilder.append("[");
407        if (packageNames != null) {
408            final int packageNameCount = packageNames.length;
409            for (int i = 0; i < packageNameCount; i++) {
410                stringBuilder.append(packageNames[i]);
411                if (i < packageNameCount - 1) {
412                    stringBuilder.append(", ");
413                }
414            }
415        }
416        stringBuilder.append("]");
417    }
418
419    private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) {
420        stringBuilder.append("eventTypes:");
421        stringBuilder.append("[");
422        while (eventTypes != 0) {
423            final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes));
424            stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit));
425            eventTypes &= ~eventTypeBit;
426            if (eventTypes != 0) {
427                stringBuilder.append(", ");
428            }
429        }
430        stringBuilder.append("]");
431    }
432
433    private static void appendFlags(StringBuilder stringBuilder, int flags) {
434        stringBuilder.append("flags:");
435        stringBuilder.append("[");
436        while (flags != 0) {
437            final int flagBit = (1 << Integer.numberOfTrailingZeros(flags));
438            stringBuilder.append(flagToString(flagBit));
439            flags &= ~flagBit;
440            if (flags != 0) {
441                stringBuilder.append(", ");
442            }
443        }
444        stringBuilder.append("]");
445    }
446
447    /**
448     * Returns the string representation of a feedback type. For example,
449     * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN.
450     *
451     * @param feedbackType The feedback type.
452     * @return The string representation.
453     */
454    public static String feedbackTypeToString(int feedbackType) {
455        switch (feedbackType) {
456            case FEEDBACK_AUDIBLE:
457                return "FEEDBACK_AUDIBLE";
458            case FEEDBACK_HAPTIC:
459                return "FEEDBACK_HAPTIC";
460            case FEEDBACK_GENERIC:
461                return "FEEDBACK_GENERIC";
462            case FEEDBACK_SPOKEN:
463                return "FEEDBACK_SPOKEN";
464            case FEEDBACK_VISUAL:
465                return "FEEDBACK_VISUAL";
466            default:
467                return null;
468        }
469    }
470
471    /**
472     * Returns the string representation of a flag. For example,
473     * {@link #DEFAULT} is represented by the string DEFAULT.
474     *
475     * @param flag The flag.
476     * @return The string representation.
477     */
478    public static String flagToString(int flag) {
479        switch (flag) {
480            case DEFAULT:
481                return "DEFAULT";
482            default:
483                return null;
484        }
485    }
486
487    /**
488     * @see Parcelable.Creator
489     */
490    public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
491            new Parcelable.Creator<AccessibilityServiceInfo>() {
492        public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
493            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
494            info.initFromParcel(parcel);
495            return info;
496        }
497
498        public AccessibilityServiceInfo[] newArray(int size) {
499            return new AccessibilityServiceInfo[size];
500        }
501    };
502}
503