blob: 63c9eaac6d74193106a1314c9e78e714a300f42c [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) {
326 for (int i = 0, count = mServices.size(); i < count; i++) {
327 Service service = mServices.get(i);
328
329 if (service.mIsDefault == isDefault) {
330 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
331 mHandledFeedbackTypes |= service.mFeedbackType;
332 notifyAccessibilityServiceDelayedLocked(service, event);
333 }
334 }
335 }
336 }
337
338 /**
339 * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
340 * and denotes the period after the last event before notifying the service.
341 *
342 * @param service The service.
343 * @param event The event.
344 */
345 private void notifyAccessibilityServiceDelayedLocked(Service service,
346 AccessibilityEvent event) {
347 synchronized (mLock) {
348 int eventType = event.getEventType();
349 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
350 service.mPendingEvents.put(eventType, event);
351
352 int what = eventType | (service.mId << 16);
353 if (oldEvent != null) {
354 mHandler.removeMessages(what);
355 tryRecycleLocked(oldEvent);
356 }
357
358 Message message = mHandler.obtainMessage(what, service);
359 message.arg1 = event.getEventType();
360 mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
361 }
362 }
363
364 /**
365 * Recycles an event if it can be safely recycled. The condition is that no
366 * not notified service is interested in the event.
367 *
368 * @param event The event.
369 */
370 private void tryRecycleLocked(AccessibilityEvent event) {
Charles Chenbbc19342009-07-24 16:06:09 -0700371 if (event == null) {
372 return;
373 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700374 int eventType = event.getEventType();
375 List<Service> services = mServices;
376
377 // linear in the number of service which is not large
378 for (int i = 0, count = services.size(); i < count; i++) {
379 Service service = services.get(i);
380 if (service.mPendingEvents.get(eventType) == event) {
381 return;
382 }
383 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700384 event.recycle();
385 }
386
387 /**
388 * Notifies a service for a scheduled event given the event type.
389 *
390 * @param service The service.
391 * @param eventType The type of the event to dispatch.
392 */
393 private void notifyEventListenerLocked(Service service, int eventType) {
394 IEventListener listener = service.mServiceInterface;
395 AccessibilityEvent event = service.mPendingEvents.get(eventType);
396
397 try {
398 listener.onAccessibilityEvent(event);
399 if (LOGV) {
400 Log.i(LOG_TAG, "Event " + event + " sent to " + listener);
401 }
402 } catch (RemoteException re) {
403 if (re instanceof DeadObjectException) {
404 Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
405 synchronized (mLock) {
406 removeDeadServiceLocked(service);
407 }
408 } else {
409 Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
410 }
411 }
412 }
413
414 /**
415 * Removes a dead service.
416 *
417 * @param service The service.
418 * @return True if the service was removed, false otherwise.
419 */
420 private boolean removeDeadServiceLocked(Service service) {
421 mServices.remove(service);
422 mHandler.removeMessages(service.mId);
423
424 if (LOGV) {
425 Log.i(LOG_TAG, "Dead service " + service.mService + " removed");
426 }
427
428 if (mServices.isEmpty()) {
429 mIsEnabled = false;
430 updateClientsLocked();
431 }
432
433 return true;
434 }
435
436 /**
437 * Determines if given event can be dispatched to a service based on the package of the
438 * event source and already notified services for that event type. Specifically, a
439 * service is notified if it is interested in events from the package and no other service
440 * providing the same feedback type has been notified. Exception are services the
441 * provide generic feedback (feedback type left as a safety net for unforeseen feedback
442 * types) which are always notified.
443 *
444 * @param service The potential receiver.
445 * @param event The event.
446 * @param handledFeedbackTypes The feedback types for which services have been notified.
447 * @return True if the listener should be notified, false otherwise.
448 */
449 private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
450 int handledFeedbackTypes) {
451
452 if (!service.isConfigured()) {
453 return false;
454 }
455
456 if (!service.mService.isBinderAlive()) {
457 removeDeadServiceLocked(service);
458 return false;
459 }
460
461 int eventType = event.getEventType();
462 if ((service.mEventTypes & eventType) != eventType) {
463 return false;
464 }
465
466 Set<String> packageNames = service.mPackageNames;
467 CharSequence packageName = event.getPackageName();
468
469 if (packageNames.isEmpty() || packageNames.contains(packageName)) {
470 int feedbackType = service.mFeedbackType;
471 if ((handledFeedbackTypes & feedbackType) != feedbackType
472 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
473 return true;
474 }
475 }
476
477 return false;
478 }
479
480 /**
481 * Manages services by starting enabled ones and stopping disabled ones.
482 */
483 private void manageServicesLocked() {
484 populateEnabledServicesLocked(mEnabledServices);
485 updateServicesStateLocked(mInstalledServices, mEnabledServices);
486 }
487
488 /**
489 * Unbinds all bound services.
490 */
491 private void unbindAllServicesLocked() {
492 List<Service> services = mServices;
493
494 for (int i = 0, count = services.size(); i < count; i++) {
495 Service service = services.get(i);
496
497 service.unbind();
498 mComponentNameToServiceMap.remove(service.mComponentName);
499 }
500 services.clear();
501 }
502
503 /**
504 * Populates a list with the {@link ComponentName}s of all enabled
505 * {@link AccessibilityService}s.
506 *
507 * @param enabledServices The list.
508 */
509 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
510 enabledServices.clear();
511
512 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
513 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
514
515 if (servicesValue != null) {
516 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
517 splitter.setString(servicesValue);
518 while (splitter.hasNext()) {
519 ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
520 enabledServices.add(enabledService);
521 }
522 }
523 }
524
525 /**
526 * Updates the state of each service by starting (or keeping running) enabled ones and
527 * stopping the rest.
528 *
529 * @param installedServices All installed {@link AccessibilityService}s.
530 * @param enabledServices The {@link ComponentName}s of the enabled services.
531 */
532 private void updateServicesStateLocked(List<ServiceInfo> installedServices,
533 Set<ComponentName> enabledServices) {
534
535 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
536 List<Service> services = mServices;
537
538 for (int i = 0, count = installedServices.size(); i < count; i++) {
539 ServiceInfo intalledService = installedServices.get(i);
540 ComponentName componentName = new ComponentName(intalledService.packageName,
541 intalledService.name);
542 Service service = componentNameToServiceMap.get(componentName);
543
544 if (enabledServices.contains(componentName)) {
545 if (service == null) {
546 new Service(componentName).bind();
547 }
548 } else {
549 if (service != null) {
550 service.unbind();
551 componentNameToServiceMap.remove(componentName);
552 services.remove(service);
553 }
554 }
555 }
556 }
557
558 /**
559 * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
560 */
561 private void updateClientsLocked() {
562 for (int i = 0, count = mClients.size(); i < count; i++) {
563 try {
564 mClients.get(i).setEnabled(mIsEnabled);
565 } catch (RemoteException re) {
566 mClients.remove(i);
567 count--;
568 }
569 }
570 }
571
572 /**
573 * This class represents an accessibility service. It stores all per service
574 * data required for the service management, provides API for starting/stopping the
575 * service and is responsible for adding/removing the service in the data structures
576 * for service management. The class also exposes configuration interface that is
577 * passed to the service it represents as soon it is bound. It also serves as the
578 * connection for the service.
579 */
580 class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
581 int mId = 0;
582
583 IBinder mService;
584
585 IEventListener mServiceInterface;
586
587 int mEventTypes;
588
589 int mFeedbackType;
590
591 Set<String> mPackageNames = new HashSet<String>();
592
593 boolean mIsDefault;
594
595 long mNotificationTimeout;
596
597 boolean mIsActive;
598
599 ComponentName mComponentName;
600
601 Intent mIntent;
602
603 // the events pending events to be dispatched to this service
604 final SparseArray<AccessibilityEvent> mPendingEvents =
605 new SparseArray<AccessibilityEvent>();
606
607 Service(ComponentName componentName) {
608 mId = sIdCounter++;
609 mComponentName = componentName;
610 mIntent = new Intent().setComponent(mComponentName);
611 }
612
613 /**
614 * Binds to the accessibility service.
615 */
616 public void bind() {
617 if (mService == null) {
618 mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
619 }
620 }
621
622 /**
623 * Unbinds form the accessibility service and removes it from the data
624 * structures for service management.
625 */
626 public void unbind() {
627 if (mService != null) {
628 mContext.unbindService(this);
629 }
630 }
631
632 /**
633 * Returns if the service is configured i.e. at least event types of interest
634 * and feedback type must be set.
635 *
636 * @return True if the service is configured, false otherwise.
637 */
638 public boolean isConfigured() {
639 return (mEventTypes != 0 && mFeedbackType != 0);
640 }
641
642 public void setServiceInfo(AccessibilityServiceInfo info) {
643 mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
644 }
645
646 public void onServiceConnected(ComponentName componentName, IBinder service) {
647 mService = service;
648 mServiceInterface = IEventListener.Stub.asInterface(service);
649
650 try {
651 mServiceInterface.setConnection(this);
652 synchronized (mLock) {
653 if (!mServices.contains(this)) {
654 mServices.add(this);
655 mComponentNameToServiceMap.put(componentName, this);
656 }
657 }
658 } catch (RemoteException re) {
659 Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
660 }
661 }
662
663 public void onServiceDisconnected(ComponentName componentName) {
664 synchronized (mLock) {
665 Service service = mComponentNameToServiceMap.remove(componentName);
666 mServices.remove(service);
667 }
668 }
669 }
670}