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> <service android:name=".MyAccessibilityService"> 54 * <intent-filter> 55 * <action android:name="android.accessibilityservice.AccessibilityService" /> 56 * </intent-filter> 57 * . . . 58 * </service></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> <service android:name=".MyAccessibilityService"> 73 * <intent-filter> 74 * <action android:name="android.accessibilityservice.AccessibilityService" /> 75 * </intent-filter> 76 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> 77 * </service></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><{@link android.R.styleable#AccessibilityService accessibility-service}></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><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 204 * tag. This is a a sample XML file configuring an accessibility service: 205 * <pre> <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 * /></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