Fix shared dim layer not cleared when all users removed

For secondary display, shared fullscreen dim layer user is
the only stack that is present on the display. When this stack
is removed the shared dim layer was not cleared. Because of
that it was crashing when trying to obtain DisplayContent from it.

Now when we're removing a dim layer user we check if we've removed
the last one and clear shared fullscreen dim layer if needed.

Bug: 34367817
Test: bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
Change-Id: I415196a345c491eddd26a55370bb819ce6485151
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 2ec2dba..3a6e328 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -322,6 +322,9 @@
             }
             mState.remove(dimLayerUser);
         }
+        if (mState.isEmpty()) {
+            mSharedFullScreenDimLayer = null;
+        }
     }
 
     @VisibleForTesting
@@ -329,6 +332,11 @@
         return mState.containsKey(dimLayerUser);
     }
 
+    @VisibleForTesting
+    boolean hasSharedFullScreenDimLayer() {
+        return mSharedFullScreenDimLayer != null;
+    }
+
     void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
         applyDim(dimLayerUser, animator, false /* aboveApp */);
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3a74ded..679f178 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -627,6 +627,8 @@
     };
 
     /**
+     * Create new {@link DisplayContent} instance, add itself to the root window container and
+     * 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
@@ -661,6 +663,9 @@
         super.addChild(mTaskStackContainers, null);
         super.addChild(mAboveAppWindowsContainers, null);
         super.addChild(mImeWindowsContainers, null);
+
+        // Add itself as a child to the root container.
+        mService.mRoot.addChild(this, null);
     }
 
     int getDisplayId() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 22abf30..80e6655 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -213,7 +213,6 @@
         final int displayId = display.getDisplayId();
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
-        addChild(dc, null);
 
         final DisplayInfo displayInfo = dc.getDisplayInfo();
         final Rect rect = new Rect();
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
new file mode 100644
index 0000000..c3a471a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
@@ -0,0 +1,65 @@
+/*
+ * 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 static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link DimLayerController} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
+ */
+@SmallTest
+@Presubmit
+@org.junit.runner.RunWith(AndroidJUnit4.class)
+public class DimLayerControllerTests extends WindowTestsBase {
+
+    /**
+     * This tests if shared fullscreen dim layer is added when stack is added to display
+     * and is removed when the only stack on the display is removed.
+     */
+    @Test
+    public void testSharedFullScreenDimLayer() throws Exception {
+        // Create a display.
+        final DisplayContent dc = createNewDisplay();
+        assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
+
+        // Add stack with activity.
+        final TaskStack stack = createTaskStackOnDisplay(dc);
+        assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
+        assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
+
+        // Remove the only stack on the display and check if the shared dim layer clears.
+        stack.removeImmediately();
+        assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
+        assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index e54e319..30f99e5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -29,6 +29,7 @@
 
 import java.util.ArrayList;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
@@ -215,13 +216,8 @@
      */
     @Test
     public void testMoveStackBetweenDisplays() throws Exception {
-        // Create second display.
-        final Display display = new Display(DisplayManagerGlobal.getInstance(),
-                sDisplayContent.getDisplayId() + 1, new DisplayInfo(),
-                DEFAULT_DISPLAY_ADJUSTMENTS);
-        final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
-                new WallpaperController(sWm));
-        sWm.mRoot.addChild(dc, 1);
+        // Create a second display.
+        final DisplayContent dc = createNewDisplay();
 
         // Add stack with activity.
         final TaskStack stack = createTaskStackOnDisplay(dc);
@@ -261,10 +257,31 @@
 
         // Check that override config is applied.
         assertEquals(newOverrideConfig, sDisplayContent.getOverrideConfiguration());
+    }
+
+    /**
+     * This tests global configuration updates when default display config is updated.
+     */
+    @Test
+    public void testDefaultDisplayOverrideConfigUpdate() throws Exception {
+        final Configuration currentOverrideConfig = sDisplayContent.getOverrideConfiguration();
+
+        // Create new, slightly changed override configuration and apply it to the display.
+        final Configuration newOverrideConfig = new Configuration(currentOverrideConfig);
+        newOverrideConfig.densityDpi += 120;
+        newOverrideConfig.fontScale += 0.3;
+
+        sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, DEFAULT_DISPLAY);
 
         // Check that global configuration is updated, as we've updated default display's config.
-        final Configuration globalConfig = sWm.mRoot.getConfiguration();
+        Configuration globalConfig = sWm.mRoot.getConfiguration();
         assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
+
+        // Return back to original values.
+        sWm.setNewDisplayOverrideConfiguration(currentOverrideConfig, DEFAULT_DISPLAY);
+        globalConfig = sWm.mRoot.getConfiguration();
+        assertEquals(currentOverrideConfig.densityDpi, globalConfig.densityDpi);
+        assertEquals(currentOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
index 7a789d4..b0eba0b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -102,12 +102,7 @@
         task1.mOnDisplayChangedCalled = false;
 
         // Create second display and put second stack on it.
-        final Display display = new Display(DisplayManagerGlobal.getInstance(),
-                sDisplayContent.getDisplayId() + 1, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
-        final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
-                new WallpaperController(sWm));
-        sWm.mRoot.addChild(dc, 1);
-
+        final DisplayContent dc = createNewDisplay();
         final StackWindowController stack2Controller =
                 createStackControllerOnDisplay(dc);
         final TaskStack stack2 = stack2Controller.mContainer;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 3ee1da43..f79908e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -128,13 +128,10 @@
                 new TestTaskWindowContainerController(stack1Controller);
         final TestTask task1 = (TestTask) taskController.mContainer;
         task1.mOnDisplayChangedCalled = false;
+        assertEquals(sDisplayContent, stack1.getDisplayContent());
 
         // Create second display and put second stack on it.
-        final Display display = new Display(DisplayManagerGlobal.getInstance(),
-                sDisplayContent.getDisplayId() + 1, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
-        final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
-                new WallpaperController(sWm));
-        sWm.mRoot.addChild(dc, 1);
+        final DisplayContent dc = createNewDisplay();
         final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
         final TaskStack stack2 = stack2Controller.mContainer;
         final TestTaskWindowContainerController taskController2 =
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 72157b6..ca9dd9d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -20,7 +20,10 @@
 import android.app.ActivityManagerInternal;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.IApplicationToken;
 import org.junit.Assert;
 import org.junit.Before;
@@ -40,6 +43,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.EMPTY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -65,11 +69,13 @@
     static TestWindowManagerPolicy sPolicy = null;
     private final static IWindow sIWindow = new TestIWindow();
     private final static Session sMockSession = mock(Session.class);
+    private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1;
     static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
     private static int sNextTaskId = 0;
 
     private static boolean sOneTimeSetupDone = false;
     static DisplayContent sDisplayContent;
+    static DisplayInfo sDisplayInfo = new DisplayInfo();
     static WindowLayersController sLayersController;
     static WindowState sWallpaperWindow;
     static WindowState sImeWindow;
@@ -100,9 +106,15 @@
         if (sDisplayContent != null) {
             sDisplayContent.removeImmediately();
         }
-        sDisplayContent = new DisplayContent(context.getDisplay(), sWm, sLayersController,
-                new WallpaperController(sWm));
-        sWm.mRoot.addChild(sDisplayContent, 0);
+        // Make sure that display ids don't overlap, so there won't be several displays with same
+        // ids among RootWindowContainer children.
+        for (DisplayContent dc : sWm.mRoot.mChildren) {
+            if (dc.getDisplayId() >= sNextDisplayId) {
+                sNextDisplayId = dc.getDisplayId() + 1;
+            }
+        }
+        context.getDisplay().getDisplayInfo(sDisplayInfo);
+        sDisplayContent = createNewDisplay();
         sWm.mDisplayEnabled = true;
         sWm.mDisplayReady = true;
 
@@ -187,7 +199,7 @@
                 true /* onTop */, new Rect(), sWm);
     }
 
-    /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
+    /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
     static Task createTaskInStack(TaskStack stack, int userId) {
         final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
                 false, false, new TaskDescription(), null);
@@ -195,6 +207,14 @@
         return newTask;
     }
 
+    /** Creates a {@link DisplayContent} and adds it to the system. */
+    DisplayContent createNewDisplay() {
+        final int displayId = sNextDisplayId++;
+        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                sDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+        return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm));
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowToken} class */
     static class TestWindowToken extends WindowToken {