Blank or unblank all displays as need.

Ensures that both the internal display and HDMI are blanked
or unblanked in tandem.

Bug: 7309812
Change-Id: Ie8b96d393e8bb20d23c92f3320142d9f7cf42aff
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index f5aa3d4..a3ab3c1 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -105,6 +105,18 @@
     }
 
     /**
+     * Blanks the display, if supported.
+     */
+    public void blankLocked() {
+    }
+
+    /**
+     * Unblanks the display, if supported.
+     */
+    public void unblankLocked() {
+    }
+
+    /**
      * Sets the display layer stack while in a transaction.
      */
     public final void setLayerStackInTransactionLocked(int layerStack) {
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index b8c6cd5..0a42528 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -103,6 +103,10 @@
     private static final int MSG_REQUEST_TRAVERSAL = 4;
     private static final int MSG_UPDATE_VIEWPORT = 5;
 
+    private static final int DISPLAY_BLANK_STATE_UNKNOWN = 0;
+    private static final int DISPLAY_BLANK_STATE_BLANKED = 1;
+    private static final int DISPLAY_BLANK_STATE_UNBLANKED = 2;
+
     private final Context mContext;
     private final boolean mHeadless;
     private final DisplayManagerHandler mHandler;
@@ -141,6 +145,9 @@
             new SparseArray<LogicalDisplay>();
     private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
 
+    // Set to true if all displays have been blanked by the power manager.
+    private int mAllDisplayBlankStateFromPowerManager;
+
     // Set to true when there are pending display changes that have yet to be applied
     // to the surface flinger state.
     private boolean mPendingTraversal;
@@ -286,6 +293,40 @@
     }
 
     /**
+     * Called by the power manager to blank all displays.
+     */
+    public void blankAllDisplaysFromPowerManager() {
+        synchronized (mSyncRoot) {
+            if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) {
+                mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED;
+
+                final int count = mDisplayDevices.size();
+                for (int i = 0; i < count; i++) {
+                    DisplayDevice device = mDisplayDevices.get(i);
+                    device.blankLocked();
+                }
+            }
+        }
+    }
+
+    /**
+     * Called by the power manager to unblank all displays.
+     */
+    public void unblankAllDisplaysFromPowerManager() {
+        synchronized (mSyncRoot) {
+            if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) {
+                mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED;
+
+                final int count = mDisplayDevices.size();
+                for (int i = 0; i < count; i++) {
+                    DisplayDevice device = mDisplayDevices.get(i);
+                    device.unblankLocked();
+                }
+            }
+        }
+    }
+
+    /**
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
@@ -528,6 +569,17 @@
             mDisplayDevices.add(device);
             addLogicalDisplayLocked(device);
             scheduleTraversalLocked(false);
+
+            // Blank or unblank the display immediately to match the state requested
+            // by the power manager (if known).
+            switch (mAllDisplayBlankStateFromPowerManager) {
+                case DISPLAY_BLANK_STATE_BLANKED:
+                    device.blankLocked();
+                    break;
+                case DISPLAY_BLANK_STATE_UNBLANKED:
+                    device.unblankLocked();
+                    break;
+            }
         }
     }
 
@@ -788,9 +840,18 @@
         }
 
         pw.println("DISPLAY MANAGER (dumpsys display)");
-        pw.println("  mHeadless=" + mHeadless);
 
         synchronized (mSyncRoot) {
+            pw.println("  mHeadless=" + mHeadless);
+            pw.println("  mOnlyCode=" + mOnlyCore);
+            pw.println("  mSafeMode=" + mSafeMode);
+            pw.println("  mPendingTraversal=" + mPendingTraversal);
+            pw.println("  mAllDisplayBlankStateFromPowerManager="
+                    + mAllDisplayBlankStateFromPowerManager);
+            pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
+            pw.println("  mDefaultViewport=" + mDefaultViewport);
+            pw.println("  mExternalTouchViewport=" + mExternalTouchViewport);
+
             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
             ipw.increaseIndent();
 
@@ -817,10 +878,6 @@
                 pw.println("  Display " + displayId + ":");
                 display.dumpLocked(ipw);
             }
-
-            pw.println();
-            pw.println("Default viewport: " + mDefaultViewport);
-            pw.println("External touch viewport: " + mExternalTouchViewport);
         }
     }
 
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index 679a67e..d780006 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -92,6 +92,7 @@
 
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
+        private boolean mBlanked;
 
         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
                 PhysicalDisplayInfo phys) {
@@ -150,10 +151,23 @@
         }
 
         @Override
+        public void blankLocked() {
+            mBlanked = true;
+            Surface.blankDisplay(getDisplayTokenLocked());
+        }
+
+        @Override
+        public void unblankLocked() {
+            mBlanked = false;
+            Surface.unblankDisplay(getDisplayTokenLocked());
+        }
+
+        @Override
         public void dumpLocked(PrintWriter pw) {
             super.dumpLocked(pw);
             pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
             pw.println("mPhys=" + mPhys);
+            pw.println("mBlanked=" + mBlanked);
         }
     }
 
diff --git a/services/java/com/android/server/power/DisplayBlanker.java b/services/java/com/android/server/power/DisplayBlanker.java
new file mode 100644
index 0000000..6072053
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayBlanker.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 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.power;
+
+/**
+ * Blanks or unblanks all displays.
+ */
+interface DisplayBlanker {
+    public void blankAllDisplays();
+    public void unblankAllDisplays();
+}
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 82c3617..6a57372 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -159,6 +159,9 @@
     // A suspend blocker.
     private final SuspendBlocker mSuspendBlocker;
 
