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