Merge "Use boundsChangeTransaction to improve split-screen enter/exit" into rvc-dev am: 82352f9db8 am: eabcec5bf2 am: 1c32099b52

Original change: undetermined

Change-Id: I75e4580f7012dca7d5e84ed1f1b6d91c5ccb63e3
This commit is contained in:
Evan Rosky
2020-06-01 23:48:45 +00:00
committed by Automerger Merge Worker
9 changed files with 406 additions and 68 deletions

View File

@@ -209,7 +209,11 @@ public class WindowlessWindowManager implements IWindowSession {
/** @hide */ /** @hide */
protected SurfaceControl getSurfaceControl(View rootView) { protected SurfaceControl getSurfaceControl(View rootView) {
final State s = mStateForWindow.get(rootView.getViewRootImpl().mWindow.asBinder()); final ViewRootImpl root = rootView.getViewRootImpl();
if (root == null) {
return null;
}
final State s = mStateForWindow.get(root.mWindow.asBinder());
if (s == null) { if (s == null) {
return null; return null;
} }

View File

@@ -264,6 +264,29 @@ public final class WindowContainerTransaction implements Parcelable {
return this; return this;
} }
/**
* Merges another WCT into this one.
* @param transfer When true, this will transfer everything from other potentially leaving
* other in an unusable state. When false, other is left alone, but
* SurfaceFlinger Transactions will not be merged.
* @hide
*/
public void merge(WindowContainerTransaction other, boolean transfer) {
for (int i = 0, n = other.mChanges.size(); i < n; ++i) {
final IBinder key = other.mChanges.keyAt(i);
Change existing = mChanges.get(key);
if (existing == null) {
existing = new Change();
mChanges.put(key, existing);
}
existing.merge(other.mChanges.valueAt(i), transfer);
}
for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) {
mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
: new HierarchyOp(other.mHierarchyOps.get(i)));
}
}
/** @hide */ /** @hide */
public Map<IBinder, Change> getChanges() { public Map<IBinder, Change> getChanges() {
return mChanges; return mChanges;
@@ -359,6 +382,41 @@ public final class WindowContainerTransaction implements Parcelable {
mActivityWindowingMode = in.readInt(); mActivityWindowingMode = in.readInt();
} }
/**
* @param transfer When true, this will transfer other into this leaving other in an
* undefined state. Use this if you don't intend to use other. When false,
* SurfaceFlinger Transactions will not merge.
*/
public void merge(Change other, boolean transfer) {
mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask);
mConfigSetMask |= other.mConfigSetMask;
mWindowSetMask |= other.mWindowSetMask;
if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) {
mFocusable = other.mFocusable;
}
if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) {
mBoundsChangeTransaction = other.mBoundsChangeTransaction;
other.mBoundsChangeTransaction = null;
}
if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) {
mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds);
}
if ((other.mChangeMask & CHANGE_HIDDEN) != 0) {
mHidden = other.mHidden;
}
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
}
if (other.mWindowingMode >= 0) {
mWindowingMode = other.mWindowingMode;
}
if (other.mBoundsChangeSurfaceBounds != null) {
mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
: new Rect(other.mBoundsChangeSurfaceBounds);
}
}
public int getWindowingMode() { public int getWindowingMode() {
return mWindowingMode; return mWindowingMode;
} }
@@ -522,6 +580,12 @@ public final class WindowContainerTransaction implements Parcelable {
mToTop = toTop; mToTop = toTop;
} }
public HierarchyOp(@NonNull HierarchyOp copy) {
mContainer = copy.mContainer;
mReparent = copy.mReparent;
mToTop = copy.mToTop;
}
protected HierarchyOp(Parcel in) { protected HierarchyOp(Parcel in) {
mContainer = in.readStrongBinder(); mContainer = in.readStrongBinder();
mReparent = in.readStrongBinder(); mReparent = in.readStrongBinder();

View File

@@ -99,16 +99,19 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
private Handler mHandler; private Handler mHandler;
private KeyguardStateController mKeyguardStateController; private KeyguardStateController mKeyguardStateController;
private WindowManagerProxy mWindowManagerProxy;
private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners = private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
new ArrayList<>(); new ArrayList<>();
private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this); private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
private DisplayChangeController.OnDisplayChangingListener mRotationController = private DisplayChangeController.OnDisplayChangingListener mRotationController =
(display, fromRotation, toRotation, t) -> { (display, fromRotation, toRotation, wct) -> {
if (!mSplits.isSplitScreenSupported()) { if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
return; return;
} }
WindowContainerTransaction t = new WindowContainerTransaction();
DisplayLayout displayLayout = DisplayLayout displayLayout =
new DisplayLayout(mDisplayController.getDisplayLayout(display)); new DisplayLayout(mDisplayController.getDisplayLayout(display));
SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits); SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
@@ -127,6 +130,17 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
if (isSplitActive()) { if (isSplitActive()) {
WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t); WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
} }
if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) {
// Because sync transactions are serialized, its possible for an "older"
// bounds-change to get applied after a screen rotation. In that case, we
// want to actually defer on that rather than apply immediately. Of course,
// this means that the bounds may not change until after the rotation so
// the user might see some artifacts. This should be rare.
Slog.w(TAG, "Screen rotated while other operations were pending, this may"
+ " result in some graphical artifacts.");
} else {
wct.merge(t, true /* transfer */);
}
}; };
private final DividerImeController mImePositionProcessor; private final DividerImeController mImePositionProcessor;
@@ -159,6 +173,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
mRecentsOptionalLazy = recentsOptionalLazy; mRecentsOptionalLazy = recentsOptionalLazy;
mForcedResizableController = new ForcedResizableInfoActivityController(context, this); mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
mTransactionPool = transactionPool; mTransactionPool = transactionPool;
mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler); mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
} }
@@ -278,9 +293,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null); LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId()); DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout, mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
mImePositionProcessor); mImePositionProcessor, mWindowManagerProxy);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable); mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
final int size = dctx.getResources().getDimensionPixelSize( final int size = dctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness); com.android.internal.R.dimen.docked_stack_divider_thickness);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE; final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
@@ -303,7 +318,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
addDivider(configuration); addDivider(configuration);
if (mMinimized) { if (mMinimized) {
mView.setMinimizedDockStack(true, mHomeStackResizable); mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */);
updateTouchable(); updateTouchable();
} }
mView.setHidden(isDividerHidden); mView.setHidden(isDividerHidden);
@@ -327,11 +342,13 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
if (visible) { if (visible) {
mView.enterSplitMode(mHomeStackResizable); mView.enterSplitMode(mHomeStackResizable);
// Update state because animations won't finish. // Update state because animations won't finish.
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable); mWindowManagerProxy.runInSync(
t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t));
} else { } else {
mView.exitSplitMode(); mView.exitSplitMode();
// un-minimize so that next entry triggers minimize anim. mWindowManagerProxy.runInSync(
mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable); t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t));
} }
// Notify existence listeners // Notify existence listeners
synchronized (mDockedStackExistsListeners) { synchronized (mDockedStackExistsListeners) {
@@ -344,12 +361,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
} }
} }
void onSplitDismissed() {
updateVisibility(false /* visible */);
mMinimized = false;
removeDivider();
}
/** Switch to minimized state if appropriate */ /** Switch to minimized state if appropriate */
public void setMinimized(final boolean minimized) { public void setMinimized(final boolean minimized) {
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible); if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
@@ -405,7 +416,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
} }
} }
updateTouchable(); updateTouchable();
WindowOrganizer.applyTransaction(wct); mWindowManagerProxy.applySyncTransaction(wct);
} }
void setAdjustedForIme(boolean adjustedForIme) { void setAdjustedForIme(boolean adjustedForIme) {
@@ -501,7 +512,14 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
update(mDisplayController.getDisplayContext( update(mDisplayController.getDisplayContext(
mContext.getDisplayId()).getResources().getConfiguration()); mContext.getDisplayId()).getResources().getConfiguration());
// Set resizable directly here because applyEnterSplit already resizes home stack. // Set resizable directly here because applyEnterSplit already resizes home stack.
mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout); mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
}
void startDismissSplit() {
mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, true /* dismissOrMaximize */);
updateVisibility(false /* visible */);
mMinimized = false;
removeDivider();
} }
void ensureMinimizedSplit() { void ensureMinimizedSplit() {
@@ -530,6 +548,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
return mSplitLayout; return mSplitLayout;
} }
WindowManagerProxy getWmProxy() {
return mWindowManagerProxy;
}
/** @return the container token for the secondary split root task. */ /** @return the container token for the secondary split root task. */
public WindowContainerToken getSecondaryRoot() { public WindowContainerToken getSecondaryRoot() {
if (mSplits == null || mSplits.mSecondary == null) { if (mSplits == null || mSplits.mSecondary == null) {

View File

@@ -29,7 +29,6 @@ import android.view.SurfaceControl;
import android.window.TaskOrganizer; import android.window.TaskOrganizer;
import android.window.WindowContainerToken; import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -213,7 +212,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
} }
WindowOrganizer.applyTransaction(wct); mSplits.mDivider.getWmProxy().applySyncTransaction(wct);
// Update all the adjusted-for-ime states // Update all the adjusted-for-ime states
if (!mPaused) { if (!mPaused) {

View File

@@ -46,6 +46,7 @@ import android.view.VelocityTracker;
import android.view.View; import android.view.View;
import android.view.View.OnTouchListener; import android.view.View.OnTouchListener;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver.InternalInsetsInfo; import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowManager; import android.view.WindowManager;
@@ -138,7 +139,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private final Rect mOtherInsetRect = new Rect(); private final Rect mOtherInsetRect = new Rect();
private final Rect mLastResizeRect = new Rect(); private final Rect mLastResizeRect = new Rect();
private final Rect mTmpRect = new Rect(); private final Rect mTmpRect = new Rect();
private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance(); private WindowManagerProxy mWindowManagerProxy;
private DividerWindowManager mWindowManager; private DividerWindowManager mWindowManager;
private VelocityTracker mVelocityTracker; private VelocityTracker mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils; private FlingAnimationUtils mFlingAnimationUtils;
@@ -360,13 +361,14 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState, public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl, DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
DividerImeController imeController) { DividerImeController imeController, WindowManagerProxy wmProxy) {
mWindowManager = windowManager; mWindowManager = windowManager;
mState = dividerState; mState = dividerState;
mCallback = callback; mCallback = callback;
mTiles = tiles; mTiles = tiles;
mSplitLayout = sdl; mSplitLayout = sdl;
mImeController = imeController; mImeController = imeController;
mWindowManagerProxy = wmProxy;
if (mState.mRatioPositionBeforeMinimized == 0) { if (mState.mRatioPositionBeforeMinimized == 0) {
// Set the middle target as the initial state // Set the middle target as the initial state
@@ -376,10 +378,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
} }
} }
public WindowManagerProxy getWindowManagerProxy() {
return mWindowManagerProxy;
}
public Rect getNonMinimizedSplitScreenSecondaryBounds() { public Rect getNonMinimizedSplitScreenSecondaryBounds() {
mOtherTaskRect.set(mSplitLayout.mSecondary); mOtherTaskRect.set(mSplitLayout.mSecondary);
return mOtherTaskRect; return mOtherTaskRect;
@@ -519,7 +517,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) { if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget( SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
mStartPosition, 0 /* velocity */, false /* hardDismiss */); mStartPosition, 0 /* velocity */, false /* hardDismiss */);
resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget); resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget,
null /* transaction */);
} }
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
@@ -608,7 +607,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
? TASK_POSITION_SAME ? TASK_POSITION_SAME
: snapTarget.taskPosition, : snapTarget.taskPosition,
snapTarget)); snapTarget, null /* transaction */));
Consumer<Boolean> endAction = cancelled -> { Consumer<Boolean> endAction = cancelled -> {
if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction); if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
final boolean wasMinimizeInteraction = mIsInMinimizeInteraction; final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
@@ -716,7 +715,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|| mDockSide == WindowManager.DOCKED_BOTTOM; || mDockSide == WindowManager.DOCKED_BOTTOM;
} }
mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize); mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, mSplitLayout, dismissOrMaximize);
Transaction t = mTiles.getTransaction(); Transaction t = mTiles.getTransaction();
setResizeDimLayer(t, true /* primary */, 0f); setResizeDimLayer(t, true /* primary */, 0f);
setResizeDimLayer(t, false /* primary */, 0f); setResizeDimLayer(t, false /* primary */, 0f);
@@ -806,7 +805,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mWindowManager.setTouchRegion(touchRegion); mWindowManager.setTouchRegion(touchRegion);
} }
public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) { void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable,
Transaction t) {
mHomeStackResizable = isHomeStackResizable; mHomeStackResizable = isHomeStackResizable;
updateDockSide(); updateDockSide();
if (!minimized) { if (!minimized) {
@@ -840,9 +840,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
// Relayout to recalculate the divider shadow when minimizing // Relayout to recalculate the divider shadow when minimizing
requestLayout(); requestLayout();
mIsInMinimizeInteraction = true; mIsInMinimizeInteraction = true;
resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget()); resizeStackSurfaces(
mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget(), t);
} else { } else {
resizeStackSurfaces(mSnapTargetBeforeMinimized); resizeStackSurfaces(mSnapTargetBeforeMinimized, t);
mIsInMinimizeInteraction = false; mIsInMinimizeInteraction = false;
} }
} }
@@ -873,10 +874,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
* assigned to it. * assigned to it.
*/ */
private SurfaceControl getWindowSurfaceControl() { private SurfaceControl getWindowSurfaceControl() {
if (getViewRootImpl() == null) { final ViewRootImpl root = getViewRootImpl();
if (root == null) {
return null; return null;
} }
SurfaceControl out = getViewRootImpl().getSurfaceControl(); SurfaceControl out = root.getSurfaceControl();
if (out != null && out.isValid()) { if (out != null && out.isValid()) {
return out; return out;
} }
@@ -885,7 +887,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
void exitSplitMode() { void exitSplitMode() {
// Reset tile bounds // Reset tile bounds
post(() -> {
final SurfaceControl sc = getWindowSurfaceControl(); final SurfaceControl sc = getWindowSurfaceControl();
if (sc == null) { if (sc == null) {
return; return;
@@ -893,7 +894,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
Transaction t = mTiles.getTransaction(); Transaction t = mTiles.getTransaction();
t.hide(sc).apply(); t.hide(sc).apply();
mTiles.releaseTransaction(t); mTiles.releaseTransaction(t);
});
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout); WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
} }
@@ -1049,8 +1049,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDividerSize); mDividerSize);
} }
private void resizeStackSurfaces(SnapTarget taskSnapTarget) { private void resizeStackSurfaces(SnapTarget taskSnapTarget, Transaction t) {
resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget); resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget, t);
} }
void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) { void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
@@ -1105,7 +1105,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
} }
} }
void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget) { void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget,
Transaction transaction) {
if (mRemoved) { if (mRemoved) {
// This divider view has been removed so shouldn't have any additional influence. // This divider view has been removed so shouldn't have any additional influence.
return; return;
@@ -1123,7 +1124,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackground.invalidate(); mBackground.invalidate();
} }
Transaction t = mTiles.getTransaction(); final boolean ownTransaction = transaction == null;
final Transaction t = ownTransaction ? mTiles.getTransaction() : transaction;
mLastResizeRect.set(mDockedRect); mLastResizeRect.set(mDockedRect);
if (mHomeStackResizable && mIsInMinimizeInteraction) { if (mHomeStackResizable && mIsInMinimizeInteraction) {
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide, calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
@@ -1138,8 +1140,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
} }
resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
mOtherTaskRect); mOtherTaskRect);
if (ownTransaction) {
t.apply(); t.apply();
mTiles.releaseTransaction(t); mTiles.releaseTransaction(t);
}
return; return;
} }
@@ -1201,9 +1205,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position); SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget); float dimFraction = getDimFraction(position, closestDismissTarget);
setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction); setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
if (ownTransaction) {
t.apply(); t.apply();
mTiles.releaseTransaction(t); mTiles.releaseTransaction(t);
} }
}
private void applyExitAnimationParallax(Rect taskRect, int position) { private void applyExitAnimationParallax(Rect taskRect, int position) {
if (mDockSide == WindowManager.DOCKED_TOP) { if (mDockSide == WindowManager.DOCKED_TOP) {
@@ -1383,7 +1389,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
resizeStackSurfaces(calculatePositionForInsetBounds(), resizeStackSurfaces(calculatePositionForInsetBounds(),
mSplitLayout.getSnapAlgorithm().getMiddleTarget().position, mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
mSplitLayout.getSnapAlgorithm().getMiddleTarget()); mSplitLayout.getSnapAlgorithm().getMiddleTarget(),
null /* transaction */);
} }
void onRecentsDrawn() { void onRecentsDrawn() {

View File

@@ -200,8 +200,7 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
Log.d(TAG, " was in split, so this means leave it " Log.d(TAG, " was in split, so this means leave it "
+ mPrimary.topActivityType + " " + mSecondary.topActivityType); + mPrimary.topActivityType + " " + mSecondary.topActivityType);
} }
WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */); mDivider.startDismissSplit();
mDivider.onSplitDismissed();
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) { } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
// Wasn't in split-mode (both were empty), but now that the primary split is // Wasn't in split-mode (both were empty), but now that the primary split is
// populated, we should fully enter split by moving everything else into secondary. // populated, we should fully enter split by moving everything else into secondary.

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2020 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.systemui.stackdivider;
import android.os.Handler;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
import androidx.annotation.NonNull;
import com.android.systemui.TransactionPool;
import java.util.ArrayList;
/**
* Helper for serializing sync-transactions and corresponding callbacks.
*/
class SyncTransactionQueue {
private static final boolean DEBUG = Divider.DEBUG;
private static final String TAG = "SyncTransactionQueue";
// Just a little longer than the sync-engine timeout of 5s
private static final int REPLY_TIMEOUT = 5300;
private final TransactionPool mTransactionPool;
private final Handler mHandler;
// Sync Transactions currently don't support nesting or interleaving properly, so
// queue up transactions to run them serially.
private final ArrayList<SyncCallback> mQueue = new ArrayList<>();
private SyncCallback mInFlight = null;
private final ArrayList<TransactionRunnable> mRunnables = new ArrayList<>();
private final Runnable mOnReplyTimeout = () -> {
synchronized (mQueue) {
if (mInFlight != null && mQueue.contains(mInFlight)) {
Slog.w(TAG, "Sync Transaction timed-out: " + mInFlight.mWCT);
mInFlight.onTransactionReady(mInFlight.mId, new SurfaceControl.Transaction());
}
}
};
SyncTransactionQueue(TransactionPool pool, Handler handler) {
mTransactionPool = pool;
mHandler = handler;
}
/**
* Queues a sync transaction to be sent serially to WM.
*/
void queue(WindowContainerTransaction wct) {
SyncCallback cb = new SyncCallback(wct);
synchronized (mQueue) {
if (DEBUG) Slog.d(TAG, "Queueing up " + wct);
mQueue.add(cb);
if (mQueue.size() == 1) {
cb.send();
}
}
}
/**
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
* Otherwise just returns without queueing.
* @return {@code true} if queued, {@code false} if not.
*/
boolean queueIfWaiting(WindowContainerTransaction wct) {
synchronized (mQueue) {
if (mQueue.isEmpty()) {
if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct);
return false;
}
if (DEBUG) Slog.d(TAG, "Queue is non-empty, so queueing up " + wct);
SyncCallback cb = new SyncCallback(wct);
mQueue.add(cb);
if (mQueue.size() == 1) {
cb.send();
}
}
return true;
}
/**
* Runs a runnable in sync with sync transactions (ie. when the current in-flight transaction
* returns. If there are no transactions in-flight, runnable executes immediately.
*/
void runInSync(TransactionRunnable runnable) {
synchronized (mQueue) {
if (DEBUG) Slog.d(TAG, "Run in sync. mInFlight=" + mInFlight);
if (mInFlight != null) {
mRunnables.add(runnable);
return;
}
}
SurfaceControl.Transaction t = mTransactionPool.acquire();
runnable.runWithTransaction(t);
t.apply();
mTransactionPool.release(t);
}
// Synchronized on mQueue
private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) {
if (DEBUG) Slog.d(TAG, " Running " + mRunnables.size() + " sync runnables");
for (int i = 0, n = mRunnables.size(); i < n; ++i) {
mRunnables.get(i).runWithTransaction(t);
}
mRunnables.clear();
t.apply();
t.close();
}
interface TransactionRunnable {
void runWithTransaction(SurfaceControl.Transaction t);
}
private class SyncCallback extends WindowContainerTransactionCallback {
int mId = -1;
final WindowContainerTransaction mWCT;
SyncCallback(WindowContainerTransaction wct) {
mWCT = wct;
}
// Must be sychronized on mQueue
void send() {
if (mInFlight != null) {
throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
+ mInFlight.mId + " - " + mInFlight.mWCT);
}
mInFlight = this;
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
}
@Override
public void onTransactionReady(int id,
@androidx.annotation.NonNull SurfaceControl.Transaction t) {
mHandler.post(() -> {
synchronized (mQueue) {
if (mId != id) {
Slog.e(TAG, "Got an unexpected onTransactionReady. Expected "
+ mId + " but got " + id);
return;
}
mInFlight = null;
mHandler.removeCallbacks(mOnReplyTimeout);
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
onTransactionReceived(t);
if (!mQueue.isEmpty()) {
mQueue.get(0).send();
}
}
});
}
}
}

