• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.accessibilityservice;
18 
19 import com.android.internal.os.HandlerCaller;
20 
21 import android.app.Service;
22 import android.content.Intent;
23 import android.os.IBinder;
24 import android.os.Message;
25 import android.os.RemoteException;
26 import android.util.Log;
27 import android.view.accessibility.AccessibilityEvent;
28 import android.view.accessibility.AccessibilityNodeInfo;
29 
30 /**
31  * An accessibility service runs in the background and receives callbacks by the system
32  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
33  * in the user interface, for example, the focus has changed, a button has been clicked,
34  * etc. Such a service can optionally request the capability for querying the content
35  * of the active window. Development of an accessibility service requires extending this
36  * class and implementing its abstract methods.
37  * <h3>Lifecycle</h3>
38  * <p>
39  * The lifecycle of an accessibility service is managed exclusively by the system and
40  * follows the established service life cycle. Additionally, starting or stopping an
41  * accessibility service is triggered exclusively by an explicit user action through
42  * enabling or disabling it in the device settings. After the system binds to a service it
43  * calls {@link AccessibilityService#onServiceConnected()}. This method can be
44  * overriden by clients that want to perform post binding setup.
45  * </p>
46  * <h3>Declaration</h3>
47  * <p>
48  * An accessibility is declared as any other service in an AndroidManifest.xml but it
49  * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
50  * {@link android.content.Intent}. Failure to declare this intent will cause the system to
51  * ignore the accessibility service. Following is an example declaration:
52  * </p>
53  * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
54  *     &lt;intent-filter&gt;
55  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
56  *     &lt;/intent-filter&gt;
57  *     . . .
58  * &lt;/service&gt;</pre>
59  * <h3>Configuration</h3>
60  * <p>
61  * An accessibility service can be configured to receive specific types of accessibility events,
62  * listen only to specific packages, get events from each type only once in a given time frame,
63  * retrieve window content, specify a settings activity, etc.
64  * </p>
65  * <p>
66  * There are two approaches for configuring an accessibility service:
67  * </p>
68  * <ul>
69  * <li>
70  * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
71  * the service. A service declaration with a meta-data tag is presented below:
72  * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
73  *     &lt;intent-filter&gt;
74  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
75  *     &lt;/intent-filter&gt;
76  *     &lt;meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /&gt;
77  * &lt;/service&gt;</pre>
78  * <p class="note">
79  * <strong>Note:</strong> This approach enables setting all properties.
80  * </p>
81  * <p>
82  * For more details refer to {@link #SERVICE_META_DATA} and
83  * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>.
84  * </p>
85  * </li>
86  * <li>
87  * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
88  * that this method can be called any time to dynamically change the service configuration.
89  * <p class="note">
90  * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
91  * {@link AccessibilityServiceInfo#eventTypes},
92  * {@link AccessibilityServiceInfo#feedbackType},
93  * {@link AccessibilityServiceInfo#flags},
94  * {@link AccessibilityServiceInfo#notificationTimeout},
95  * {@link AccessibilityServiceInfo#packageNames}
96  * </p>
97  * <p>
98  * For more details refer to {@link AccessibilityServiceInfo}.
99  * </p>
100  * </li>
101  * </ul>
102  * <h3>Retrieving window content</h3>
103  * <p>
104  * An service can specify in its declaration that it can retrieve the active window
105  * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
106  * declaring this capability requires that the service declares its configuration via
107  * an XML resource referenced by {@link #SERVICE_META_DATA}.
108  * </p>
109  * <p>
110  * For security purposes an accessibility service can retrieve only the content of the
111  * currently active window. The currently active window is defined as the window from
112  * which was fired the last event of the following types:
113  * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
114  * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
115  * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
116  * In other words, the last window that was shown or the last window that the user has touched
117  * during touch exploration.
118  * </p>
119  * <p>
120  * The entry point for retrieving window content is through calling
121  * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received
122  * event of the above types or a previous event from the same window
123  * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking
124  * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the
125  * window content which represented as a tree of such objects.
126  * </p>
127  * <p class="note">
128  * <strong>Note</strong> An accessibility service may have requested to be notified for
129  * a subset of the event types, thus be unaware that the active window has changed. Therefore
130  * accessibility service that would like to retrieve window content should:
131  * <ul>
132  * <li>
133  * Register for all event types with no notification timeout and keep track for the active
134  * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and
135  * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval
136  * methods on the latter.
137  * </li>
138  * <li>
139  * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the
140  * active window has changed and the service did not get the accessibility event yet. Note
141  * that it is possible to have a retrieval method failing even adopting the strategy
142  * specified in the previous bullet because the accessibility event dispatch is asynchronous
143  * and crosses process boundaries.
144  * </li>
145  * </ul>
146  * </p>
147  * <h3>Notification strategy</h3>
148  * <p>
149  * For each feedback type only one accessibility service is notified. Services are notified
150  * in the order of registration. Hence, if two services are registered for the same
151  * feedback type in the same package the first one wins. It is possible however, to
152  * register a service as the default one for a given feedback type. In such a case this
153  * service is invoked if no other service was interested in the event. In other words, default
154  * services do not compete with other services and are notified last regardless of the
155  * registration order. This enables "generic" accessibility services that work reasonably
156  * well with most applications to coexist with "polished" ones that are targeted for
157  * specific applications.
158  * </p>
159  * <p class="note">
160  * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
161  * events to the client too frequently since this is accomplished via an expensive
162  * interprocess call. One can think of the timeout as a criteria to determine when
163  * event generation has settled down.</p>
164  * <h3>Event types</h3>
165  * <ul>
166  * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}
167  * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
168  * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
169  * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}
170  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
171  * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
172  * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
173  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
174  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
175  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
176  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
177  * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
178  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
179  * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
180  * </ul>
181  * <h3>Feedback types</h3>
182  * <ul>
183  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
184  * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
185  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
186  * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
187  * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
188  * </ul>
189  * @see AccessibilityEvent
190  * @see AccessibilityServiceInfo
191  * @see android.view.accessibility.AccessibilityManager
192  */
193 public abstract class AccessibilityService extends Service {
194     /**
195      * The {@link Intent} that must be declared as handled by the service.
196      */
197     public static final String SERVICE_INTERFACE =
198         "android.accessibilityservice.AccessibilityService";
199 
200     /**
201      * Name under which an AccessibilityService component publishes information
202      * about itself. This meta-data must reference an XML resource containing an
203      * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
204      * tag. This is a a sample XML file configuring an accessibility service:
205      * <pre> &lt;accessibility-service
206      *     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
207      *     android:packageNames="foo.bar, foo.baz"
208      *     android:accessibilityFeedbackType="feedbackSpoken"
209      *     android:notificationTimeout="100"
210      *     android:accessibilityFlags="flagDefault"
211      *     android:settingsActivity="foo.bar.TestBackActivity"
212      *     android:canRetrieveWindowContent="true"
213      *     . . .
214      * /&gt;</pre>
215      */
216     public static final String SERVICE_META_DATA = "android.accessibilityservice";
217 
218     private static final String LOG_TAG = "AccessibilityService";
219 
220     private AccessibilityServiceInfo mInfo;
221 
222     IAccessibilityServiceConnection mConnection;
223 
224     /**
225      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
226      *
227      * @param event An event.
228      */
onAccessibilityEvent(AccessibilityEvent event)229     public abstract void onAccessibilityEvent(AccessibilityEvent event);
230 
231     /**
232      * Callback for interrupting the accessibility feedback.
233      */
onInterrupt()234     public abstract void onInterrupt();
235 
236     /**
237      * This method is a part of the {@link AccessibilityService} lifecycle and is
238      * called after the system has successfully bound to the service. If is
239      * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
240      *
241      * @see AccessibilityServiceInfo
242      * @see #setServiceInfo(AccessibilityServiceInfo)
243      */
onServiceConnected()244     protected void onServiceConnected() {
245 
246     }
247 
248     /**
249      * Sets the {@link AccessibilityServiceInfo} that describes this service.
250      * <p>
251      * Note: You can call this method any time but the info will be picked up after
252      *       the system has bound to this service and when this method is called thereafter.
253      *
254      * @param info The info.
255      */
setServiceInfo(AccessibilityServiceInfo info)256     public final void setServiceInfo(AccessibilityServiceInfo info) {
257         mInfo = info;
258         sendServiceInfo();
259     }
260 
261     /**
262      * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
263      * properly set and there is an {@link IAccessibilityServiceConnection} to the
264      * AccessibilityManagerService.
265      */
sendServiceInfo()266     private void sendServiceInfo() {
267         if (mInfo != null && mConnection != null) {
268             try {
269                 mConnection.setServiceInfo(mInfo);
270             } catch (RemoteException re) {
271                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
272             }
273         }
274     }
275 
276     /**
277      * Implement to return the implementation of the internal accessibility
278      * service interface.
279      */
280     @Override
onBind(Intent intent)281     public final IBinder onBind(Intent intent) {
282         return new IEventListenerWrapper(this);
283     }
284 
285     /**
286      * Implements the internal {@link IEventListener} interface to convert
287      * incoming calls to it back to calls on an {@link AccessibilityService}.
288      */
289     class IEventListenerWrapper extends IEventListener.Stub
290             implements HandlerCaller.Callback {
291 
292         private static final int DO_SET_SET_CONNECTION = 10;
293         private static final int DO_ON_INTERRUPT = 20;
294         private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
295 
296         private final HandlerCaller mCaller;
297 
298         private final AccessibilityService mTarget;
299 
IEventListenerWrapper(AccessibilityService context)300         public IEventListenerWrapper(AccessibilityService context) {
301             mTarget = context;
302             mCaller = new HandlerCaller(context, this);
303         }
304 
setConnection(IAccessibilityServiceConnection connection)305         public void setConnection(IAccessibilityServiceConnection connection) {
306             Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection);
307             mCaller.sendMessage(message);
308         }
309 
onInterrupt()310         public void onInterrupt() {
311             Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
312             mCaller.sendMessage(message);
313         }
314 
onAccessibilityEvent(AccessibilityEvent event)315         public void onAccessibilityEvent(AccessibilityEvent event) {
316             Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
317             mCaller.sendMessage(message);
318         }
319 
executeMessage(Message message)320         public void executeMessage(Message message) {
321             switch (message.what) {
322                 case DO_ON_ACCESSIBILITY_EVENT :
323                     AccessibilityEvent event = (AccessibilityEvent) message.obj;
324                     if (event != null) {
325                         mTarget.onAccessibilityEvent(event);
326                         event.recycle();
327                     }
328                     return;
329                 case DO_ON_INTERRUPT :
330                     mTarget.onInterrupt();
331                     return;
332                 case DO_SET_SET_CONNECTION :
333                     mConnection = ((IAccessibilityServiceConnection) message.obj);
334                     mTarget.onServiceConnected();
335                     return;
336                 default :
337                     Log.w(LOG_TAG, "Unknown message type " + message.what);
338             }
339         }
340     }
341 }
342