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