blob: 5257fb0d1630f239da4218a429e3fee05e668e4d [file] [log] [blame]
svetoslavganov75986cf2009-05-14 22:28:01 -07001/*
2 ** Copyright 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
Jeff Brown6e6cd7a2011-03-30 03:27:08 -070017package com.android.server.accessibility;
svetoslavganov75986cf2009-05-14 22:28:01 -070018
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080019import com.android.internal.content.PackageMonitor;
svetoslavganov75986cf2009-05-14 22:28:01 -070020import com.android.internal.os.HandlerCaller;
21import com.android.internal.os.HandlerCaller.SomeArgs;
Jeff Brown0029c662011-03-30 02:25:18 -070022import com.android.server.wm.WindowManagerService;
svetoslavganov75986cf2009-05-14 22:28:01 -070023
24import android.accessibilityservice.AccessibilityService;
25import android.accessibilityservice.AccessibilityServiceInfo;
26import android.accessibilityservice.IAccessibilityServiceConnection;
27import android.accessibilityservice.IEventListener;
Dianne Hackborndd9b82c2009-09-03 00:18:47 -070028import android.app.PendingIntent;
svetoslavganov75986cf2009-05-14 22:28:01 -070029import android.content.BroadcastReceiver;
30import android.content.ComponentName;
31import android.content.ContentResolver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.ServiceConnection;
36import android.content.pm.PackageManager;
37import android.content.pm.ResolveInfo;
38import android.content.pm.ServiceInfo;
39import android.database.ContentObserver;
40import android.net.Uri;
41import android.os.Binder;
42import android.os.DeadObjectException;
43import android.os.Handler;
44import android.os.IBinder;
45import android.os.Message;
46import android.os.RemoteException;
Jeff Brown0029c662011-03-30 02:25:18 -070047import android.os.ServiceManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070048import android.provider.Settings;
49import android.text.TextUtils;
50import android.text.TextUtils.SimpleStringSplitter;
Svetoslav Ganov714cff02010-02-17 19:36:28 -080051import android.util.Config;
Joe Onorato8a9b2202010-02-26 18:56:32 -080052import android.util.Slog;
svetoslavganov75986cf2009-05-14 22:28:01 -070053import android.util.SparseArray;
54import android.view.accessibility.AccessibilityEvent;
55import android.view.accessibility.IAccessibilityManager;
56import android.view.accessibility.IAccessibilityManagerClient;
57
58import java.util.ArrayList;
59import java.util.Arrays;
60import java.util.HashMap;
61import java.util.HashSet;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080062import java.util.Iterator;
svetoslavganov75986cf2009-05-14 22:28:01 -070063import java.util.List;
64import java.util.Map;
65import java.util.Set;
66
67/**
68 * This class is instantiated by the system as a system level service and can be
69 * accessed only by the system. The task of this service is to be a centralized
70 * event dispatch for {@link AccessibilityEvent}s generated across all processes
71 * on the device. Events are dispatched to {@link AccessibilityService}s.
72 *
73 * @hide
74 */
75public class AccessibilityManagerService extends IAccessibilityManager.Stub
76 implements HandlerCaller.Callback {
77
78 private static final String LOG_TAG = "AccessibilityManagerService";
79
80 private static int sIdCounter = 0;
81
82 private static final int OWN_PROCESS_ID = android.os.Process.myPid();
83
84 private static final int DO_SET_SERVICE_INFO = 10;
85
86 final HandlerCaller mCaller;
87
88 final Context mContext;
89
90 final Object mLock = new Object();
91
92 final List<Service> mServices = new ArrayList<Service>();
93
94 final List<IAccessibilityManagerClient> mClients =
95 new ArrayList<IAccessibilityManagerClient>();
96
97 final Map<ComponentName, Service> mComponentNameToServiceMap =
98 new HashMap<ComponentName, Service>();
99
100 private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
101
102 private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
103
104 private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
105
106 private PackageManager mPackageManager;
107
108 private int mHandledFeedbackTypes = 0;
109
110 private boolean mIsEnabled;
Jeff Brown0029c662011-03-30 02:25:18 -0700111 private AccessibilityInputFilter mInputFilter;
svetoslavganov75986cf2009-05-14 22:28:01 -0700112
113 /**
114 * Handler for delayed event dispatch.
115 */
116 private Handler mHandler = new Handler() {
117
118 @Override
119 public void handleMessage(Message message) {
120 Service service = (Service) message.obj;
121 int eventType = message.arg1;
122
123 synchronized (mLock) {
124 notifyEventListenerLocked(service, eventType);
125 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
126 service.mPendingEvents.remove(eventType);
127 tryRecycleLocked(oldEvent);
128 }
129 }
130 };
131
132 /**
133 * Creates a new instance.
134 *
135 * @param context A {@link Context} instance.
136 */
Jeff Brown6e6cd7a2011-03-30 03:27:08 -0700137 public AccessibilityManagerService(Context context) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700138 mContext = context;
139 mPackageManager = mContext.getPackageManager();
140 mCaller = new HandlerCaller(context, this);
141
142 registerPackageChangeAndBootCompletedBroadcastReceiver();
143 registerSettingsContentObservers();
svetoslavganov75986cf2009-05-14 22:28:01 -0700144 }
145
146 /**
147 * Registers a {@link BroadcastReceiver} for the events of
148 * adding/changing/removing/restarting a package and boot completion.
149 */
150 private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
151 Context context = mContext;
152
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800153 PackageMonitor monitor = new PackageMonitor() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700154 @Override
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800155 public void onSomePackagesChanged() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700156 synchronized (mLock) {
157 populateAccessibilityServiceListLocked();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800158 manageServicesLocked();
159 }
160 }
161
162 @Override
163 public boolean onHandleForceStop(Intent intent, String[] packages,
164 int uid, boolean doit) {
165 synchronized (mLock) {
166 boolean changed = false;
167 Iterator<ComponentName> it = mEnabledServices.iterator();
168 while (it.hasNext()) {
169 ComponentName comp = it.next();
170 String compPkg = comp.getPackageName();
171 for (String pkg : packages) {
172 if (compPkg.equals(pkg)) {
173 if (!doit) {
174 return true;
175 }
176 it.remove();
177 changed = true;
178 }
179 }
180 }
181 if (changed) {
182 it = mEnabledServices.iterator();
183 StringBuilder str = new StringBuilder();
184 while (it.hasNext()) {
185 if (str.length() > 0) {
186 str.append(':');
187 }
188 str.append(it.next().flattenToShortString());
189 }
190 Settings.Secure.putString(mContext.getContentResolver(),
191 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
192 str.toString());
193 manageServicesLocked();
194 }
195 return false;
196 }
197 }
198
199 @Override
200 public void onReceive(Context context, Intent intent) {
201 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
202 synchronized (mLock) {
203 populateAccessibilityServiceListLocked();
svetoslavganov75986cf2009-05-14 22:28:01 -0700204
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800205 // get the accessibility enabled setting on boot
svetoslavganov75986cf2009-05-14 22:28:01 -0700206 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
207 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800208
209 // if accessibility is enabled inform our clients we are on
210 if (mIsEnabled) {
211 updateClientsLocked();
212 }
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800213
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800214 manageServicesLocked();
Jeff Brown0029c662011-03-30 02:25:18 -0700215 updateInputFilterLocked();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800216 }
217
218 return;
svetoslavganov75986cf2009-05-14 22:28:01 -0700219 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800220
221 super.onReceive(context, intent);
svetoslavganov75986cf2009-05-14 22:28:01 -0700222 }
223 };
224
225 // package changes
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800226 monitor.register(context, true);
svetoslavganov75986cf2009-05-14 22:28:01 -0700227
228 // boot completed
229 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800230 mContext.registerReceiver(monitor, bootFiler);
svetoslavganov75986cf2009-05-14 22:28:01 -0700231 }
232
233 /**
234 * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
235 * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
236 */
237 private void registerSettingsContentObservers() {
238 ContentResolver contentResolver = mContext.getContentResolver();
239
240 Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
241 contentResolver.registerContentObserver(enabledUri, false,
242 new ContentObserver(new Handler()) {
243 @Override
244 public void onChange(boolean selfChange) {
245 super.onChange(selfChange);
246
svetoslavganov75986cf2009-05-14 22:28:01 -0700247 synchronized (mLock) {
Svetoslav Ganov51f36f22010-12-18 16:20:53 -0800248 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
249 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
svetoslavganov75986cf2009-05-14 22:28:01 -0700250 if (mIsEnabled) {
251 manageServicesLocked();
252 } else {
253 unbindAllServicesLocked();
254 }
255 updateClientsLocked();
Jeff Brown0029c662011-03-30 02:25:18 -0700256 updateInputFilterLocked();
svetoslavganov75986cf2009-05-14 22:28:01 -0700257 }
258 }
259 });
260
261 Uri providersUri =
262 Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
263 contentResolver.registerContentObserver(providersUri, false,
264 new ContentObserver(new Handler()) {
265 @Override
266 public void onChange(boolean selfChange) {
267 super.onChange(selfChange);
268
269 synchronized (mLock) {
270 manageServicesLocked();
271 }
272 }
273 });
274 }
275
Svetoslav Ganovdd64a9b2010-04-13 18:41:17 -0700276 public boolean addClient(IAccessibilityManagerClient client) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700277 synchronized (mLock) {
Svetoslav Ganovdd64a9b2010-04-13 18:41:17 -0700278 mClients.add(client);
279 return mIsEnabled;
svetoslavganov75986cf2009-05-14 22:28:01 -0700280 }
281 }
282
283 public boolean sendAccessibilityEvent(AccessibilityEvent event) {
284 synchronized (mLock) {
285 notifyAccessibilityServicesDelayedLocked(event, false);
286 notifyAccessibilityServicesDelayedLocked(event, true);
287 }
288 // event not scheduled for dispatch => recycle
289 if (mHandledFeedbackTypes == 0) {
290 event.recycle();
291 } else {
292 mHandledFeedbackTypes = 0;
293 }
294
295 return (OWN_PROCESS_ID != Binder.getCallingPid());
296 }
297
298 public List<ServiceInfo> getAccessibilityServiceList() {
299 synchronized (mLock) {
300 return mInstalledServices;
301 }
302 }
303
304 public void interrupt() {
305 synchronized (mLock) {
306 for (int i = 0, count = mServices.size(); i < count; i++) {
307 Service service = mServices.get(i);
308 try {
309 service.mServiceInterface.onInterrupt();
310 } catch (RemoteException re) {
311 if (re instanceof DeadObjectException) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800312 Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
svetoslavganov75986cf2009-05-14 22:28:01 -0700313 if (removeDeadServiceLocked(service)) {
314 count--;
315 i--;
316 }
317 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800318 Slog.e(LOG_TAG, "Error during sending interrupt request to "
svetoslavganov75986cf2009-05-14 22:28:01 -0700319 + service.mService, re);
320 }
321 }
322 }
323 }
324 }
325
326 public void executeMessage(Message message) {
327 switch (message.what) {
328 case DO_SET_SERVICE_INFO:
329 SomeArgs arguments = ((SomeArgs) message.obj);
330
331 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
332 Service service = (Service) arguments.arg2;
333
334 synchronized (mLock) {
335 service.mEventTypes = info.eventTypes;
336 service.mFeedbackType = info.feedbackType;
337 String[] packageNames = info.packageNames;
338 if (packageNames != null) {
339 service.mPackageNames.addAll(Arrays.asList(packageNames));
340 }
341 service.mNotificationTimeout = info.notificationTimeout;
342 service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
343 }
344 return;
345 default:
Joe Onorato8a9b2202010-02-26 18:56:32 -0800346 Slog.w(LOG_TAG, "Unknown message type: " + message.what);
svetoslavganov75986cf2009-05-14 22:28:01 -0700347 }
348 }
349
350 /**
351 * Populates the cached list of installed {@link AccessibilityService}s.
352 */
353 private void populateAccessibilityServiceListLocked() {
354 mInstalledServices.clear();
355
356 List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
357 new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
358
359 for (int i = 0, count = installedServices.size(); i < count; i++) {
360 mInstalledServices.add(installedServices.get(i).serviceInfo);
361 }
362 }
363
364 /**
365 * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
366 * and denotes the period after the last event before notifying the service.
367 *
368 * @param event The event.
369 * @param isDefault True to notify default listeners, not default services.
370 */
371 private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
372 boolean isDefault) {
Charles Chen85b598b2009-07-29 17:23:50 -0700373 try {
374 for (int i = 0, count = mServices.size(); i < count; i++) {
375 Service service = mServices.get(i);
svetoslavganov75986cf2009-05-14 22:28:01 -0700376
Charles Chen85b598b2009-07-29 17:23:50 -0700377 if (service.mIsDefault == isDefault) {
378 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
379 mHandledFeedbackTypes |= service.mFeedbackType;
380 notifyAccessibilityServiceDelayedLocked(service, event);
381 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700382 }
383 }
Charles Chen85b598b2009-07-29 17:23:50 -0700384 } catch (IndexOutOfBoundsException oobe) {
385 // An out of bounds exception can happen if services are going away
386 // as the for loop is running. If that happens, just bail because
387 // there are no more services to notify.
388 return;
svetoslavganov75986cf2009-05-14 22:28:01 -0700389 }
390 }
391
392 /**
393 * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
394 * and denotes the period after the last event before notifying the service.
395 *
396 * @param service The service.
397 * @param event The event.
398 */
399 private void notifyAccessibilityServiceDelayedLocked(Service service,
400 AccessibilityEvent event) {
401 synchronized (mLock) {
402 int eventType = event.getEventType();
403 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
404 service.mPendingEvents.put(eventType, event);
405
406 int what = eventType | (service.mId << 16);
407 if (oldEvent != null) {
408 mHandler.removeMessages(what);
409 tryRecycleLocked(oldEvent);
410 }
411
412 Message message = mHandler.obtainMessage(what, service);
413 message.arg1 = event.getEventType();
414 mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
415 }
416 }
417
418 /**
419 * Recycles an event if it can be safely recycled. The condition is that no
420 * not notified service is interested in the event.
421 *
422 * @param event The event.
423 */
424 private void tryRecycleLocked(AccessibilityEvent event) {
Charles Chenbbc19342009-07-24 16:06:09 -0700425 if (event == null) {
426 return;
427 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700428 int eventType = event.getEventType();
429 List<Service> services = mServices;
430
431 // linear in the number of service which is not large
432 for (int i = 0, count = services.size(); i < count; i++) {
433 Service service = services.get(i);
434 if (service.mPendingEvents.get(eventType) == event) {
435 return;
436 }
437 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700438 event.recycle();
439 }
440
441 /**
442 * Notifies a service for a scheduled event given the event type.
443 *
444 * @param service The service.
445 * @param eventType The type of the event to dispatch.
446 */
447 private void notifyEventListenerLocked(Service service, int eventType) {
448 IEventListener listener = service.mServiceInterface;
449 AccessibilityEvent event = service.mPendingEvents.get(eventType);
450
451 try {
452 listener.onAccessibilityEvent(event);
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800453 if (Config.DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800454 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
svetoslavganov75986cf2009-05-14 22:28:01 -0700455 }
456 } catch (RemoteException re) {
457 if (re instanceof DeadObjectException) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800458 Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
Svetoslav Ganov51f36f22010-12-18 16:20:53 -0800459 removeDeadServiceLocked(service);
svetoslavganov75986cf2009-05-14 22:28:01 -0700460 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800461 Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
svetoslavganov75986cf2009-05-14 22:28:01 -0700462 }
463 }
464 }
465
466 /**
467 * Removes a dead service.
468 *
469 * @param service The service.
470 * @return True if the service was removed, false otherwise.
471 */
472 private boolean removeDeadServiceLocked(Service service) {
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800473 if (Config.DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800474 Slog.i(LOG_TAG, "Dead service " + service.mService + " removed");
svetoslavganov75986cf2009-05-14 22:28:01 -0700475 }
Svetoslav Ganov51f36f22010-12-18 16:20:53 -0800476 mHandler.removeMessages(service.mId);
477 return mServices.remove(service);
svetoslavganov75986cf2009-05-14 22:28:01 -0700478 }
479
480 /**
481 * Determines if given event can be dispatched to a service based on the package of the
482 * event source and already notified services for that event type. Specifically, a
483 * service is notified if it is interested in events from the package and no other service
484 * providing the same feedback type has been notified. Exception are services the
485 * provide generic feedback (feedback type left as a safety net for unforeseen feedback
486 * types) which are always notified.
487 *
488 * @param service The potential receiver.
489 * @param event The event.
490 * @param handledFeedbackTypes The feedback types for which services have been notified.
491 * @return True if the listener should be notified, false otherwise.
492 */
493 private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
494 int handledFeedbackTypes) {
495
496 if (!service.isConfigured()) {
497 return false;
498 }
499
500 if (!service.mService.isBinderAlive()) {
501 removeDeadServiceLocked(service);
502 return false;
503 }
504
505 int eventType = event.getEventType();
506 if ((service.mEventTypes & eventType) != eventType) {
507 return false;
508 }
509
510 Set<String> packageNames = service.mPackageNames;
511 CharSequence packageName = event.getPackageName();
512
513 if (packageNames.isEmpty() || packageNames.contains(packageName)) {
514 int feedbackType = service.mFeedbackType;
515 if ((handledFeedbackTypes & feedbackType) != feedbackType
516 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
517 return true;
518 }
519 }
520
521 return false;
522 }
523
524 /**
525 * Manages services by starting enabled ones and stopping disabled ones.
526 */
527 private void manageServicesLocked() {
528 populateEnabledServicesLocked(mEnabledServices);
529 updateServicesStateLocked(mInstalledServices, mEnabledServices);
530 }
531
532 /**
533 * Unbinds all bound services.
534 */
535 private void unbindAllServicesLocked() {
536 List<Service> services = mServices;
537
538 for (int i = 0, count = services.size(); i < count; i++) {
539 Service service = services.get(i);
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800540 if (service.unbind()) {
541 i--;
542 count--;
543 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700544 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700545 }
546
547 /**
548 * Populates a list with the {@link ComponentName}s of all enabled
549 * {@link AccessibilityService}s.
550 *
551 * @param enabledServices The list.
552 */
553 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
554 enabledServices.clear();
555
556 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
557 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
558
559 if (servicesValue != null) {
560 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
561 splitter.setString(servicesValue);
562 while (splitter.hasNext()) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800563 String str = splitter.next();
564 if (str == null || str.length() <= 0) {
565 continue;
566 }
567 ComponentName enabledService = ComponentName.unflattenFromString(str);
568 if (enabledService != null) {
569 enabledServices.add(enabledService);
570 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700571 }
572 }
573 }
574
575 /**
576 * Updates the state of each service by starting (or keeping running) enabled ones and
577 * stopping the rest.
578 *
579 * @param installedServices All installed {@link AccessibilityService}s.
580 * @param enabledServices The {@link ComponentName}s of the enabled services.
581 */
582 private void updateServicesStateLocked(List<ServiceInfo> installedServices,
583 Set<ComponentName> enabledServices) {
584
585 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800586 boolean isEnabled = mIsEnabled;
svetoslavganov75986cf2009-05-14 22:28:01 -0700587
588 for (int i = 0, count = installedServices.size(); i < count; i++) {
589 ServiceInfo intalledService = installedServices.get(i);
590 ComponentName componentName = new ComponentName(intalledService.packageName,
591 intalledService.name);
592 Service service = componentNameToServiceMap.get(componentName);
593
Svetoslav Ganovf2245aa2010-12-28 15:48:52 -0800594 if (isEnabled) {
Svetoslav Ganov563d7842011-01-06 17:40:26 -0800595 if (enabledServices.contains(componentName)) {
596 if (service == null) {
597 service = new Service(componentName);
598 }
599 service.bind();
600 } else if (!enabledServices.contains(componentName)) {
601 if (service != null) {
602 service.unbind();
603 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700604 }
605 } else {
606 if (service != null) {
607 service.unbind();
svetoslavganov75986cf2009-05-14 22:28:01 -0700608 }
609 }
610 }
611 }
612
613 /**
614 * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
615 */
616 private void updateClientsLocked() {
617 for (int i = 0, count = mClients.size(); i < count; i++) {
618 try {
619 mClients.get(i).setEnabled(mIsEnabled);
620 } catch (RemoteException re) {
621 mClients.remove(i);
622 count--;
Svetoslav Ganovfb606da2010-02-18 10:54:36 -0800623 i--;
svetoslavganov75986cf2009-05-14 22:28:01 -0700624 }
625 }
626 }
627
628 /**
Jeff Brown0029c662011-03-30 02:25:18 -0700629 * Installs or removes the accessibility input filter when accessibility is enabled
630 * or disabled.
631 */
632 private void updateInputFilterLocked() {
633 WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
634 Context.WINDOW_SERVICE);
635 if (wm != null) {
636 if (mIsEnabled) {
637 if (mInputFilter == null) {
638 mInputFilter = new AccessibilityInputFilter(mContext);
639 }
640 wm.setInputFilter(mInputFilter);
641 } else {
642 wm.setInputFilter(null);
643 }
644 }
645 }
646
647 /**
svetoslavganov75986cf2009-05-14 22:28:01 -0700648 * This class represents an accessibility service. It stores all per service
649 * data required for the service management, provides API for starting/stopping the
650 * service and is responsible for adding/removing the service in the data structures
651 * for service management. The class also exposes configuration interface that is
652 * passed to the service it represents as soon it is bound. It also serves as the
653 * connection for the service.
654 */
655 class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
656 int mId = 0;
657
658 IBinder mService;
659
660 IEventListener mServiceInterface;
661
662 int mEventTypes;
663
664 int mFeedbackType;
665
666 Set<String> mPackageNames = new HashSet<String>();
667
668 boolean mIsDefault;
669
670 long mNotificationTimeout;
671
672 boolean mIsActive;
673
674 ComponentName mComponentName;
675
676 Intent mIntent;
677
678 // the events pending events to be dispatched to this service
679 final SparseArray<AccessibilityEvent> mPendingEvents =
680 new SparseArray<AccessibilityEvent>();
681
682 Service(ComponentName componentName) {
683 mId = sIdCounter++;
684 mComponentName = componentName;
685 mIntent = new Intent().setComponent(mComponentName);
Dianne Hackborndd9b82c2009-09-03 00:18:47 -0700686 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
687 com.android.internal.R.string.accessibility_binding_label);
688 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
689 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
svetoslavganov75986cf2009-05-14 22:28:01 -0700690 }
691
692 /**
693 * Binds to the accessibility service.
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800694 *
695 * @return True if binding is successful.
svetoslavganov75986cf2009-05-14 22:28:01 -0700696 */
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800697 public boolean bind() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700698 if (mService == null) {
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800699 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
svetoslavganov75986cf2009-05-14 22:28:01 -0700700 }
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800701 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -0700702 }
703
704 /**
705 * Unbinds form the accessibility service and removes it from the data
706 * structures for service management.
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800707 *
708 * @return True if unbinding is successful.
svetoslavganov75986cf2009-05-14 22:28:01 -0700709 */
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800710 public boolean unbind() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700711 if (mService != null) {
Svetoslav Ganov6f089b82011-01-11 15:04:40 -0800712 mService = null;
svetoslavganov75986cf2009-05-14 22:28:01 -0700713 mContext.unbindService(this);
Svetoslav Ganov563d7842011-01-06 17:40:26 -0800714 mComponentNameToServiceMap.remove(mComponentName);
715 mServices.remove(this);
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800716 return true;
svetoslavganov75986cf2009-05-14 22:28:01 -0700717 }
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800718 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -0700719 }
720
721 /**
722 * Returns if the service is configured i.e. at least event types of interest
723 * and feedback type must be set.
724 *
725 * @return True if the service is configured, false otherwise.
726 */
727 public boolean isConfigured() {
728 return (mEventTypes != 0 && mFeedbackType != 0);
729 }
730
731 public void setServiceInfo(AccessibilityServiceInfo info) {
732 mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
733 }
734
735 public void onServiceConnected(ComponentName componentName, IBinder service) {
736 mService = service;
737 mServiceInterface = IEventListener.Stub.asInterface(service);
738
739 try {
740 mServiceInterface.setConnection(this);
741 synchronized (mLock) {
742 if (!mServices.contains(this)) {
743 mServices.add(this);
744 mComponentNameToServiceMap.put(componentName, this);
745 }
746 }
747 } catch (RemoteException re) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800748 Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
svetoslavganov75986cf2009-05-14 22:28:01 -0700749 }
750 }
751
752 public void onServiceDisconnected(ComponentName componentName) {
753 synchronized (mLock) {
754 Service service = mComponentNameToServiceMap.remove(componentName);
755 mServices.remove(service);
756 }
757 }
758 }
759}