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