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