summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorsvetoslavganov <svetoslavganov@google.com>2009-05-14 22:28:01 -0700
committersvetoslavganov <svetoslavganov@google.com>2009-05-14 23:47:05 -0700
commit75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec (patch)
tree84e1843368037d24f83749d152f818d537267bfa /services
parent669ec3a6e47248fee0a3a0f4877b46875eb42140 (diff)
downloadframeworks_base-75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec.zip
frameworks_base-75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec.tar.gz
frameworks_base-75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec.tar.bz2
Accessibility feature - framework changes (replacing 698, 699, 700, 701 and merging with the latest Donut)
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/AccessibilityManagerService.java668
-rw-r--r--services/java/com/android/server/NotificationManagerService.java43
-rw-r--r--services/java/com/android/server/SystemServer.java8
3 files changed, 709 insertions, 10 deletions
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
new file mode 100644
index 0000000..c205fc0
--- /dev/null
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -0,0 +1,668 @@
+/*
+ ** Copyright 2009, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.util.Config.LOGV;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.HandlerCaller.SomeArgs;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.IEventListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class is instantiated by the system as a system level service and can be
+ * accessed only by the system. The task of this service is to be a centralized
+ * event dispatch for {@link AccessibilityEvent}s generated across all processes
+ * on the device. Events are dispatched to {@link AccessibilityService}s.
+ *
+ * @hide
+ */
+public class AccessibilityManagerService extends IAccessibilityManager.Stub
+ implements HandlerCaller.Callback {
+
+ private static final String LOG_TAG = "AccessibilityManagerService";
+
+ private static int sIdCounter = 0;
+
+ private static final int OWN_PROCESS_ID = android.os.Process.myPid();
+
+ private static final int DO_SET_SERVICE_INFO = 10;
+
+ final HandlerCaller mCaller;
+
+ final Context mContext;
+
+ final Object mLock = new Object();
+
+ final List<Service> mServices = new ArrayList<Service>();
+
+ final List<IAccessibilityManagerClient> mClients =
+ new ArrayList<IAccessibilityManagerClient>();
+
+ final Map<ComponentName, Service> mComponentNameToServiceMap =
+ new HashMap<ComponentName, Service>();
+
+ private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
+
+ private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+
+ private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
+
+ private PackageManager mPackageManager;
+
+ private int mHandledFeedbackTypes = 0;
+
+ private boolean mIsEnabled;
+
+ /**
+ * Handler for delayed event dispatch.
+ */
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message message) {
+ Service service = (Service) message.obj;
+ int eventType = message.arg1;
+
+ synchronized (mLock) {
+ notifyEventListenerLocked(service, eventType);
+ AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
+ service.mPendingEvents.remove(eventType);
+ tryRecycleLocked(oldEvent);
+ }
+ }
+ };
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context A {@link Context} instance.
+ */
+ AccessibilityManagerService(Context context) {
+ mContext = context;
+ mPackageManager = mContext.getPackageManager();
+ mCaller = new HandlerCaller(context, this);
+
+ registerPackageChangeAndBootCompletedBroadcastReceiver();
+ registerSettingsContentObservers();
+
+ synchronized (mLock) {
+ populateAccessibilityServiceListLocked();
+ }
+ }
+
+ /**
+ * Registers a {@link BroadcastReceiver} for the events of
+ * adding/changing/removing/restarting a package and boot completion.
+ */
+ private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
+ Context context = mContext;
+
+ BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ populateAccessibilityServiceListLocked();
+ manageServicesLocked();
+
+ if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
+ mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+ updateClientsLocked();
+ }
+ }
+ }
+ };
+
+ // package changes
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ packageFilter.addDataScheme("package");
+ context.registerReceiver(broadcastReceiver, packageFilter);
+
+ // boot completed
+ IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ mContext.registerReceiver(broadcastReceiver, bootFiler);
+ }
+
+ /**
+ * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
+ * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
+ */
+ private void registerSettingsContentObservers() {
+ ContentResolver contentResolver = mContext.getContentResolver();
+
+ Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
+ contentResolver.registerContentObserver(enabledUri, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+
+ synchronized (mLock) {
+ if (mIsEnabled) {
+ manageServicesLocked();
+ } else {
+ unbindAllServicesLocked();
+ }
+ updateClientsLocked();
+ }
+ }
+ });
+
+ Uri providersUri =
+ Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ contentResolver.registerContentObserver(providersUri, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ synchronized (mLock) {
+ manageServicesLocked();
+ }
+ }
+ });
+ }
+
+ public void addClient(IAccessibilityManagerClient client) {
+ synchronized (mLock) {
+ try {
+ client.setEnabled(mIsEnabled);
+ mClients.add(client);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
+ }
+ }
+ }
+
+ public boolean sendAccessibilityEvent(AccessibilityEvent event) {
+ synchronized (mLock) {
+ notifyAccessibilityServicesDelayedLocked(event, false);
+ notifyAccessibilityServicesDelayedLocked(event, true);
+ }
+ // event not scheduled for dispatch => recycle
+ if (mHandledFeedbackTypes == 0) {
+ event.recycle();
+ } else {
+ mHandledFeedbackTypes = 0;
+ }
+
+ return (OWN_PROCESS_ID != Binder.getCallingPid());
+ }
+
+ public List<ServiceInfo> getAccessibilityServiceList() {
+ synchronized (mLock) {
+ return mInstalledServices;
+ }
+ }
+
+ public void interrupt() {
+ synchronized (mLock) {
+ for (int i = 0, count = mServices.size(); i < count; i++) {
+ Service service = mServices.get(i);
+ try {
+ service.mServiceInterface.onInterrupt();
+ } catch (RemoteException re) {
+ if (re instanceof DeadObjectException) {
+ Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
+ if (removeDeadServiceLocked(service)) {
+ count--;
+ i--;
+ }
+ } else {
+ Log.e(LOG_TAG, "Error during sending interrupt request to "
+ + service.mService, re);
+ }
+ }
+ }
+ }
+ }
+
+ public void executeMessage(Message message) {
+ switch (message.what) {
+ case DO_SET_SERVICE_INFO:
+ SomeArgs arguments = ((SomeArgs) message.obj);
+
+ AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
+ Service service = (Service) arguments.arg2;
+
+ synchronized (mLock) {
+ service.mEventTypes = info.eventTypes;
+ service.mFeedbackType = info.feedbackType;
+ String[] packageNames = info.packageNames;
+ if (packageNames != null) {
+ service.mPackageNames.addAll(Arrays.asList(packageNames));
+ }
+ service.mNotificationTimeout = info.notificationTimeout;
+ service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+ }
+ return;
+ default:
+ Log.w(LOG_TAG, "Unknown message type: " + message.what);
+ }
+ }
+
+ /**
+ * Populates the cached list of installed {@link AccessibilityService}s.
+ */
+ private void populateAccessibilityServiceListLocked() {
+ mInstalledServices.clear();
+
+ List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
+ new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
+
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ mInstalledServices.add(installedServices.get(i).serviceInfo);
+ }
+ }
+
+ /**
+ * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
+ * and denotes the period after the last event before notifying the service.
+ *
+ * @param event The event.
+ * @param isDefault True to notify default listeners, not default services.
+ */
+ private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
+ boolean isDefault) {
+ for (int i = 0, count = mServices.size(); i < count; i++) {
+ Service service = mServices.get(i);
+
+ if (service.mIsDefault == isDefault) {
+ if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
+ mHandledFeedbackTypes |= service.mFeedbackType;
+ notifyAccessibilityServiceDelayedLocked(service, event);
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
+ * and denotes the period after the last event before notifying the service.
+ *
+ * @param service The service.
+ * @param event The event.
+ */
+ private void notifyAccessibilityServiceDelayedLocked(Service service,
+ AccessibilityEvent event) {
+ synchronized (mLock) {
+ int eventType = event.getEventType();
+ AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
+ service.mPendingEvents.put(eventType, event);
+
+ int what = eventType | (service.mId << 16);
+ if (oldEvent != null) {
+ mHandler.removeMessages(what);
+ tryRecycleLocked(oldEvent);
+ }
+
+ Message message = mHandler.obtainMessage(what, service);
+ message.arg1 = event.getEventType();
+ mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
+ }
+ }
+
+ /**
+ * Recycles an event if it can be safely recycled. The condition is that no
+ * not notified service is interested in the event.
+ *
+ * @param event The event.
+ */
+ private void tryRecycleLocked(AccessibilityEvent event) {
+ int eventType = event.getEventType();
+ List<Service> services = mServices;
+
+ // linear in the number of service which is not large
+ for (int i = 0, count = services.size(); i < count; i++) {
+ Service service = services.get(i);
+ if (service.mPendingEvents.get(eventType) == event) {
+ return;
+ }
+ }
+
+ event.recycle();
+ }
+
+ /**
+ * Notifies a service for a scheduled event given the event type.
+ *
+ * @param service The service.
+ * @param eventType The type of the event to dispatch.
+ */
+ private void notifyEventListenerLocked(Service service, int eventType) {
+ IEventListener listener = service.mServiceInterface;
+ AccessibilityEvent event = service.mPendingEvents.get(eventType);
+
+ try {
+ listener.onAccessibilityEvent(event);
+ if (LOGV) {
+ Log.i(LOG_TAG, "Event " + event + " sent to " + listener);
+ }
+ } catch (RemoteException re) {
+ if (re instanceof DeadObjectException) {
+ Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
+ synchronized (mLock) {
+ removeDeadServiceLocked(service);
+ }
+ } else {
+ Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
+ }
+ }
+ }
+
+ /**
+ * Removes a dead service.
+ *
+ * @param service The service.
+ * @return True if the service was removed, false otherwise.
+ */
+ private boolean removeDeadServiceLocked(Service service) {
+ mServices.remove(service);
+ mHandler.removeMessages(service.mId);
+
+ if (LOGV) {
+ Log.i(LOG_TAG, "Dead service " + service.mService + " removed");
+ }
+
+ if (mServices.isEmpty()) {
+ mIsEnabled = false;
+ updateClientsLocked();
+ }
+
+ return true;
+ }
+
+ /**
+ * Determines if given event can be dispatched to a service based on the package of the
+ * event source and already notified services for that event type. Specifically, a
+ * service is notified if it is interested in events from the package and no other service
+ * providing the same feedback type has been notified. Exception are services the
+ * provide generic feedback (feedback type left as a safety net for unforeseen feedback
+ * types) which are always notified.
+ *
+ * @param service The potential receiver.
+ * @param event The event.
+ * @param handledFeedbackTypes The feedback types for which services have been notified.
+ * @return True if the listener should be notified, false otherwise.
+ */
+ private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
+ int handledFeedbackTypes) {
+
+ if (!service.isConfigured()) {
+ return false;
+ }
+
+ if (!service.mService.isBinderAlive()) {
+ removeDeadServiceLocked(service);
+ return false;
+ }
+
+ int eventType = event.getEventType();
+ if ((service.mEventTypes & eventType) != eventType) {
+ return false;
+ }
+
+ Set<String> packageNames = service.mPackageNames;
+ CharSequence packageName = event.getPackageName();
+
+ if (packageNames.isEmpty() || packageNames.contains(packageName)) {
+ int feedbackType = service.mFeedbackType;
+ if ((handledFeedbackTypes & feedbackType) != feedbackType
+ || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Manages services by starting enabled ones and stopping disabled ones.
+ */
+ private void manageServicesLocked() {
+ populateEnabledServicesLocked(mEnabledServices);
+ updateServicesStateLocked(mInstalledServices, mEnabledServices);
+ }
+
+ /**
+ * Unbinds all bound services.
+ */
+ private void unbindAllServicesLocked() {
+ List<Service> services = mServices;
+
+ for (int i = 0, count = services.size(); i < count; i++) {
+ Service service = services.get(i);
+
+ service.unbind();
+ mComponentNameToServiceMap.remove(service.mComponentName);
+ }
+ services.clear();
+ }
+
+ /**
+ * Populates a list with the {@link ComponentName}s of all enabled
+ * {@link AccessibilityService}s.
+ *
+ * @param enabledServices The list.
+ */
+ private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
+ enabledServices.clear();
+
+ String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+
+ if (servicesValue != null) {
+ TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ splitter.setString(servicesValue);
+ while (splitter.hasNext()) {
+ ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
+ enabledServices.add(enabledService);
+ }
+ }
+ }
+
+ /**
+ * Updates the state of each service by starting (or keeping running) enabled ones and
+ * stopping the rest.
+ *
+ * @param installedServices All installed {@link AccessibilityService}s.
+ * @param enabledServices The {@link ComponentName}s of the enabled services.
+ */
+ private void updateServicesStateLocked(List<ServiceInfo> installedServices,
+ Set<ComponentName> enabledServices) {
+
+ Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
+ List<Service> services = mServices;
+
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ServiceInfo intalledService = installedServices.get(i);
+ ComponentName componentName = new ComponentName(intalledService.packageName,
+ intalledService.name);
+ Service service = componentNameToServiceMap.get(componentName);
+
+ if (enabledServices.contains(componentName)) {
+ if (service == null) {
+ new Service(componentName).bind();
+ }
+ } else {
+ if (service != null) {
+ service.unbind();
+ componentNameToServiceMap.remove(componentName);
+ services.remove(service);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
+ */
+ private void updateClientsLocked() {
+ for (int i = 0, count = mClients.size(); i < count; i++) {
+ try {
+ mClients.get(i).setEnabled(mIsEnabled);
+ } catch (RemoteException re) {
+ mClients.remove(i);
+ count--;
+ }
+ }
+ }
+
+ /**
+ * This class represents an accessibility service. It stores all per service
+ * data required for the service management, provides API for starting/stopping the
+ * service and is responsible for adding/removing the service in the data structures
+ * for service management. The class also exposes configuration interface that is
+ * passed to the service it represents as soon it is bound. It also serves as the
+ * connection for the service.
+ */
+ class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
+ int mId = 0;
+
+ IBinder mService;
+
+ IEventListener mServiceInterface;
+
+ int mEventTypes;
+
+ int mFeedbackType;
+
+ Set<String> mPackageNames = new HashSet<String>();
+
+ boolean mIsDefault;
+
+ long mNotificationTimeout;
+
+ boolean mIsActive;
+
+ ComponentName mComponentName;
+
+ Intent mIntent;
+
+ // the events pending events to be dispatched to this service
+ final SparseArray<AccessibilityEvent> mPendingEvents =
+ new SparseArray<AccessibilityEvent>();
+
+ Service(ComponentName componentName) {
+ mId = sIdCounter++;
+ mComponentName = componentName;
+ mIntent = new Intent().setComponent(mComponentName);
+ }
+
+ /**
+ * Binds to the accessibility service.
+ */
+ public void bind() {
+ if (mService == null) {
+ mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ /**
+ * Unbinds form the accessibility service and removes it from the data
+ * structures for service management.
+ */
+ public void unbind() {
+ if (mService != null) {
+ mContext.unbindService(this);
+ }
+ }
+
+ /**
+ * Returns if the service is configured i.e. at least event types of interest
+ * and feedback type must be set.
+ *
+ * @return True if the service is configured, false otherwise.
+ */
+ public boolean isConfigured() {
+ return (mEventTypes != 0 && mFeedbackType != 0);
+ }
+
+ public void setServiceInfo(AccessibilityServiceInfo info) {
+ mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
+ }
+
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ mService = service;
+ mServiceInterface = IEventListener.Stub.asInterface(service);
+
+ try {
+ mServiceInterface.setConnection(this);
+ synchronized (mLock) {
+ if (!mServices.contains(this)) {
+ mServices.add(this);
+ mComponentNameToServiceMap.put(componentName, this);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName componentName) {
+ synchronized (mLock) {
+ Service service = mComponentNameToServiceMap.remove(componentName);
+ mServices.remove(service);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6ed8b4c..4a2808b 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,6 +16,10 @@
package com.android.server;
+import com.android.server.status.IconData;
+import com.android.server.status.NotificationData;
+import com.android.server.status.StatusBarService;
+
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.INotificationManager;
@@ -30,33 +34,29 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
-import android.media.AudioManager;
import android.media.AsyncPlayer;
-import android.media.RingtoneManager;
+import android.media.AudioManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
-import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Power;
+import android.os.RemoteException;
import android.os.Vibrator;
import android.provider.Settings;
-import android.util.Config;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
-import com.android.server.status.StatusBarService;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.io.IOException;
class NotificationManagerService extends INotificationManager.Stub
{
@@ -98,7 +98,7 @@ class NotificationManagerService extends INotificationManager.Stub
private boolean mBatteryLow;
private boolean mBatteryFull;
private NotificationRecord mLedNotification;
-
+
private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
@@ -594,6 +594,9 @@ class NotificationManagerService extends INotificationManager.Stub
Binder.restoreCallingIdentity(identity);
}
}
+
+ sendAccessibilityEventTypeNotificationChangedDoCheck(notification, pkg);
+
} else {
if (old != null && old.statusBarKey != null) {
long identity = Binder.clearCallingIdentity();
@@ -676,6 +679,26 @@ class NotificationManagerService extends INotificationManager.Stub
idOut[0] = id;
}
+ private void sendAccessibilityEventTypeNotificationChangedDoCheck(Notification notification,
+ CharSequence packageName) {
+ AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+ if (!manager.isEnabled()) {
+ return;
+ }
+
+ AccessibilityEvent event =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ event.setPackageName(packageName);
+ event.setClassName(Notification.class.getName());
+ event.setParcelableData(notification);
+ CharSequence tickerText = notification.tickerText;
+ if (!TextUtils.isEmpty(tickerText)) {
+ event.getText().add(tickerText);
+ }
+
+ manager.sendAccessibilityEvent(event);
+ }
+
private void cancelNotificationLocked(NotificationRecord r) {
// status bar
if (r.notification.icon != 0) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8b7260b..f6c1525 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -228,6 +228,14 @@ class ServerThread extends Thread {
}
try {
+ Log.i(TAG, "Starting Accessibility Manager.");
+ ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
+ new AccessibilityManagerService(context));
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting Accessibility Manager", e);
+ }
+
+ try {
Log.i(TAG, "Starting Notification Manager.");
ServiceManager.addService(Context.NOTIFICATION_SERVICE,
new NotificationManagerService(context, statusBar, hardware));