+    // The display blanker.
+    private final DisplayBlanker mDisplayBlanker;
+
     // Our handler.
     private final DisplayControllerHandler mHandler;
 
@@ -343,10 +346,12 @@
      */
     public DisplayPowerController(Looper looper, Context context, Notifier notifier,
             LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker,
+            DisplayBlanker displayBlanker,
             Callbacks callbacks, Handler callbackHandler) {
         mHandler = new DisplayControllerHandler(looper);
         mNotifier = notifier;
         mSuspendBlocker = suspendBlocker;
+        mDisplayBlanker = displayBlanker;
         mCallbacks = callbacks;
         mCallbackHandler = callbackHandler;
 
@@ -520,7 +525,8 @@
                 new ElectronBeam(display),
                 new PhotonicModulator(executor,
                         mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
-                        mSuspendBlocker));
+                        mSuspendBlocker),
+                mDisplayBlanker);
 
         mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
                 mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
index f6ce7a7..fdfcacc 100644
--- a/services/java/com/android/server/power/DisplayPowerState.java
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -51,7 +51,8 @@
 
     private final Choreographer mChoreographer;
     private final ElectronBeam mElectronBeam;
-    private final PhotonicModulator mScreenBrightnessModulator;
+    private final PhotonicModulator mPhotonicModulator;
+    private final DisplayBlanker mDisplayBlanker;
 
     private int mDirty;
     private boolean mScreenOn;
@@ -61,10 +62,11 @@
     private Runnable mCleanListener;
 
     public DisplayPowerState(ElectronBeam electronBean,
-            PhotonicModulator screenBrightnessModulator) {
+            PhotonicModulator photonicModulator, DisplayBlanker displayBlanker) {
         mChoreographer = Choreographer.getInstance();
         mElectronBeam = electronBean;
-        mScreenBrightnessModulator = screenBrightnessModulator;
+        mPhotonicModulator = photonicModulator;
+        mDisplayBlanker = displayBlanker;
 
         // At boot time, we know that the screen is on and the electron beam
         // animation is not playing.  We don't know the screen's brightness though,
@@ -238,8 +240,8 @@
     private void apply() {
         if (mDirty != 0) {
             if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
-                mScreenBrightnessModulator.setBrightness(0, true /*sync*/);
-                PowerManagerService.nativeSetScreenState(false);
+                mPhotonicModulator.setBrightness(0, true /*sync*/);
+                mDisplayBlanker.blankAllDisplays();
             }
 
             if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
@@ -247,12 +249,12 @@
             }
 
             if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
-                PowerManagerService.nativeSetScreenState(true);
+                mDisplayBlanker.unblankAllDisplays();
             }
 
             if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0
                     && mScreenOn) {
-                mScreenBrightnessModulator.setBrightness(
+                mPhotonicModulator.setBrightness(
                         (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/);
             }
 
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index abbae5b..85bf17a 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -155,6 +155,7 @@
     private Context mContext;
     private LightsService mLightsService;
     private BatteryService mBatteryService;
+    private DisplayManagerService mDisplayManagerService;
     private IBatteryStats mBatteryStats;
     private HandlerThread mHandlerThread;
     private PowerManagerHandler mHandler;
@@ -230,6 +231,9 @@
     // screen is coming up.
     private final ScreenOnBlockerImpl mScreenOnBlocker;
 
+    // The display blanker used to turn the screen on or off.
+    private final DisplayBlankerImpl mDisplayBlanker;
+
     // True if systemReady() has been called.
     private boolean mSystemReady;
 
@@ -319,14 +323,15 @@
     private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
     private static native void nativeAcquireSuspendBlocker(String name);
     private static native void nativeReleaseSuspendBlocker(String name);
-
-    static native void nativeSetScreenState(boolean on);
+    private static native void nativeSetInteractive(boolean enable);
+    private static native void nativeSetAutoSuspend(boolean enable);
 
     public PowerManagerService() {
         synchronized (mLock) {
             mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
             mWakeLockSuspendBlocker.acquire();
             mScreenOnBlocker = new ScreenOnBlockerImpl();
+            mDisplayBlanker = new DisplayBlankerImpl();
             mHoldingWakeLockSuspendBlocker = true;
             mWakefulness = WAKEFULNESS_AWAKE;
         }
@@ -342,23 +347,24 @@
     public void init(Context context, LightsService ls,
             ActivityManagerService am, BatteryService bs, IBatteryStats bss,
             DisplayManagerService dm) {
+        mContext = context;
+        mLightsService = ls;
+        mBatteryService = bs;
+        mBatteryStats = bss;
+        mDisplayManagerService = dm;
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
+        Watchdog.getInstance().addMonitor(this);
+
         // Forcibly turn the screen on at boot so that it is in a known power state.
         // We do this in init() rather than in the constructor because setting the
         // screen state requires a call into surface flinger which then needs to call back
         // into the activity manager to check permissions.  Unfortunately the
         // activity manager is not running when the constructor is called, so we
         // have to defer setting the screen state until this point.
-        nativeSetScreenState(true);
-
-        mContext = context;
-        mLightsService = ls;
-        mBatteryService = bs;
-        mBatteryStats = bss;
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
-
-        Watchdog.getInstance().addMonitor(this);
+        mDisplayBlanker.unblankAllDisplays();
     }
 
     public void setPolicy(WindowManagerPolicy policy) {
@@ -388,7 +394,7 @@
             mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
                     mContext, mNotifier, mLightsService, twilight,
                     createSuspendBlockerLocked("PowerManagerService.Display"),
-                    mDisplayPowerControllerCallbacks, mHandler);
+                    mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);
 
             mSettingsObserver = new SettingsObserver(mHandler);
             mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
@@ -2106,6 +2112,9 @@
             pw.println();
             pw.println("Screen On Blocker: " + mScreenOnBlocker);
 
+            pw.println();
+            pw.println("Display Blanker: " + mDisplayBlanker);
+
             dpc = mDisplayPowerController;
         }
 
@@ -2397,5 +2406,36 @@
                 return "held=" + (mNestCount != 0) + ", mNestCount=" + mNestCount;
             }
         }