View File

@@ -25,9 +25,11 @@ import android.annotation.NonNull;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.view.Display; import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal; import android.view.WindowManagerGlobal;
import android.window.TaskOrganizer; import android.window.TaskOrganizer;
import android.window.WindowContainerToken; import android.window.WindowContainerToken;
@@ -35,6 +37,7 @@ import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer; import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.systemui.TransactionPool;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -49,8 +52,6 @@ public class WindowManagerProxy {
private static final String TAG = "WindowManagerProxy"; private static final String TAG = "WindowManagerProxy";
private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS}; private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
@GuardedBy("mDockedRect") @GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect(); private final Rect mDockedRect = new Rect();
@@ -61,6 +62,8 @@ public class WindowManagerProxy {
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final SyncTransactionQueue mSyncTransactionQueue;
private final Runnable mSetTouchableRegionRunnable = new Runnable() { private final Runnable mSetTouchableRegionRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -76,16 +79,13 @@ public class WindowManagerProxy {
} }
}; };
private WindowManagerProxy() { WindowManagerProxy(TransactionPool transactionPool, Handler handler) {
mSyncTransactionQueue = new SyncTransactionQueue(transactionPool, handler);
} }
public static WindowManagerProxy getInstance() { void dismissOrMaximizeDocked(final SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
return sInstance; final boolean dismissOrMaximize) {
} mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize));
void dismissOrMaximizeDocked(
final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
} }
public void setResizing(final boolean resizing) { public void setResizing(final boolean resizing) {
@@ -163,7 +163,7 @@ public class WindowManagerProxy {
* *
* @return whether the home stack is resizable * @return whether the home stack is resizable
*/ */
static boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) { boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
// Set launchtile first so that any stack created after // Set launchtile first so that any stack created after
// getAllStackInfos and before reparent (even if unlikely) are placed // getAllStackInfos and before reparent (even if unlikely) are placed
// correctly. // correctly.
@@ -174,6 +174,8 @@ public class WindowManagerProxy {
if (rootTasks.isEmpty()) { if (rootTasks.isEmpty()) {
return false; return false;
} }
ActivityManager.RunningTaskInfo topHomeTask = null;
boolean homeIsTop = false;
for (int i = rootTasks.size() - 1; i >= 0; --i) { for (int i = rootTasks.size() - 1; i >= 0; --i) {
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i); final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
// Only move resizeable task to split secondary. WM will just ignore this anyways... // Only move resizeable task to split secondary. WM will just ignore this anyways...
@@ -183,16 +185,25 @@ public class WindowManagerProxy {
!= WINDOWING_MODE_FULLSCREEN) { != WINDOWING_MODE_FULLSCREEN) {
continue; continue;
} }
// Since this iterates from bottom to top, update topHomeTask for every fullscreen task
// so it will be left with the status of the top one.
topHomeTask = isHomeOrRecentTask(rootTask) ? rootTask : null;
wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */); wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
} }
// Move the secondary split-forward. // Move the secondary split-forward.
wct.reorder(tiles.mSecondary.token, true /* onTop */); wct.reorder(tiles.mSecondary.token, true /* onTop */);
boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct); boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
WindowOrganizer.applyTransaction(wct); if (isHomeResizable && topHomeTask != null) {
// Translate/update-crop of secondary out-of-band with sync transaction -- Until BALST
// is enabled, this temporarily syncs the home surface position with offset until
// sync transaction finishes.
wct.setBoundsChangeTransaction(topHomeTask.token, tiles.mHomeBounds);
}
applySyncTransaction(wct);
return isHomeResizable; return isHomeResizable;
} }
private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) { static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
final int atype = ti.configuration.windowConfiguration.getActivityType(); final int atype = ti.configuration.windowConfiguration.getActivityType();
return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS; return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
} }
@@ -203,7 +214,8 @@ public class WindowManagerProxy {
* split (thus resulting in the top of the secondary split becoming * split (thus resulting in the top of the secondary split becoming
* fullscreen. {@code false} resolves the other way. * fullscreen. {@code false} resolves the other way.
*/ */
static void applyDismissSplit(SplitScreenTaskOrganizer tiles, boolean dismissOrMaximize) { void applyDismissSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
boolean dismissOrMaximize) {
// Set launch root first so that any task created after getChildContainers and // Set launch root first so that any task created after getChildContainers and
// before reparent (pretty unlikely) are put into fullscreen. // before reparent (pretty unlikely) are put into fullscreen.
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
@@ -229,6 +241,7 @@ public class WindowManagerProxy {
wct.reparent(primaryChildren.get(i).token, null /* parent */, wct.reparent(primaryChildren.get(i).token, null /* parent */,
true /* onTop */); true /* onTop */);
} }
boolean homeOnTop = false;
// Don't need to worry about home tasks because they are already in the "proper" // Don't need to worry about home tasks because they are already in the "proper"
// order within the secondary split. // order within the secondary split.
for (int i = secondaryChildren.size() - 1; i >= 0; --i) { for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
@@ -236,8 +249,31 @@ public class WindowManagerProxy {
wct.reparent(ti.token, null /* parent */, true /* onTop */); wct.reparent(ti.token, null /* parent */, true /* onTop */);
if (isHomeOrRecentTask(ti)) { if (isHomeOrRecentTask(ti)) {
wct.setBounds(ti.token, null); wct.setBounds(ti.token, null);
if (i == 0) {
homeOnTop = true;
} }
} }
}
if (homeOnTop) {
// Translate/update-crop of secondary out-of-band with sync transaction -- instead
// play this in sync with new home-app frame because until BALST is enabled this
// shows up on screen before the syncTransaction returns.
// We only have access to the secondary root surface, though, so in order to
// position things properly, we have to take into account the existing negative
// offset/crop of the minimized-home task.
final boolean landscape = layout.mDisplayLayout.isLandscape();
final int posX = landscape ? layout.mSecondary.left - tiles.mHomeBounds.left
: layout.mSecondary.left;
final int posY = landscape ? layout.mSecondary.top
: layout.mSecondary.top - tiles.mHomeBounds.top;
final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
sft.setPosition(tiles.mSecondarySurface, posX, posY);
final Rect crop = new Rect(0, 0, layout.mDisplayLayout.width(),
layout.mDisplayLayout.height());
crop.offset(-posX, -posY);
sft.setWindowCrop(tiles.mSecondarySurface, crop);
wct.setBoundsChangeTransaction(tiles.mSecondary.token, sft);
}
} else { } else {
// Maximize, so move non-home secondary split first // Maximize, so move non-home secondary split first
for (int i = secondaryChildren.size() - 1; i >= 0; --i) { for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
@@ -267,6 +303,29 @@ public class WindowManagerProxy {
} }
// Reset focusable to true // Reset focusable to true
wct.setFocusable(tiles.mPrimary.token, true /* focusable */); wct.setFocusable(tiles.mPrimary.token, true /* focusable */);
WindowOrganizer.applyTransaction(wct); applySyncTransaction(wct);
}
/**
* Utility to apply a sync transaction serially with other sync transactions.
*
* @see SyncTransactionQueue#queue
*/
void applySyncTransaction(WindowContainerTransaction wct) {
mSyncTransactionQueue.queue(wct);
}
/**
* @see SyncTransactionQueue#queueIfWaiting
*/
boolean queueSyncTransactionIfWaiting(WindowContainerTransaction wct) {
return mSyncTransactionQueue.queueIfWaiting(wct);
}
/**
* @see SyncTransactionQueue#runInSync
*/
void runInSync(SyncTransactionQueue.TransactionRunnable runnable) {
mSyncTransactionQueue.runInSync(runnable);
} }
} }

View File

@@ -21,6 +21,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.PowerManager.DRAW_WAKE_LOCK;
@@ -3757,6 +3758,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) { if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
return false; return false;
} }
// TODO(157912944): formalize drag-resizing so that exceptions aren't hardcoded like this
if (task.getActivityType() == ACTIVITY_TYPE_HOME) {
// The current sys-ui implementations never live-resize home, so to prevent WSA from
// creating/destroying surfaces (which messes up sync-transactions), skip HOME tasks.
return false;
}
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) { if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
// Floating windows never enter drag resize mode. // Floating windows never enter drag resize mode.
return false; return false;