Merge "Make mirroring automatic based on Windows on display." into jb-mr1-dev
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index ae958df..dc85d3f 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -58,7 +58,7 @@
  * </p><p>
  * Display adapters are only weakly coupled to the display manager service.
  * Display adapters communicate changes in display device state to the display manager
- * service asynchronously via a {@link DisplayAdapter.DisplayAdapterListener} registered
+ * service asynchronously via a {@link DisplayAdapter.Listener} registered
  * by the display manager service.  This separation of concerns is important for
  * two main reasons.  First, it neatly encapsulates the responsibilities of these
  * two classes: display adapters handle individual display devices whereas
@@ -254,8 +254,8 @@
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
-     * @param The logical display info, or null if the display does not exist.
-     * This object must be treated as immutable.
+     * @return The logical display info, or null if the display does not exist.  The
+     * returned object must be treated as immutable.
      */
     @Override // Binder call
     public DisplayInfo getDisplayInfo(int displayId) {
@@ -481,14 +481,34 @@
         }
     }
 
-    private void configureDisplayInTransactionLocked(DisplayDevice device) {
-        // TODO: add a proper per-display mirroring control
-        boolean isMirroring = SystemProperties.getBoolean("debug.display.mirror", true);
+    /**
+     * Tells the display manager whether there is interesting unique content on the
+     * specified logical display.  This is used to control automatic mirroring.
+     * <p>
+     * If the display has unique content, then the display manager arranges for it
+     * to be presented on a physical display if appropriate.  Otherwise, the display manager
+     * may choose to make the physical display mirror some other logical display.
+     * </p>
+     *
+     * @param displayId The logical display id to update.
+     * @param hasContent True if the logical display has content.
+     */
+    public void setDisplayHasContent(int displayId, boolean hasContent) {
+        synchronized (mSyncRoot) {
+            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            if (display != null && display.hasContentLocked() != hasContent) {
+                display.setHasContentLocked(hasContent);
+                scheduleTraversalLocked();
+            }
+        }
 
+    }
+
+    private void configureDisplayInTransactionLocked(DisplayDevice device) {
         // Find the logical display that the display device is showing.
-        LogicalDisplay display = null;
-        if (!isMirroring) {
-            display = findLogicalDisplayForDeviceLocked(device);
+        LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+        if (display != null && !display.hasContentLocked()) {
+            display = null;
         }
         if (display == null) {
             display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
@@ -611,8 +631,9 @@
      */
     public interface WindowManagerFuncs {
         /**
-         * Request that the window manager call {@link #performTraversalInTransaction}
-         * within a surface transaction at a later time.
+         * Request that the window manager call
+         * {@link #performTraversalInTransactionFromWindowManager} within a surface
+         * transaction at a later time.
          */
         void requestTraversal();
     }
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index c864189..e0f63dd 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -63,6 +63,9 @@
     private DisplayDevice mPrimaryDisplayDevice;
     private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
 
+    // True if the logical display has unique content.
+    private boolean mHasContent;
+
     // Temporary rectangle used when needed.
     private final Rect mTempLayerStackRect = new Rect();
     private final Rect mTempDisplayRect = new Rect();
@@ -126,7 +129,7 @@
 
     /**
      * Returns true if the logical display is in a valid state.
-     * This method should be checked after calling {@link #update} to handle the
+     * This method should be checked after calling {@link #updateLocked} to handle the
      * case where a logical display should be removed because all of its associated
      * display devices are gone or if it is otherwise no longer needed.
      *
@@ -256,6 +259,29 @@
         device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
     }
 
+    /**
+     * Returns true if the logical display has unique content.
+     * <p>
+     * If the display has unique content then we will try to ensure that it is
+     * visible on at least its primary display device.  Otherwise we will ignore the
+     * logical display and perhaps show mirrored content on the primary display device.
+     * </p>
+     *
+     * @return True if the display has unique content.
+     */
+    public boolean hasContentLocked() {
+        return mHasContent;
+    }
+
+    /**
+     * Sets whether the logical display has unique content.
+     *
+     * @param hasContent True if the display has unique content.
+     */
+    public void setHasContentLocked(boolean hasContent) {
+        mHasContent = hasContent;
+    }
+
     public void dumpLocked(PrintWriter pw) {
         pw.println("mLayerStack=" + mLayerStack);
         pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 0ea051f..4df692b 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -94,10 +94,13 @@
     }
 
     DisplayInfo getDisplayInfo() {
-        // TODO: Add a listener for changes to Display and update mDisplayInfo when appropriate.
         return mDisplayInfo;
     }
 
+    public void updateDisplayInfo() {
+        mDisplay.getDisplayInfo(mDisplayInfo);
+    }
+
     public void dump(PrintWriter pw) {
         pw.print("  Display: mDisplayId="); pw.println(mDisplayId);
         pw.print("  init="); pw.print(mInitialDisplayWidth); pw.print("x");
@@ -121,7 +124,7 @@
         pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
         pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
         pw.print("  layoutNeeded="); pw.println(layoutNeeded);
-        pw.print("magnificationSpec="); pw.println(mMagnificationSpec.toString());
+        pw.print("magnificationSpec="); pw.println(mMagnificationSpec);
         pw.println();
     }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index a668efb..18e793d 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -165,7 +165,7 @@
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
-                DisplayManagerService.WindowManagerFuncs {
+                DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener {
     static final String TAG = "WindowManager";
     static final boolean DEBUG = false;
     static final boolean DEBUG_ADD_REMOVE = false;
@@ -782,9 +782,15 @@
         mLimitedAlphaCompositing = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_sf_limitedAlpha);
         mDisplayManagerService = displayManager;
-        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mHeadless = displayManager.isHeadless();
 
+        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(this, null);
+        Display[] displays = mDisplayManager.getDisplays();
+        for (Display display : displays) {
+            createDisplayContent(display);
+        }
+
         mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
 
         mPowerManager = pm;
@@ -1120,6 +1126,10 @@
         if (win.mAppToken != null && addToToken) {
             win.mAppToken.allAppWindows.add(win);
         }
+
+        if (windows.size() == 1) {
+            mDisplayManagerService.setDisplayHasContent(win.getDisplayId(), true);
+        }
     }
 
     /** TODO(cmautner): Is this the same as {@link WindowState#canReceiveKeys()} */
@@ -2408,6 +2418,9 @@
 
         final WindowList windows = win.getWindowList();
         windows.remove(win);
+        if (windows.isEmpty()) {
+            mDisplayManagerService.setDisplayHasContent(win.getDisplayId(), false);
+        }
         mPendingRemove.remove(win);
         mWindowsChanged = true;
         if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
@@ -7194,6 +7207,10 @@
         public static final int NOTIFY_WINDOW_TRANSITION = 29;
         public static final int NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 30;
 
+        public static final int DO_DISPLAY_ADDED = 31;
+        public static final int DO_DISPLAY_REMOVED = 32;
+        public static final int DO_DISPLAY_CHANGED = 33;
+
         public static final int ANIMATOR_WHAT_OFFSET = 100000;
         public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
         public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 2;
@@ -7631,18 +7648,21 @@
                     }
                     break;
                 }
+
                 case NOTIFY_ROTATION_CHANGED: {
                     final int displayId = msg.arg1;
                     final int rotation = msg.arg2;
                     handleNotifyRotationChanged(displayId, rotation);
                     break;
                 }
+
                 case NOTIFY_WINDOW_TRANSITION: {
                     final int transition = msg.arg1;
                     WindowInfo info = (WindowInfo) msg.obj;
                     handleNotifyWindowTranstion(transition, info);
                     break;
                 }
+
                 case NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
                     final int displayId = msg.arg1;
                     final boolean immediate = (msg.arg2 == 1);
@@ -7650,6 +7670,24 @@
                     handleNotifyRectangleOnScreenRequested(displayId, rectangle, immediate);
                     break;
                 }
+
+                case DO_DISPLAY_ADDED:
+                    synchronized (mWindowMap) {
+                        handleDisplayAddedLocked(msg.arg1);
+                    }
+                    break;
+
+                case DO_DISPLAY_REMOVED:
+                    synchronized (mWindowMap) {
+                        handleDisplayRemovedLocked(msg.arg1);
+                    }
+                    break;
+
+                case DO_DISPLAY_CHANGED:
+                    synchronized (mWindowMap) {
+                        handleDisplayChangedLocked(msg.arg1);
+                    }
+                    break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -8866,8 +8904,6 @@
         final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
         final int defaultDw = defaultInfo.logicalWidth;
         final int defaultDh = defaultInfo.logicalHeight;
-        final int defaultInnerDw = defaultInfo.appWidth;
-        final int defaultInnerDh = defaultInfo.appHeight;
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
@@ -9441,6 +9477,7 @@
         }
     }
 