-    };
+    }
+
+    private final class DisplayBlankerImpl implements DisplayBlanker {
+        private boolean mBlanked;
+
+        @Override
+        public void blankAllDisplays() {
+            synchronized (this) {
+                mBlanked = true;
+                mDisplayManagerService.blankAllDisplaysFromPowerManager();
+                nativeSetInteractive(false);
+                nativeSetAutoSuspend(true);
+            }
+        }
+
+        @Override
+        public void unblankAllDisplays() {
+            synchronized (this) {
+                nativeSetAutoSuspend(false);
+                nativeSetInteractive(true);
+                mDisplayManagerService.unblankAllDisplaysFromPowerManager();
+                mBlanked = false;
+            }
+        }
+
+        @Override
+        public String toString() {
+            synchronized (this) {
+                return "blanked=" + mBlanked;
+            }
+        }
+    }
 }
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index dcc2b58..75f77b9 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -26,7 +26,6 @@
 #include <limits.h>
 
 #include <android_runtime/AndroidRuntime.h>
-#include <gui/ISurfaceComposer.h>
 #include <utils/Timers.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
@@ -36,8 +35,6 @@
 #include <cutils/android_reboot.h>
 #include <suspend/autosuspend.h>
 
-#include <private/gui/ComposerService.h>
-
 #include "com_android_server_power_PowerManagerService.h"
 
 namespace android {
@@ -170,40 +167,23 @@
     release_wake_lock(name.c_str());
 }
 
-static void nativeSetScreenState(JNIEnv *env, jclass clazz, jboolean on) {
-    sp<ISurfaceComposer> s(ComposerService::getComposerService());
-    if (on) {
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
-            autosuspend_disable();
-        }
-
-        if (gPowerModule) {
-            ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
-            gPowerModule->setInteractive(gPowerModule, true);
-        }
-
-        const sp<IBinder>& display = s->getBuiltInDisplay(0);   // TODO: support multiple displays
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in unblank() while turning screen on");
-            s->unblank(display);
-        }
+static void nativeSetInteractive(JNIEnv *env, jclass clazz, jboolean enable) {
+    if (enable) {
+        ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
+        gPowerModule->setInteractive(gPowerModule, true);
     } else {
-        const sp<IBinder>& display = s->getBuiltInDisplay(0);   // TODO: support multiple displays
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in blank() while turning screen off");
-            s->blank(display);
-        }
+        ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
+        gPowerModule->setInteractive(gPowerModule, false);
+    }
+}
 
-        if (gPowerModule) {
-            ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
-            gPowerModule->setInteractive(gPowerModule, false);
-        }
-
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
-            autosuspend_enable();
-        }
+static void nativeSetAutoSuspend(JNIEnv *env, jclass clazz, jboolean enable) {
+    if (enable) {
+        ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
+        autosuspend_enable();
+    } else {
+        ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
+        autosuspend_disable();
     }
 }
 
@@ -235,8 +215,10 @@
             (void*) nativeAcquireSuspendBlocker },
     { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
             (void*) nativeReleaseSuspendBlocker },
-    { "nativeSetScreenState", "(Z)V",
-            (void*) nativeSetScreenState },
+    { "nativeSetInteractive", "(Z)V",
+            (void*) nativeSetInteractive },
+    { "nativeSetAutoSuspend", "(Z)V",
+            (void*) nativeSetAutoSuspend },
     { "nativeShutdown", "()V",
             (void*) nativeShutdown },
     { "nativeReboot", "(Ljava/lang/String;)V",