Implement screen on hack for wireless chargers.

We can't accurately detect whether the device is resting
on a wireless charger unless it is actually charging.
So we need to tweak the screen on when plugged / unplugged
policy accordingly to avoid spurious wakeups.

Bug: 7234284
Change-Id: I624b559e2e92b8813b12090bc20eca5f5158997e
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 4b20a53..0045f4a 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -197,6 +197,15 @@
     }
 
     /**
+     * Returns the current plug type.
+     */
+    public int getPlugType() {
+        synchronized (mLock) {
+            return mPlugType;
+        }
+    }
+
+    /**
      * Returns battery level as a percentage.
      */
     public int getBatteryLevel() {
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index a782d88..2ddda6c 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -135,6 +135,11 @@
     // minimum screen off timeout should be longer than this.
     private static final int SCREEN_DIM_DURATION = 7 * 1000;
 
+    // Upper bound on the battery charge percentage in order to consider turning
+    // the screen on when the device starts charging wirelessly.
+    // See point of use for more details.
+    private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
+
     private Context mContext;
     private LightsService mLightsService;
     private BatteryService mBatteryService;
@@ -218,6 +223,9 @@
     // True if the device is plugged into a power source.
     private boolean mIsPowered;
 
+    // The current plug type, such as BatteryManager.BATTERY_PLUGGED_WIRELESS.
+    private int mPlugType;
+
     // True if the device should wake up when plugged or unplugged.
     private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
 
@@ -1013,15 +1021,19 @@
      */
     private void updateIsPoweredLocked(int dirty) {
         if ((dirty & DIRTY_BATTERY_STATE) != 0) {
-            boolean wasPowered = mIsPowered;
+            final boolean wasPowered = mIsPowered;
+            final int oldPlugType = mPlugType;
             mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+            mPlugType = mBatteryService.getPlugType();
 
             if (DEBUG) {
                 Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
-                        + ", mIsPowered=" + mIsPowered);
+                        + ", mIsPowered=" + mIsPowered
+                        + ", oldPlugType=" + oldPlugType
+                        + ", mPlugType=" + mPlugType);
             }
 
-            if (wasPowered != mIsPowered) {
+            if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
                 mDirty |= DIRTY_IS_POWERED;
 
                 // Treat plugging and unplugging the devices as a user activity.
@@ -1030,7 +1042,7 @@
                 // Some devices also wake the device when plugged or unplugged because
                 // they don't have a charging LED.
                 final long now = SystemClock.uptimeMillis();
-                if (mWakeUpWhenPluggedOrUnpluggedConfig) {
+                if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType)) {
                     wakeUpNoUpdateLocked(now);
                 }
                 userActivityNoUpdateLocked(
@@ -1039,6 +1051,44 @@
         }
     }
 
+    private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(boolean wasPowered, int oldPlugType) {
+        if (mWakeUpWhenPluggedOrUnpluggedConfig) {
+            // FIXME: Need more accurate detection of wireless chargers.
+            //
+            // We are unable to accurately detect whether the device is resting on the
+            // charger unless it is actually receiving power.  This causes us some grief
+            // because the device might not appear to be plugged into the wireless charger
+            // unless it actually charging.
+            //
+            // To avoid spuriously waking the screen, we apply a special policy to
+            // wireless chargers.
+            //
+            // 1. Don't wake the device when unplugged from wireless charger because
+            //    it might be that the device is still resting on the wireless charger
+            //    but is not receiving power anymore because the battery is full.
+            //
+            // 2. Don't wake the device when plugged into a wireless charger if the
+            //    battery already appears to be mostly full.  This situation may indicate
+            //    that the device was resting on the charger the whole time and simply
+            //    wasn't receiving power because the battery was full.  We can't tell
+            //    whether the device was just placed on the charger or whether it has
+            //    been there for half of the night slowly discharging until it hit
+            //    the point where it needed to start charging again.
+            if (wasPowered && !mIsPowered
+                    && oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                return false;
+            }
+            if (!wasPowered && mIsPowered
+                    && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
+                    && mBatteryService.getBatteryLevel() >=
+                            WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Updates the value of mStayOn.
      * Sets DIRTY_STAY_ON if a change occurred.
@@ -1891,6 +1941,7 @@
             pw.println("  mDirty=0x" + Integer.toHexString(mDirty));
             pw.println("  mWakefulness=" + wakefulnessToString(mWakefulness));
             pw.println("  mIsPowered=" + mIsPowered);
+            pw.println("  mPlugType=" + mPlugType);
             pw.println("  mStayOn=" + mStayOn);
             pw.println("  mBootCompleted=" + mBootCompleted);
             pw.println("  mSystemReady=" + mSystemReady);