+    @Override
     public void requestTraversal() {
         synchronized (mWindowMap) {
             requestTraversalLocked();
@@ -10465,7 +10502,7 @@
 
     boolean dumpWindows(PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll) {
-        ArrayList<WindowState> windows = new ArrayList<WindowState>();
+        WindowList windows = new WindowList();
         if ("visible".equals(name)) {
             synchronized(mWindowMap) {
                 final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
@@ -10673,6 +10710,14 @@
         }
     }
 
+    public void createDisplayContent(final Display display) {
+        if (display == null) {
+            throw new IllegalArgumentException("getDisplayContent: display must not be null");
+        }
+        final DisplayContent displayContent = new DisplayContent(display);
+        mDisplayContents.put(display.getDisplayId(), displayContent);
+    }
+
     public DisplayContent getDisplayContent(final int displayId) {
         DisplayContent displayContent = mDisplayContents.get(displayId);
         if (displayContent == null) {
@@ -10780,4 +10825,40 @@
     public WindowList getWindowList(final Display display) {
         return getDisplayContent(display.getDisplayId()).getWindowList();
     }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
+    }
+
+    private void handleDisplayAddedLocked(int displayId) {
+        createDisplayContent(mDisplayManager.getDisplay(displayId));
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_REMOVED, displayId, 0));
+    }
+
+    private void handleDisplayRemovedLocked(int displayId) {
+        final DisplayContent displayContent = getDisplayContent(displayId);
+        mDisplayContents.delete(displayId);
+        WindowList windows = displayContent.getWindowList();
+        for (int i = windows.size() - 1; i >= 0; --i) {
+            final WindowState win = windows.get(i);
+            removeWindowLocked(win.mSession, win);
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_CHANGED, displayId, 0));
+    }
+
+    private void handleDisplayChangedLocked(int displayId) {
+        final DisplayContent displayContent = getDisplayContent(displayId);
+        if (displayContent != null) {
+            displayContent.updateDisplayInfo();
+        }
+    }
 }