AccessibilityService.java revision 75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec
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 * The lifecycle of an accessibility service is managed exclusively by the system. Starting 49 * or stopping an accessibility service is triggered by an explicit user action through 50 * enabling or disabling it in the device settings. After the system binds to a service it 51 * calls {@link AccessibilityService#onServiceConnected()}. This method can be 52 * overriden by clients that want to perform post binding setup. An accessibility service 53 * is configured though setting an {@link AccessibilityServiceInfo} by calling 54 * {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. You can call this 55 * method any time to change the service configuration but it is good practice to do that 56 * in the overriden {@link AccessibilityService#onServiceConnected()}. 57 * <p> 58 * An accessibility service can be registered for events in specific packages to provide a 59 * specific type of feedback and is notified with a certain timeout after the last event 60 * of interest has been fired. 61 * <p> 62 * <b>Notification strategy</b> 63 * <p> 64 * For each feedback type only one accessibility service is notified. Services are notified 65 * in the order of registration. Hence, if two services are registered for the same 66 * feedback type in the same package the first one wins. It is possible however, to 67 * register a service as the default one for a given feedback type. In such a case this 68 * service is invoked if no other service was interested in the event. In other words, default 69 * services do not compete with other services and are notified last regardless of the 70 * registration order. This enables "generic" accessibility services that work reasonably 71 * well with most applications to coexist with "polished" ones that are targeted for 72 * specific applications. 73 * <p> 74 * <b>Event types</b> 75 * <p> 76 * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} 77 * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} 78 * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED} 79 * {@link AccessibilityEvent#TYPE_VIEW_SELECTED} 80 * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} 81 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} 82 * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} 83 * <p> 84 * <b>Feedback types</b> 85 * <p> 86 * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} 87 * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC} 88 * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} 89 * {@link AccessibilityServiceInfo#FEEDBACK_VISUAL} 90 * {@link AccessibilityServiceInfo#FEEDBACK_GENERIC} 91 * 92 * @see AccessibilityEvent 93 * @see AccessibilityServiceInfo 94 * @see android.view.accessibility.AccessibilityManager 95 * 96 * Note: The event notification timeout is useful to avoid propagating events to the client 97 * too frequently since this is accomplished via an expensive interprocess call. 98 * One can think of the timeout as a criteria to determine when event generation has 99 * settled down. 100 */ 101public abstract class AccessibilityService extends Service { 102 /** 103 * The {@link Intent} that must be declared as handled by the service. 104 */ 105 public static final String SERVICE_INTERFACE = 106 "android.accessibilityservice.AccessibilityService"; 107 108 private static final String LOG_TAG = "AccessibilityService"; 109 110 private AccessibilityServiceInfo mInfo; 111 112 IAccessibilityServiceConnection mConnection; 113 114 /** 115 * Callback for {@link android.view.accessibility.AccessibilityEvent}s. 116 * 117 * @param event An event. 118 */ 119 public abstract void onAccessibilityEvent(AccessibilityEvent event); 120 121 /** 122 * Callback for interrupting the accessibility feedback. 123 */ 124 public abstract void onInterrupt(); 125 126 /** 127 * This method is a part of the {@link AccessibilityService} lifecycle and is 128 * called after the system has successfully bound to the service. If is 129 * convenient to use this method for setting the {@link AccessibilityServiceInfo}. 130 * 131 * @see AccessibilityServiceInfo 132 * @see #setServiceInfo(AccessibilityServiceInfo) 133 */ 134 protected void onServiceConnected() { 135 136 } 137 138 /** 139 * Sets the {@link AccessibilityServiceInfo} that describes this service. 140 * <p> 141 * Note: You can call this method any time but the info will be picked up after 142 * the system has bound to this service and when this method is called thereafter. 143 * 144 * @param info The info. 145 */ 146 public final void setServiceInfo(AccessibilityServiceInfo info) { 147 mInfo = info; 148 sendServiceInfo(); 149 } 150 151 /** 152 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 153 * properly set and there is an {@link IAccessibilityServiceConnection} to the 154 * AccessibilityManagerService. 155 */ 156 private void sendServiceInfo() { 157 if (mInfo != null && mConnection != null) { 158 try { 159 mConnection.setServiceInfo(mInfo); 160 } catch (RemoteException re) { 161 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 162 } 163 } 164 } 165 166 @Override 167 public final IBinder onBind(Intent intent) { 168 return new IEventListenerWrapper(this); 169 } 170 171 /** 172 * Implements the internal {@link IEventListener} interface to convert 173 * incoming calls to it back to calls on an {@link AccessibilityService}. 174 */ 175 class IEventListenerWrapper extends IEventListener.Stub 176 implements HandlerCaller.Callback { 177 178 private static final int DO_SET_SET_CONNECTION = 10; 179 private static final int DO_ON_INTERRUPT = 20; 180 private static final int DO_ON_ACCESSIBILITY_EVENT = 30; 181 182 private final HandlerCaller mCaller; 183 184 private AccessibilityService mTarget; 185 186 public IEventListenerWrapper(AccessibilityService context) { 187 mTarget = context; 188 mCaller = new HandlerCaller(context, this); 189 } 190 191 public void setConnection(IAccessibilityServiceConnection connection) { 192 Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection); 193 mCaller.sendMessage(message); 194 } 195 196 public void onInterrupt() { 197 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 198 mCaller.sendMessage(message); 199 } 200 201 public void onAccessibilityEvent(AccessibilityEvent event) { 202 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 203 mCaller.sendMessage(message); 204 } 205 206 public void executeMessage(Message message) { 207 switch (message.what) { 208 case DO_ON_ACCESSIBILITY_EVENT : 209 AccessibilityEvent event = (AccessibilityEvent) message.obj; 210 mTarget.onAccessibilityEvent(event); 211 event.recycle(); 212 return; 213 case DO_ON_INTERRUPT : 214 mTarget.onInterrupt(); 215 return; 216 case DO_SET_SET_CONNECTION : 217 mConnection = ((IAccessibilityServiceConnection) message.obj); 218 mTarget.onServiceConnected(); 219 return; 220 default : 221 Log.w(LOG_TAG, "Unknown message type " + message.what); 222 } 223 } 224 } 225} 226