Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e911964 authored by Phil Weaver's avatar Phil Weaver Committed by Android (Google) Code Review
Browse files

Merge "Move a11y event dispatch back to ui thread."

parents 83827705 65097bf8
Loading
Loading
Loading
Loading
+34 −122
Original line number Diff line number Diff line
@@ -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,7 +298,14 @@ public final class AccessibilityManager {
     * their descendants.
     */
    public void sendAccessibilityEvent(AccessibilityEvent event) {
        if (!isEnabled()) {
        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(
@@ -317,18 +319,23 @@ public final class AccessibilityManager {
                    return;
                }
            }
        event.setEventTime(SystemClock.uptimeMillis());

        getEventDispatchThread().scheduleEvent(event);
            userId = mUserId;
        }

    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();
            }
        }
    }
}
+1 −4
Original line number Diff line number Diff line
@@ -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);
+0 −14
Original line number Diff line number Diff line
@@ -497,20 +497,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) {