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 static android.util.Config.LOGV; 20 21import android.content.Context; 22import android.content.pm.ServiceInfo; 23import android.os.Binder; 24import android.os.Handler; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.os.SystemClock; 31import android.util.Log; 32 33import java.util.Collections; 34import java.util.List; 35 36/** 37 * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. 38 * Such events are generated when something notable happens in the user interface, 39 * for example an {@link android.app.Activity} starts, the focus or selection of a 40 * {@link android.view.View} changes etc. Parties interested in handling accessibility 41 * events implement and register an accessibility service which extends 42 * {@link android.accessibilityservice.AccessibilityService}. 43 * 44 * @see AccessibilityEvent 45 * @see android.accessibilityservice.AccessibilityService 46 * @see android.content.Context#getSystemService 47 */ 48public final class AccessibilityManager { 49 private static final String LOG_TAG = "AccessibilityManager"; 50 51 static final Object sInstanceSync = new Object(); 52 53 private static AccessibilityManager sInstance; 54 55 private static final int DO_SET_ENABLED = 10; 56 57 final IAccessibilityManager mService; 58 59 final Handler mHandler; 60 61 boolean mIsEnabled; 62 63 final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { 64 public void setEnabled(boolean enabled) { 65 mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); 66 } 67 }; 68 69 class MyHandler extends Handler { 70 71 MyHandler(Looper mainLooper) { 72 super(mainLooper); 73 } 74 75 @Override 76 public void handleMessage(Message message) { 77 switch (message.what) { 78 case DO_SET_ENABLED : 79 synchronized (mHandler) { 80 mIsEnabled = (message.arg1 == 1); 81 } 82 return; 83 default : 84 Log.w(LOG_TAG, "Unknown message type: " + message.what); 85 } 86 } 87 } 88 89 /** 90 * Get an AccessibilityManager instance (create one if necessary). 91 * 92 * @hide 93 */ 94 public static AccessibilityManager getInstance(Context context) { 95 synchronized (sInstanceSync) { 96 if (sInstance == null) { 97 sInstance = new AccessibilityManager(context); 98 } 99 } 100 return sInstance; 101 } 102 103 /** 104 * Create an instance. 105 * 106 * @param context A {@link Context}. 107 */ 108 private AccessibilityManager(Context context) { 109 mHandler = new MyHandler(context.getMainLooper()); 110 IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 111 mService = IAccessibilityManager.Stub.asInterface(iBinder); 112 try { 113 mService.addClient(mClient); 114 } catch (RemoteException re) { 115 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 116 } 117 } 118 119 /** 120 * Returns if the {@link AccessibilityManager} is enabled. 121 * 122 * @return True if this {@link AccessibilityManager} is enabled, false otherwise. 123 */ 124 public boolean isEnabled() { 125 synchronized (mHandler) { 126 return mIsEnabled; 127 } 128 } 129 130 /** 131 * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not 132 * enabled the call is a NOOP. 133 * 134 * @param event The {@link AccessibilityEvent}. 135 * 136 * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent} 137 * while accessibility is not enabled. 138 */ 139 public void sendAccessibilityEvent(AccessibilityEvent event) { 140 if (!mIsEnabled) { 141 throw new IllegalStateException("Accessibility off. Did you forget to check that?"); 142 } 143 boolean doRecycle = false; 144 try { 145 event.setEventTime(SystemClock.uptimeMillis()); 146 // it is possible that this manager is in the same process as the service but 147 // client using it is called through Binder from another process. Example: MMS 148 // app adds a SMS notification and the NotificationManagerService calls this method 149 long identityToken = Binder.clearCallingIdentity(); 150 doRecycle = mService.sendAccessibilityEvent(event); 151 Binder.restoreCallingIdentity(identityToken); 152 if (LOGV) { 153 Log.i(LOG_TAG, event + " sent"); 154 } 155 } catch (RemoteException re) { 156 Log.e(LOG_TAG, "Error during sending " + event + " ", re); 157 } finally { 158 if (doRecycle) { 159 event.recycle(); 160 } 161 } 162 } 163 164 /** 165 * Requests interruption of the accessibility feedback from all accessibility services. 166 */ 167 public void interrupt() { 168 if (!mIsEnabled) { 169 throw new IllegalStateException("Accessibility off. Did you forget to check that?"); 170 } 171 try { 172 mService.interrupt(); 173 if (LOGV) { 174 Log.i(LOG_TAG, "Requested interrupt from all services"); 175 } 176 } catch (RemoteException re) { 177 Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); 178 } 179 } 180 181 /** 182 * Returns the {@link ServiceInfo}s of the installed accessibility services. 183 * 184 * @return An unmodifiable list with {@link ServiceInfo}s. 185 */ 186 public List<ServiceInfo> getAccessibilityServiceList() { 187 List<ServiceInfo> services = null; 188 try { 189 services = mService.getAccessibilityServiceList(); 190 if (LOGV) { 191 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 192 } 193 } catch (RemoteException re) { 194 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 195 } 196 return Collections.unmodifiableList(services); 197 } 198} 199