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