Allow stacks to hold tasks on various sizes.

Tasks are now resizeable just like stacks. Adjusting the size
of a stack will also adjust the size of all it's containing
tasks. This model allows us to be more flexible
in using stacks as workspaces (like you would have in a
desktop environment) and tasks as the resizeable windows
within the workspace.

Also added "adb shell dumpsys window visible-apps" for
getting the list of visible app windows.

Bug: 22068114
Bug: 19182363

Change-Id: I5bf77063252a5abae4e327f6fc42e607091749f9
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0c3cf65..09d70d8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,13 +17,29 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.WindowManagerService.TAG;
+import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
 
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
 import com.android.server.EventLogTags;
 
-class Task {
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class Task implements DimLayer.DimLayerUser {
+    /** Amount of time in milliseconds to animate the dim surface from one value to another,
+     * when no window animation is driving it. */
+    private static final int DEFAULT_DIM_DURATION = 200;
+
     TaskStack mStack;
     final AppTokenList mAppTokens = new AppTokenList();
     final int mTaskId;
@@ -31,11 +47,41 @@
     boolean mDeferRemoval = false;
     final WindowManagerService mService;
 
+    // Content limits relative to the DisplayContent this sits in.
+    private Rect mBounds = new Rect();
+
+    // Device rotation as of the last time {@link #mBounds} was set.
+    int mRotation;
+
+    // Whether mBounds is fullscreen
+    private boolean mFullscreen = true;
+
+    // Contains configurations settings that are different from the global configuration due to
+    // stack specific operations. E.g. {@link #setBounds}.
+    Configuration mOverrideConfig;
+
+    // For comparison with DisplayContent bounds.
+    private Rect mTmpRect = new Rect();
+    // For handling display rotations.
+    private Rect mTmpRect2 = new Rect();
+
+    // The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
+    WindowStateAnimator mDimWinAnimator;
+    // Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
+    private DimLayer mDimLayer;
+    // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
+    // then stop any dimming.
+    private boolean mContinueDimming;
+    // Shared dim layer for fullscreen tasks. {@link #mDimLayer} will point to this instead
+    // of creating a new object per fullscreen task on a display.
+    private static final SparseArray<DimLayer> sSharedFullscreenDimLayers = new SparseArray<>();
+
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service) {
         mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
         mService = service;
+        mOverrideConfig = Configuration.EMPTY;
     }
 
     DisplayContent getDisplayContent() {
@@ -107,13 +153,290 @@
         }
     }
 
