blob: 04ae4901d74bcbc2e3d659700c59abf218cd0ab3 [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
17package com.android.server;
18
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;
22
23import android.accessibilityservice.AccessibilityService;
24import android.accessibilityservice.AccessibilityServiceInfo;
25import android.accessibilityservice.IAccessibilityServiceConnection;
26import android.accessibilityservice.IEventListener;
Dianne Hackborndd9b82c2009-09-03 00:18:47 -070027import android.app.PendingIntent;
svetoslavganov75986cf2009-05-14 22:28:01 -070028import android.content.BroadcastReceiver;
29import android.content.ComponentName;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.ServiceConnection;
35import android.content.pm.PackageManager;
36import android.content.pm.ResolveInfo;
37import android.content.pm.ServiceInfo;
38import android.database.ContentObserver;
39import android.net.Uri;
40import android.os.Binder;
41import android.os.DeadObjectException;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.Message;
45import android.os.RemoteException;
46import android.provider.Settings;
47import android.text.TextUtils;
48import android.text.TextUtils.SimpleStringSplitter;
Svetoslav Ganov714cff02010-02-17 19:36:28 -080049import android.util.Config;
Joe Onorato8a9b2202010-02-26 18:56:32 -080050import android.util.Slog;
svetoslavganov75986cf2009-05-14 22:28:01 -070051import android.util.SparseArray;
52import android.view.accessibility.AccessibilityEvent;
53import android.view.accessibility.IAccessibilityManager;
54import android.view.accessibility.IAccessibilityManagerClient;
55
56import java.util.ArrayList;
57import java.util.Arrays;
58import java.util.HashMap;
59import java.util.HashSet;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080060import java.util.Iterator;
svetoslavganov75986cf2009-05-14 22:28:01 -070061import java.util.List;
62import java.util.Map;
63import java.util.Set;
64
65/**
66 * This class is instantiated by the system as a system level service and can be
67 * accessed only by the system. The task of this service is to be a centralized
68 * event dispatch for {@link AccessibilityEvent}s generated across all processes
69 * on the device. Events are dispatched to {@link AccessibilityService}s.
70 *
71 * @hide
72 */
73public class AccessibilityManagerService extends IAccessibilityManager.Stub
74 implements HandlerCaller.Callback {
75
76 private static final String LOG_TAG = "AccessibilityManagerService";
77
78 private static int sIdCounter = 0;
79
80 private static final int OWN_PROCESS_ID = android.os.Process.myPid();
81
82 private static final int DO_SET_SERVICE_INFO = 10;
83
84 final HandlerCaller mCaller;
85
86 final Context mContext;
87
88 final Object mLock = new Object();
89
90 final List<Service> mServices = new ArrayList<Service>();
91
92 final List<IAccessibilityManagerClient> mClients =
93 new ArrayList<IAccessibilityManagerClient>();
94
95 final Map<ComponentName, Service> mComponentNameToServiceMap =
96 new HashMap<ComponentName, Service>();
97
98 private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
99
100 private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
101
102 private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
103
104 private PackageManager mPackageManager;
105
106 private int mHandledFeedbackTypes = 0;
107
108 private boolean mIsEnabled;
109
110 /**
111 * Handler for delayed event dispatch.
112 */
113 private Handler mHandler = new Handler() {
114
115 @Override
116 public void handleMessage(Message message) {
117 Service service = (Service) message.obj;
118 int eventType = message.arg1;
119
120 synchronized (mLock) {
121 notifyEventListenerLocked(service, eventType);
122 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
123 service.mPendingEvents.remove(eventType);
124 tryRecycleLocked(oldEvent);
125 }
126 }
127 };
128
129 /**
130 * Creates a new instance.
131 *
132 * @param context A {@link Context} instance.
133 */
134 AccessibilityManagerService(Context context) {
135 mContext = context;
136 mPackageManager = mContext.getPackageManager();
137 mCaller = new HandlerCaller(context, this);
138
139 registerPackageChangeAndBootCompletedBroadcastReceiver();
140 registerSettingsContentObservers();
svetoslavganov75986cf2009-05-14 22:28:01 -0700141 }
142
143 /**
144 * Registers a {@link BroadcastReceiver} for the events of
145 * adding/changing/removing/restarting a package and boot completion.
146 */
147 private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
148 Context context = mContext;
149
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800150 PackageMonitor monitor = new PackageMonitor() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700151 @Override
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800152 public void onSomePackagesChanged() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700153 synchronized (mLock) {
154 populateAccessibilityServiceListLocked();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800155 manageServicesLocked();
156 }
157 }
158
159 @Override
160 public boolean onHandleForceStop(Intent intent, String[] packages,
161 int uid, boolean doit) {
162 synchronized (mLock) {
163 boolean changed = false;
164 Iterator<ComponentName> it = mEnabledServices.iterator();
165 while (it.hasNext()) {
166 ComponentName comp = it.next();
167 String compPkg = comp.getPackageName();
168 for (String pkg : packages) {
169 if (compPkg.equals(pkg)) {
170 if (!doit) {
171 return true;
172 }
173 it.remove();
174 changed = true;
175 }
176 }
177 }
178 if (changed) {
179 it = mEnabledServices.iterator();
180 StringBuilder str = new StringBuilder();
181 while (it.hasNext()) {
182 if (str.length() > 0) {
183 str.append(':');
184 }
185 str.append(it.next().flattenToShortString());
186 }
187 Settings.Secure.putString(mContext.getContentResolver(),
188 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
189 str.toString());
190 manageServicesLocked();
191 }
192 return false;
193 }
194 }
195
196 @Override
197 public void onReceive(Context context, Intent intent) {
198 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
199 synchronized (mLock) {
200 populateAccessibilityServiceListLocked();
svetoslavganov75986cf2009-05-14 22:28:01 -0700201
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800202 // get the accessibility enabled setting on boot
svetoslavganov75986cf2009-05-14 22:28:01 -0700203 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
204 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800205
206 // if accessibility is enabled inform our clients we are on
207 if (mIsEnabled) {
208 updateClientsLocked();
209 }
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800210
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800211 manageServicesLocked();
212 }
213
214 return;
svetoslavganov75986cf2009-05-14 22:28:01 -0700215 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800216
217 super.onReceive(context, intent);
svetoslavganov75986cf2009-05-14 22:28:01 -0700218 }
219 };
220
221 // package changes
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800222 monitor.register(context, true);
svetoslavganov75986cf2009-05-14 22:28:01 -0700223
224 // boot completed
225 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800226 mContext.registerReceiver(monitor, bootFiler);
svetoslavganov75986cf2009-05-14 22:28:01 -0700227 }
228
229 /**
230 * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
231 * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
232 */
233 private void registerSettingsContentObservers() {
234 ContentResolver contentResolver = mContext.getContentResolver();
235
236 Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
237 contentResolver.registerContentObserver(enabledUri, false,
238 new ContentObserver(new Handler()) {
239 @Override
240 public void onChange(boolean selfChange) {
241 super.onChange(selfChange);
242
svetoslavganov75986cf2009-05-14 22:28:01 -0700243 synchronized (mLock) {
Svetoslav Ganov51f36f22010-12-18 16:20:53 -0800244 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
245 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
svetoslavganov75986cf2009-05-14 22:28:01 -0700246 if (mIsEnabled) {
247 manageServicesLocked();
248 } else {
249 unbindAllServicesLocked();
250 }
251 updateClientsLocked();
252 }
253 }
254 });
255
256 Uri providersUri =
257 Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
258 contentResolver.registerContentObserver(providersUri, false,
259 new ContentObserver(new Handler()) {
260 @Override
261 public void onChange(boolean selfChange) {
262 super.onChange(selfChange);
263
264 synchronized (mLock) {
265 manageServicesLocked();
266 }
267 }
268 });
269 }
270
Svetoslav Ganovdd64a9b2010-04-13 18:41:17 -0700271 public boolean addClient(IAccessibilityManagerClient client) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700272 synchronized (mLock) {
Svetoslav Ganovdd64a9b2010-04-13 18:41:17 -0700273 mClients.add(client);
274 return mIsEnabled;
svetoslavganov75986cf2009-05-14 22:28:01 -0700275 }
276 }
277
278 public boolean sendAccessibilityEvent(AccessibilityEvent event) {
279 synchronized (mLock) {
280 notifyAccessibilityServicesDelayedLocked(event, false);
281 notifyAccessibilityServicesDelayedLocked(event, true);
282 }
283 // event not scheduled for dispatch => recycle
284 if (mHandledFeedbackTypes == 0) {
285 event.recycle();
286 } else {
287 mHandledFeedbackTypes = 0;
288 }
289
290 return (OWN_PROCESS_ID != Binder.getCallingPid());
291 }
292
293 public List<ServiceInfo> getAccessibilityServiceList() {
294 synchronized (mLock) {
295 return mInstalledServices;
296 }
297 }
298
299 public void interrupt() {
300 synchronized (mLock) {
301 for (int i = 0, count = mServices.size(); i < count; i++) {
302 Service service = mServices.get(i);
303 try {
304 service.mServiceInterface.onInterrupt();
305 } catch (RemoteException re) {
306 if (re instanceof DeadObjectException) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800307 Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
svetoslavganov75986cf2009-05-14 22:28:01 -0700308 if (removeDeadServiceLocked(service)) {
309 count--;
310 i--;
311 }
312 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800313 Slog.e(LOG_TAG, "Error during sending interrupt request to "
svetoslavganov75986cf2009-05-14 22:28:01 -0700314 + service.mService, re);
315 }
316 }
317 }
318 }
319 }
320
321 public void executeMessage(Message message) {
322 switch (message.what) {
323 case DO_SET_SERVICE_INFO:
324 SomeArgs arguments = ((SomeArgs) message.obj);
325
326 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
327 Service service = (Service) arguments.arg2;
328
329 synchronized (mLock) {
330 service.mEventTypes = info.eventTypes;
331 service.mFeedbackType = info.feedbackType;
332 String[] packageNames = info.packageNames;
333 if (packageNames != null) {
334 service.mPackageNames.addAll(Arrays.asList(packageNames));
335 }
336 service.mNotificationTimeout = info.notificationTimeout;
337 service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
338 }
339 return;
340 default:
Joe Onorato8a9b2202010-02-26 18:56:32 -0800341 Slog.w(LOG_TAG, "Unknown message type: " + message.what);
svetoslavganov75986cf2009-05-14 22:28:01 -0700342 }
343 }
344
345 /**
346 * Populates the cached list of installed {@link AccessibilityService}s.
347 */
348 private void populateAccessibilityServiceListLocked() {
349 mInstalledServices.clear();
350
351 List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
352 new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
353
354 for (int i = 0, count = installedServices.size(); i < count; i++) {
355 mInstalledServices.add(installedServices.get(i).serviceInfo);
356 }
357 }
358
359 /**
360 * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
361 * and denotes the period after the last event before notifying the service.
362 *
363 * @param event The event.
364 * @param isDefault True to notify default listeners, not default services.
365 */
366 private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
367 boolean isDefault) {
Charles Chen85b598b2009-07-29 17:23:50 -0700368 try {
369 for (int i = 0, count = mServices.size(); i < count; i++) {
370 Service service = mServices.get(i);
svetoslavganov75986cf2009-05-14 22:28:01 -0700371
Charles Chen85b598b2009-07-29 17:23:50 -0700372 if (service.mIsDefault == isDefault) {
373 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
374 mHandledFeedbackTypes |= service.mFeedbackType;
375 notifyAccessibilityServiceDelayedLocked(service, event);
376 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700377 }
378 }
Charles Chen85b598b2009-07-29 17:23:50 -0700379 } catch (IndexOutOfBoundsException oobe) {
380 // An out of bounds exception can happen if services are going away
381 // as the for loop is running. If that happens, just bail because
382 // there are no more services to notify.
383 return;
svetoslavganov75986cf2009-05-14 22:28:01 -0700384 }
385 }
386
387 /**
388 * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
389 * and denotes the period after the last event before notifying the service.
390 *
391 * @param service The service.
392 * @param event The event.
393 */
394 private void notifyAccessibilityServiceDelayedLocked(Service service,
395 AccessibilityEvent event) {
396 synchronized (mLock) {
397 int eventType = event.getEventType();
398 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
399 service.mPendingEvents.put(eventType, event);
400
401 int what = eventType | (service.mId << 16);
402 if (oldEvent != null) {
403 mHandler.removeMessages(what);
404 tryRecycleLocked(oldEvent);
405 }
406
407 Message message = mHandler.obtainMessage(what, service);
408 message.arg1 = event.getEventType();
409 mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
410 }
411 }
412
413 /**
414 * Recycles an event if it can be safely recycled. The condition is that no
415 * not notified service is interested in the event.
416 *
417 * @param event The event.
418 */
419 private void tryRecycleLocked(AccessibilityEvent event) {
Charles Chenbbc19342009-07-24 16:06:09 -0700420 if (event == null) {
421 return;
422 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700423 int eventType = event.getEventType();
424 List<Service> services = mServices;
425
426 // linear in the number of service which is not large
427 for (int i = 0, count = services.size(); i < count; i++) {
428 Service service = services.get(i);
429 if (service.mPendingEvents.get(eventType) == event) {
430 return;
431 }
432 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700433 event.recycle();
434 }
435
436 /**
437 * Notifies a service for a scheduled event given the event type.
438 *
439 * @param service The service.
440 * @param eventType The type of the event to dispatch.
441 */
442 private void notifyEventListenerLocked(Service service, int eventType) {
443 IEventListener listener = service.mServiceInterface;
444 AccessibilityEvent event = service.mPendingEvents.get(eventType);
445
446 try {
447 listener.onAccessibilityEvent(event);
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800448 if (Config.DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800449 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
svetoslavganov75986cf2009-05-14 22:28:01 -0700450 }
451 } catch (RemoteException re) {
452 if (re instanceof DeadObjectException) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800453 Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
Svetoslav Ganov51f36f22010-12-18 16:20:53 -0800454 removeDeadServiceLocked(service);
svetoslavganov75986cf2009-05-14 22:28:01 -0700455 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800456 Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
svetoslavganov75986cf2009-05-14 22:28:01 -0700457 }
458 }
459 }
460
461 /**
462 * Removes a dead service.
463 *
464 * @param service The service.
465 * @return True if the service was removed, false otherwise.
466 */
467 private boolean removeDeadServiceLocked(Service service) {
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800468 if (Config.DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800469 Slog.i(LOG_TAG, "Dead service " + service.mService + " removed");
svetoslavganov75986cf2009-05-14 22:28:01 -0700470 }
Svetoslav Ganov51f36f22010-12-18 16:20:53 -0800471 mHandler.removeMessages(service.mId);
472 return mServices.remove(service);
svetoslavganov75986cf2009-05-14 22:28:01 -0700473 }
474
475 /**
476 * Determines if given event can be dispatched to a service based on the package of the
477 * event source and already notified services for that event type. Specifically, a
478 * service is notified if it is interested in events from the package and no other service
479 * providing the same feedback type has been notified. Exception are services the
480 * provide generic feedback (feedback type left as a safety net for unforeseen feedback
481 * types) which are always notified.
482 *
483 * @param service The potential receiver.
484 * @param event The event.
485 * @param handledFeedbackTypes The feedback types for which services have been notified.
486 * @return True if the listener should be notified, false otherwise.
487 */
488 private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
489 int handledFeedbackTypes) {
490
491 if (!service.isConfigured()) {
492 return false;
493 }
494
495 if (!service.mService.isBinderAlive()) {
496 removeDeadServiceLocked(service);
497 return false;
498 }
499
500 int eventType = event.getEventType();
501 if ((service.mEventTypes & eventType) != eventType) {
502 return false;
503 }
504
505 Set<String> packageNames = service.mPackageNames;
506 CharSequence packageName = event.getPackageName();
507
508 if (packageNames.isEmpty() || packageNames.contains(packageName)) {
509 int feedbackType = service.mFeedbackType;
510 if ((handledFeedbackTypes & feedbackType) != feedbackType
511 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
512 return true;
513 }
514 }
515
516 return false;
517 }
518
519 /**
520 * Manages services by starting enabled ones and stopping disabled ones.
521 */
522 private void manageServicesLocked() {
523 populateEnabledServicesLocked(mEnabledServices);
524 updateServicesStateLocked(mInstalledServices, mEnabledServices);
525 }
526
527 /**
528 * Unbinds all bound services.
529 */
530 private void unbindAllServicesLocked() {
531 List<Service> services = mServices;
532
533 for (int i = 0, count = services.size(); i < count; i++) {
534 Service service = services.get(i);
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800535 if (service.unbind()) {
536 i--;
537 count--;
538 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700539 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700540 }
541
542 /**
543 * Populates a list with the {@link ComponentName}s of all enabled
544 * {@link AccessibilityService}s.
545 *
546 * @param enabledServices The list.
547 */
548 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
549 enabledServices.clear();
550
551 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
552 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
553
554 if (servicesValue != null) {
555 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
556 splitter.setString(servicesValue);
557 while (splitter.hasNext()) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800558 String str = splitter.next();
559 if (str == null || str.length() <= 0) {
560 continue;
561 }
562 ComponentName enabledService = ComponentName.unflattenFromString(str);
563 if (enabledService != null) {
564 enabledServices.add(enabledService);
565 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700566 }
567 }
568 }
569
570 /**
571 * Updates the state of each service by starting (or keeping running) enabled ones and
572 * stopping the rest.
573 *
574 * @param installedServices All installed {@link AccessibilityService}s.
575 * @param enabledServices The {@link ComponentName}s of the enabled services.
576 */
577 private void updateServicesStateLocked(List<ServiceInfo> installedServices,
578 Set<ComponentName> enabledServices) {
579
580 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
Svetoslav Ganov714cff02010-02-17 19:36:28 -0800581 boolean isEnabled = mIsEnabled;
svetoslavganov75986cf2009-05-14 22:28:01 -0700582
583 for (int i = 0, count = installedServices.size(); i < count; i++) {
584 ServiceInfo intalledService = installedServices.get(i);
585 ComponentName componentName = new ComponentName(intalledService.packageName,
586 intalledService.name);
587 Service service = componentNameToServiceMap.get(componentName);
588
Svetoslav Ganovf2245aa2010-12-28 15:48:52 -0800589 if (isEnabled) {
Svetoslav Ganov563d7842011-01-06 17:40:26 -0800590 if (enabledServices.contains(componentName)) {
591 if (service == null) {
592 service = new Service(componentName);
593 }
594 service.bind();
595 } else if (!enabledServices.contains(componentName)) {
596 if (service != null) {
597 service.unbind();
598 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700599 }
600 } else {
601 if (service != null) {
602 service.unbind();
svetoslavganov75986cf2009-05-14 22:28:01 -0700603 }
604 }
605 }
606 }
607
608 /**
609 * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
610 */
611 private void updateClientsLocked() {
612 for (int i = 0, count = mClients.size(); i < count; i++) {
613 try {
614 mClients.get(i).setEnabled(mIsEnabled);
615 } catch (RemoteException re) {
616 mClients.remove(i);
617 count--;
Svetoslav Ganovfb606da2010-02-18 10:54:36 -0800618 i--;
svetoslavganov75986cf2009-05-14 22:28:01 -0700619 }
620 }
621 }
622
623 /**
624 * This class represents an accessibility service. It stores all per service
625 * data required for the service management, provides API for starting/stopping the
626 * service and is responsible for adding/removing the service in the data structures
627 * for service management. The class also exposes configuration interface that is
628 * passed to the service it represents as soon it is bound. It also serves as the
629 * connection for the service.
630 */
631 class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
632 int mId = 0;
633
634 IBinder mService;
635
636 IEventListener mServiceInterface;
637
638 int mEventTypes;
639
640 int mFeedbackType;
641
642 Set<String> mPackageNames = new HashSet<String>();
643
644 boolean mIsDefault;
645
646 long mNotificationTimeout;
647
648 boolean mIsActive;
649
650 ComponentName mComponentName;
651
652 Intent mIntent;
653
654 // the events pending events to be dispatched to this service
655 final SparseArray<AccessibilityEvent> mPendingEvents =
656 new SparseArray<AccessibilityEvent>();
657
658 Service(ComponentName componentName) {
659 mId = sIdCounter++;
660 mComponentName = componentName;
661 mIntent = new Intent().setComponent(mComponentName);
Dianne Hackborndd9b82c2009-09-03 00:18:47 -0700662 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
663 com.android.internal.R.string.accessibility_binding_label);
664 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
665 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
svetoslavganov75986cf2009-05-14 22:28:01 -0700666 }
667
668 /**
669 * Binds to the accessibility service.
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800670 *
671 * @return True if binding is successful.
svetoslavganov75986cf2009-05-14 22:28:01 -0700672 */
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800673 public boolean bind() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700674 if (mService == null) {
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800675 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
svetoslavganov75986cf2009-05-14 22:28:01 -0700676 }
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800677 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -0700678 }
679
680 /**
681 * Unbinds form the accessibility service and removes it from the data
682 * structures for service management.
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800683 *
684 * @return True if unbinding is successful.
svetoslavganov75986cf2009-05-14 22:28:01 -0700685 */
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800686 public boolean unbind() {
svetoslavganov75986cf2009-05-14 22:28:01 -0700687 if (mService != null) {
Svetoslav Ganov6f089b82011-01-11 15:04:40 -0800688 mService = null;
svetoslavganov75986cf2009-05-14 22:28:01 -0700689 mContext.unbindService(this);
Svetoslav Ganov563d7842011-01-06 17:40:26 -0800690 mComponentNameToServiceMap.remove(mComponentName);
691 mServices.remove(this);
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800692 return true;
svetoslavganov75986cf2009-05-14 22:28:01 -0700693 }
Svetoslav Ganov6ff5f102011-01-10 13:12:55 -0800694 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -0700695 }
696
697 /**
698 * Returns if the service is configured i.e. at least event types of interest
699 * and feedback type must be set.
700 *
701 * @return True if the service is configured, false otherwise.
702 */
703 public boolean isConfigured() {
704 return (mEventTypes != 0 && mFeedbackType != 0);
705 }
706
707 public void setServiceInfo(AccessibilityServiceInfo info) {
708 mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
709 }
710
711 public void onServiceConnected(ComponentName componentName, IBinder service) {
712 mService = service;
713 mServiceInterface = IEventListener.Stub.asInterface(service);
714
715 try {
716 mServiceInterface.setConnection(this);
717 synchronized (mLock) {
718 if (!mServices.contains(this)) {
719 mServices.add(this);
720 mComponentNameToServiceMap.put(componentName, this);
721 }
722 }
723 } catch (RemoteException re) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800724 Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
svetoslavganov75986cf2009-05-14 22:28:01 -0700725 }
726 }
727
728 public void onServiceDisconnected(ComponentName componentName) {
729 synchronized (mLock) {
730 Service service = mComponentNameToServiceMap.remove(componentName);
731 mServices.remove(service);
732 }
733 }
734 }
735}