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

Commit cb4810a6 authored by Winson Chung's avatar Winson Chung
Browse files

5/ Update splitscreen to use shell main thread

- Remove some usages of handler
- Remove separate executor for transaction updates (until it's clear
  we need to offload them)
- Only animations running on the main shell thread are using the animator
  handlers, so repurpose that to be for the main thread until we need it
  for things that actually run on the animation thread (for transitions)

Bug: 161979899
Test: atest WMShellUnitTests
Test: atest SplitScreenTests
Change-Id: I0ea9aae2deddafb620f1f41658e8366443bf9aaa
parent 813664c8
Loading
Loading
Loading
Loading
+9 −10
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.graphics.Region;
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Slog;
@@ -60,7 +59,6 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;

import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm;
@@ -146,8 +144,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    private LegacySplitDisplayLayout mSplitLayout;
    private DividerImeController mImeController;
    private DividerCallbacks mCallback;
    private final AnimationHandler mAnimationHandler = new AnimationHandler();

    private AnimationHandler mSfVsyncAnimationHandler;
    private ValueAnimator mCurrentAnimator;
    private boolean mEntranceAnimationRunning;
    private boolean mExitAnimationRunning;
@@ -172,8 +170,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    // used interact with keyguard.
    private boolean mSurfaceHidden = false;

    private final Handler mHandler = new Handler();

    private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
        @Override
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -284,7 +280,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
        final DisplayManager displayManager =
                (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
        mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
        mAnimationHandler.setProvider(new SfVsyncFrameCallbackProvider());
    }

    public void setAnimationHandler(AnimationHandler sfVsyncAnimationHandler) {
        mSfVsyncAnimationHandler = sfVsyncAnimationHandler;
    }

    @Override
@@ -669,12 +668,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                } else {
                    final Boolean cancelled = mCancelled;
                    if (DEBUG) Slog.d(TAG, "Posting endFling " + cancelled + " d:" + delay + "ms");
                    mHandler.postDelayed(() -> endAction.accept(cancelled), delay);
                    getHandler().postDelayed(() -> endAction.accept(cancelled), delay);
                }
            }
        });
        anim.setAnimationHandler(mAnimationHandler);
        mCurrentAnimator = anim;
        mCurrentAnimator.setAnimationHandler(mSfVsyncAnimationHandler);
        return anim;
    }

@@ -1061,8 +1060,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
            }
        }
        if (getViewRootImpl() != null) {
            mHandler.removeCallbacks(mUpdateEmbeddedMatrix);
            mHandler.post(mUpdateEmbeddedMatrix);
            getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
            getHandler().post(mUpdateEmbeddedMatrix);
        }
    }

+2 −0
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ import com.android.wm.shell.R;
/**
 * Translucent activity that gets started on top of a task in multi-window to inform the user that
 * we forced the activity below to be resizable.
 *
 * Note: This activity runs on the main thread of the process hosting the Shell lib.
 */