+    /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
+    boolean setBounds(Rect bounds) {
+        boolean oldFullscreen = mFullscreen;
+        int rotation = Surface.ROTATION_0;
+        final DisplayContent displayContent = mStack.getDisplayContent();
+        if (displayContent != null) {
+            displayContent.getLogicalDisplayRect(mTmpRect);
+            rotation = displayContent.getDisplayInfo().rotation;
+            if (bounds == null) {
+                bounds = mTmpRect;
+                mFullscreen = true;
+            } else {
+                // ensure bounds are entirely within the display rect
+                if (!bounds.intersect(mTmpRect)) {
+                    // Can't set bounds outside the containing display...Sorry!
+                    return false;
+                }
+                mFullscreen = mTmpRect.equals(bounds);
+            }
+        }
+
+        if (bounds == null) {
+            // Can't set to fullscreen if we don't have a display to get bounds from...
+            return false;
+        }
+        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+            return false;
+        }
+
+        mBounds.set(bounds);
+        mRotation = rotation;
+        updateDimLayer();
+        updateOverrideConfiguration();
+        return true;
+    }
+
+    void getBounds(Rect out) {
+        out.set(mBounds);
+    }
+
+    private void updateOverrideConfiguration() {
+        final Configuration serviceConfig = mService.mCurConfiguration;
+        if (mFullscreen) {
+            mOverrideConfig = Configuration.EMPTY;
+            return;
+        }
+
+        if (mOverrideConfig == Configuration.EMPTY) {
+            mOverrideConfig  = new Configuration();
+        }
+
+        // TODO(multidisplay): Update Dp to that of display stack is on.
+        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        mOverrideConfig.screenWidthDp =
+                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
+        mOverrideConfig.screenHeightDp =
+                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
+        mOverrideConfig.smallestScreenWidthDp =
+                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
+        mOverrideConfig.orientation =
+                (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
+                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+    }
+
+    void updateDisplayInfo(final DisplayContent displayContent) {
+        if (displayContent == null) {
+            return;
+        }
+        if (mFullscreen) {
+            setBounds(null);
+            return;
+        }
+        final int newRotation = displayContent.getDisplayInfo().rotation;
+        if (mRotation == newRotation) {
+            return;
+        }
+
+        // Device rotation changed. We don't want the task to move around on the screen when
+        // this happens, so update the task bounds so it stays in the same place.
+        final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
+        displayContent.getLogicalDisplayRect(mTmpRect);
+        switch (rotationDelta) {
+            case Surface.ROTATION_0:
+                mTmpRect2.set(mBounds);
+                break;
+            case Surface.ROTATION_90:
+                mTmpRect2.top = mTmpRect.bottom - mBounds.right;
+                mTmpRect2.left = mBounds.top;
+                mTmpRect2.right = mTmpRect2.left + mBounds.height();
+                mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
+                break;
+            case Surface.ROTATION_180:
+                mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
+                mTmpRect2.left = mTmpRect.right - mBounds.right;
+                mTmpRect2.right = mTmpRect2.left + mBounds.width();
+                mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
+                break;
+            case Surface.ROTATION_270:
+                mTmpRect2.top = mBounds.left;
+                mTmpRect2.left = mTmpRect.right - mBounds.bottom;
+                mTmpRect2.right = mTmpRect2.left + mBounds.height();
+                mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
+                break;
+        }
+        setBounds(mTmpRect2);
+    }
+
+    /** Updates the dim layer bounds, recreating it if needed. */
+    private void updateDimLayer() {
+        DimLayer newDimLayer;
+        final boolean previousFullscreen =
+                mDimLayer != null && sSharedFullscreenDimLayers.indexOfValue(mDimLayer) > -1;
+        final int displayId = mStack.getDisplayContent().getDisplayId();
+        if (mFullscreen) {
+            if (previousFullscreen) {
+                // Nothing to do here...
+                return;
+            }
+            // Use shared fullscreen dim layer
+            newDimLayer = sSharedFullscreenDimLayers.get(displayId);
+            if (newDimLayer == null) {
+                if (mDimLayer != null) {
+                    // Re-purpose the previous dim layer.
+                    newDimLayer = mDimLayer;
+                } else {
+                    // Create new full screen dim layer.
+                    newDimLayer = new DimLayer(mService, this, displayId);
+                }
+                newDimLayer.setBounds(mBounds);
+                sSharedFullscreenDimLayers.put(displayId, newDimLayer);
+            } else if (mDimLayer != null) {
+                mDimLayer.destroySurface();
+            }
+        } else {
+            newDimLayer = (mDimLayer == null || previousFullscreen)
+                    ? new DimLayer(mService, this, displayId) : mDimLayer;
+            newDimLayer.setBounds(mBounds);
+        }
+        mDimLayer = newDimLayer;
+    }
+
+    boolean animateDimLayers() {
+        final int dimLayer;
+        final float dimAmount;
+        if (mDimWinAnimator == null) {
+            dimLayer = mDimLayer.getLayer();
+            dimAmount = 0;
+        } else {
+            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
+            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
+        }
+        final float targetAlpha = mDimLayer.getTargetAlpha();
+        if (targetAlpha != dimAmount) {
+            if (mDimWinAnimator == null) {
+                mDimLayer.hide(DEFAULT_DIM_DURATION);
+            } else {
+                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
+                        ? mDimWinAnimator.mAnimation.computeDurationHint()
+                        : DEFAULT_DIM_DURATION;
+                if (targetAlpha > dimAmount) {
+                    duration = getDimBehindFadeDuration(duration);
+                }
+                mDimLayer.show(dimLayer, dimAmount, duration);
+            }
+        } else if (mDimLayer.getLayer() != dimLayer) {
+            mDimLayer.setLayer(dimLayer);
+        }
+        if (mDimLayer.isAnimating()) {
+            if (!mService.okToDisplay()) {
+                // Jump to the end of the animation.
+                mDimLayer.show();
+            } else {
+                return mDimLayer.stepAnimation();
+            }
+        }
+        return false;
+    }
+
+    private long getDimBehindFadeDuration(long duration) {
+        TypedValue tv = new TypedValue();
+        mService.mContext.getResources().getValue(
+                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+        if (tv.type == TypedValue.TYPE_FRACTION) {
+            duration = (long)tv.getFraction(duration, duration);
+        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+            duration = tv.data;
+        }
+        return duration;
+    }
+
+    void clearContinueDimming() {
+        mContinueDimming = false;
+    }
+
+    void setContinueDimming() {
+        mContinueDimming = true;
+    }
+
+    boolean getContinueDimming() {
+        return mContinueDimming;
+    }
+
+    boolean isDimming() {
+        return mDimLayer.isDimming();
+    }
+
+    boolean isDimming(WindowStateAnimator winAnimator) {
+        return mDimWinAnimator == winAnimator && isDimming();
+    }
+
+    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
+        // Only set dim params on the highest dimmed layer.
+        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
+                || !mDimWinAnimator.mSurfaceShown
+                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+            mDimWinAnimator = newWinAnimator;
+            if (mDimWinAnimator.mWin.mAppToken == null
+                    && !mFullscreen && mStack.getDisplayContent() != null) {
+                // Dim should cover the entire screen for system windows.
+                mStack.getDisplayContent().getLogicalDisplayRect(mTmpRect);
+                mDimLayer.setBounds(mTmpRect);
+            }
+        }
+    }
+
+    void stopDimmingIfNeeded() {
+        if (!mContinueDimming && isDimming()) {
+            mDimWinAnimator = null;
+            mDimLayer.setBounds(mBounds);
+        }
+    }
+
+    void close() {
+        if (mDimLayer != null) {
+            mDimLayer.destroySurface();
+            mDimLayer = null;
+        }
+    }
+
+    void resizeWindows() {
+        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState win = windows.get(winNdx);
+                if (!resizingWindows.contains(win)) {
+                    if (DEBUG_RESIZE) Slog.d(TAG, "setBounds: Resizing " + win);
+                    resizingWindows.add(win);
+                }
+            }
+        }
+    }
+
     boolean showForAllUsers() {
         final int tokensCount = mAppTokens.size();
         return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
     }
 
     @Override
+    public boolean isFullscreen() {
+        return mFullscreen;
+    }
+
+    @Override
+    public DisplayInfo getDisplayInfo() {
+        return mStack.getDisplayContent().getDisplayInfo();
+    }
+
+    @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
     }
+
+    public void printTo(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("taskId="); pw.print(mTaskId);
+                pw.print(prefix); pw.print("appTokens="); pw.print(mAppTokens);
+                pw.print(prefix); pw.print("mdr="); pw.println(mDeferRemoval);
+        if (mDimLayer.isDimming()) {
+            pw.print(prefix); pw.println("mDimLayer:");
+            mDimLayer.printTo(prefix + " ", pw);
+            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
+        } else {
+            pw.println();
+        }
+    }
 }