Move a11y event dispatch back to ui thread.
Test: Ran CTS Bug: 31753900 Change-Id: I0ba096cb99dd8347a7e9d6c41ff0aa8293dc5d15
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
event.setEventTime(SystemClock.uptimeMillis());
|
||||
|
||||
getEventDispatchThread().scheduleEvent(event);
|
||||
}
|
||||
|
||||
private EventDispatchThread getEventDispatchThread() {
|
||||
synchronized (mLock) {
|
||||
if (mEventDispatchThread == null) {
|
||||
mEventDispatchThread = new EventDispatchThread(mService, mUserId);
|
||||
mEventDispatchThread.start();
|
||||
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;
|
||||
}
|
||||
}
|
||||
return mEventDispatchThread;
|
||||
userId = mUserId;
|
||||
}
|
||||
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");
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user