Ignore draw requests when the display is off

When WindowManagerService's events are enabled/disabled, the state of the
display is dispatched to the known windows. This allows ViewRootImpl to
ignore draw requests until the screen is turned back on. This can potentially
lead to significant battery savings. For instance, a launcher widget showing
a repeating animation will cause the CPU and the GPU to wake up regularly
without this change.
(Change submitted by Intel and merged manually)

Change-Id: I7f93b0e60c3e6de1705f619e80860c36b1cdb978
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 715fa7b..acb1387 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -49,6 +49,7 @@
             boolean reportDraw, in Configuration newConfig);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
+    void dispatchScreenStatus(boolean on);
 
     /**
      * Tell the window that it is either gaining or losing focus.  Keep it up
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 49f6023..1a6c97b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14845,6 +14845,8 @@
         boolean mHardwareAccelerationRequested;
         HardwareRenderer mHardwareRenderer;
 
+        boolean mScreenOn;
+
         /**
          * Scale factor used by the compatibility mode
          */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e0d0763..3bc867c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -46,6 +46,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;
@@ -390,6 +391,9 @@
         mProfileRendering = Boolean.parseBoolean(
                 SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false"));
         mChoreographer = Choreographer.getInstance();
+
+        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mAttachInfo.mScreenOn = powerManager.isScreenOn();
     }
 
     /**
@@ -757,6 +761,16 @@
         scheduleTraversals();
     }
 
+    void handleScreenStatusChange(boolean on) {
+        if (on != mAttachInfo.mScreenOn) {
+            mAttachInfo.mScreenOn = on;
+            if (on) {
+                mFullRedrawNeeded = true;
+                scheduleTraversals();
+            }
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -1886,6 +1900,8 @@
     }
 
     private void performDraw() {
+        if (!mAttachInfo.mScreenOn) return;
+
         final long drawStartTime;
         if (ViewDebug.DEBUG_LATENCY) {
             drawStartTime = System.nanoTime();
@@ -2018,8 +2034,7 @@
         }
 
         if (!dirty.isEmpty() || mIsAnimating) {
-            if (mAttachInfo.mHardwareRenderer != null
-                    && mAttachInfo.mHardwareRenderer.isEnabled()) {
+            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                 // Draw with hardware renderer.
                 mIsAnimating = false;
                 mHardwareYOffset = yoff;
@@ -2485,6 +2500,7 @@
     private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21;
     private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22;
     private final static int MSG_PROCESS_INPUT_EVENTS = 23;
+    private final static int MSG_DISPATCH_SCREEN_STATUS = 24;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -2741,6 +2757,11 @@
                         .findAccessibilityNodeInfosByTextUiThread(msg);
                 }
             } break;
+            case MSG_DISPATCH_SCREEN_STATUS: {
+                if (mView != null) {
+                    handleScreenStatusChange(msg.arg1 == 1);
+                }
+            } break;
             }
         }
     }
@@ -4025,6 +4046,12 @@
         mHandler.sendMessage(msg);
     }
 
+    public void dispatchScreenStatusChange(boolean on) {
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATUS);
+        msg.arg1 = on ? 1 : 0;
+        mHandler.sendMessage(msg);
+    }
+
     public void dispatchGetNewSurface() {
         Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE);
         mHandler.sendMessage(msg);
@@ -4226,6 +4253,13 @@
             }
         }
 
+        public void dispatchScreenStatus(boolean on) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchScreenStatusChange(on);
+            }
+        }
+        
         public void dispatchGetNewSurface() {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index b227700..e695f8e 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -49,6 +49,9 @@
     public void dispatchGetNewSurface() {
     }
 
+    public void dispatchScreenStatus(boolean on) {
+    }
+
     public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
     }
 
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 1c5a70f..023f97d 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6347,6 +6347,8 @@
         synchronized (mWindowMap) {
             mInputMonitor.setEventDispatchingLw(enabled);
         }
+
+        sendScreenStatusToClients();
     }
 
     /**
@@ -6570,6 +6572,20 @@
         mPolicy.systemReady();
     }
 
+    private void sendScreenStatusToClients() {
+        final ArrayList<WindowState> windows = mWindows;
+        final int count = windows.size();
+        boolean on = mPowerManager.isScreenOn();
+        for (int i = count - 1; i >= 0; i--) {
+            WindowState win = mWindows.get(i);
+            try {
+                win.mClient.dispatchScreenStatus(on);
+            } catch (RemoteException e) {
+                // Ignored
+            }
+        }
+    }
+
     // This is an animation that does nothing: it just immediately finishes
     // itself every time it is called.  It is used as a stub animation in cases
     // where we want to synchronize multiple things that may be animating.