Add a function to boost screen brightness temporarily.

When PowerManager.boostScreenBrightness() is called, the screen
brightness is set to maximum for 5 seconds.  This action is
also considered to be user activity.

Bug: 17934954
Change-Id: I1cb4a03a60705c6c1c5cc9ff84b1c5dbd2932fcd
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 8447dde..bb162153 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -172,9 +172,12 @@
         // If true, enables automatic brightness control.
         public boolean useAutoBrightness;
 
-        //If true, scales the brightness to half of desired.
+        // If true, scales the brightness to half of desired.
         public boolean lowPowerMode;
 
+        // If true, applies a brightness boost.
+        public boolean boostScreenBrightness;
+
         // If true, prevents the screen from completely turning on if it is currently off.
         // The display does not enter a "ready" state if this flag is true and screen on is
         // blocked.  The window manager policy blocks screen on while it prepares the keyguard to
@@ -216,6 +219,7 @@
             useAutoBrightness = other.useAutoBrightness;
             blockScreenOn = other.blockScreenOn;
             lowPowerMode = other.lowPowerMode;
+            boostScreenBrightness = other.boostScreenBrightness;
             dozeScreenBrightness = other.dozeScreenBrightness;
             dozeScreenState = other.dozeScreenState;
         }
@@ -235,6 +239,7 @@
                     && useAutoBrightness == other.useAutoBrightness
                     && blockScreenOn == other.blockScreenOn
                     && lowPowerMode == other.lowPowerMode
+                    && boostScreenBrightness == other.boostScreenBrightness
                     && dozeScreenBrightness == other.dozeScreenBrightness
                     && dozeScreenState == other.dozeScreenState;
         }
@@ -253,6 +258,7 @@
                     + ", useAutoBrightness=" + useAutoBrightness
                     + ", blockScreenOn=" + blockScreenOn
                     + ", lowPowerMode=" + lowPowerMode
+                    + ", boostScreenBrightness=" + boostScreenBrightness
                     + ", dozeScreenBrightness=" + dozeScreenBrightness
                     + ", dozeScreenState=" + Display.stateToString(dozeScreenState);
         }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 182dbee..ec30684 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -50,6 +50,7 @@
 
     void setStayOnSetting(int val);
     void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs);
+    void boostScreenBrightness(long time);
 
     // temporarily overrides the screen brightness settings to allow the user to
     // see the effect of a settings change without applying it immediately
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3b6ce53..8307d9b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -669,6 +669,28 @@
     }
 
     /**
+     * Boosts the brightness of the screen to maximum for a predetermined
+     * period of time.  This is used to make the screen more readable in bright
+     * daylight for a short duration.
+     * <p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to boost was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the boost request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to boost.
+     *
+     * @hide Requires signature permission.
+     */
+    public void boostScreenBrightness(long time) {
+        try {
+            mService.boostScreenBrightness(time);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Sets the brightness of the backlights (screen, keyboard, button).
      * <p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2d5b99e..1d8003b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -570,8 +570,11 @@
         state = mPowerState.getScreenState();
 
         // Use zero brightness when screen is off.
+        // Use full brightness when screen brightness is boosted.
         if (state == Display.STATE_OFF) {
             brightness = PowerManager.BRIGHTNESS_OFF;
+        } else if (mPowerRequest.boostScreenBrightness) {
+            brightness = PowerManager.BRIGHTNESS_ON;
         }
 
         // Use default brightness when dozing unless overridden.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 5e95dfe..8682f5c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -88,6 +88,8 @@
     private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
     // Message: Sent when the device enters or exits a dreaming or dozing state.
     private static final int MSG_SANDMAN = 2;
+    // Message: Sent when the screen brightness boost expires.
+    private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3;
 
     // Dirty bit: mWakeLocks changed
     private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -111,6 +113,8 @@
     private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9;
     // Dirty bit: dock state changed
     private static final int DIRTY_DOCK_STATE = 1 << 10;
+    // Dirty bit: brightness boost changed
+    private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 << 11;
 
     // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
     // The screen should be off or in the process of being turned off by the display controller.
@@ -149,6 +153,11 @@
     private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
     private static final int DEFAULT_SLEEP_TIMEOUT = -1;
 
+    // Screen brightness boost timeout.
+    // Hardcoded for now until we decide what the right policy should be.
+    // This should perhaps be a setting.
+    private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000;
+
     // Power hints defined in hardware/libhardware/include/hardware/power.h.
     private static final int POWER_HINT_INTERACTION = 2;
     private static final int POWER_HINT_LOW_POWER = 5;
@@ -215,6 +224,10 @@
     // Timestamp of last interactive power hint.
     private long mLastInteractivePowerHintTime;
 
+    // Timestamp of the last screen brightness boost.
+    private long mLastScreenBrightnessBoostTime;
+    private boolean mScreenBrightnessBoostInProgress;
+
     // A bitfield that summarizes the effect of the user activity timer.
     private int mUserActivitySummary;
 
@@ -1812,9 +1825,13 @@
         final boolean oldDisplayReady = mDisplayReady;
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                 | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
-                | DIRTY_SETTINGS)) != 0) {
+                | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
             mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
 
+            // Handle screen brightness boost timeout.
+            updateScreenBrightnessBoostLocked();
+
+            // Determine appropriate screen brightness and auto-brightness adjustments.
             int screenBrightness = mScreenBrightnessSettingDefault;
             float screenAutoBrightnessAdjustment = 0.0f;
             boolean autoBrightness = (mScreenBrightnessModeSetting ==
@@ -1842,14 +1859,15 @@
                     mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
             screenAutoBrightnessAdjustment = Math.max(Math.min(
                     screenAutoBrightnessAdjustment, 1.0f), -1.0f);
+
+            // Update display power request.
             mDisplayPowerRequest.screenBrightness = screenBrightness;
             mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                     screenAutoBrightnessAdjustment;
             mDisplayPowerRequest.useAutoBrightness = autoBrightness;
-
             mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
-
             mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
+            mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress;
 
             if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
                 mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
@@ -1861,21 +1879,40 @@
             }
 
             mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
-                    mRequestWaitForNegativeProximity);
+                    mRequestWaitForNegativeProximity) && !mScreenBrightnessBoostInProgress;
             mRequestWaitForNegativeProximity = false;
 
             if (DEBUG_SPEW) {
-                Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady
+                Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
                         + ", policy=" + mDisplayPowerRequest.policy
                         + ", mWakefulness=" + mWakefulness
                         + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
                         + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
-                        + ", mBootCompleted=" + mBootCompleted);
+                        + ", mBootCompleted=" + mBootCompleted
+                        + ", mScreenBrightnessBoostInProgress="
+                                + mScreenBrightnessBoostInProgress);
             }
         }
         return mDisplayReady && !oldDisplayReady;
     }
 
