AccessibilityManager.java revision 38e8b4e5bc3c93affdffbc064fd9db5aeccc3e8e
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.view.accessibility; 18 19import android.accessibilityservice.AccessibilityServiceInfo; 20import android.content.Context; 21import android.content.pm.ServiceInfo; 22import android.os.Binder; 23import android.os.Handler; 24import android.os.IBinder; 25import android.os.Looper; 26import android.os.Message; 27import android.os.RemoteException; 28import android.os.ServiceManager; 29import android.os.SystemClock; 30import android.util.Log; 31import android.view.IWindow; 32import android.view.View; 33 34import java.util.ArrayList; 35import java.util.Collections; 36import java.util.List; 37import java.util.concurrent.CopyOnWriteArrayList; 38 39/** 40 * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, 41 * and provides facilities for querying the accessibility state of the system. 42 * Accessibility events are generated when something notable happens in the user interface, 43 * for example an {@link android.app.Activity} starts, the focus or selection of a 44 * {@link android.view.View} changes etc. Parties interested in handling accessibility 45 * events implement and register an accessibility service which extends 46 * {@link android.accessibilityservice.AccessibilityService}. 47 * <p> 48 * To obtain a handle to the accessibility manager do the following: 49 * </p> 50 * <p> 51 * <code> 52 * <pre> 53 * AccessibilityManager accessibilityManager = 54 * (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); 55 * </pre> 56 * </code> 57 * </p> 58 * 59 * @see AccessibilityEvent 60 * @see AccessibilityNodeInfo 61 * @see android.accessibilityservice.AccessibilityService 62 * @see Context#getSystemService 63 * @see Context#ACCESSIBILITY_SERVICE 64 */ 65public final class AccessibilityManager { 66 private static final boolean DEBUG = false; 67 68 private static final String LOG_TAG = "AccessibilityManager"; 69 70 static final Object sInstanceSync = new Object(); 71 72 private static AccessibilityManager sInstance; 73 74 private static final int DO_SET_ENABLED = 10; 75 76 final IAccessibilityManager mService; 77 78 final Handler mHandler; 79 80 boolean mIsEnabled; 81 82 final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners = 83 new CopyOnWriteArrayList<AccessibilityStateChangeListener>(); 84 85 /** 86 * Listener for the accessibility state. 87 */ 88 public interface AccessibilityStateChangeListener { 89 90 /** 91 * Called back on change in the accessibility state. 92 * 93 * @param enabled Whether accessibility is enabled. 94 */ 95 public void onAccessibilityStateChanged(boolean enabled); 96 } 97 98 final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { 99 public void setEnabled(boolean enabled) { 100 mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); 101 } 102 }; 103 104 class MyHandler extends Handler { 105 106 MyHandler(Looper mainLooper) { 107 super(mainLooper); 108 } 109 110 @Override 111 public void handleMessage(Message message) { 112 switch (message.what) { 113 case DO_SET_ENABLED : 114 final boolean isEnabled = (message.arg1 == 1); 115 setAccessibilityState(isEnabled); 116 return; 117 default : 118 Log.w(LOG_TAG, "Unknown message type: " + message.what); 119 } 120 } 121 } 122 123 /** 124 * Get an AccessibilityManager instance (create one if necessary). 125 * 126 * @hide 127 */ 128 public static AccessibilityManager getInstance(Context context) { 129 synchronized (sInstanceSync) { 130 if (sInstance == null) { 131 IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 132 IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder); 133 sInstance = new AccessibilityManager(context, service); 134 } 135 } 136 137 AccessibilityManager accessibilityManager = 138 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); 139 return sInstance; 140 } 141 142 /** 143 * Create an instance. 144 * 145 * @param context A {@link Context}. 146 * @param service An interface to the backing service. 147 * 148 * @hide 149 */ 150 public AccessibilityManager(Context context, IAccessibilityManager service) { 151 mHandler = new MyHandler(context.getMainLooper()); 152 mService = service; 153 154 try { 155 final boolean isEnabled = mService.addClient(mClient); 156 setAccessibilityState(isEnabled); 157 } catch (RemoteException re) { 158 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 159 } 160 } 161 162 /** 163 * Returns if the accessibility in the system is enabled. 164 * 165 * @return True if accessibility is enabled, false otherwise. 166 */ 167 public boolean isEnabled() { 168 synchronized (mHandler) { 169 return mIsEnabled; 170 } 171 } 172 173 /** 174 * Returns the client interface this instance registers in 175 * the centralized accessibility manager service. 176 * 177 * @return The client. 178 * 179 * @hide 180 */ 181 public IAccessibilityManagerClient getClient() { 182 return (IAccessibilityManagerClient) mClient.asBinder(); 183 } 184 185 /** 186 * Sends an {@link AccessibilityEvent}. 187 * 188 * @param event The event to send. 189 * 190 * @throws IllegalStateException if accessibility is not enabled. 191 */ 192 public void sendAccessibilityEvent(AccessibilityEvent event) { 193 if (!mIsEnabled) { 194 throw new IllegalStateException("Accessibility off. Did you forget to check that?"); 195 } 196 boolean doRecycle = false; 197 try { 198 event.setEventTime(SystemClock.uptimeMillis()); 199 // it is possible that this manager is in the same process as the service but 200 // client using it is called through Binder from another process. Example: MMS 201 // app adds a SMS notification and the NotificationManagerService calls this method 202 long identityToken = Binder.clearCallingIdentity(); 203 doRecycle = mService.sendAccessibilityEvent(event); 204 Binder.restoreCallingIdentity(identityToken); 205 if (DEBUG) { 206 Log.i(LOG_TAG, event + " sent"); 207 } 208 } catch (RemoteException re) { 209 Log.e(LOG_TAG, "Error during sending " + event + " ", re); 210 } finally { 211 if (doRecycle) { 212 event.recycle(); 213 } 214 } 215 } 216 217 /** 218 * Requests feedback interruption from all accessibility services. 219 */ 220 public void interrupt() { 221 if (!mIsEnabled) { 222 throw new IllegalStateException("Accessibility off. Did you forget to check that?"); 223 } 224 try { 225 mService.interrupt(); 226 if (DEBUG) { 227 Log.i(LOG_TAG, "Requested interrupt from all services"); 228 } 229 } catch (RemoteException re) { 230 Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); 231 } 232 } 233 234 /** 235 * Returns the {@link ServiceInfo}s of the installed accessibility services. 236 * 237 * @return An unmodifiable list with {@link ServiceInfo}s. 238 * 239 * @deprecated Use {@link #getInstalledAccessibilityServiceList()} 240 */ 241 @Deprecated 242 public List<ServiceInfo> getAccessibilityServiceList() { 243 List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList(); 244 List<ServiceInfo> services = new ArrayList<ServiceInfo>(); 245 final int infoCount = infos.size(); 246 for (int i = 0; i < infoCount; i++) { 247 AccessibilityServiceInfo info = infos.get(i); 248 services.add(info.getResolveInfo().serviceInfo); 249 } 250 return Collections.unmodifiableList(services); 251 } 252 253 /** 254 * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services. 255 * 256 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 257 */ 258 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { 259 List<AccessibilityServiceInfo> services = null; 260 try { 261 services = mService.getInstalledAccessibilityServiceList(); 262 if (DEBUG) { 263 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 264 } 265 } catch (RemoteException re) { 266 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 267 } 268 return Collections.unmodifiableList(services); 269 } 270 271 /** 272 * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services 273 * for a given feedback type. 274 * 275 * @param feedbackTypeFlags The feedback type flags. 276 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 277 * 278 * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE 279 * @see AccessibilityServiceInfo#FEEDBACK_GENERIC 280 * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC 281 * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN 282 * @see AccessibilityServiceInfo#FEEDBACK_VISUAL 283 */ 284 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 285 int feedbackTypeFlags) { 286 List<AccessibilityServiceInfo> services = null; 287 try { 288 services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags); 289 if (DEBUG) { 290 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 291 } 292 } catch (RemoteException re) { 293 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 294 } 295 return Collections.unmodifiableList(services); 296 } 297 298 /** 299 * Registers an {@link AccessibilityStateChangeListener} for changes in 300 * the global accessibility state of the system. 301 * 302 * @param listener The listener. 303 * @return True if successfully registered. 304 */ 305 public boolean addAccessibilityStateChangeListener( 306 AccessibilityStateChangeListener listener) { 307 return mAccessibilityStateChangeListeners.add(listener); 308 } 309 310 /** 311 * Unregisters an {@link AccessibilityStateChangeListener}. 312 * 313 * @param listener The listener. 314 * @return True if successfully unregistered. 315 */ 316 public boolean removeAccessibilityStateChangeListener( 317 AccessibilityStateChangeListener listener) { 318 return mAccessibilityStateChangeListeners.remove(listener); 319 } 320 321 /** 322 * Sets the enabled state. 323 * 324 * @param isEnabled The accessibility state. 325 */ 326 private void setAccessibilityState(boolean isEnabled) { 327 synchronized (mHandler) { 328 if (isEnabled != mIsEnabled) { 329 mIsEnabled = isEnabled; 330 notifyAccessibilityStateChanged(); 331 } 332 } 333 } 334 335 /** 336 * Notifies the registered {@link AccessibilityStateChangeListener}s. 337 */ 338 private void notifyAccessibilityStateChanged() { 339 final int listenerCount = mAccessibilityStateChangeListeners.size(); 340 for (int i = 0; i < listenerCount; i++) { 341 mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled); 342 } 343 } 344 345 /** 346 * Adds an accessibility interaction connection interface for a given window. 347 * @param windowToken The window token to which a connection is added. 348 * @param connection The connection. 349 * 350 * @hide 351 */ 352 public int addAccessibilityInteractionConnection(IWindow windowToken, 353 IAccessibilityInteractionConnection connection) { 354 try { 355 return mService.addAccessibilityInteractionConnection(windowToken, connection); 356 } catch (RemoteException re) { 357 Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); 358 } 359 return View.NO_ID; 360 } 361 362 /** 363 * Removed an accessibility interaction connection interface for a given window. 364 * @param windowToken The window token to which a connection is removed. 365 * 366 * @hide 367 */ 368 public void removeAccessibilityInteractionConnection(IWindow windowToken) { 369 try { 370 mService.removeAccessibilityInteractionConnection(windowToken); 371 } catch (RemoteException re) { 372 Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); 373 } 374 } 375} 376