Merge "Hold a wake lock while dozing when display updates are pending." into lmp-mr1-modular-dev
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d52dd30..e303f61 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -205,6 +205,20 @@
     public static final int DOZE_WAKE_LOCK = 0x00000040;
 
     /**
+     * Wake lock level: Keep the device awake enough to allow drawing to occur.
+     * <p>
+     * This is used by the window manager to allow applications to draw while the
+     * system is dozing.  It currently has no effect unless the power manager is in
+     * the dozing state.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * {@hide}
+     */
+    public static final int DRAW_WAKE_LOCK = 0x00000080;
+
+    /**
      * Mask for the wake lock level component of a combined wake lock level and flags integer.
      *
      * @hide
@@ -489,6 +503,7 @@
             case FULL_WAKE_LOCK:
             case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
             case DOZE_WAKE_LOCK:
+            case DRAW_WAKE_LOCK:
                 break;
             default:
                 throw new IllegalArgumentException("Must specify a valid wake lock level.");
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7b13e84..63e1a85 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -197,4 +197,19 @@
     void onRectangleOnScreenRequested(IBinder token, in Rect rectangle);
 
     IWindowId getWindowId(IBinder window);
+
+    /**
+     * When the system is dozing in a low-power partially suspended state, pokes a short
+     * lived wake lock and ensures that the display is ready to accept the next frame
+     * of content drawn in the window.
+     *
+     * This mechanism is bound to the window rather than to the display manager or the
+     * power manager so that the system can ensure that the window is actually visible
+     * and prevent runaway applications from draining the battery.  This is similar to how
+     * FLAG_KEEP_SCREEN_ON works.
+     *
+     * This method is synchronous because it may need to acquire a wake lock before returning.
+     * The assumption is that this method will be called rather infrequently.
+     */
+    void pokeDrawLock(IBinder window);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e4d82b1..90c2bd1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -47,6 +47,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -831,6 +832,7 @@
                 final int newDisplayState = mDisplay.getState();
                 if (oldDisplayState != newDisplayState) {
                     mAttachInfo.mDisplayState = newDisplayState;
+                    pokeDrawLockIfNeeded();
                     if (oldDisplayState != Display.STATE_UNKNOWN) {
                         final int oldScreenState = toViewScreenState(oldDisplayState);
                         final int newScreenState = toViewScreenState(newDisplayState);
@@ -861,6 +863,19 @@
         }
     };
 
+    void pokeDrawLockIfNeeded() {
+        final int displayState = mAttachInfo.mDisplayState;
+        if (mView != null && mAdded && mTraversalScheduled
+                && (displayState == Display.STATE_DOZE
+                        || displayState == Display.STATE_DOZE_SUSPEND)) {
+            try {
+                mWindowSession.pokeDrawLock(mWindow);
+            } catch (RemoteException ex) {
+                // System server died, oh well.
+            }
+        }
+    }
+
     @Override
     public void requestFitSystemWindows() {
         checkThread();
@@ -1035,6 +1050,7 @@
                 scheduleConsumeBatchedInput();
             }
             notifyRendererOfFramePending();
+            pokeDrawLockIfNeeded();
         }
     }
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0d8d725..17b87d6 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1844,6 +1844,12 @@
         <item>users</item>
     </string-array>
 
+    <!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
+         flushed to the display while dozing.  This value needs to be large enough
+         to account for processing and rendering time plus a frame or two of latency
+         in the display pipeline plus some slack just to be sure. -->
+    <integer name="config_drawLockTimeoutMillis">120</integer>
+
     <!-- default telephony hardware configuration for this platform.
     -->
     <!-- this string array should be overridden by the device to present a list
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 35802c9..390661c 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -345,6 +345,7 @@
   <java-symbol type="integer" name="config_bluetooth_max_advertisers" />
   <java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
+  <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
   <java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
   <java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
   <java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9786b42..66c2f5f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -128,6 +128,7 @@
     private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
     private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake
     private static final int WAKE_LOCK_DOZE = 1 << 6;
+    private static final int WAKE_LOCK_DRAW = 1 << 7;
 
     // Summarizes the user activity state.
     private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
@@ -1398,12 +1399,15 @@
                     case PowerManager.DOZE_WAKE_LOCK:
                         mWakeLockSummary |= WAKE_LOCK_DOZE;
                         break;
+                    case PowerManager.DRAW_WAKE_LOCK:
+                        mWakeLockSummary |= WAKE_LOCK_DRAW;
+                        break;
                 }
             }
 
             // Cancel wake locks that make no sense based on the current state.
             if (mWakefulness != WAKEFULNESS_DOZING) {
-                mWakeLockSummary &= ~WAKE_LOCK_DOZE;
+                mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
             }
             if (mWakefulness == WAKEFULNESS_ASLEEP
                     || (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
@@ -1422,6 +1426,9 @@
                     mWakeLockSummary |= WAKE_LOCK_CPU;
                 }
             }
+            if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
+                mWakeLockSummary |= WAKE_LOCK_CPU;
+            }
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
@@ -1845,6 +1852,10 @@
 
             if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
                 mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+                if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
