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
This commit is contained in:
Winson Chung
2021-01-07 21:45:30 -08:00
parent 813664c88c
commit cb4810a673
7 changed files with 213 additions and 93 deletions

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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");
}
}
}
}

View File

@@ -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);
}
}
});
try {
ActivityTaskManager.getService().setSplitScreenResizing(resizing);
} catch (RemoteException e) {
Log.w(TAG, "Error calling setDockedStackResizing: " + e);
}
}
/** Sets a touch region */
public void setTouchRegion(Rect region) {
synchronized (mDockedRect) {
mTouchableRegion.set(region);
try {
synchronized (mDockedRect) {
mTouchableRegion.set(region);
}
WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion(
mTouchableRegion);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set touchable region: " + e);
}
mExecutor.execute(mSetTouchableRegionRunnable);
}
void applyResizeSplits(int position, LegacySplitDisplayLayout splitLayout) {

View File

@@ -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);
}
}

View File

@@ -145,18 +145,18 @@ public abstract class WMShellBaseModule {
}
/**
* Provide a Shell animation-thread AnimationHandler. The AnimationHandler can be set on
* Provide a Shell main-thread AnimationHandler. The AnimationHandler can be set on
* {@link android.animation.ValueAnimator}s and will ensure that the animation will run on
* the Shell animation-thread.
* the Shell main-thread with the SF vsync.
*/
@WMSingleton
@Provides
@ChoreographerSfVsync
public static AnimationHandler provideShellAnimationExecutorSfVsyncAnimationHandler(
@ShellAnimationThread ShellExecutor shellAnimationExecutor) {
public static AnimationHandler provideShellMainExecutorSfVsyncAnimationHandler(
@ShellMainThread ShellExecutor mainExecutor) {
try {
AnimationHandler handler = new AnimationHandler();
shellAnimationExecutor.executeBlocking(() -> {
mainExecutor.executeBlocking(() -> {
// This is called on the animation thread since it calls
// Choreographer.getSfInstance() which returns a thread-local Choreographer instance
// that uses the SF vsync

View File

@@ -16,6 +16,7 @@
package com.android.systemui.wmshell;
import android.animation.AnimationHandler;
import android.content.Context;
import android.view.IWindowManager;
@@ -32,6 +33,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;
@@ -75,10 +77,11 @@ public class WMShellModule {
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);
}
@WMSingleton