AccessibilityManager.java revision cc4053e031371456fe54d51bbad1db721db4ae38
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;
31
32import java.util.ArrayList;
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 boolean DEBUG = false;
50
51    private static final String LOG_TAG = "AccessibilityManager";
52
53    static final Object sInstanceSync = new Object();
54
55    private static AccessibilityManager sInstance;
56
57    private static final int DO_SET_ENABLED = 10;
58
59    final IAccessibilityManager mService;
60
61    final Handler mHandler;
62
63    boolean mIsEnabled;
64
65    final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
66        public void setEnabled(boolean enabled) {
67            mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget();
68        }
69    };
70
71    class MyHandler extends Handler {
72
73        MyHandler(Looper mainLooper) {
74            super(mainLooper);
75        }
76
77        @Override
78        public void handleMessage(Message message) {
79            switch (message.what) {
80                case DO_SET_ENABLED :
81                    synchronized (mHandler) {
82                        mIsEnabled = (message.arg1 == 1);
83                    }
84                    return;
85                default :
86                    Log.w(LOG_TAG, "Unknown message type: " + message.what);
87            }
88        }
89    }
90
91    /**
92     * Get an AccessibilityManager instance (create one if necessary).
93     *
94     * @hide
95     */
96    public static AccessibilityManager getInstance(Context context) {
97        synchronized (sInstanceSync) {
98            if (sInstance == null) {
99                IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
100                IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
101                sInstance = new AccessibilityManager(context, service);
102            }
103        }
104        return sInstance;
105    }
106
107    /**
108     * Create an instance.
109     *
110     * @param context A {@link Context}.
111     * @param service An interface to the backing service.
112     *
113     * @hide
114     */
115    public AccessibilityManager(Context context, IAccessibilityManager service) {
116        mHandler = new MyHandler(context.getMainLooper());
117        mService = service;
118
119        try {
120            mIsEnabled = mService.addClient(mClient);
121        } catch (RemoteException re) {
122            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
123        }
124    }
125
126    /**
127     * Returns if the {@link AccessibilityManager} is enabled.
128     *
129     * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
130     */
131    public boolean isEnabled() {
132        synchronized (mHandler) {
133            return mIsEnabled;
134        }
135    }
136
137    /**
138     * Returns the client interface this instance registers in
139     * the centralized accessibility manager service.
140     *
141     * @return The client.
142     *
143     * @hide
144     */
145    public IAccessibilityManagerClient getClient() {
146       return (IAccessibilityManagerClient) mClient.asBinder();
147    }
148
149    /**
150     * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not
151     * enabled the call is a NOOP.
152     *
153     * @param event The {@link AccessibilityEvent}.
154     *
155     * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent}
156     *         while accessibility is not enabled.
157     */
158    public void sendAccessibilityEvent(AccessibilityEvent event) {
159        if (!mIsEnabled) {
160            throw new IllegalStateException("Accessibility off. Did you forget to check that?");
161        }
162        boolean doRecycle = false;
163        try {
164            event.setEventTime(SystemClock.uptimeMillis());
165            // it is possible that this manager is in the same process as the service but
166            // client using it is called through Binder from another process. Example: MMS
167            // app adds a SMS notification and the NotificationManagerService calls this method
168            long identityToken = Binder.clearCallingIdentity();
169            doRecycle = mService.sendAccessibilityEvent(event);
170            Binder.restoreCallingIdentity(identityToken);
171            if (DEBUG) {
172                Log.i(LOG_TAG, event + " sent");
173            }
174        } catch (RemoteException re) {
175            Log.e(LOG_TAG, "Error during sending " + event + " ", re);
176        } finally {
177            if (doRecycle) {
178                event.recycle();
179            }
180        }
181    }
182
183    /**
184     * Requests interruption of the accessibility feedback from all accessibility services.
185     */
186    public void interrupt() {
187        if (!mIsEnabled) {
188            throw new IllegalStateException("Accessibility off. Did you forget to check that?");
189        }
190        try {
191            mService.interrupt();
192            if (DEBUG) {
193                Log.i(LOG_TAG, "Requested interrupt from all services");
194            }
195        } catch (RemoteException re) {
196            Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
197        }
198    }
199
200    /**
201     * Returns the {@link ServiceInfo}s of the installed accessibility services.
202     *
203     * @return An unmodifiable list with {@link ServiceInfo}s.
204     *
205     * @deprecated Use {@link #getInstalledAccessibilityServiceList()}
206     */
207    @Deprecated
208    public List<ServiceInfo> getAccessibilityServiceList() {
209        List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList();
210        List<ServiceInfo> services = new ArrayList<ServiceInfo>();
211        final int infoCount = infos.size();
212        for (int i = 0; i < infoCount; i++) {
213            AccessibilityServiceInfo info = infos.get(i);
214            services.add(info.getResolveInfo().serviceInfo);
215        }
216        return Collections.unmodifiableList(services);
217    }
218
219    /**
220     * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
221     *
222     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
223     */
224    public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
225        List<AccessibilityServiceInfo> services = null;
226        try {
227            services = mService.getInstalledAccessibilityServiceList();
228            if (DEBUG) {
229                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
230            }
231        } catch (RemoteException re) {
232            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
233        }
234        return Collections.unmodifiableList(services);
235    }
236
237    /**
238     * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
239     * for a given feedback type.
240     *
241     * @param feedbackType The feedback type (can be bitwise or of multiple types).
242     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
243     */
244    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
245        List<AccessibilityServiceInfo> services = null;
246        try {
247            services = mService.getEnabledAccessibilityServiceList(feedbackType);
248            if (DEBUG) {
249                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
250            }
251        } catch (RemoteException re) {
252            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
253        }
254        return Collections.unmodifiableList(services);
255    }
256}
257