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 * <service android:name=".MyAccessibilityService"><br> 42 * <intent-filter><br> 43 * <action android:name="android.accessibilityservice.AccessibilityService" /><br> 44 * </intent-filter><br> 45 * </service><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 * <service android:name=".MyAccessibilityService"><br> 64 * <intent-filter><br> 65 * <action android:name="android.accessibilityservice.AccessibilityService" /><br> 66 * </intent-filter><br> 67 * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br> 68 * </service><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><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 149 * tag. This is a a sample XML file configuring an accessibility service: 150 * <p> 151 * <code> 152 * <?xml version="1.0" encoding="utf-8"?><br> 153 * <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br> 154 * android:eventTypes="typeViewClicked|typeViewFocused"<br> 155 * android:packageNames="foo.bar, foo.baz"<br> 156 * android:feedbackType="feedbackSpoken"<br> 157 * android:notificationTimeout="100"<br> 158 * android:flags="flagDefault"<br> 159 * android:settingsActivity="foo.bar.TestBackActivity"<br> 160 * . . .<br> 161 * /> 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