AccessibilityService.java revision cc4053e031371456fe54d51bbad1db721db4ae38
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 com.android.internal.os.HandlerCaller;
20
21import android.app.Service;
22import android.content.Intent;
23import android.os.IBinder;
24import android.os.Message;
25import android.os.RemoteException;
26import android.util.Log;
27import android.view.accessibility.AccessibilityEvent;
28
29/**
30 * An accessibility service runs in the background and receives callbacks by the system
31 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
32 * in the user interface, for example, the focus has changed, a button has been clicked,
33 * etc.
34 * <p>
35 * An accessibility service extends this class and implements its abstract methods. Such
36 * a service is declared as any other service in an AndroidManifest.xml but it must also
37 * specify that it handles the "android.accessibilityservice.AccessibilityService"
38 * {@link android.content.Intent}. Following is an example of such a declaration:
39 * <p>
40 * <code>
41 * &lt;service android:name=".MyAccessibilityService"&gt;<br>
42 * &nbsp;&nbsp;&lt;intent-filter&gt;<br>
43 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;<br>
44 * &nbsp;&nbsp;&lt;/intent-filter&gt;<br>
45 * &lt;/service&gt;<br>
46 * </code>
47 * </p>
48 * <p>
49 * The lifecycle of an accessibility service is managed exclusively by the system. Starting
50 * or stopping an accessibility service is triggered by an explicit user action through
51 * enabling or disabling it in the device settings. After the system binds to a service it
52 * calls {@link AccessibilityService#onServiceConnected()}. This method can be
53 * overriden by clients that want to perform post binding setup.
54 * <p>
55 * </p>
56 * There are two approaches for configuring an accessibility service:
57 * <ul>
58 *   <li>
59 *     Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
60 *     the service. A service declaration with a meta-data tag is presented below:
61 *     <p>
62 *     <code>
63 *       &lt;service android:name=".MyAccessibilityService"&gt;<br>
64 *       &nbsp;&nbsp;&lt;intent-filter&gt;<br>
65 *       &nbsp;&nbsp;&nbsp;&nbsp;&lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;<br>
66 *       &nbsp;&nbsp;&lt;/intent-filter&gt;<br>
67 *       &nbsp;&nbsp;&lt;meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /&gt;<br>
68 *       &lt;/service&gt;<br>
69 *     </code>
70 *     </p>
71 *     <p>
72 *     <strong>
73 *       This approach enables setting all accessibility service properties.
74 *     </strong>
75 *     </p>
76 *   </li>
77 *   <li>
78 *     Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
79 *     that this method can be called any time to change the service configuration.<br>
80 *     <p>
81 *     <strong>
82 *       This approach enables setting only dynamically configurable accessibility
83 *       service properties:
84 *       {@link AccessibilityServiceInfo#eventTypes},
85 *       {@link AccessibilityServiceInfo#feedbackType},
86 *       {@link AccessibilityServiceInfo#flags},
87 *       {@link AccessibilityServiceInfo#notificationTimeout},
88 *       {@link AccessibilityServiceInfo#packageNames}
89 *     </strong>
90 *     </p>
91 *   </li>
92 * </ul>
93 * <p>
94 * An accessibility service can be registered for events in specific packages to provide a
95 * specific type of feedback and is notified with a certain timeout after the last event
96 * of interest has been fired.
97 * <p>
98 * <b>Notification strategy</b>
99 * <p>
100 * For each feedback type only one accessibility service is notified. Services are notified
101 * in the order of registration. Hence, if two services are registered for the same
102 * feedback type in the same package the first one wins. It is possible however, to
103 * register a service as the default one for a given feedback type. In such a case this
104 * service is invoked if no other service was interested in the event. In other words, default
105 * services do not compete with other services and are notified last regardless of the
106 * registration order. This enables "generic" accessibility services that work reasonably
107 * well with most applications to coexist with "polished" ones that are targeted for
108 * specific applications.
109 * <p>
110 * <b>Event types</b>
111 * <p>
112 * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}
113 * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
114 * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
115 * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}
116 * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
117 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
118 * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
119 *  <p>
120 *  <b>Feedback types</b>
121 *  <p>
122 * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
123 * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
124 * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
125 * {@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
126 * {@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
127 *
128 * @see AccessibilityEvent
129 * @see AccessibilityServiceInfo
130 * @see android.view.accessibility.AccessibilityManager
131 *
132 * Note: The event notification timeout is useful to avoid propagating events to the client
133 *       too frequently since this is accomplished via an expensive interprocess call.
134 *       One can think of the timeout as a criteria to determine when event generation has
135 *       settled down.
136 */
137public abstract class AccessibilityService extends Service {
138    /**
139     * The {@link Intent} that must be declared as handled by the service.
140     */
141    public static final String SERVICE_INTERFACE =
142        "android.accessibilityservice.AccessibilityService";
143
144    /**
145     * Name under which an AccessibilityService component publishes information
146     * about itself. This meta-data must reference an XML resource containing
147     * an
148     * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
149     * tag. This is a a sample XML file configuring an accessibility service:
150     * <p>
151     * <code>
152     *   &lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
153     *   &lt;accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br>
154     *   &nbsp;&nbsp;android:eventTypes="typeViewClicked|typeViewFocused"<br>
155     *   &nbsp;&nbsp;android:packageNames="foo.bar, foo.baz"<br>
156     *   &nbsp;&nbsp;android:feedbackType="feedbackSpoken"<br>
157     *   &nbsp;&nbsp;android:notificationTimeout="100"<br>
158     *   &nbsp;&nbsp;android:flags="flagDefault"<br>
159     *   &nbsp;&nbsp;android:settingsActivity="foo.bar.TestBackActivity"<br>
160     *   &nbsp;&nbsp;. . .<br>
161     *   /&gt;
162     * </code>
163     * </p>
164     */
165    public static final String SERVICE_META_DATA = "android.accessibilityservice";
166
167    private static final String LOG_TAG = "AccessibilityService";
168
169    private AccessibilityServiceInfo mInfo;
170
171    IAccessibilityServiceConnection mConnection;
172
173    /**
174     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
175     *
176     * @param event An event.
177     */
178    public abstract void onAccessibilityEvent(AccessibilityEvent event);
179
180    /**
181     * Callback for interrupting the accessibility feedback.
182     */
183    public abstract void onInterrupt();
184
185    /**
186     * This method is a part of the {@link AccessibilityService} lifecycle and is
187     * called after the system has successfully bound to the service. If is
188     * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
189     *
190     * @see AccessibilityServiceInfo
191     * @see #setServiceInfo(AccessibilityServiceInfo)
192     */
193    protected void onServiceConnected() {
194
195    }
196
197    /**
198     * Sets the {@link AccessibilityServiceInfo} that describes this service.
199     * <p>
200     * Note: You can call this method any time but the info will be picked up after
201     *       the system has bound to this service and when this method is called thereafter.
202     *
203     * @param info The info.
204     */
205    public final void setServiceInfo(AccessibilityServiceInfo info) {
206        mInfo = info;
207        sendServiceInfo();
208    }
209
210    /**
211     * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
212     * properly set and there is an {@link IAccessibilityServiceConnection} to the
213     * AccessibilityManagerService.
214     */
215    private void sendServiceInfo() {
216        if (mInfo != null && mConnection != null) {
217            try {
218                mConnection.setServiceInfo(mInfo);
219            } catch (RemoteException re) {
220                Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
221            }
222        }
223    }
224
225    /**
226     * Implement to return the implementation of the internal accessibility
227     * service interface.  Subclasses should not override.
228     */
229    @Override
230    public final IBinder onBind(Intent intent) {
231        return new IEventListenerWrapper(this);
232    }
233
234    /**
235     * Implements the internal {@link IEventListener} interface to convert
236     * incoming calls to it back to calls on an {@link AccessibilityService}.
237     */
238    class IEventListenerWrapper extends IEventListener.Stub
239            implements HandlerCaller.Callback {
240
241        private static final int DO_SET_SET_CONNECTION = 10;
242        private static final int DO_ON_INTERRUPT = 20;
243        private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
244
245        private final HandlerCaller mCaller;
246
247        private final AccessibilityService mTarget;
248
249        public IEventListenerWrapper(AccessibilityService context) {
250            mTarget = context;
251            mCaller = new HandlerCaller(context, this);
252        }
253
254        public void setConnection(IAccessibilityServiceConnection connection) {
255            Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection);
256            mCaller.sendMessage(message);
257        }
258
259        public void onInterrupt() {
260            Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
261            mCaller.sendMessage(message);
262        }
263
264        public void onAccessibilityEvent(AccessibilityEvent event) {
265            Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
266            mCaller.sendMessage(message);
267        }
268
269        public void executeMessage(Message message) {
270            switch (message.what) {
271                case DO_ON_ACCESSIBILITY_EVENT :
272                    AccessibilityEvent event = (AccessibilityEvent) message.obj;
273                    if (event != null) {
274                        mTarget.onAccessibilityEvent(event);
275                        event.recycle();
276                    }
277                    return;
278                case DO_ON_INTERRUPT :
279                    mTarget.onInterrupt();
280                    return;
281                case DO_SET_SET_CONNECTION :
282                    mConnection = ((IAccessibilityServiceConnection) message.obj);
283                    mTarget.onServiceConnected();
284                    return;
285                default :
286                    Log.w(LOG_TAG, "Unknown message type " + message.what);
287            }
288        }
289    }
290}
291