private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+ private static native void nativeMergeTransaction(long transactionObj,
+ long otherTransactionObj);
private static native void nativeSetAnimationTransaction(long transactionObj);
private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
}
}
+ /**
+ * Merge the supplied transaction in to the deprecated "global" transaction.
+ * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
+ * <p>
+ * This is a utility for interop with legacy-code and will go away with the Global Transaction.
+ */
+ @Deprecated
+ public static void mergeToGlobalTransaction(Transaction t) {
+ synchronized(sGlobalTransaction) {
+ sGlobalTransaction.merge(t);
+ }
+ }
+
/** end a transaction */
public static void closeTransaction() {
closeTransaction(false);
* Sets the security of the surface. Setting the flag is equivalent to creating the
* Surface with the {@link #SECURE} flag.
*/
- Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+ public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
sc.checkNotReleased();
if (isSecure) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
nativeSetAnimationTransaction(mNativeObject);
return this;
}
+
+ /**
+ * Merge the other transaction into this transaction, clearing the
+ * other transaction as if it had been applied.
+ */
+ public Transaction merge(Transaction other) {
+ nativeMergeTransaction(mNativeObject, other.mNativeObject);
+ return this;
+ }
}
}
void writeToProto(ProtoOutputStream proto, long fieldId);
/**
- * Returns whether a given window type can be magnified.
- *
- * @param windowType The window type.
- * @return True if the window can be magnified.
- */
- public boolean canMagnifyWindow(int windowType);
-
- /**
* Returns whether a given window type is considered a top level one.
* A top level window does not have a container, i.e. attached window,
* or if it has a container it is laid out as a top-level window, not
transaction->apply(sync);
}
+static void nativeMergeTransaction(JNIEnv* env, jclass clazz,
+ jlong transactionObj, jlong otherTransactionObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ auto otherTransaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(
+ otherTransactionObj);
+ transaction->merge(std::move(*otherTransaction));
+}
+
static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz, jlong transactionObj) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->setAnimationTransaction();
(void*)nativeApplyTransaction },
{"nativeGetNativeTransactionFinalizer", "()J",
(void*)nativeGetNativeTransactionFinalizer },
+ {"nativeMergeTransaction", "(JJ)V",
+ (void*)nativeMergeTransaction },
{"nativeSetAnimationTransaction", "(J)V",
(void*)nativeSetAnimationTransaction },
{"nativeSetLayer", "(JJI)V",
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- switch (windowType) {
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
- case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
- case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
- return false;
- }
- }
- return true;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
&& windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
public void setMagnificationSpecLocked(MagnificationSpec spec) {
mMagnifedViewport.updateMagnificationSpecLocked(spec);
mMagnifedViewport.recomputeBoundsLocked();
+
+ mService.applyMagnificationSpec(spec);
mService.scheduleAnimationLocked();
}
public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
if (spec != null && !spec.isNop()) {
- if (!mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (!windowState.shouldMagnify()) {
return null;
}
}
private final ViewportWindow mWindow;
private boolean mFullRedrawNeeded;
+ private int mTempLayer = 0;
public MagnifiedViewport() {
mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
- if (mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (windowState.shouldMagnify()) {
mMagnificationRegion.op(windowBounds, Region.Op.UNION);
mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
} else {
private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ mTempLayer = 0;
dc.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisibleLw()
&& !w.mWinAnimator.mEnterAnimationPending) {
- outWindows.put(w.mLayer, w);
+ mTempLayer++;
+ outWindows.put(mTempLayer, w);
}
}, false /* traverseTopToBottom */ );
}
SurfaceControl surfaceControl = null;
try {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+ surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
.setName(SURFACE_TITLE)
.setSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
/* ignore */
}
mSurfaceControl = surfaceControl;
- mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
- .getLayerStack());
mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
TYPE_MAGNIFICATION_OVERLAY)
* WindowManagerService.TYPE_LAYER_MULTIPLIER);
private final long mRecurringAccessibilityEventsIntervalMillis;
+ private int mTempLayer = 0;
+
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
WindowsForAccessibilityCallback callback) {
mContext = windowManagerService.mContext;
if (isReportedWindowType(windowState.mAttrs.type)) {
// Add the window to the ones to be reported.
WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
+ window.layer = addedWindows.size();
addedWindows.add(window.token);
windows.add(window);
if (windowState.isFocused()) {
private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ mTempLayer = 0;
dc.forAllWindows((w) -> {
if (w.isVisibleLw()) {
- outWindows.put(w.mLayer, w);
+ outWindows.put(mTempLayer++, w);
}
}, false /* traverseTopToBottom */ );
}
private void updateLayers() {
mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
- updateThumbnailLayer();
}
private void stepThumbnailAnimation(long currentTime) {
+ "][" + tmpFloats[Matrix.MSKEW_X]
+ "," + tmpFloats[Matrix.MSCALE_Y] + "]");
thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- updateThumbnailLayer();
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
- * Updates the thumbnail layer z order to just above the highest animation layer if changed
- */
- void updateThumbnailLayer() {
- if (thumbnail != null) {
- final int layer = mAppToken.getHighestAnimLayer();
- if (DEBUG_LAYERS) Slog.v(TAG,
- "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
- thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- mThumbnailLayer = layer;
- }
- }
-
- /**
* Sometimes we need to synchronize the first frame of animation with some external event, e.g.
* Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
* and keep producing the first frame of the animation.
import android.util.Slog;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Four black surfaces put together to make a black frame.
final int layer;
final SurfaceControl surface;
- BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
- throws OutOfResourcesException {
+ BlackSurface(int layer,
+ int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
left = l;
top = t;
this.layer = layer;
int w = r-l;
int h = b-t;
- surface = new SurfaceControl.Builder(session)
+ surface = dc.makeOverlay()
.setName("BlackSurface")
.setSize(w, h)
.setColorLayer(true)
+ .setParent(null) // TODO: Work-around for b/69259549
.build();
surface.setAlpha(1);
- surface.setLayerStack(layerStack);
surface.setLayer(layer);
surface.show();
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
}
}
- public BlackFrame(SurfaceSession session, Rect outer, Rect inner, int layer, int layerStack,
+ public BlackFrame(Rect outer, Rect inner, int layer, DisplayContent dc,
boolean forceDefaultOrientation) throws OutOfResourcesException {
boolean success = false;
mForceDefaultOrientation = forceDefaultOrientation;
+ // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
+ // b/68253229
mOuterRect = new Rect(outer);
mInnerRect = new Rect(inner);
try {
if (outer.top < inner.top) {
- mBlackSurfaces[0] = new BlackSurface(session, layer,
- outer.left, outer.top, inner.right, inner.top, layerStack);
+ mBlackSurfaces[0] = new BlackSurface(layer,
+ outer.left, outer.top, inner.right, inner.top, dc);
}
if (outer.left < inner.left) {
- mBlackSurfaces[1] = new BlackSurface(session, layer,
- outer.left, inner.top, inner.left, outer.bottom, layerStack);
+ mBlackSurfaces[1] = new BlackSurface(layer,
+ outer.left, inner.top, inner.left, outer.bottom, dc);
}
if (outer.bottom > inner.bottom) {
- mBlackSurfaces[2] = new BlackSurface(session, layer,
- inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
+ mBlackSurfaces[2] = new BlackSurface(layer,
+ inner.left, inner.bottom, outer.right, outer.bottom, dc);
}
if (outer.right > inner.right) {
- mBlackSurfaces[3] = new BlackSurface(session, layer,
- inner.right, outer.top, outer.right, inner.bottom, layerStack);
+ mBlackSurfaces[3] = new BlackSurface(layer,
+ inner.right, outer.top, outer.right, inner.bottom, dc);
}
success = true;
} finally {
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class CircularDisplayMask {
private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
private boolean mDimensionsUnequal = false;
private int mMaskThickness;
- public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+ public CircularDisplayMask(DisplayContent dc, int zOrder,
int screenOffset, int maskThickness) {
+ final Display display = dc.getDisplay();
+
mScreenSize = new Point();
display.getSize(mScreenSize);
if (mScreenSize.x != mScreenSize.y + screenOffset) {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("CircularDisplayMask")
.setSize(mScreenSize.x, mScreenSize.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
return toString();
}
+ boolean isAlwaysOnTop() {
+ return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
+ }
+
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
private boolean mDestroyed = false;
- private final int mDisplayId;
-
+ private final DisplayContent mDisplayContent;
/** Interface implemented by users of the dim layer */
interface DimLayerUser {
private final String mName;
- DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
+ DimLayer(WindowManagerService service, DimLayerUser user, DisplayContent dc, String name) {
mUser = user;
- mDisplayId = displayId;
+ mDisplayContent = dc;
mService = service;
mName = name;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: dc=" + dc);
}
private void constructSurface(WindowManagerService service) {
service.openSurfaceTransaction();
try {
- mDimSurface = new SurfaceControl.Builder(service.mFxSession)
+ mDimSurface = mDisplayContent.makeSurface()
.setName(mName)
.setSize(16, 16)
.setColorLayer(true)
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
" DIM " + mDimSurface + ": CREATE");
- mDimSurface.setLayerStack(mDisplayId);
adjustBounds();
adjustAlpha(mAlpha);
adjustLayer(mLayer);
final boolean previousFullscreen = state.dimLayer != null
&& state.dimLayer == mSharedFullScreenDimLayer;
DimLayer newDimLayer;
- final int displayId = mDisplayContent.getDisplayId();
if (dimLayerUser.dimFullscreen()) {
if (previousFullscreen && mSharedFullScreenDimLayer != null) {
// Update the bounds for fullscreen in case of rotation.
newDimLayer = state.dimLayer;
} else {
// Create new full screen dim layer.
- newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
- getDimLayerTag(dimLayerUser));
+ newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser,
+ mDisplayContent, getDimLayerTag(dimLayerUser));
}
dimLayerUser.getDimBounds(mTmpBounds);
newDimLayer.setBounds(mTmpBounds);
}
} else {
newDimLayer = (state.dimLayer == null || previousFullscreen)
- ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+ ? new DimLayer(mDisplayContent.mService, dimLayerUser, mDisplayContent,
getDimLayerTag(dimLayerUser))
: state.dimLayer;
dimLayerUser.getDimBounds(mTmpBounds);
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputDevice;
+import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.WindowManagerPolicy;
import com.android.internal.annotations.VisibleForTesting;
// {@code false} if this display is in the processing of being created.
private boolean mDisplayReady = false;
- private final WindowLayersController mLayersController;
WallpaperController mWallpaperController;
int mInputMethodAnimLayerAdjustment;
+ private final SurfaceSession mSession = new SurfaceSession();
+
+ /**
+ * We organize all top-level Surfaces in to the following layers.
+ * mOverlayLayer contains a few Surfaces which are always on top of others
+ * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+ * {@link #mWindowingLayer} contains everything else.
+ */
+ private SurfaceControl mOverlayLayer;
+
+ /**
+ * See {@link #mOverlayLayer}
+ */
+ private SurfaceControl mWindowingLayer;
+
+ /**
+ * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
+ * <p>
+ * For these surfaces currently we use a surface based on the larger of width or height so we
+ * don't have to resize when rotating the display.
+ */
+ private int mSurfaceSize;
+
+ /**
+ * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+ */
+ private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
if (winAnimator.hasSurface()) {
return true;
};
- private final Consumer<WindowState> mPrepareWindowSurfaces =
- w -> w.mWinAnimator.prepareSurfaceLocked(true);
-
private final Consumer<WindowState> mPerformLayout = w -> {
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
* initialize direct children.
* @param display May not be null.
* @param service You know.
- * @param layersController window layer controller used to assign layer to the windows on this
- * display.
* @param wallpaperController wallpaper windows controller used to adjust the positioning of the
* wallpaper windows in the window list.
*/
DisplayContent(Display display, WindowManagerService service,
- WindowLayersController layersController, WallpaperController wallpaperController) {
+ WallpaperController wallpaperController) {
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
mDisplay = display;
mDisplayId = display.getDisplayId();
- mLayersController = layersController;
mWallpaperController = wallpaperController;
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
mDimLayerController = new DimLayerController(this);
+ mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
+ .setSize(mSurfaceSize, mSurfaceSize)
+ .setOpaque(true);
+ mWindowingLayer = b.setName("Display Root").build();
+ mOverlayLayer = b.setName("Display Overlays").build();
+
+ mPendingTransaction.setLayer(mWindowingLayer, 0)
+ .setLayerStack(mWindowingLayer, mDisplayId)
+ .show(mWindowingLayer)
+ .setLayer(mOverlayLayer, 1)
+ .setLayerStack(mOverlayLayer, mDisplayId)
+ .show(mOverlayLayer);
+ mPendingTransaction.apply();
+
// These are the only direct children we should ever have and they are permanent.
super.addChild(mBelowAppWindowsContainers, null);
super.addChild(mTaskStackContainers, null);
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
- if (screenRotationAnimation.setRotationInTransaction(
- rotation, mService.mFxSession,
+ if (screenRotationAnimation.setRotationInTransaction(rotation,
MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
mService.scheduleAnimationLocked();
mService.unregisterPointerEventListener(mService.mMousePositionTracker);
}
}
+ // The pending transaction won't be applied so we should
+ // just clean up any surfaces pending destruction.
+ onPendingTransactionApplied();
} finally {
mRemovingDisplay = false;
}
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
- mLayersController.assignWindowLayers(this);
+ assignChildLayers(mPendingTransaction);
if (setLayoutNeeded) {
setLayoutNeeded();
}
+
+ // We accumlate the layer changes in-to "mPendingTransaction" but we defer
+ // the application of this transaction until the animation pass triggers
+ // prepareSurfaces. This allows us to synchronize Z-ordering changes with
+ // the hiding and showing of surfaces.
+ scheduleAnimation();
}
// TODO: This should probably be called any time a visual change is made to the hierarchy like
}
}
- void prepareWindowSurfaces() {
- forAllWindows(mPrepareWindowSurfaces, false /* traverseTopToBottom */);
- }
-
boolean inputMethodClientHasFocus(IInputMethodClient client) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
// Include this window.
final WindowStateAnimator winAnim = w.mWinAnimator;
- int layer = winAnim.mSurfaceController.getLayer();
- if (mScreenshotApplicationState.maxLayer < layer) {
- mScreenshotApplicationState.maxLayer = layer;
- }
- if (mScreenshotApplicationState.minLayer > layer) {
- mScreenshotApplicationState.minLayer = layer;
- }
// Don't include wallpaper in bounds calculation
if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
final WindowState appWin = mScreenshotApplicationState.appWin;
final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
- final int maxLayer = mScreenshotApplicationState.maxLayer;
- final int minLayer = mScreenshotApplicationState.minLayer;
if (appToken != null && appWin == null) {
// Can't find a window to snapshot.
// because we don't want to release the mWindowMap lock until the screenshot is
// taken.
- if (maxLayer == 0) {
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
- + ": returning null maxLayer=" + maxLayer);
- return null;
- }
if (!mutableIncludeFullDisplay.value) {
// Constrain frame to the screen size.
convertCropForSurfaceFlinger(crop, rot, dw, dh);
if (DEBUG_SCREENSHOT) {
- Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
- + maxLayer + " appToken=" + appToken);
forAllWindows(w -> {
final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
Slog.i(TAG_WM, w + ": " + w.mLayer
SurfaceControl.openTransaction();
SurfaceControl.closeTransactionSync();
- bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
+ // TODO(b/68392460): We should screenshot Task controls directly
+ // but it's difficult at the moment as the Task doesn't have the
+ // correct size set.
+ bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
inRotation, rot);
if (bitmap == null) {
- Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
- + ") to layer " + maxLayer);
+ Slog.w(TAG_WM, "Failed to take screenshot");
return null;
}
}
* I.e Activities.
*/
private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+ /**
+ * A control placed at the appropriate level for transitions to occur.
+ */
+ SurfaceControl mAnimationLayer = null;
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
// to prevent freezing/unfreezing the display too early.
return mLastOrientation;
}
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ final int NORMAL_STACK_STATE = 0;
+ final int BOOSTED_STATE = 1;
+ final int ALWAYS_ON_TOP_STATE = 2;
+
+ // We allow stacks to change visual order from the AM specified order due to
+ // Z-boosting during animations. However we must take care to ensure TaskStacks
+ // which are marked as alwaysOnTop remain that way.
+ int layer = 0;
+ for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ final TaskStack s = mChildren.get(i);
+ layer++;
+ if (state == NORMAL_STACK_STATE) {
+ s.assignLayer(t, layer);
+ } else if (state == BOOSTED_STATE && s.needsZBoost()) {
+ s.assignLayer(t, layer);
+ } else if (state == ALWAYS_ON_TOP_STATE &&
+ s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer);
+ }
+ s.assignChildLayers(t);
+ }
+ // The appropriate place for App-Transitions to occur is right
+ // above all other animations but still below things in the Picture-and-Picture
+ // windowing mode.
+ if (state == BOOSTED_STATE && mAnimationLayer != null) {
+ t.setLayer(mAnimationLayer, layer + 1);
+ }
+ }
+ }
+
+ @Override
+ void onParentSet() {
+ super.onParentSet();
+ if (getParent() != null) {
+ mAnimationLayer = makeSurface().build();
+ } else {
+ mAnimationLayer.destroy();
+ mAnimationLayer = null;
+ }
+ }
}
/**
E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean useIdentityTransform, int rotation);
}
+
+ SurfaceControl.Builder makeSurface(SurfaceSession s) {
+ return mService.makeSurfaceBuilder(s)
+ .setParent(mWindowingLayer);
+ }
+
+ @Override
+ SurfaceSession getSession() {
+ return mSession;
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(child.getSession());
+ b.setName(child.getName());
+
+ b.setSize(mSurfaceSize, mSurfaceSize);
+ if (child.isScreenOverlay()) {
+ return b.setParent(mOverlayLayer);
+ } else {
+ return b.setParent(mWindowingLayer);
+ }
+ }
+
+ /**
+ * The makeSurface variants are for use by the window-container
+ * hierarchy. makeOverlay here is a function for various non windowing
+ * overlays like the ScreenRotation screenshot, the Strict Mode Flash
+ * and other potpourii.
+ */
+ SurfaceControl.Builder makeOverlay() {
+ return mService.makeSurfaceBuilder(mSession)
+ .setParent(mOverlayLayer);
+ }
+
+ void applyMagnificationSpec(MagnificationSpec spec) {
+ applyMagnificationSpec(mPendingTransaction, spec);
+ mPendingTransaction.apply();
+ }
+
+ @Override
+ void onParentSet() {
+ // Since we are the top of the SurfaceControl hierarchy here
+ // we create the root surfaces explicitly rather than chaining
+ // up as the default implementation in onParentSet does. So we
+ // explicitly do NOT call super here.
+ }
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ t.setLayer(mOverlayLayer, 1)
+ .setLayer(mWindowingLayer, 0);
+
+ // These are layers as children of "mWindowingLayer"
+ mBelowAppWindowsContainers.assignLayer(t, 0);
+ mTaskStackContainers.assignLayer(t, 1);
+ mAboveAppWindowsContainers.assignLayer(t, 2);
+
+ WindowState imeTarget = mService.mInputMethodTarget;
+ if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) {
+ // In split-screen windowing mode we can't layer the
+ // IME relative to the IME target because it needs to
+ // go over the docked divider, so instead we place it on top
+ // of everything and use relative layering of windows which need
+ // to go above it (see special logic in WindowState#assignLayer)
+ mImeWindowsContainers.assignLayer(t, 3);
+ } else {
+ t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
+ imeTarget.getSurfaceControl(),
+ // TODO: We need to use an extra level on the app surface to ensure
+ // this is always above SurfaceView but always below attached window.
+ 1);
+ }
+
+ // Above we have assigned layers to our children, now we ask them to assign
+ // layers to their children.
+ mBelowAppWindowsContainers.assignChildLayers(t);
+ mTaskStackContainers.assignChildLayers(t);
+ mAboveAppWindowsContainers.assignChildLayers(t);
+ mImeWindowsContainers.assignChildLayers(t);
+ }
+
+ /**
+ * Here we satisfy an unfortunate special case of the IME in split-screen mode. Imagine
+ * that the IME target is one of the docked applications. We'd like the docked divider to be
+ * above both of the applications, and we'd like the IME to be above the docked divider.
+ * However we need child windows of the applications to be above the IME (Text drag handles).
+ * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
+ * somehow. We do this by relatively ordering children of the target to the IME in cooperation
+ * with {@link #WindowState#assignLayer}
+ */
+ void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
+ t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+ }
+
+ @Override
+ void destroyAfterPendingTransaction(SurfaceControl surface) {
+ mPendingDestroyingSurfaces.add(surface);
+ }
+
+ /**
+ * Destroys any surfaces that have been put into the pending list with
+ * {@link #destroyAfterTransaction}.
+ */
+ void onPendingTransactionApplied() {
+ for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+ mPendingDestroyingSurfaces.get(i).destroy();
+ }
+ mPendingDestroyingSurfaces.clear();
+ }
}
mService = service;
mDisplayContent = displayContent;
final Context context = service.mContext;
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
+ mDimLayer = new DimLayer(displayContent.mService, this, displayContent,
"DockedStackDim");
mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
context, android.R.interpolator.fast_out_slow_in);
private int mRotation;
private boolean mVisible;
- public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+ public EmulatorDisplayOverlay(Context context, DisplayContent dc,
int zOrder) {
+ final Display display = dc.getDisplay();
mScreenSize = new Point();
display.getSize(mScreenSize);
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("EmulatorDisplayOverlay")
.setSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
ctrl.show();
ParcelFileDescriptor mSurfaceTraceFd;
RemoteEventTrace mRemoteEventTrace;
- private final WindowLayersController mLayersController;
final WallpaperController mWallpaperController;
private final Handler mHandler;
RootWindowContainer(WindowManagerService service) {
mService = service;
mHandler = new MyHandler(service.mH.getLooper());
- mLayersController = new WindowLayersController(mService);
mWallpaperController = new WallpaperController(mService);
}
}
private DisplayContent createDisplayContent(final Display display) {
- final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+ final DisplayContent dc = new DisplayContent(display, mService,
mWallpaperController);
final int displayId = display.getDisplayId();
String getName() {
return "ROOT";
}
+
+ @Override
+ void scheduleAnimation() {
+ mService.scheduleAnimationLocked();
+ }
}
}
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
+ boolean inTransaction, boolean forceDefaultOrientation,
boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
try {
try {
- mSurfaceControl = new SurfaceControl.Builder(session)
+ mSurfaceControl = displayContent.makeOverlay()
.setName("ScreenshotSurface")
.setSize(mWidth, mHeight)
.setSecure(isSecure)
// TODO(multidisplay): we should use the proper display
SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
- mSurfaceControl.setLayerStack(display.getLayerStack());
mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
mSurfaceControl.setAlpha(0);
mSurfaceControl.show();
}
// Must be called while in a transaction.
- public boolean setRotationInTransaction(int rotation, SurfaceSession session,
+ public boolean setRotationInTransaction(int rotation,
long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
setRotationInTransaction(rotation);
if (TWO_PHASE_ANIMATION) {
- return startAnimation(session, maxAnimationDuration, animationScale,
+ return startAnimation(maxAnimationDuration, animationScale,
finalWidth, finalHeight, false, 0, 0);
}
/**
* Returns true if animating.
*/
- private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+ private boolean startAnimation(long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, boolean dismissing,
int exitAnim, int enterAnim) {
if (mSurfaceControl == null) {
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mCustomBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
+ mCustomBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
mOriginalWidth*2, mOriginalHeight*2);
inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
}
- mExitingBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
+ mExitingBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
finalWidth*2, finalHeight*2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mEnteringBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
+ mEnteringBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
/**
* Returns true if animating.
*/
- public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+ public boolean dismiss(long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
if (mSurfaceControl == null) {
return false;
}
if (!mStarted) {
- startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+ startAnimation(maxAnimationDuration, animationScale, finalWidth, finalHeight,
true, exitAnim, enterAnim);
}
if (!mStarted) {
private boolean mDrawNeeded;
private final int mThickness = 20;
- public StrictModeFlash(Display display, SurfaceSession session) {
+ public StrictModeFlash(DisplayContent dc) {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("StrictModeFlash")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
ctrl.setPosition(0, 0);
ctrl.show();
--- /dev/null
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.view.SurfaceSession;
+import android.view.SurfaceControl;
+
+interface SurfaceBuilderFactory {
+ SurfaceControl.Builder make(SurfaceSession s);
+};
+
@Override
void onParentSet() {
+ super.onParentSet();
+
// Update task bounds if needed.
updateDisplayInfo(getDisplayContent());
/**
* @param display The Display that the window being dragged is on.
*/
- void register(Display display) {
+ void register(DisplayContent displayContent) {
+ final Display display = displayContent.getDisplay();
+
if (DEBUG_TASK_POSITIONING) {
Slog.d(TAG, "Registering task positioner");
}
}
mService.pauseRotationLocked();
- mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
+ mDimLayer = new DimLayer(mService, this, displayContent, TAG_LOCAL);
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
}
mDisplayContent = dc;
- mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
+ mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent,
"animation background stackId=" + mStackId);
updateBoundsForWindowModeChange();
super.onDisplayChanged(dc);
@Override
void onParentSet() {
+ super.onParentSet();
+
if (getParent() != null || mDisplayContent == null) {
return;
}
private int mLastDH;
private boolean mDrawNeeded;
- Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+ Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
if (false) {
Log.i(TAG_WM, "*********************** WATERMARK");
for (int i=0; i<tokens.length; i++) {
}
}
- mDisplay = display;
+ mDisplay = dc.getDisplay();
mTokens = tokens;
StringBuilder builder = new StringBuilder(32);
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("WatermarkSurface")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
++mAnimTransactionSequence;
dc.updateWindowsForAnimator(this);
dc.updateWallpaperForAnimator(this);
- dc.prepareWindowSurfaces();
+ dc.prepareSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ dc.onPendingTransactionApplied();
+ }
+
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
boolean doRequest = false;
if (mBulkUpdateParams != 0) {
mService.destroyPreservedSurfaceLocked();
mService.mWindowPlacerLocked.destroyPendingSurfaces();
+
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static android.view.SurfaceControl.Transaction;
import android.annotation.CallSuper;
import android.content.res.Configuration;
+import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.util.Pools;
import android.util.proto.ProtoOutputStream;
new Pools.SynchronizedPool<>(3);
// The owner/creator for this container. No controller if null.
- private WindowContainerController mController;
+ WindowContainerController mController;
+
+ protected SurfaceControl mSurfaceControl;
+
+ /**
+ * Applied as part of the animation pass in "prepareSurfaces".
+ */
+ protected Transaction mPendingTransaction = new Transaction();
@Override
final protected WindowContainer getParent() {
* Supposed to be overridden and contain actions that should be executed after parent was set.
*/
void onParentSet() {
- // Do nothing by default.
+ if (mParent == null) {
+ return;
+ }
+ if (mSurfaceControl == null) {
+ // If we don't yet have a surface, but we now have a parent, we should
+ // build a surface.
+ mSurfaceControl = makeSurface().build();
+ mPendingTransaction.show(mSurfaceControl);
+ } else {
+ // If we have a surface but a new parent, we just need to perform a reparent.
+ mPendingTransaction.reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+ }
+
+ // Either way we need to ask the parent to assign us a Z-order.
+ mParent.assignChildLayers();
+ scheduleAnimation();
}
// Temp. holders for a chain of containers we are currently processing.
mChildren.remove(child);
}
+ if (mSurfaceControl != null) {
+ destroyAfterPendingTransaction(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+
if (mParent != null) {
mParent.removeChild(this);
}
if (mController != null) {
setController(null);
}
+
}
/**
}
/**
-a * Returns whether this child is on top of the window hierarchy.
+ * @return Whether this child is on top of the window hierarchy.
*/
boolean isOnTop() {
return getParent().getTopChild() == this && getParent().isOnTop();
mController = controller;
}
+ SurfaceControl.Builder makeSurface() {
+ final WindowContainer p = getParent();
+ return p.makeChildSurface(this);
+ }
+
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ final WindowContainer p = getParent();
+ // Give the parent a chance to set properties. In hierarchy v1 we rely
+ // on this to set full-screen dimensions on all our Surface-less Layers.
+ final SurfaceControl.Builder b = p.makeChildSurface(child);
+ if (child.isScreenOverlay()) {
+ // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
+ // WindowContainer hierarchy vs the SurfaceControl hierarchy)
+ // and we shouldn't set ourselves as the parent.
+ return b;
+ } else {
+ return b.setParent(mSurfaceControl);
+ }
+ }
+
+ /**
+ * There are various layers which require promotion from the WindowContainer
+ * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
+ * for the particular usage.
+ *
+ * TODO: Perhaps this should be eliminated, either through modifying
+ * the window container hierarchy or through modifying the way we express these overlay
+ * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
+ * Flash and not actually use a WindowState).
+ */
+ boolean isScreenOverlay() {
+ return false;
+ }
+
+ /**
+ * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
+ */
+ boolean shouldMagnify() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (!mChildren.get(i).shouldMagnify()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ SurfaceSession getSession() {
+ if (getParent() != null) {
+ return getParent().getSession();
+ }
+ return null;
+ }
+
+ void assignLayer(Transaction t, int layer) {
+ if (mSurfaceControl != null) {
+ t.setLayer(mSurfaceControl, layer);
+ }
+ }
+
+ void assignChildLayers(Transaction t) {
+ int layer = 0;
+ boolean boosting = false;
+
+ // We use two passes as a way to promote children which
+ // need Z-boosting to the end of the list.
+ for (int i = 0; i < 2; i++ ) {
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ if (wc.needsZBoost() && !boosting) {
+ continue;
+ }
+ wc.assignLayer(t, layer);
+ wc.assignChildLayers(t);
+
+ layer++;
+ }
+ boosting = true;
+ }
+ }
+
+ void assignChildLayers() {
+ assignChildLayers(mPendingTransaction);
+ }
+
+ boolean needsZBoost() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (mChildren.get(i).needsZBoost()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
* {@link com.android.server.wm.proto.WindowContainerProto}.
mConsumerWrapperPool.release(this);
}
}
+
+ // TODO(b/68336570): Should this really be on WindowContainer since it
+ // can only be used on the top-level nodes that aren't animated?
+ // (otherwise we would be fighting other callers of setMatrix).
+ void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
+ if (shouldMagnify()) {
+ t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
+ .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
+ } else {
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).applyMagnificationSpec(t, spec);
+ }
+ }
+ }
+
+ /**
+ * TODO: Once we totally eliminate global transaction we will pass transaction in here
+ * rather than merging to global.
+ */
+ void prepareSurfaces() {
+ SurfaceControl.mergeToGlobalTransaction(mPendingTransaction);
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).prepareSurfaces();
+ }
+ }
+
+ /**
+ * Trigger a call to prepareSurfaces from the animation thread, such that
+ * mPendingTransaction will be applied.
+ */
+ void scheduleAnimation() {
+ if (mParent != null) {
+ mParent.scheduleAnimation();
+ }
+ }
+
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ /**
+ * Destroy a given surface after executing mPendingTransaction. This is
+ * largely a workaround for destroy not being part of transactions
+ * rather than an intentional design, so please take care when
+ * expanding use.
+ */
+ void destroyAfterPendingTransaction(SurfaceControl surface) {
+ if (mParent != null) {
+ mParent.destroyAfterPendingTransaction(surface);
+ }
+ }
}
+++ /dev/null
-/*
- * Copyright (C) 2015 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.server.wm;
-
-import android.util.Slog;
-
-import java.util.ArrayDeque;
-import java.util.function.Consumer;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
-
-/**
- * Controller for assigning layers to windows on the display.
- *
- * This class encapsulates general algorithm for assigning layers and special rules that we need to
- * apply on top. The general algorithm goes through windows from bottom to the top and the higher
- * the window is, the higher layer is assigned. The final layer is equal to base layer +
- * adjustment from the order. This means that the window list is assumed to be ordered roughly by
- * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
- * handled with care, because they break the algorithm).
- *
- * On top of the general algorithm we add special rules, that govern such amazing things as:
- * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
- * <li>docked/pinned windows (that need to be lifted above other application windows, including
- * animations)
- * <li>dock divider (which needs to live above applications, but below IME)</li>
- * <li>replaced windows, which need to live above their normal level, because they anticipate
- * an animation</li>.
- */
-class WindowLayersController {
- private final WindowManagerService mService;
-
- WindowLayersController(WindowManagerService service) {
- mService = service;
- }
-
- private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
- private WindowState mDockDivider = null;
- private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
- private int mCurBaseLayer;
- private int mCurLayer;
- private boolean mAnyLayerChanged;
- private int mHighestApplicationLayer;
- private int mHighestDockedAffectedLayer;
- private int mHighestLayerInImeTargetBaseLayer;
- private WindowState mImeTarget;
- private boolean mAboveImeTarget;
- private ArrayDeque<WindowState> mAboveImeTargetAppWindows = new ArrayDeque();
-
- private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
- boolean layerChanged = false;
-
- int oldLayer = w.mLayer;
- if (w.mBaseLayer == mCurBaseLayer) {
- mCurLayer += WINDOW_LAYER_MULTIPLIER;
- } else {
- mCurBaseLayer = mCurLayer = w.mBaseLayer;
- }
- assignAnimLayer(w, mCurLayer);
-
- // TODO: Preserved old behavior of code here but not sure comparing oldLayer to
- // mAnimLayer and mLayer makes sense...though the worst case would be unintentional
- // layer reassignment.
- if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
- layerChanged = true;
- mAnyLayerChanged = true;
- }
-
- if (w.mAppToken != null) {
- mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
- w.mWinAnimator.mAnimLayer);
- }
- if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
- mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
- w.mWinAnimator.mAnimLayer);
- }
- if (w.getAppToken() != null && w.inSplitScreenSecondaryWindowingMode()) {
- mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
- w.mWinAnimator.mAnimLayer);
- }
-
- collectSpecialWindows(w);
-
- if (layerChanged) {
- w.scheduleAnimationIfDimming();
- }
- };
-
- final void assignWindowLayers(DisplayContent dc) {
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based",
- new RuntimeException("here").fillInStackTrace());
-
- reset();
- dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
-
- adjustSpecialWindows();
-
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && mAnyLayerChanged
- && dc.getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowLayersChangedLocked();
- }
-
- if (DEBUG_LAYERS) logDebugLayers(dc);
- }
-
- private void logDebugLayers(DisplayContent dc) {
- dc.forAllWindows((w) -> {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
- + " mLayer=" + w.mLayer + (w.mAppToken == null
- ? "" : " mAppLayer=" + w.mAppToken.getAnimLayerAdjustment())
- + " =mAnimLayer=" + winAnimator.mAnimLayer);
- }, false /* traverseTopToBottom */);
- }
-
- private void reset() {
- mPinnedWindows.clear();
- mInputMethodWindows.clear();
- mDockedWindows.clear();
- mAssistantWindows.clear();
- mReplacingWindows.clear();
- mDockDivider = null;
-
- mCurBaseLayer = 0;
- mCurLayer = 0;
- mAnyLayerChanged = false;
-
- mHighestApplicationLayer = 0;
- mHighestDockedAffectedLayer = 0;
- mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
- mImeTarget = mService.mInputMethodTarget;
- mAboveImeTarget = false;
- mAboveImeTargetAppWindows.clear();
- }
-
- private void collectSpecialWindows(WindowState w) {
- if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDockDivider = w;
- return;
- }
- if (w.mWillReplaceWindow) {
- mReplacingWindows.add(w);
- }
- if (w.mIsImWindow) {
- mInputMethodWindows.add(w);
- return;
- }
- if (mImeTarget != null) {
- if (w.getParentWindow() == mImeTarget && w.mSubLayer > 0) {
- // Child windows of the ime target with a positive sub-layer should be placed above
- // the IME.
- mAboveImeTargetAppWindows.add(w);
- } else if (mAboveImeTarget && w.mAppToken != null) {
- // windows of apps above the IME target should be placed above the IME.
- mAboveImeTargetAppWindows.add(w);
- }
- if (w == mImeTarget) {
- mAboveImeTarget = true;
- }
- }
-
- final int windowingMode = w.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- mPinnedWindows.add(w);
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mDockedWindows.add(w);
- }
- if (w.isActivityTypeAssistant()) {
- mAssistantWindows.add(w);
- }
- }
-
- private void adjustSpecialWindows() {
- // The following adjustments are beyond the highest docked-affected layer
- int layer = mHighestDockedAffectedLayer + TYPE_LAYER_OFFSET;
-
- // Adjust the docked stack windows and dock divider above only the windows that are affected
- // by the docked stack. When this happens, also boost the assistant window layers, otherwise
- // the docked stack windows & divider would be promoted above the assistant.
- if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
- while (!mDockedWindows.isEmpty()) {
- final WindowState window = mDockedWindows.remove();
- layer = assignAndIncreaseLayerIfNeeded(window, layer);
- }
-
- layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
-
- while (!mAssistantWindows.isEmpty()) {
- final WindowState window = mAssistantWindows.remove();
- if (window.mLayer > mHighestDockedAffectedLayer) {
- layer = assignAndIncreaseLayerIfNeeded(window, layer);
- }
- }
- }
-
- // The following adjustments are beyond the highest app layer or boosted layer
- layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
-
- // We know that we will be animating a relaunching window in the near future, which will
- // receive a z-order increase. We want the replaced window to immediately receive the same
- // treatment, e.g. to be above the dock divider.
- while (!mReplacingWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
- }
-
- while (!mPinnedWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
- }
-
- // Make sure IME is the highest window in the base layer of it's target.
- if (mImeTarget != null) {
- if (mImeTarget.mAppToken == null) {
- // For non-app ime targets adjust the layer we start from to match what we found
- // when assigning layers. Otherwise, just use the highest app layer we have some far.
- layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER;
- }
-
- while (!mInputMethodWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
- }
-
- // Adjust app windows the should be displayed above the IME since they are above the IME
- // target.
- while (!mAboveImeTargetAppWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mAboveImeTargetAppWindows.remove(), layer);
- }
- }
-
- }
-
- private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
- if (win != null) {
- assignAnimLayer(win, layer);
- // Make sure we leave space in-between normal windows for dims and such.
- layer += WINDOW_LAYER_MULTIPLIER;
- }
- return layer;
- }
-
- private void assignAnimLayer(WindowState w, int layer) {
- w.mLayer = layer;
- w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
- + w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null) {
- w.mAppToken.mAppAnimator.updateThumbnailLayer();
- }
- }
-}
AccessibilityController mAccessibilityController;
- final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
CircularDisplayMask mCircularDisplayMask;
static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
new WindowManagerThreadPriorityBooster();
+ class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new SurfaceControl.Builder(s);
+ }
+ };
+ SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+
static void boostPriorityForLockedSection() {
sThreadPriorityBooster.boost();
}
mPointerEventDispatcher = null;
}
- mFxSession = new SurfaceSession();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplays = mDisplayManager.getDisplays();
for (Display display : mDisplays) {
com.android.internal.R.dimen.circular_display_mask_thickness);
mCircularDisplayMask = new CircularDisplayMask(
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
+ getDefaultDisplayContentLocked(),
mPolicy.getWindowLayerFromTypeLw(
WindowManager.LayoutParams.TYPE_POINTER)
* TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
if (mEmulatorDisplayOverlay == null) {
mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
mContext,
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
+ getDefaultDisplayContentLocked(),
mPolicy.getWindowLayerFromTypeLw(
WindowManager.LayoutParams.TYPE_POINTER)
* TYPE_LAYER_MULTIPLIER + 10);
// TODO(multi-display): support multiple displays
if (mStrictModeFlash == null) {
mStrictModeFlash = new StrictModeFlash(
- getDefaultDisplayContentLocked().getDisplay(), mFxSession);
+ getDefaultDisplayContentLocked());
}
mStrictModeFlash.setVisibility(on);
} finally {
Display display = displayContent.getDisplay();
mTaskPositioner = new TaskPositioner(this);
- mTaskPositioner.register(display);
+ mTaskPositioner.register(displayContent);
mInputMonitor.updateInputWindowsLw(true /*force*/);
// We need to grab the touch focus so that the touch events during the
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
+ inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
mExitAnimId = mEnterAnimId = 0;
}
- if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+ if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
scheduleAnimationLocked();
if (toks != null && toks.length > 0) {
// TODO(multi-display): Show watermarks on secondary displays.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- mWatermark = new Watermark(displayContent.getDisplay(),
- displayContent.mRealDisplayMetrics, mFxSession, toks);
+ mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+ toks);
}
}
} catch (FileNotFoundException e) {
w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
}, false /* traverseTopToBottom */);
}
+
+ public void applyMagnificationSpec(MagnificationSpec spec) {
+ getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+ }
+
+ SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
+ return mSurfaceBuilderFactory.make(s);
+ }
}
+
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
return false;
}
}
+
+ @Override
+ boolean shouldMagnify() {
+ if (mAttrs.type == TYPE_INPUT_METHOD ||
+ mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+ return false;
+ } else if (isScreenOverlay()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ boolean isScreenOverlay() {
+ // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+ // worry not: it's a fake TYPE_NAVIGATION_BAR.
+ if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+ mAttrs.type == TYPE_NAVIGATION_BAR ||
+ mAttrs.type == TYPE_STATUS_BAR) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ SurfaceSession getSession() {
+ if (mSession.mSurfaceSession != null) {
+ return mSession.mSurfaceSession;
+ } else {
+ return getParent().getSession();
+ }
+ }
+
+ @Override
+ boolean needsZBoost() {
+ return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
+ }
+
+ @Override
+ SurfaceControl.Builder makeSurface() {
+ return mToken.makeChildSurface(this);
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mWinAnimator.prepareSurfaceLocked(true);
+ super.prepareSurfaces();
+ }
+
+ @Override
+ void assignLayer(Transaction t, int layer) {
+ // See comment in assignRelativeLayerForImeTargetChild
+ if (!isChildWindow()
+ || (mService.mInputMethodTarget != getParentWindow())
+ || !inSplitScreenWindowingMode()) {
+ super.assignLayer(t, layer);
+ return;
+ }
+ getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+ }
}
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.view.MagnificationSpec;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.WindowManager;
}
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
if (mSurfaceController != null) {
- mSurfaceController.setLayer(mAnimLayer + 1);
+ // Our SurfaceControl is always at layer 0 within the parent Surface managed by
+ // window-state. We want this old Surface to stay on top of the new one
+ // until we do the swap, so we place it at layer 1.
+ mSurfaceController.mSurfaceControl.setLayer(1);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
try {
mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
mSurfaceController.setLayerStackInTransaction(getLayerStack());
- mSurfaceController.setLayer(mAnimLayer);
} finally {
mService.closeSurfaceTransaction("createSurfaceLocked");
}
mPendingDestroySurface = null;
}
- void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
- final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
- final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
-
- if (spec != null && !spec.isNop()) {
- float scale = spec.scale;
- transform.postScale(scale, scale);
- transform.postTranslate(spec.offsetX, spec.offsetY);
-
- // As we are scaling the whole surface, to keep the content
- // in the same position we will also have to scale the surfaceInsets.
- transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
- -(surfaceInsetTop*scale - surfaceInsetTop));
- }
- }
-
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
Transformation attachedTransformation =
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null) {
- applyMagnificationSpec(spec, tmpMatrix);
- }
-
// "convert" it into SurfaceFlinger's format
// (a 2x2 matrix + an offset)
// Here we must not transform the position of the surface
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null) {
- final Rect frame = mWin.mFrame;
- final float tmpFloats[] = mService.mTmpFloats;
- final Matrix tmpMatrix = mWin.mTmpMatrix;
-
- tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
- tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
-
- applyMagnificationSpec(spec, tmpMatrix);
-
- tmpMatrix.getValues(tmpFloats);
-
- mHaveMatrix = true;
- mDsDx = tmpFloats[Matrix.MSCALE_X];
- mDtDx = tmpFloats[Matrix.MSKEW_Y];
- mDtDy = tmpFloats[Matrix.MSKEW_X];
- mDsDy = tmpFloats[Matrix.MSCALE_Y];
- float x = tmpFloats[Matrix.MTRANS_X];
- float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set(Math.round(x), Math.round(y));
-
- mShownAlpha = mAlpha;
- } else {
- mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
- if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
- mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
- }
- mShownAlpha = mAlpha;
- mHaveMatrix = false;
- mDsDx = mWin.mGlobalScale;
- mDtDx = 0;
- mDtDy = 0;
- mDsDy = mWin.mGlobalScale;
- }
- }
-
- private MagnificationSpec getMagnificationSpec() {
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && mWin.getDisplayId() == DEFAULT_DISPLAY) {
- return mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
+ mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
+ if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+ mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
}
- return null;
+ mShownAlpha = mAlpha;
+ mHaveMatrix = false;
+ mDsDx = mWin.mGlobalScale;
+ mDtDx = 0;
+ mDtDy = 0;
+ mDsDy = mWin.mGlobalScale;
}
/**
w.expandForSurfaceInsets(finalClipRect);
}
- // We may be applying a magnification spec to all windows,
- // simulating a transformation in screen space, in which case
- // we need to transform all other screen space values...including
- // the final crop. This is kind of messed up and we should look
- // in to actually transforming screen-space via a parent-layer.
- // b/38322835
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null && !spec.isNop()) {
- Matrix transform = mWin.mTmpMatrix;
- RectF finalCrop = mService.mTmpRectF;
- transform.reset();
- transform.postScale(spec.scale, spec.scale);
- transform.postTranslate(-spec.offsetX, -spec.offsetY);
- transform.mapRect(finalCrop);
- finalClipRect.top = (int) finalCrop.top;
- finalClipRect.left = (int) finalCrop.left;
- finalClipRect.right = (int) finalCrop.right;
- finalClipRect.bottom = (int) finalCrop.bottom;
- }
-
return true;
}
mDtDy * w.mHScale * mExtraHScale,
mDsDy * w.mVScale * mExtraVScale,
recoveringMemory);
- mSurfaceController.setLayer(mAnimLayer);
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
final WindowStateAnimator mAnimator;
- private SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControlWithBackground mSurfaceControl;
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
- final SurfaceControl.Builder b = new SurfaceControl.Builder(s)
+ final SurfaceControl.Builder b = win.makeSurface()
+ .setParent(win.getSurfaceControl())
.setName(name)
.setSize(w, h)
.setFormat(format)
}
}
- void setLayer(int layer) {
- if (mSurfaceControl != null) {
- mService.openSurfaceTransaction();
- try {
- if (mAnimator.mWin.usesRelativeZOrdering()) {
- mSurfaceControl.setRelativeLayer(
- mAnimator.mWin.getParentWindow()
- .mWinAnimator.mSurfaceController.mSurfaceControl,
- -1);
- } else {
- mSurfaceLayer = layer;
- mSurfaceControl.setLayer(layer);
- }
- } finally {
- mService.closeSurfaceTransaction("setLayer");
- }
- }
- }
-
void setLayerStackInTransaction(int layerStack) {
if (mSurfaceControl != null) {
mSurfaceControl.setLayerStack(layerStack);
// Create a new surface for the thumbnail
WindowState window = appToken.findMainWindow();
- final SurfaceControl surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+ final SurfaceControl surfaceControl = appToken.makeSurface()
.setName("thumbnail anim")
.setSize(dirty.width(), dirty.height())
.setFormat(PixelFormat.TRANSLUCENT)
window != null ? window.mOwnerUid : Binder.getCallingUid())
.build();
- surfaceControl.setLayerStack(display.getLayerStack());
if (SHOW_TRANSACTIONS) {
Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE");
}
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
- openingAppAnimator.updateThumbnailLayer();
openingAppAnimator.thumbnail = surfaceControl;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+
+ // We parent the thumbnail to the app token, and just place it
+ // on top of anything else in the app token.
+ surfaceControl.setLayer(Integer.MAX_VALUE);
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
+ dirty.width() + " h=" + dirty.height(), e);
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
controller.removeStartingWindow();
waitUntilHandlersIdle();
assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+ controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+ mDisplayContent.onPendingTransactionApplied();
}
}
@RunWith(AndroidJUnit4.class)
public class AppWindowTokenTests extends WindowTestsBase {
+ TaskStack mStack;
+ Task mTask;
+ WindowTestUtils.TestAppWindowToken mToken;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mStack = createTaskStackOnDisplay(mDisplayContent);
+ mTask = createTaskInStack(mStack, 0 /* userId */);
+ mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+ mTask.addChild(mToken, 0);
+ }
+
@Test
@Presubmit
public void testAddWindow_Order() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-
- assertEquals(0, token.getWindowsCount());
+ assertEquals(0, mToken.getWindowsCount());
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
"startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+ final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+ final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
// Should not contain the windows that were added above.
- assertEquals(4, token.getWindowsCount());
- assertTrue(token.hasWindow(win1));
- assertTrue(token.hasWindow(startingWin));
- assertTrue(token.hasWindow(baseWin));
- assertTrue(token.hasWindow(win4));
+ assertEquals(4, mToken.getWindowsCount());
+ assertTrue(mToken.hasWindow(win1));
+ assertTrue(mToken.hasWindow(startingWin));
+ assertTrue(mToken.hasWindow(baseWin));
+ assertTrue(mToken.hasWindow(win4));
// The starting window should be on-top of all other windows.
- assertEquals(startingWin, token.getLastChild());
+ assertEquals(startingWin, mToken.getLastChild());
// The base application window should be below all other windows.
- assertEquals(baseWin, token.getFirstChild());
- token.removeImmediately();
+ assertEquals(baseWin, mToken.getFirstChild());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testFindMainWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertNull(mToken.findMainWindow());
- assertNull(token.findMainWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- assertEquals(window1, token.findMainWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+ final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+ assertEquals(window1, mToken.findMainWindow());
window1.mAnimatingExit = true;
- assertEquals(window1, token.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
- assertEquals(window2, token.findMainWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.findMainWindow());
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+ assertEquals(window2, mToken.findMainWindow());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testGetTopFullscreenWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-
- assertNull(token.getTopFullscreenWindow());
+ assertNull(mToken.getTopFullscreenWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
- assertEquals(window12, token.getTopFullscreenWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+ final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+ assertEquals(window12, mToken.getTopFullscreenWindow());
window12.mAttrs.width = 500;
- assertEquals(window11, token.getTopFullscreenWindow());
+ assertEquals(window11, mToken.getTopFullscreenWindow());
window11.mAttrs.width = 500;
- assertEquals(window1, token.getTopFullscreenWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.getTopFullscreenWindow());
+ mToken.removeImmediately();
}
@Test
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.resizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
- final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
performRotation(Surface.ROTATION_90);
@Test
@Presubmit
public void testGetOrientation() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- token.setFillsParent(false);
+ mToken.setFillsParent(false);
// Can specify orientation if app doesn't fill parent.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
- token.setFillsParent(true);
- token.hidden = true;
- token.sendingToBottom = true;
+ mToken.setFillsParent(true);
+ mToken.hidden = true;
+ mToken.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it fills parent.
- assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
- token.sendingToBottom = false;
- token.setIsOnTop(true);
- // Allow for token to provide orientation hidden if on top and not being sent to bottom.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ mToken.sendingToBottom = false;
+ mToken.setIsOnTop(true);
+ // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
}
@Test
@Presubmit
public void testKeyguardFlagsDuringRelaunch() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
// Add window with show when locked flag
- token.addWindow(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.addWindow(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Start relaunching
- token.startRelaunching();
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.startRelaunching();
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Remove window and make sure that we still report back flag
- token.removeChild(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.removeChild(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Finish relaunching and ensure flag is now not reported
- token.finishRelaunching();
- assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+ mToken.finishRelaunching();
+ assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
}
}
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
mPositioner = new TaskPositioner(sWm);
- mPositioner.register(display);
+ mPositioner.register(mDisplayContent);
}
/**
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- return false;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
return false;
}
+++ /dev/null
-/*
- * Copyright (C) 2016 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.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
- @Test
- public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
- sWm.mInputMethodTarget = null;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The Ime has an higher base layer than app windows and lower base layer than system
- // windows, so it should be above app windows and below system windows if there isn't an IME
- // target.
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows and below system windows if it is targeting an app
- // window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
- "imeAppTargetChildAboveWindow");
- final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
- "imeAppTargetChildBelowWindow");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for child windows that are z-ordered above it
- // and below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
- final WindowState appBelowImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState appAboveImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for non-fullscreen app window above it and
- // below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
- assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
- final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
- mDisplayContent, "imeSystemOverlayTarget",
- true /* ownerCanAddInternalSystemWindow */);
-
- sWm.mInputMethodTarget = imeSystemOverlayTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The IME target base layer is higher than all window except for the nav bar window, so the
- // IME should be above all windows except for the nav bar.
- assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testStackLayers() throws Exception {
- WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
- WindowState dockedStackWindow = createWindowOnStack(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "dockedStackWindow");
- WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
- mDisplayContent, "assistantStackWindow");
-
- mLayersController.assignWindowLayers(mDisplayContent);
-
- assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
- assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
- assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
- }
-
- private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
- throws Exception {
- assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
- }
-
-}
private static boolean sOneTimeSetupDone = false;
DisplayContent mDisplayContent;
DisplayInfo mDisplayInfo = new DisplayInfo();
- WindowLayersController mLayersController;
WindowState mWallpaperWindow;
WindowState mImeWindow;
WindowState mImeDialogWindow;
final Context context = InstrumentationRegistry.getTargetContext();
AttributeCache.init(context);
+
sWm = TestWindowManagerPolicy.getWindowManagerService(context);
- mLayersController = new WindowLayersController(sWm);
+ beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
waitUntilHandlersIdle();
}
+ void beforeCreateDisplay() {
+ // Called before display is created.
+ }
+
@After
public void tearDown() throws Exception {
final LinkedList<WindowState> nonCommonWindows = new LinkedList();
waitUntilHandlersIdle();
}
+ /**
+ * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+ * set-up (or null).
+ */
+ SurfaceBuilderFactory getSurfaceBuilderFactory() {
+ return null;
+ }
+
private WindowState createCommonWindow(WindowState parent, int type, String name) {
final WindowState win = createWindow(parent, type, name);
mCommonWindows.add(win);
Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
}
+ /** Asserts that the first entry is greater than the second entry. */
+ void assertLessThan(int first, int second) throws Exception {
+ Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+ }
+
/**
* Waits until the main handler for WM has processed all messages.
*/
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+ return new DisplayContent(display, sWm, new WallpaperController(sWm));
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
assertEquals(null, dc.getWindowToken(token.token));
}
- @Test
- public void testAdjustAnimLayer() throws Exception {
- final WindowTestUtils.TestWindowToken token =
- new WindowTestUtils.TestWindowToken(0, mDisplayContent);
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
- final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
- window2.mLayer = 100;
- window3.mLayer = 200;
-
- // We assign layers once, to get the base values computed by
- // the controller.
- mLayersController.assignWindowLayers(mDisplayContent);
-
- final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
- final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
- final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
- final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
- final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
- // Then we set an adjustment, and assign them again, they should
- // be offset.
- int adj = token.adj = 50;
- mLayersController.assignWindowLayers(mDisplayContent);
- final int highestLayer = token.getHighestAnimLayer();
-
- assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
- assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
- assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
- assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, highestLayer);
- }
-
/**
* Test that a window token isn't orphaned by the system when it is requested to be removed.
* Tokens should only be removed from the system when all their windows are gone.
--- /dev/null
+/*
+ * Copyright (C) 2016 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.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+ private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+ HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+ HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+ @Override
+ public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+ mRelativeLayersForControl.remove(sc);
+ mLayersForControl.put(sc, layer);
+ return super.setLayer(sc, layer);
+ }
+
+ @Override
+ public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+ SurfaceControl relativeTo,
+ int layer) {
+ mRelativeLayersForControl.put(sc, relativeTo);
+ mLayersForControl.put(sc, layer);
+ return super.setRelativeLayer(sc, relativeTo, layer);
+ }
+
+ int getLayer(SurfaceControl sc) {
+ return mLayersForControl.get(sc);
+ }
+
+ SurfaceControl getRelativeLayer(SurfaceControl sc) {
+ return mRelativeLayersForControl.get(sc);
+ }
+ };
+
+ // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+ // such that we can keep track of the parents of Surfaces as they are constructed.
+ private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+ private class HierarchyRecorder extends SurfaceControl.Builder {
+ SurfaceControl mPendingParent;
+
+ HierarchyRecorder(SurfaceSession s) {
+ super(s);
+ }
+
+ public SurfaceControl.Builder setParent(SurfaceControl sc) {
+ mPendingParent = sc;
+ return super.setParent(sc);
+ }
+ public SurfaceControl build() {
+ SurfaceControl sc = super.build();
+ mParentFor.put(sc, mPendingParent);
+ mPendingParent = null;
+ return sc;
+ }
+ };
+
+ class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new HierarchyRecorder(s);
+ }
+ };
+
+ private LayerRecordingTransaction mTransaction;
+
+ @Override
+ void beforeCreateDisplay() {
+ // We can't use @Before here because it may happen after WindowTestsBase @Before
+ // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+ // would miss construction of the top-level layers.
+ mTransaction = new LayerRecordingTransaction();
+ sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+ }
+
+ @After
+ public void after() {
+ mTransaction.close();
+ mParentFor.clear();
+ }
+
+ LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+ LinkedList<SurfaceControl> p = new LinkedList();
+ SurfaceControl current = sc;
+ do {
+ p.addLast(current);
+
+ SurfaceControl rs = t.getRelativeLayer(current);
+ if (rs != null) {
+ current = rs;
+ } else {
+ current = mParentFor.get(current);
+ }
+ } while (current != null);
+ return p;
+ }
+
+ void assertZOrderGreaterThan(LayerRecordingTransaction t,
+ SurfaceControl left, SurfaceControl right) throws Exception {
+ final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+ final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+ SurfaceControl commonAncestor = null;
+ SurfaceControl leftTop = leftParentChain.peekLast();
+ SurfaceControl rightTop = rightParentChain.peekLast();
+ while (leftTop != null && rightTop != null && leftTop == rightTop) {
+ commonAncestor = leftParentChain.removeLast();
+ rightParentChain.removeLast();
+ leftTop = leftParentChain.peekLast();
+ rightTop = rightParentChain.peekLast();
+ }
+
+ if (rightTop == null) { // right is the parent of left.
+ assertGreaterThan(t.getLayer(leftTop), 0);
+ } else if (leftTop == null) { // left is the parent of right.
+ assertGreaterThan(0, t.getLayer(rightTop));
+ } else {
+ assertGreaterThan(t.getLayer(leftTop),
+ t.getLayer(rightTop));
+ }
+ }
+
+ void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+ WindowState left, WindowState right) throws Exception {
+ assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+ sWm.mInputMethodTarget = null;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The Ime has an higher base layer than app windows and lower base layer than system
+ // windows, so it should be above app windows and below system windows if there isn't an IME
+ // target.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows and below system windows if it is targeting an app
+ // window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+ "imeAppTargetChildAboveWindow");
+ final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+ "imeAppTargetChildBelowWindow");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for child windows that are z-ordered above it
+ // and below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+ final WindowState appBelowImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState appAboveImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for non-fullscreen app window above it and
+ // below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+ assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+ final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+ mDisplayContent, "imeSystemOverlayTarget",
+ true /* ownerCanAddInternalSystemWindow */);
+
+ sWm.mInputMethodTarget = imeSystemOverlayTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The IME target base layer is higher than all window except for the nav bar window, so the
+ // IME should be above all windows except for the nav bar.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+ // The IME has a higher base layer than the status bar so we may expect it to go
+ // above the status bar once they are both in the Non-App layer, as past versions of this
+ // test enforced. However this seems like the wrong behavior unless the status bar is the
+ // IME target.
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+ sWm.mInputMethodTarget = mStatusBarWindow;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testStackLayers() throws Exception {
+ final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+ "pinnedStackWindow");
+ final WindowState dockedStackWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "dockedStackWindow");
+ final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+ assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+ }
+}