summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Phil Weaver <pweaver@google.com> 2016-10-04 16:38:45 -0700
committer Phil Weaver <pweaver@google.com> 2016-10-05 11:08:20 -0700
commit65097bf83f12554fa847f50bfbbe3e40f516b705 (patch)
tree6fa7e478707aa7aa9254c579e1297b4d3d5b9ab0
parent85153a12eb5d787091929b8e7d2f9366cec95d46 (diff)
Move a11y event dispatch back to ui thread.
Test: Ran CTS Bug: 31753900 Change-Id: I0ba096cb99dd8347a7e9d6c41ff0aa8293dc5d15
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java156
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java14
3 files changed, 35 insertions, 140 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 44f6facd88f5..c125b32f237a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -92,9 +92,6 @@ public final class AccessibilityManager {
/** @hide */
public static final int AUTOCLICK_DELAY_DEFAULT = 600;
- /** @hide */
- public static final int MAX_A11Y_EVENTS_PER_SERVICE_CALL = 20;
-
static final Object sInstanceSync = new Object();
private static AccessibilityManager sInstance;
@@ -103,8 +100,6 @@ public final class AccessibilityManager {
private IAccessibilityManager mService;
- private EventDispatchThread mEventDispatchThread;
-
final int mUserId;
final Handler mHandler;
@@ -303,32 +298,44 @@ public final class AccessibilityManager {
* their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
- if (!isEnabled()) {
- Looper myLooper = Looper.myLooper();
- if (myLooper == Looper.getMainLooper()) {
- throw new IllegalStateException(
- "Accessibility off. Did you forget to check that?");
- } else {
- // If we're not running on the thread with the main looper, it's possible for
- // the state of accessibility to change between checking isEnabled and
- // calling this method. So just log the error rather than throwing the
- // exception.
- Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
return;
}
+ if (!mIsEnabled) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ return;
+ }
+ }
+ userId = mUserId;
}
- event.setEventTime(SystemClock.uptimeMillis());
-
- getEventDispatchThread().scheduleEvent(event);
- }
-
- private EventDispatchThread getEventDispatchThread() {
- synchronized (mLock) {
- if (mEventDispatchThread == null) {
- mEventDispatchThread = new EventDispatchThread(mService, mUserId);
- mEventDispatchThread.start();
+ try {
+ event.setEventTime(SystemClock.uptimeMillis());
+ // it is possible that this manager is in the same process as the service but
+ // client using it is called through Binder from another process. Example: MMS
+ // app adds a SMS notification and the NotificationManagerService calls this method
+ long identityToken = Binder.clearCallingIdentity();
+ service.sendAccessibilityEvent(event, userId);
+ Binder.restoreCallingIdentity(identityToken);
+ if (DEBUG) {
+ Log.i(LOG_TAG, event + " sent");
}
- return mEventDispatchThread;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+ } finally {
+ event.recycle();
}
}
@@ -713,99 +720,4 @@ public final class AccessibilityManager {
}
}
}
-
- private static class EventDispatchThread extends Thread {
- // Second lock used to keep UI thread performant. Never try to grab mLock when holding
- // this one, or the UI thread will block in send AccessibilityEvent.
- private final Object mEventQueueLock = new Object();
-
- // Two lists to hold events. The app thread fills one while we empty the other.
- private final ArrayList<AccessibilityEvent> mEventLists0 =
- new ArrayList<>(MAX_A11Y_EVENTS_PER_SERVICE_CALL);
- private final ArrayList<AccessibilityEvent> mEventLists1 =
- new ArrayList<>(MAX_A11Y_EVENTS_PER_SERVICE_CALL);
-
- private boolean mPingPongListToggle;
-
- private final IAccessibilityManager mService;
-
- private final int mUserId;
-
- EventDispatchThread(IAccessibilityManager service, int userId) {
- mService = service;
- mUserId = userId;
- }
-
- @Override
- public void run() {
- while (true) {
- ArrayList<AccessibilityEvent> listBeingDrained;
- synchronized (mEventQueueLock) {
- ArrayList<AccessibilityEvent> listBeingFilled = getListBeingFilledLocked();
- if (listBeingFilled.isEmpty()) {
- try {
- mEventQueueLock.wait();
- } catch (InterruptedException e) {
- // Treat as a notify
- }
- }
- // Swap buffers
- mPingPongListToggle = !mPingPongListToggle;
- listBeingDrained = listBeingFilled;
- }
- dispatchEvents(listBeingDrained);
- }
- }
-
- public void scheduleEvent(AccessibilityEvent event) {
- synchronized (mEventQueueLock) {
- getListBeingFilledLocked().add(event);
- mEventQueueLock.notifyAll();
- }
- }
-
- private ArrayList<AccessibilityEvent> getListBeingFilledLocked() {
- return (mPingPongListToggle) ? mEventLists0 : mEventLists1;
- }
-
- private void dispatchEvents(ArrayList<AccessibilityEvent> events) {
- int eventListCapacityLowerBound = events.size();
- while (events.size() > 0) {
- // We don't want to consume extra memory if an app sends a lot of events in a
- // one-off event. Cap the list length at double the max events per call.
- // We'll end up with extra GC for apps that send huge numbers of events, but
- // sending that many events will lead to bad performance in any case.
- if ((eventListCapacityLowerBound > 2 * MAX_A11Y_EVENTS_PER_SERVICE_CALL)
- && (events.size() <= 2 * MAX_A11Y_EVENTS_PER_SERVICE_CALL)) {
- events.trimToSize();
- eventListCapacityLowerBound = events.size();
- }
- // We only expect this loop to run once, as the app shouldn't be sending
- // huge numbers of events.
- // The clear in the called method will remove the sent events
- dispatchOneBatchOfEvents(events.subList(0,
- Math.min(events.size(), MAX_A11Y_EVENTS_PER_SERVICE_CALL)));
- }
- }
-
- private void dispatchOneBatchOfEvents(List<AccessibilityEvent> events) {
- if (events.isEmpty()) {
- return;
- }
- long identityToken = Binder.clearCallingIdentity();
- try {
- mService.sendAccessibilityEvents(new ParceledListSlice<>(events),
- mUserId);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error sending multiple events");
- }
- Binder.restoreCallingIdentity(identityToken);
- if (DEBUG) {
- Log.i(LOG_TAG, events.size() + " events sent");
- }
- for (int i = events.size() - 1; i >= 0; i--) {
- events.remove(i).recycle();
- }
- }
- }
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index aa9cb39062f9..71e77c417ec3 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -21,7 +21,6 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
-import android.content.pm.ParceledListSlice;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -30,7 +29,7 @@ import android.view.IWindow;
/**
* Interface implemented by the AccessibilityManagerService called by
- * the AccessibilityMasngers.
+ * the AccessibilityManagers.
*
* @hide
*/
@@ -40,8 +39,6 @@ interface IAccessibilityManager {
void sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId);
- void sendAccessibilityEvents(in ParceledListSlice events, int userId);
-
List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0b83e66febba..54c9afea5766 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -498,20 +498,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
@Override
- public void sendAccessibilityEvents(ParceledListSlice events, int userId) {
- List<AccessibilityEvent> a11yEvents = events.getList();
- // Grab the lock once for the entire batch
- synchronized (mLock) {
- int numEventsToProcess = Math.min(a11yEvents.size(),
- AccessibilityManager.MAX_A11Y_EVENTS_PER_SERVICE_CALL);
- for (int i = 0; i < numEventsToProcess; i++) {
- AccessibilityEvent event = a11yEvents.get(i);
- sendAccessibilityEvent(event, userId);
- }
- }
- }
-
- @Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles