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

Commit 28616560 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "One thread to rule them all" into ub-launcher3-master

parents a5e0f43a 451987c5
Loading
Loading
Loading
Loading
+5.11 KiB (149 KiB)

File changed.

No diff preview for this file type.

+0 −119
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.quickstep;

import android.annotation.TargetApi;
import android.os.Build;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.VelocityTracker;

/**
 * A TouchConsumer which defers all events on the UIThread until the consumer is created.
 */
@TargetApi(Build.VERSION_CODES.P)
public class DeferredTouchConsumer implements TouchConsumer {

    private final VelocityTracker mVelocityTracker;
    private final DeferredTouchProvider mTouchProvider;

    private MotionEventQueue mMyQueue;
    private TouchConsumer mTarget;

    public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
        mVelocityTracker = VelocityTracker.obtain();
        mTouchProvider = touchProvider;
    }

    @Override
    public void accept(MotionEvent event) {
        mTarget.accept(event);
    }

    @Override
    public void reset() {
        mTarget.reset();
    }

    @Override
    public void onQuickScrubStart() {
        mTarget.onQuickScrubStart();
    }

    @Override
    public void onQuickScrubEnd() {
        mTarget.onQuickScrubEnd();
    }

    @Override
    public void onQuickScrubProgress(float progress) {
        mTarget.onQuickScrubProgress(progress);
    }

    @Override
    public void onQuickStep(MotionEvent ev) {
        mTarget.onQuickStep(ev);
    }

    @Override
    public void onCommand(int command) {
        mTarget.onCommand(command);
    }

    @Override
    public void preProcessMotionEvent(MotionEvent ev) {
        mVelocityTracker.addMovement(ev);
    }

    @Override
    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
        mMyQueue = queue;
        return null;
    }

    @Override
    public void deferInit() {
        mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
        mTarget.getIntrimChoreographer(mMyQueue);
    }

    @Override
    public boolean forceToLauncherConsumer() {
        return mTarget.forceToLauncherConsumer();
    }

    @Override
    public OtherActivityTouchConsumer.RecentsAnimationState getRecentsAnimationStateToReuse() {
        return mTarget.getRecentsAnimationStateToReuse();
    }

    @Override
    public boolean deferNextEventToMainThread() {
        // If our target is still null, defer the next target as well
        TouchConsumer target = mTarget;
        return target == null ? true : target.deferNextEventToMainThread();
    }

    @Override
    public void onShowOverviewFromAltTab() {
        mTarget.onShowOverviewFromAltTab();
    }

    public interface DeferredTouchProvider {

        TouchConsumer createTouchConsumer(VelocityTracker tracker);
    }
}
+93 −145
Original line number Diff line number Diff line
@@ -15,21 +15,23 @@
 */
package com.android.quickstep;

import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_MASK;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;

import android.annotation.TargetApi;
import android.os.Build;
import android.os.Looper;
import android.util.Log;
import android.util.Pair;
import android.view.Choreographer;
import android.view.InputEvent;
import android.view.MotionEvent;

import com.android.systemui.shared.system.ChoreographerCompat;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;

import java.util.ArrayList;
import java.util.function.Supplier;

/**
 * Helper class for batching input events
@@ -49,100 +51,37 @@ public class MotionEventQueue {
            ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
    private static final int ACTION_RESET =
            ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
    private static final int ACTION_DEFER_INIT =
            ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
    private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB =
            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
            ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
    private static final int ACTION_QUICK_STEP =
            ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
    private static final int ACTION_COMMAND =
            ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
    private static final int ACTION_SWITCH_CONSUMER =
            ACTION_VIRTUAL | (8 << ACTION_POINTER_INDEX_SHIFT);

    private final EventArray mEmptyArray = new EventArray();
    private final Object mExecutionLock = new Object();

    // We use two arrays and swap the current index when one array is being consumed
    private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()};
    private int mCurrentIndex = 0;
    private final InputEventDispatcher mDispatcher;
    private final InputEventReceiver mReceiver;

    private final Runnable mMainFrameCallback = this::frameCallbackForMainChoreographer;
    private final Runnable mInterimFrameCallback = this::frameCallbackForInterimChoreographer;
    private final Object mConsumerParamsLock = new Object();
    private Supplier[] mConsumerParams = new Supplier[2];

    private final Choreographer mMainChoreographer;
    private TouchConsumer mConsumer;

    private final TouchConsumer mConsumer;
    public MotionEventQueue(Looper looper, Choreographer choreographer) {
        Pair<InputEventDispatcher, InputEventReceiver> pair = InputChannelCompat.createPair(
                "sysui-callbacks", looper, choreographer, this::onInputEvent);

    private Choreographer mInterimChoreographer;
    private Choreographer mCurrentChoreographer;

    private Runnable mCurrentRunnable;

    public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
        mMainChoreographer = choreographer;
        mConsumer = consumer;
        mCurrentChoreographer = mMainChoreographer;
        mCurrentRunnable = mMainFrameCallback;

        setInterimChoreographer(consumer.getIntrimChoreographer(this));
        mConsumer = TouchConsumer.NO_OP;
        mDispatcher = pair.first;
        mReceiver = pair.second;
    }

    public void setInterimChoreographer(Choreographer choreographer) {
        synchronized (mExecutionLock) {
            synchronized (mArrays) {
                setInterimChoreographerLocked(choreographer);
                ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
            }
        }
    }

    private void  setInterimChoreographerLocked(Choreographer choreographer) {
        mInterimChoreographer = choreographer;
        if (choreographer == null) {
            mCurrentChoreographer = mMainChoreographer;
            mCurrentRunnable = mMainFrameCallback;
        } else {
            mCurrentChoreographer = mInterimChoreographer;
            mCurrentRunnable = mInterimFrameCallback;
        }
    }

    public void queue(MotionEvent event) {
        mConsumer.preProcessMotionEvent(event);
        queueNoPreProcess(event);
    }

    private void queueNoPreProcess(MotionEvent event) {
        synchronized (mArrays) {
            EventArray array = mArrays[mCurrentIndex];
            if (array.isEmpty()) {
                ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
            }

            int eventAction = event.getAction();
            if (eventAction == ACTION_MOVE && array.lastEventAction == ACTION_MOVE) {
                // Replace and recycle the last event
                array.set(array.size() - 1, event).recycle();
            } else {
                array.add(event);
                array.lastEventAction = eventAction;
            }
    private void onInputEvent(InputEvent ev) {
        if (!(ev instanceof MotionEvent)) {
            throw new IllegalStateException("Unknown event " + ev);
        }
    }

    private void frameCallbackForMainChoreographer() {
        runFor(mMainChoreographer);
    }

    private void frameCallbackForInterimChoreographer() {
        runFor(mInterimChoreographer);
    }

    private void runFor(Choreographer caller) {
        synchronized (mExecutionLock) {
            EventArray array = swapAndGetCurrentArray(caller);
            int size = array.size();
            for (int i = 0; i < size; i++) {
                MotionEvent event = array.get(i);
        MotionEvent event = (MotionEvent) ev;
        if (event.getActionMasked() == ACTION_VIRTUAL) {
            switch (event.getAction()) {
                case ACTION_QUICK_SCRUB_START:
@@ -157,9 +96,6 @@ public class MotionEventQueue {
                case ACTION_RESET:
                    mConsumer.reset();
                    break;
                        case ACTION_DEFER_INIT:
                            mConsumer.deferInit();
                            break;
                case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
                    mConsumer.onShowOverviewFromAltTab();
                    mConsumer.onQuickScrubStart();
@@ -170,32 +106,33 @@ public class MotionEventQueue {
                case ACTION_COMMAND:
                    mConsumer.onCommand(event.getSource());
                    break;
                case ACTION_SWITCH_CONSUMER:
                    synchronized (mConsumerParamsLock) {
                        int index = event.getSource();
                        mConsumer = (TouchConsumer) mConsumerParams[index].get();
                        mConsumerParams[index] = null;
                    }
                    break;
                default:
                    Log.e(TAG, "Invalid virtual event: " + event.getAction());
            }
        } else {
            mConsumer.accept(event);
        }
                event.recycle();
            }
            array.clear();
            array.lastEventAction = ACTION_CANCEL;
        }
    }

    private EventArray swapAndGetCurrentArray(Choreographer caller) {
        synchronized (mArrays) {
            if (caller != mCurrentChoreographer) {
                return mEmptyArray;
            }
            EventArray current = mArrays[mCurrentIndex];
            mCurrentIndex = mCurrentIndex ^ 1;
            return current;
    public void queue(MotionEvent event) {
        mDispatcher.dispatch(event);
    }

    private void queueVirtualAction(int action, float param) {
        queue(MotionEvent.obtain(0, 0, action, param, 0, 0));
    }

    private void queueVirtualAction(int action, float progress) {
        queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0));
    private void queueVirtualAction(int action, int param) {
        MotionEvent ev = MotionEvent.obtain(0, 0, action, 0, 0, 0);
        ev.setSource(param);
        queue(ev);
    }

    public void onQuickScrubStart() {
@@ -216,33 +153,44 @@ public class MotionEventQueue {

    public void onQuickStep(MotionEvent event) {
        event.setAction(ACTION_QUICK_STEP);
        queueNoPreProcess(event);
        queue(event);
    }

    public void reset() {
        queueVirtualAction(ACTION_RESET, 0);
    }

    public void deferInit() {
        queueVirtualAction(ACTION_DEFER_INIT, 0);
    public void onCommand(int command) {
        queueVirtualAction(ACTION_COMMAND, command);
    }

    public void onCommand(int command) {
        MotionEvent ev = MotionEvent.obtain(0, 0, ACTION_COMMAND, 0, 0, 0);
        ev.setSource(command);
        queueNoPreProcess(ev);
    public void switchConsumer(Supplier<TouchConsumer> consumer) {
        int index = -1;
        synchronized (mConsumerParamsLock) {
            // Find a null index
            for (int i = 0; i < mConsumerParams.length; i++) {
                if (mConsumerParams[i] == null) {
                    index = i;
                    break;
                }
            }
            if (index < 0) {
                index = mConsumerParams.length;
                final Supplier[] newValues = new Supplier[index + 1];
                System.arraycopy(mConsumerParams, 0, newValues, 0, index);
                mConsumerParams = newValues;
            }
            mConsumerParams[index] = consumer;
        }
        queueVirtualAction(ACTION_SWITCH_CONSUMER, index);
    }

    public TouchConsumer getConsumer() {
        return mConsumer;
    }

    private static class EventArray extends ArrayList<MotionEvent> {

        public int lastEventAction = ACTION_CANCEL;

        public EventArray() {
            super(4);
        }
    public void dispose() {
        mDispatcher.dispose();
        mReceiver.dispose();
    }
}
+28 −0
Original line number Diff line number Diff line
@@ -15,8 +15,12 @@
 */
package com.android.quickstep;

import static com.android.quickstep.WindowTransformSwipeHandler.STATES;

import android.util.Log;
import android.util.SparseArray;

import java.util.StringJoiner;
import java.util.function.Consumer;

/**
@@ -24,6 +28,9 @@ import java.util.function.Consumer;
 */
public class MultiStateCallback {

    private static final String TAG = "MultiStateCallback";
    private static final boolean DEBUG_STATES = false;

    private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
    private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();

@@ -97,4 +104,25 @@ public class MultiStateCallback {
    public boolean hasStates(int stateMask) {
        return (mState & stateMask) == stateMask;
    }

    private void debugNewState(int stateFlag) {
        if (!DEBUG_STATES) {
            return;
        }

        int state = getState();
        StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
        String stateFlagStr = "Unknown-" + stateFlag;
        for (int i = 0; i < STATES.length; i++) {
            if ((state & (i << i)) != 0) {
                currentStateStr.add(STATES[i]);
            }
            if (stateFlag == (1 << i)) {
                stateFlagStr = STATES[i] + " (" + stateFlag + ")";
            }
        }
        Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
                + currentStateStr);
    }

}
+36 −77
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;

import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -35,10 +36,8 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.SystemClock;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -63,9 +62,6 @@ import com.android.systemui.shared.system.RecentsAnimationListener;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import androidx.annotation.Nullable;