public class ForcedResizableInfoActivity extends Activity implements OnTouchListener {

+171 −38
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Display.DEFAULT_DISPLAY;

import android.animation.AnimationHandler;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -40,6 +41,8 @@ import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.annotation.Nullable;

import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -60,14 +63,14 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * Controls split screen feature.
 */
public class LegacySplitScreenController implements LegacySplitScreen,
        DisplayController.OnDisplaysChangedListener {
public class LegacySplitScreenController implements DisplayController.OnDisplaysChangedListener {
    static final boolean DEBUG = false;

    private static final String TAG = "SplitScreenCtrl";
@@ -81,11 +84,13 @@ public class LegacySplitScreenController implements LegacySplitScreen,
    private final DividerState mDividerState = new DividerState();
    private final ForcedResizableInfoActivityController mForcedResizableController;
    private final ShellExecutor mMainExecutor;
    private final AnimationHandler mSfVsyncAnimationHandler;
    private final LegacySplitScreenTaskListener mSplits;
    private final SystemWindows mSystemWindows;
    final TransactionPool mTransactionPool;
    private final WindowManagerProxy mWindowManagerProxy;
    private final TaskOrganizer mTaskOrganizer;
    private final SplitScreenImpl mImpl = new SplitScreenImpl();

    private final CopyOnWriteArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners
            = new CopyOnWriteArrayList<>();
@@ -106,21 +111,37 @@ public class LegacySplitScreenController implements LegacySplitScreen,

    private boolean mIsKeyguardShowing;
    private boolean mVisible = false;
    private boolean mMinimized = false;
    private boolean mAdjustedForIme = false;
    private volatile boolean mMinimized = false;
    private volatile boolean mAdjustedForIme = false;
    private boolean mHomeStackResizable = false;

    /**
     * Creates {@link SplitScreen}, returns {@code null} if the feature is not supported.
     */
    @Nullable
    public static LegacySplitScreen create(Context context,
            DisplayController displayController, SystemWindows systemWindows,
            DisplayImeController imeController, TransactionPool transactionPool,
            ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
            TaskStackListenerImpl taskStackListener, Transitions transitions,
            ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) {
        return new LegacySplitScreenController(context, displayController, systemWindows,
                imeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener,
                transitions, mainExecutor, sfVsyncAnimationHandler).mImpl;
    }

    public LegacySplitScreenController(Context context,
            DisplayController displayController, SystemWindows systemWindows,
            DisplayImeController imeController, TransactionPool transactionPool,
            ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
            TaskStackListenerImpl taskStackListener, Transitions transitions,
            ShellExecutor mainExecutor) {
            ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) {
        mContext = context;
        mDisplayController = displayController;
        mSystemWindows = systemWindows;
        mImeController = imeController;
        mMainExecutor = mainExecutor;
        mSfVsyncAnimationHandler = sfVsyncAnimationHandler;
        mForcedResizableController = new ForcedResizableInfoActivityController(context, this,
                mainExecutor);
        mTransactionPool = transactionPool;
@@ -216,8 +237,7 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        mTaskOrganizer.applyTransaction(tct);
    }

    @Override
    public void onKeyguardVisibilityChanged(boolean showing) {
    private void onKeyguardVisibilityChanged(boolean showing) {
        if (!isSplitActive() || mView == null) {
            return;
        }
@@ -273,23 +293,19 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        }
    }

    @Override
    public DividerView getDividerView() {
        return mView;
    }

    @Override
    public boolean isMinimized() {
    boolean isMinimized() {
        return mMinimized;
    }

    @Override
    public boolean isHomeStackResizable() {
    boolean isHomeStackResizable() {
        return mHomeStackResizable;
    }

    @Override
    public boolean isDividerVisible() {
    DividerView getDividerView() {
        return mView;
    }

    boolean isDividerVisible() {
        return mView != null && mView.getVisibility() == View.VISIBLE;
    }

@@ -308,6 +324,7 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
        mView = (DividerView)
                LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
        mView.setAnimationHandler(mSfVsyncAnimationHandler);
        DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
        mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController,
                mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
@@ -373,8 +390,7 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        }
    }

    @Override
    public void setMinimized(final boolean minimized) {
    private void setMinimized(final boolean minimized) {
        if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
        mMainExecutor.execute(() -> {
            if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
@@ -437,23 +453,20 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        mWindowManager.setTouchable(!mAdjustedForIme);
    }

    @Override
    public void onUndockingTask() {
    private void onUndockingTask() {
        if (mView != null) {
            mView.onUndockingTask();
        }
    }

    @Override
    public void onAppTransitionFinished() {
    private void onAppTransitionFinished() {
        if (mView == null) {
            return;
        }
        mForcedResizableController.onAppTransitionFinished();
    }

    @Override
    public void dump(PrintWriter pw) {
    private void dump(PrintWriter pw) {
        pw.print("  mVisible="); pw.println(mVisible);
        pw.print("  mMinimized="); pw.println(mMinimized);
        pw.print("  mAdjustedForIme="); pw.println(mAdjustedForIme);
@@ -469,16 +482,14 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        return (long) (transitionDuration * transitionScale);
    }

    @Override
    public void registerInSplitScreenListener(Consumer<Boolean> listener) {
    void registerInSplitScreenListener(Consumer<Boolean> listener) {
        listener.accept(isDividerVisible());
        synchronized (mDockedStackExistsListeners) {
            mDockedStackExistsListeners.add(new WeakReference<>(listener));
        }
    }

    @Override
    public void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
    void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
        synchronized (mDockedStackExistsListeners) {
            for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) {
                if (mDockedStackExistsListeners.get(i) == listener) {
@@ -488,15 +499,13 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        }
    }

    @Override
    public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
    private void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
        synchronized (mBoundsChangedListeners) {
            mBoundsChangedListeners.add(new WeakReference<>(listener));
        }
    }

    @Override
    public boolean splitPrimaryTask() {
    private boolean splitPrimaryTask() {
        try {
            if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED
                    || isSplitActive()) {
@@ -529,8 +538,7 @@ public class LegacySplitScreenController implements LegacySplitScreen,
                topRunningTask.taskId, true /* onTop */);
    }

    @Override
    public void dismissSplitToPrimaryTask() {
    private void dismissSplitToPrimaryTask() {
        startDismissSplit(true /* toPrimaryTask */);
    }

@@ -621,11 +629,136 @@ public class LegacySplitScreenController implements LegacySplitScreen,
        return mWindowManagerProxy;
    }

    @Override
    public WindowContainerToken getSecondaryRoot() {
    WindowContainerToken getSecondaryRoot() {
        if (mSplits == null || mSplits.mSecondary == null) {
            return null;
        }
        return mSplits.mSecondary.token;
    }

    private class SplitScreenImpl implements LegacySplitScreen {
        @Override
        public boolean isMinimized() {
            return mMinimized;
        }

        @Override
        public boolean isHomeStackResizable() {
            return mHomeStackResizable;
        }

        /**
         * TODO: Remove usage from outside the shell.
         */
        @Override
        public DividerView getDividerView() {
            return LegacySplitScreenController.this.getDividerView();
        }

        @Override
        public boolean isDividerVisible() {
            boolean[] result = new boolean[1];
            try {
                mMainExecutor.executeBlocking(() -> {
                    result[0] = LegacySplitScreenController.this.isDividerVisible();
                });
            } catch (InterruptedException e) {
                Slog.e(TAG, "Failed to get divider visible");
            }
            return result[0];
        }

        @Override
        public void onKeyguardVisibilityChanged(boolean isShowing) {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.onKeyguardVisibilityChanged(isShowing);
            });
        }

        @Override
        public void setMinimized(boolean minimized) {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.setMinimized(minimized);
            });
        }

        @Override
        public void onUndockingTask() {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.onUndockingTask();
            });
        }

        @Override
        public void onAppTransitionFinished() {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.onAppTransitionFinished();
            });
        }

        @Override
        public void registerInSplitScreenListener(Consumer<Boolean> listener) {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.registerInSplitScreenListener(listener);
            });
        }

        @Override
        public void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.unregisterInSplitScreenListener(listener);
            });
        }

        @Override
        public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.registerBoundsChangeListener(listener);
            });
        }

        @Override
        public WindowContainerToken getSecondaryRoot() {
            WindowContainerToken[] result = new WindowContainerToken[1];
            try {
                mMainExecutor.executeBlocking(() -> {
                    result[0] = LegacySplitScreenController.this.getSecondaryRoot();
                });
            } catch (InterruptedException e) {
                Slog.e(TAG, "Failed to get secondary root");
            }
            return result[0];
        }

        @Override
        public boolean splitPrimaryTask() {
            boolean[] result = new boolean[1];
            try {
                mMainExecutor.executeBlocking(() -> {
                    result[0] = LegacySplitScreenController.this.splitPrimaryTask();
                });
            } catch (InterruptedException e) {
                Slog.e(TAG, "Failed to split primary task");
            }
            return result[0];
        }

        @Override
        public void dismissSplitToPrimaryTask() {
            mMainExecutor.execute(() -> {
                LegacySplitScreenController.this.dismissSplitToPrimaryTask();
            });
        }

        @Override
        public void dump(PrintWriter pw) {
            try {
                mMainExecutor.executeBlocking(() -> {
                    LegacySplitScreenController.this.dump(pw);
                });
            } catch (InterruptedException e) {
                Slog.e(TAG, "Failed to dump LegacySplitScreenController in 2s");
            }
        }
    }
}
+14 −34
Original line number Diff line number Diff line
@@ -44,8 +44,6 @@ import com.android.wm.shell.transition.Transitions;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Proxy to simplify calls into window manager/activity manager
@@ -63,25 +61,7 @@ class WindowManagerProxy {
    @GuardedBy("mDockedRect")
    private final Rect mTouchableRegion = new Rect();

    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();

    private final SyncTransactionQueue mSyncTransactionQueue;

    private final Runnable mSetTouchableRegionRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                synchronized (mDockedRect) {
                    mTmpRect1.set(mTouchableRegion);
                }
                WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion(
                        mTmpRect1);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to set touchable region: " + e);
            }
        }
    };

    private final TaskOrganizer mTaskOrganizer;

    WindowManagerProxy(SyncTransactionQueue syncQueue, TaskOrganizer taskOrganizer) {
@@ -94,29 +74,29 @@ class WindowManagerProxy {
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            tiles.mSplitScreenController.startDismissSplit(!dismissOrMaximize, true /* snapped */);
        } else {
            mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize));
            applyDismissSplit(tiles, layout, dismissOrMaximize);
        }
    }

    public void setResizing(final boolean resizing) {
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
        try {
            ActivityTaskManager.getService().setSplitScreenResizing(resizing);
        } catch (RemoteException e) {
            Log.w(TAG, "Error calling setDockedStackResizing: " + e);
        }
    }
        });
    }

    /** Sets a touch region */
    public void setTouchRegion(Rect region) {
        try {
            synchronized (mDockedRect) {
                mTouchableRegion.set(region);
            }
        mExecutor.execute(mSetTouchableRegionRunnable);
            WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion(
                    mTouchableRegion);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to set touchable region: " + e);
        }
    }

    void applyResizeSplits(int position, LegacySplitDisplayLayout splitLayout) {
+6 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.wmshell;

import android.animation.AnimationHandler;
import android.content.Context;
import android.view.IWindowManager;

@@ -28,6 +29,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
@@ -58,9 +60,10 @@ public class TvWMShellModule {
            DisplayImeController displayImeController, TransactionPool transactionPool,
            ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
            TaskStackListenerImpl taskStackListener, Transitions transitions,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new LegacySplitScreenController(context, displayController, systemWindows,
            @ShellMainThread ShellExecutor mainExecutor,
            @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
        return LegacySplitScreenController.create(context, displayController, systemWindows,
                displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
                taskStackListener, transitions, mainExecutor);
                taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
    }
}
Loading