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 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 */ 101 public 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 /** 167 * Implement to return the implementation of the internal accessibility 168 * service interface. Subclasses should not override. 169 */ 170 @Override 171 public final IBinder onBind(Intent intent) { 172 return new IEventListenerWrapper(this); 173 } 174 175 /** 176 * Implements the internal {@link IEventListener} interface to convert 177 * incoming calls to it back to calls on an {@link AccessibilityService}. 178 */ 179 class IEventListenerWrapper extends IEventListener.Stub 180 implements HandlerCaller.Callback { 181 182 private static final int DO_SET_SET_CONNECTION = 10; 183 private static final int DO_ON_INTERRUPT = 20; 184 private static final int DO_ON_ACCESSIBILITY_EVENT = 30; 185 186 private final HandlerCaller mCaller; 187 188 private final AccessibilityService mTarget; 189 190 public IEventListenerWrapper(AccessibilityService context) { 191 mTarget = context; 192 mCaller = new HandlerCaller(context, this); 193 } 194 195 public void setConnection(IAccessibilityServiceConnection connection) { 196 Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection); 197 mCaller.sendMessage(message); 198 } 199 200 public void onInterrupt() { 201 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 202 mCaller.sendMessage(message); 203 } 204 205 public void onAccessibilityEvent(AccessibilityEvent event) { 206 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 207 mCaller.sendMessage(message); 208 } 209 210 public void executeMessage(Message message) { 211 switch (message.what) { 212 case DO_ON_ACCESSIBILITY_EVENT : 213 AccessibilityEvent event = (AccessibilityEvent) message.obj; 214 if (event != null) { 215 mTarget.onAccessibilityEvent(event); 216 event.recycle(); 217 } 218 return; 219 case DO_ON_INTERRUPT : 220 mTarget.onInterrupt(); 221 return; 222 case DO_SET_SET_CONNECTION : 223 mConnection = ((IAccessibilityServiceConnection) message.obj); 224 mTarget.onServiceConnected(); 225 return; 226 default : 227 Log.w(LOG_TAG, "Unknown message type " + message.what); 228 } 229 } 230 } 231 } 232