diff options
| author | 2016-10-04 16:38:45 -0700 | |
|---|---|---|
| committer | 2016-10-05 11:08:20 -0700 | |
| commit | 65097bf83f12554fa847f50bfbbe3e40f516b705 (patch) | |
| tree | 6fa7e478707aa7aa9254c579e1297b4d3d5b9ab0 | |
| parent | 85153a12eb5d787091929b8e7d2f9366cec95d46 (diff) | |
Move a11y event dispatch back to ui thread.
Test: Ran CTS
Bug: 31753900
Change-Id: I0ba096cb99dd8347a7e9d6c41ff0aa8293dc5d15
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 |