blob: f67a7aec48ccf23a329f2829e7a18c870f815250 [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
19import static android.util.Config.LOGV;
20
21import com.android.internal.os.HandlerCaller;
22import com.android.internal.os.HandlerCaller.SomeArgs;
23
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;
47import android.provider.Settings;
48import android.text.TextUtils;
49import android.text.TextUtils.SimpleStringSplitter;
50import android.util.Log;
51import 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;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63
64/**
65 * This class is instantiated by the system as a system level service and can be
66 * accessed only by the system. The task of this service is to be a centralized
67 * event dispatch for {@link AccessibilityEvent}s generated across all processes
68 * on the device. Events are dispatched to {@link AccessibilityService}s.
69 *
70 * @hide
71 */
72public class AccessibilityManagerService extends IAccessibilityManager.Stub
73 implements HandlerCaller.Callback {
74
75 private static final String LOG_TAG = "AccessibilityManagerService";
76
77 private static int sIdCounter = 0;
78
79 private static final int OWN_PROCESS_ID = android.os.Process.myPid();
80
81 private static final int DO_SET_SERVICE_INFO = 10;
82
83 final HandlerCaller mCaller;
84
85 final Context mContext;
86
87 final Object mLock = new Object();
88
89 final List<Service> mServices = new ArrayList<Service>();
90
91 final List<IAccessibilityManagerClient> mClients =
92 new ArrayList<IAccessibilityManagerClient>();
93
94 final Map<ComponentName, Service> mComponentNameToServiceMap =
95 new HashMap<ComponentName, Service>();
96
97 private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
98
99 private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
100
101 private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
102
103 private PackageManager mPackageManager;
104
105 private int mHandledFeedbackTypes = 0;
106
107 private boolean mIsEnabled;
108
109 /**
110 * Handler for delayed event dispatch.
111 */
112 private Handler mHandler = new Handler() {
113
114 @Override
115 public void handleMessage(Message message) {
116 Service service = (Service) message.obj;
117 int eventType = message.arg1;
118
119 synchronized (mLock) {
120 notifyEventListenerLocked(service, eventType);
121 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
122 service.mPendingEvents.remove(eventType);
123 tryRecycleLocked(oldEvent);
124 }
125 }
126 };
127
128 /**
129 * Creates a new instance.
130 *
131 * @param context A {@link Context} instance.
132 */
133 AccessibilityManagerService(Context context) {
134 mContext = context;
135 mPackageManager = mContext.getPackageManager();
136 mCaller = new HandlerCaller(context, this);
137
138 registerPackageChangeAndBootCompletedBroadcastReceiver();
139 registerSettingsContentObservers();
140
141 synchronized (mLock) {
142 populateAccessibilityServiceListLocked();
143 }
144 }
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
153 BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
154 @Override
155 public void onReceive(Context context, Intent intent) {
156 synchronized (mLock) {
157 populateAccessibilityServiceListLocked();
158 manageServicesLocked();
159
160 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
161 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
162 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
163 updateClientsLocked();
164 }
165 }
166 }
167 };
168
169 // package changes
170 IntentFilter packageFilter = new IntentFilter();
171 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
172 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
173 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
174 packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
175 packageFilter.addDataScheme("package");
176 context.registerReceiver(broadcastReceiver, packageFilter);
177
178 // boot completed
179 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
180 mContext.registerReceiver(broadcastReceiver, bootFiler);
181 }
182
183 /**
184 * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
185 * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
186 */
187 private void registerSettingsContentObservers() {
188 ContentResolver contentResolver = mContext.getContentResolver();
189
190 Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
191 contentResolver.registerContentObserver(enabledUri, false,
192 new ContentObserver(new Handler()) {
193 @Override
194 public void onChange(boolean selfChange) {
195 super.onChange(selfChange);
196
197 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
198 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
199
200 synchronized (mLock) {
201 if (mIsEnabled) {
202 manageServicesLocked();
203 } else {
204 unbindAllServicesLocked();
205 }
206 updateClientsLocked();
207 }
208 }
209 });
210
211 Uri providersUri =
212 Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
213 contentResolver.registerContentObserver(providersUri, false,
214 new ContentObserver(new Handler()) {
215 @Override
216 public void onChange(boolean selfChange) {
217 super.onChange(selfChange);
218
219 synchronized (mLock) {
220 manageServicesLocked();
221 }
222 }
223 });
224 }
225
226 public void addClient(IAccessibilityManagerClient client) {
227 synchronized (mLock) {
228 try {
229 client.setEnabled(mIsEnabled);
230 mClients.add(client);
231 } catch (RemoteException re) {
232 Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
233 }
234 }
235 }
236
237 public boolean sendAccessibilityEvent(AccessibilityEvent event) {
238 synchronized (mLock) {
239 notifyAccessibilityServicesDelayedLocked(event, false);
240 notifyAccessibilityServicesDelayedLocked(event, true);
241 }
242 // event not scheduled for dispatch => recycle
243 if (mHandledFeedbackTypes == 0) {
244 event.recycle();
245 } else {
246 mHandledFeedbackTypes = 0;
247 }
248
249 return (OWN_PROCESS_ID != Binder.getCallingPid());
250 }
251
252 public List<ServiceInfo> getAccessibilityServiceList() {
253 synchronized (mLock) {
254 return mInstalledServices;
255 }
256 }
257
258 public void interrupt() {
259 synchronized (mLock) {
260 for (int i = 0, count = mServices.size(); i < count; i++) {
261 Service service = mServices.get(i);
262 try {
263 service.mServiceInterface.onInterrupt();
264 } catch (RemoteException re) {
265 if (re instanceof DeadObjectException) {
266 Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
267 if (removeDeadServiceLocked(service)) {
268 count--;
269 i--;
270 }
271 } else {
272 Log.e(LOG_TAG, "Error during sending interrupt request to "
273 + service.mService, re);
274 }
275 }
276 }
277 }
278 }
279
280 public void executeMessage(Message message) {
281 switch (message.what) {
282 case DO_SET_SERVICE_INFO:
283 SomeArgs arguments = ((SomeArgs) message.obj);
284
285 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
286 Service service = (Service) arguments.arg2;
287
288 synchronized (mLock) {
289 service.mEventTypes = info.eventTypes;
290 service.mFeedbackType = info.feedbackType;
291 String[] packageNames = info.packageNames;
292 if (packageNames != null) {
293 service.mPackageNames.addAll(Arrays.asList(packageNames));
294 }
295 service.mNotificationTimeout = info.notificationTimeout;
296 service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
297 }
298 return;
299 default:
300 Log.w(LOG_TAG, "Unknown message type: " + message.what);
301 }
302 }
303
304 /**
305 * Populates the cached list of installed {@link AccessibilityService}s.
306 */
307 private void populateAccessibilityServiceListLocked() {
308 mInstalledServices.clear();
309
310 List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
311 new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
312
313 for (int i = 0, count = installedServices.size(); i < count; i++) {
314 mInstalledServices.add(installedServices.get(i).serviceInfo);
315 }
316 }
317
318 /**
319 * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
320 * and denotes the period after the last event before notifying the service.
321 *
322 * @param event The event.
323 * @param isDefault True to notify default listeners, not default services.
324 */
325 private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
326 boolean isDefault) {
Charles Chen85b598b2009-07-29 17:23:50 -0700327 try {
328 for (int i = 0, count = mServices.size(); i < count; i++) {
329 Service service = mServices.get(i);
svetoslavganov75986cf2009-05-14 22:28:01 -0700330
Charles Chen85b598b2009-07-29 17:23:50 -0700331 if (service.mIsDefault == isDefault) {
332 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
333 mHandledFeedbackTypes |= service.mFeedbackType;
334 notifyAccessibilityServiceDelayedLocked(service, event);
335 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700336 }
337 }
Charles Chen85b598b2009-07-29 17:23:50 -0700338 } catch (IndexOutOfBoundsException oobe) {
339 // An out of bounds exception can happen if services are going away
340 // as the for loop is running. If that happens, just bail because
341 // there are no more services to notify.
342 return;
svetoslavganov75986cf2009-05-14 22:28:01 -0700343 }
344 }
345
346 /**
347 * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
348 * and denotes the period after the last event before notifying the service.
349 *
350 * @param service The service.
351 * @param event The event.
352 */
353 private void notifyAccessibilityServiceDelayedLocked(Service service,
354 AccessibilityEvent event) {
355 synchronized (mLock) {
356 int eventType = event.getEventType();
357 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
358 service.mPendingEvents.put(eventType, event);
359
360 int what = eventType | (service.mId << 16);
361 if (oldEvent != null) {
362 mHandler.removeMessages(what);
363 tryRecycleLocked(oldEvent);
364 }
365
366 Message message = mHandler.obtainMessage(what, service);
367 message.arg1 = event.getEventType();
368 mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
369 }
370 }
371
372 /**
373 * Recycles an event if it can be safely recycled. The condition is that no
374 * not notified service is interested in the event.
375 *
376 * @param event The event.
377 */
378 private void tryRecycleLocked(AccessibilityEvent event) {
Charles Chenbbc19342009-07-24 16:06:09 -0700379 if (event == null) {
380 return;
381 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700382 int eventType = event.getEventType();
383 List<Service> services = mServices;
384
385 // linear in the number of service which is not large
386 for (int i = 0, count = services.size(); i < count; i++) {
387 Service service = services.get(i);
388 if (service.mPendingEvents.get(eventType) == event) {
389 return;
390 }
391 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700392 event.recycle();
393 }
394
395 /**
396 * Notifies a service for a scheduled event given the event type.
397 *
398 * @param service The service.
399 * @param eventType The type of the event to dispatch.
400 */
401 private void notifyEventListenerLocked(Service service, int eventType) {
402 IEventListener listener = service.mServiceInterface;
403 AccessibilityEvent event = service.mPendingEvents.get(eventType);
404
405 try {
406 listener.onAccessibilityEvent(event);
407 if (LOGV) {
408 Log.i(LOG_TAG, "Event " + event + " sent to " + listener);
409 }
410 } catch (RemoteException re) {
411 if (re instanceof DeadObjectException) {
412 Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
413 synchronized (mLock) {
414 removeDeadServiceLocked(service);
415 }
416 } else {
417 Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
418 }
419 }
420 }
421
422 /**
423 * Removes a dead service.
424 *
425 * @param service The service.
426 * @return True if the service was removed, false otherwise.
427 */
428 private boolean removeDeadServiceLocked(Service service) {
429 mServices.remove(service);
430 mHandler.removeMessages(service.mId);
431
432 if (LOGV) {
433 Log.i(LOG_TAG, "Dead service " + service.mService + " removed");
434 }
435
436 if (mServices.isEmpty()) {
437 mIsEnabled = false;
438 updateClientsLocked();
439 }
440
441 return true;
442 }
443
444 /**
445 * Determines if given event can be dispatched to a service based on the package of the
446 * event source and already notified services for that event type. Specifically, a
447 * service is notified if it is interested in events from the package and no other service
448 * providing the same feedback type has been notified. Exception are services the
449 * provide generic feedback (feedback type left as a safety net for unforeseen feedback
450 * types) which are always notified.
451 *
452 * @param service The potential receiver.
453 * @param event The event.
454 * @param handledFeedbackTypes The feedback types for which services have been notified.
455 * @return True if the listener should be notified, false otherwise.
456 */
457 private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
458 int handledFeedbackTypes) {
459
460 if (!service.isConfigured()) {
461 return false;
462 }
463
464 if (!service.mService.isBinderAlive()) {
465 removeDeadServiceLocked(service);
466 return false;
467 }
468
469 int eventType = event.getEventType();
470 if ((service.mEventTypes & eventType) != eventType) {
471 return false;
472 }
473
474 Set<String> packageNames = service.mPackageNames;
475 CharSequence packageName = event.getPackageName();
476
477 if (packageNames.isEmpty() || packageNames.contains(packageName)) {
478 int feedbackType = service.mFeedbackType;
479 if ((handledFeedbackTypes & feedbackType) != feedbackType
480 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
481 return true;
482 }
483 }
484
485 return false;
486 }
487
488 /**
489 * Manages services by starting enabled ones and stopping disabled ones.
490 */
491 private void manageServicesLocked() {
492 populateEnabledServicesLocked(mEnabledServices);
493 updateServicesStateLocked(mInstalledServices, mEnabledServices);
494 }
495
496 /**
497 * Unbinds all bound services.
498 */
499 private void unbindAllServicesLocked() {
500 List<Service> services = mServices;
501
502 for (int i = 0, count = services.size(); i < count; i++) {
503 Service service = services.get(i);
504
505 service.unbind();
506 mComponentNameToServiceMap.remove(service.mComponentName);
507 }
508 services.clear();
509 }
510
511 /**
512 * Populates a list with the {@link ComponentName}s of all enabled
513 * {@link AccessibilityService}s.
514 *
515 * @param enabledServices The list.
516 */
517 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
518 enabledServices.clear();
519
520 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
521 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
522
523 if (servicesValue != null) {
524 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
525 splitter.setString(servicesValue);
526 while (splitter.hasNext()) {
527 ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
528 enabledServices.add(enabledService);
529 }
530 }
531 }
532
533 /**
534 * Updates the state of each service by starting (or keeping running) enabled ones and
535 * stopping the rest.
536 *
537 * @param installedServices All installed {@link AccessibilityService}s.
538 * @param enabledServices The {@link ComponentName}s of the enabled services.
539 */
540 private void updateServicesStateLocked(List<ServiceInfo> installedServices,
541 Set<ComponentName> enabledServices) {
542
543 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
544 List<Service> services = mServices;
545
546 for (int i = 0, count = installedServices.size(); i < count; i++) {
547 ServiceInfo intalledService = installedServices.get(i);
548 ComponentName componentName = new ComponentName(intalledService.packageName,
549 intalledService.name);
550 Service service = componentNameToServiceMap.get(componentName);
551
552 if (enabledServices.contains(componentName)) {
553 if (service == null) {
554 new Service(componentName).bind();
555 }
556 } else {
557 if (service != null) {
558 service.unbind();
559 componentNameToServiceMap.remove(componentName);
560 services.remove(service);
561 }
562 }
563 }
564 }
565
566 /**
567 * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
568 */
569 private void updateClientsLocked() {
570 for (int i = 0, count = mClients.size(); i < count; i++) {
571 try {
572 mClients.get(i).setEnabled(mIsEnabled);
573 } catch (RemoteException re) {
574 mClients.remove(i);
575 count--;
576 }
577 }
578 }
579
580 /**
581 * This class represents an accessibility service. It stores all per service
582 * data required for the service management, provides API for starting/stopping the
583 * service and is responsible for adding/removing the service in the data structures
584 * for service management. The class also exposes configuration interface that is
585 * passed to the service it represents as soon it is bound. It also serves as the
586 * connection for the service.
587 */
588 class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
589 int mId = 0;
590
591 IBinder mService;
592
593 IEventListener mServiceInterface;
594
595 int mEventTypes;
596
597 int mFeedbackType;
598
599 Set<String> mPackageNames = new HashSet<String>();
600
601 boolean mIsDefault;
602
603 long mNotificationTimeout;
604
605 boolean mIsActive;
606
607 ComponentName mComponentName;
608
609 Intent mIntent;
610
611 // the events pending events to be dispatched to this service
612 final SparseArray<AccessibilityEvent> mPendingEvents =
613 new SparseArray<AccessibilityEvent>();
614
615 Service(ComponentName componentName) {
616 mId = sIdCounter++;
617 mComponentName = componentName;
618 mIntent = new Intent().setComponent(mComponentName);
Dianne Hackborndd9b82c2009-09-03 00:18:47 -0700619 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
620 com.android.internal.R.string.accessibility_binding_label);
621 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
622 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
svetoslavganov75986cf2009-05-14 22:28:01 -0700623 }
624
625 /**
626 * Binds to the accessibility service.
627 */
628 public void bind() {
629 if (mService == null) {
630 mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
631 }
632 }
633
634 /**
635 * Unbinds form the accessibility service and removes it from the data
636 * structures for service management.
637 */
638 public void unbind() {
639 if (mService != null) {
640 mContext.unbindService(this);
641 }
642 }
643
644 /**
645 * Returns if the service is configured i.e. at least event types of interest
646 * and feedback type must be set.
647 *
648 * @return True if the service is configured, false otherwise.
649 */
650 public boolean isConfigured() {
651 return (mEventTypes != 0 && mFeedbackType != 0);
652 }
653
654 public void setServiceInfo(AccessibilityServiceInfo info) {
655 mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
656 }
657
658 public void onServiceConnected(ComponentName componentName, IBinder service) {
659 mService = service;
660 mServiceInterface = IEventListener.Stub.asInterface(service);
661
662 try {
663 mServiceInterface.setConnection(this);
664 synchronized (mLock) {
665 if (!mServices.contains(this)) {
666 mServices.add(this);
667 mComponentNameToServiceMap.put(componentName, this);
668 }
669 }
670 } catch (RemoteException re) {
671 Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
672 }
673 }
674
675 public void onServiceDisconnected(ComponentName componentName) {
676 synchronized (mLock) {
677 Service service = mComponentNameToServiceMap.remove(componentName);
678 mServices.remove(service);
679 }
680 }
681 }
682}