+                        && (mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
+                    mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+                }
                 mDisplayPowerRequest.dozeScreenBrightness =
                         mDozeScreenBrightnessOverrideFromDreamManager;
             } else {
@@ -2712,6 +2723,8 @@
                     return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
                 case PowerManager.DOZE_WAKE_LOCK:
                     return "DOZE_WAKE_LOCK                ";
+                case PowerManager.DRAW_WAKE_LOCK:
+                    return "DRAW_WAKE_LOCK                ";
                 default:
                     return "???                           ";
             }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a4dfd8a..a1d145c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -475,6 +475,16 @@
         return mService.getWindowId(window);
     }
 
+    @Override
+    public void pokeDrawLock(IBinder window) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mService.pokeDrawLock(this, window);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     void windowAddedLocked() {
         if (mSurfaceSession == null) {
             if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a1fe16b..f58e1dc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -194,6 +194,7 @@
     static final boolean DEBUG_TASK_MOVEMENT = false;
     static final boolean DEBUG_STACK = false;
     static final boolean DEBUG_DISPLAY = false;
+    static final boolean DEBUG_POWER = false;
     static final boolean SHOW_SURFACE_ALLOC = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -338,6 +339,7 @@
     final boolean mHaveInputMethods;
 
     final boolean mHasPermanentDpad;
+    final long mDrawLockTimeoutMillis;
 
     final boolean mAllowBootMessages;
 
@@ -827,6 +829,8 @@
                 com.android.internal.R.bool.config_hasPermanentDpad);
         mInTouchMode = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_defaultInTouchMode);
+        mDrawLockTimeoutMillis = context.getResources().getInteger(
+                com.android.internal.R.integer.config_drawLockTimeoutMillis);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplaySettings = new DisplaySettings();
@@ -2992,6 +2996,15 @@
         }
     }
 
+    public void pokeDrawLock(Session session, IBinder token) {
+        synchronized (mWindowMap) {
+            WindowState window = windowForClientLocked(session, token, false);
+            if (window != null) {
+                window.pokeDrawLockLw(mDrawLockTimeoutMillis);
+            }
+        }
+    }
+
     public int relayoutWindow(Session session, IWindow client, int seq,
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
@@ -10110,7 +10123,9 @@
             if (mAllowTheaterModeWakeFromLayout
                     || Settings.Global.getInt(mContext.getContentResolver(),
                         Settings.Global.THEATER_MODE_ON, 0) == 0) {
-                if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!");
+                if (DEBUG_VISIBILITY || DEBUG_POWER) {
+                    Slog.v(TAG, "Turning screen on after layout!");
+                }
                 mPowerManager.wakeUp(SystemClock.uptimeMillis());
             }
             mTurnOnScreen = false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 021a6e4..b621c52 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -19,9 +19,9 @@
 import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_POWER;
 import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
-
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -34,12 +34,15 @@
 
 import android.app.AppOpsManager;
 import android.os.Debug;
+import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.util.TimeUtils;
 import android.view.Display;
 import android.view.IWindowFocusObserver;
 import android.view.IWindowId;
+
 import com.android.server.input.InputWindowHandle;
 
 import android.content.Context;
@@ -345,6 +348,15 @@
      * the status bar */
     boolean mUnderStatusBar = true;
 
+    /**
+     * Wake lock for drawing.
+     * Even though it's slightly more expensive to do so, we will use a separate wake lock
+     * for each app that is requesting to draw while dozing so that we can accurately track
+     * who is preventing the system from suspending.
+     * This lock is only acquired on first use.
+     */
+    PowerManager.WakeLock mDrawLock;
+
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, final DisplayContent displayContent) {
@@ -1272,6 +1284,33 @@
         }
     }
 
+    public void pokeDrawLockLw(long timeout) {
+        if (isVisibleOrAdding()) {
+            if (mDrawLock == null) {
+                // We want the tag name to be somewhat stable so that it is easier to correlate
+                // in wake lock statistics.  So in particular, we don't want to include the
+                // window's hash code as in toString().
+                CharSequence tag = mAttrs.getTitle();
+                if (tag == null) {
+                    tag = mAttrs.packageName;
+                }
+                mDrawLock = mService.mPowerManager.newWakeLock(
+                        PowerManager.DRAW_WAKE_LOCK, "Window:" + tag);
+                mDrawLock.setReferenceCounted(false);
+                mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
+            }
+            // Each call to acquire resets the timeout.
+            if (DEBUG_POWER) {
+                Slog.d(TAG, "pokeDrawLock: poking draw lock on behalf of visible window owned by "
+                        + mAttrs.packageName);
+            }
+            mDrawLock.acquire(timeout);
+        } else if (DEBUG_POWER) {
+            Slog.d(TAG, "pokeDrawLock: suppressed draw lock request for invisible window "
+                    + "owned by " + mAttrs.packageName);
+        }
+    }
+
     @Override
     public boolean isAlive() {
         return mClient.asBinder().isBinderAlive();
@@ -1626,6 +1665,9 @@
                     pw.print(" mWallpaperDisplayOffsetY=");
                     pw.println(mWallpaperDisplayOffsetY);
         }
+        if (mDrawLock != null) {
+            pw.println("mDrawLock=" + mDrawLock);
+        }
     }
 
     String makeInputChannelName() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 0f51d00..25f7078 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -212,4 +212,9 @@
         // pass for now.
         return null;
     }
+
+    @Override
+    public void pokeDrawLock(IBinder window) {
+        // pass for now.
+    }
 }