/**
@@ -74,7 +70,6 @@ import androidx.annotation.Nullable;
@TargetApi(Build.VERSION_CODES.P)
public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {

    private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
    public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN";
    private static final String UP_EVT = "OtherActivityTouchConsumer.UP";

@@ -84,12 +79,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
    private final Intent mHomeIntent;
    private final ActivityControlHelper mActivityControlHelper;
    private final MainThreadExecutor mMainThreadExecutor;
    private final Choreographer mBackgroundThreadChoreographer;
    private final OverviewCallbacks mOverviewCallbacks;
    private final TaskOverlayFactory mTaskOverlayFactory;
    private final TouchInteractionLog mTouchInteractionLog;
    private final InputConsumerController mInputConsumer;

    private final MotionEventQueue mEventQueue;
    private final MotionPauseDetector mMotionPauseDetector;
    private VelocityTracker mVelocityTracker;

    private final boolean mIsDeferredDownTarget;
    private final PointF mDownPos = new PointF();
    private final PointF mLastPos = new PointF();
@@ -103,29 +101,28 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
    private Rect mStableInsets = new Rect();
    private boolean mCanGestureBeContinued;

    private VelocityTracker mVelocityTracker;
    private MotionPauseDetector mMotionPauseDetector;
    private MotionEventQueue mEventQueue;
    private boolean mIsGoingToLauncher;
    private RecentsAnimationState mRecentsAnimationState;

    public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
            RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
            MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
            MainThreadExecutor mainThreadExecutor,
            @HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
            TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
            VelocityTracker velocityTracker, TouchInteractionLog touchInteractionLog,
            TouchInteractionLog touchInteractionLog, MotionEventQueue eventQueue,
            @Nullable RecentsAnimationState recentsAnimationStateToReuse) {
        super(base);

        mRunningTask = runningTaskInfo;
        mRecentsModel = recentsModel;
        mHomeIntent = homeIntent;
        mVelocityTracker = velocityTracker;

        mMotionPauseDetector = new MotionPauseDetector(base);
        mEventQueue = eventQueue;
        mVelocityTracker = VelocityTracker.obtain();

        mActivityControlHelper = activityControl;
        mMainThreadExecutor = mainThreadExecutor;
        mBackgroundThreadChoreographer = backgroundThreadChoreographer;
        mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget);
        mOverviewCallbacks = overviewCallbacks;
        mTaskOverlayFactory = taskOverlayFactory;
@@ -145,6 +142,12 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        if (mVelocityTracker == null) {
            return;
        }
        mVelocityTracker.addMovement(ev);
        if (ev.getActionMasked() == ACTION_POINTER_UP) {
            mVelocityTracker.clear();
            mMotionPauseDetector.clear();
        }

        mTouchInteractionLog.addMotionEvent(ev);
        switch (ev.getActionMasked()) {
            case ACTION_DOWN: {
@@ -275,18 +278,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        mInteractionHandler = handler;
        handler.setGestureEndCallback(mEventQueue::reset);
        mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);

        CountDownLatch drawWaitLock = new CountDownLatch(1);
        handler.setLauncherOnDrawCallback(() -> {
            drawWaitLock.countDown();
            if (handler == mInteractionHandler) {
                switchToMainChoreographer();
            }
        });
        handler.initWhenReady();

        TraceHelper.beginSection("RecentsController");

        if (reuseOldAnimState) {
            handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
                    mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
                    mRecentsAnimationState.mMinimizedHomeBounds);
        } else {
            AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
                    new AssistDataReceiver() {
                        @Override
@@ -300,31 +300,11 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
                        }
                    };

        Runnable startActivity;
        if (reuseOldAnimState) {
            startActivity = () -> {
                handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
                        mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
                        mRecentsAnimationState.mMinimizedHomeBounds);
            };
        } else {
            startActivity = () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
                    mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null);
            BackgroundExecutor.get().submit(
                    () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
                            mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null));
        }


        if (Looper.myLooper() != Looper.getMainLooper()) {
            startActivity.run();
            try {
                drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
            } catch (Exception e) {
                // We have waited long enough for launcher to draw
            }
        } else {
            // We should almost always get touch-town on background thread. This is an edge case
            // when the background Choreographer has not yet initialized.
            BackgroundExecutor.get().submit(startActivity);
        }
    }

    @Override
@@ -379,12 +359,6 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        }
    }

    @Override
    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
        mEventQueue = queue;
        return mBackgroundThreadChoreographer;
    }

    @Override
    public void onQuickScrubStart() {
        if (!mPassedInitialSlop && mIsDeferredDownTarget && mInteractionHandler == null) {
@@ -444,21 +418,6 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        return displacement;
    }

    public void switchToMainChoreographer() {
        mEventQueue.setInterimChoreographer(null);
    }

    @Override
    public void preProcessMotionEvent(MotionEvent ev) {
        if (mVelocityTracker != null) {
           mVelocityTracker.addMovement(ev);
           if (ev.getActionMasked() == ACTION_POINTER_UP) {
               mVelocityTracker.clear();
               mMotionPauseDetector.clear();
           }
        }
    }

    @Override
    public boolean forceToLauncherConsumer() {
        return mIsGoingToLauncher;
Loading