+    private void updateScreenBrightnessBoostLocked() {
+        if (mScreenBrightnessBoostInProgress) {
+            mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
+            if (mLastScreenBrightnessBoostTime > mLastSleepTime) {
+                final long boostTimeout = mLastScreenBrightnessBoostTime +
+                        SCREEN_BRIGHTNESS_BOOST_TIMEOUT;
+                if (boostTimeout > SystemClock.uptimeMillis()) {
+                    Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
+                    msg.setAsynchronous(true);
+                    mHandler.sendMessageAtTime(msg, boostTimeout);
+                    return;
+                }
+            }
+            mScreenBrightnessBoostInProgress = false;
+        }
+    }
+
     private static boolean isValidBrightness(int value) {
         return value >= 0 && value <= 255;
     }
@@ -2223,6 +2260,41 @@
         light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
     }
 
+    private void boostScreenBrightnessInternal(long eventTime, int uid) {
+        synchronized (mLock) {
+            if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
+                    || eventTime < mLastScreenBrightnessBoostTime) {
+                return;
+            }
+
+            Slog.i(TAG, "Brightness boost activated (uid " + uid +")...");
+            mLastScreenBrightnessBoostTime = eventTime;
+            mScreenBrightnessBoostInProgress = true;
+            mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
+
+            userActivityNoUpdateLocked(eventTime,
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+            updatePowerStateLocked();
+        }
+    }
+
+    /**
+     * Called when a screen brightness boost timeout has occurred.
+     *
+     * This function must have no other side-effects besides setting the dirty
+     * bit and calling update power state.
+     */
+    private void handleScreenBrightnessBoostTimeout() { // runs on handler thread
+        synchronized (mLock) {
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "handleScreenBrightnessBoostTimeout");
+            }
+
+            mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
+            updatePowerStateLocked();
+        }
+    }
+
     private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
         synchronized (mLock) {
             if (mScreenBrightnessOverrideFromWindowManager != brightness) {
@@ -2366,6 +2438,10 @@
                     + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
             pw.println("  mLastInteractivePowerHintTime="
                     + TimeUtils.formatUptime(mLastInteractivePowerHintTime));
+            pw.println("  mLastScreenBrightnessBoostTime="
+                    + TimeUtils.formatUptime(mLastScreenBrightnessBoostTime));
+            pw.println("  mScreenBrightnessBoostInProgress="
+                    + mScreenBrightnessBoostInProgress);
             pw.println("  mDisplayReady=" + mDisplayReady);
             pw.println("  mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
             pw.println("  mHoldingDisplaySuspendBlocker=" + mHoldingDisplaySuspendBlocker);
@@ -2562,6 +2638,9 @@
                 case MSG_SANDMAN:
                     handleSandman();
                     break;
+                case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:
+                    handleScreenBrightnessBoostTimeout();
+                    break;
             }
         }
     }
@@ -3143,6 +3222,24 @@
         }
 
         @Override // Binder call
+        public void boostScreenBrightness(long eventTime) {
+            if (eventTime > SystemClock.uptimeMillis()) {
+                throw new IllegalArgumentException("event time must not be in the future");
+            }
+
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
+
+            final int uid = Binder.getCallingUid();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                boostScreenBrightnessInternal(eventTime, uid);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 22265a3..05a6fd6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -145,4 +145,9 @@
     public void wakeUp(long time) throws RemoteException {
         // pass for now.
     }
+
+    @Override
+    public void boostScreenBrightness(long time) throws RemoteException {
+        // pass for now.
+    }
 }