Implement reminder power dialog and invalid charger dialog.

Bug: 2510318
Bug: 2974431
Change-Id: I92eb419eeffb657e5572a35a490735a96b303d6b
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 247b281..c62715b 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -87,7 +87,7 @@
 
     /**
      * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
-     * Boolean value set to true if an unsupported charger is attached
+     * Int value set to nonzero if an unsupported charger is attached
      * to the device.
      * {@hide}
      */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c8eb7fd..a6953d4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -290,6 +290,11 @@
 
     <bool name="config_use_strict_phone_number_comparation">false</bool>
 
+    <!-- Display low battery warning when battery level dips to this value.
+         Also, the battery stats are flushed to disk when we hit this level.  -->
+    <integer name="config_criticalBatteryWarningLevel">4</integer>
+
+    <!-- Display low battery warning when battery level dips to this value -->
     <!-- Display low battery warning when battery level dips to this value -->
     <integer name="config_lowBatteryWarningLevel">15</integer>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1f24ba6..701aa9f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -53,11 +53,17 @@
     <string name="battery_low_title">Please connect charger</string>
 
     <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. -->
-    <string name="battery_low_subtitle">The battery is getting low:</string>
+    <string name="battery_low_subtitle">The battery is getting low.</string>
 
-    <!-- A message that appears when the battery level is getting low in a dialog.  This is appened to the subtitle of the low battery alert. -->
-    <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g>
-    or less remaining.</string>
+    <!-- A message that appears when the battery level is getting low in a dialog.  This is
+        appened to the subtitle of the low battery alert. -->
+    <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g> remaining</string>
+
+    <!-- A message that appears when a USB charger is plugged in and the device does not
+    support charging on it.  That is, a charger that fits into the USB port and goes into
+    a wall socket, not into a computer. (This happens because some devices require more
+    current than the USB spec allows.  -->
+    <string name="invalid_charger">USB charging not supported.\nUse only the supplied charger.</string>
 
     <!-- When the battery is low, this is the label of the button to go to the
          power usage activity to find out what drained the battery. -->
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 0b7b4c0..aeb8d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Handler;
 import android.media.AudioManager;
 import android.media.Ringtone;
@@ -46,32 +47,116 @@
 
     Handler mHandler = new Handler();
 
+    int mBatteryLevel = 100;
+    int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+    int mPlugType = 0;
+    int mInvalidCharger = 0;
+
+    int mLowBatteryAlertCloseLevel;
+    int[] mLowBatteryReminderLevels = new int[2];
+
+    AlertDialog mInvalidChargerDialog;
     AlertDialog mLowBatteryDialog;
-    int mBatteryLevel;
     TextView mBatteryLevelTextView;
 
     public void start() {
+
+        mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+        mLowBatteryReminderLevels[0] = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+
         // Register for Intent broadcasts for...
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(Intent.ACTION_BATTERY_LOW);
-        filter.addAction(Intent.ACTION_BATTERY_OKAY);
         filter.addAction(Intent.ACTION_POWER_CONNECTED);
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
     }
 
+    /**
+     * Buckets the battery level.
+     *
+     * The code in this function is a little weird because I couldn't comprehend
+     * the bucket going up when the battery level was going down. --joeo
+     *
+     * 1 means that the battery is "ok"
+     * 0 means that the battery is between "ok" and what we should warn about.
+     * less than 0 means that the battery is low
+     */
+    private int findBatteryLevelBucket(int level) {
+        if (level >= mLowBatteryAlertCloseLevel) {
+            return 1;
+        }
+        if (level >= mLowBatteryReminderLevels[0]) {
+            return 0;
+        }
+        final int N = mLowBatteryReminderLevels.length;
+        for (int i=N-1; i>=0; i--) {
+            if (level <= mLowBatteryReminderLevels[i]) {
+                return -1-i;
+            }
+        }
+        throw new RuntimeException("not possible!");
+    }
+
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                mBatteryLevel = intent.getIntExtra("level", -1);
-            } else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
-                showLowBatteryWarning();
-            } else if (action.equals(Intent.ACTION_BATTERY_OKAY)
-                    || action.equals(Intent.ACTION_POWER_CONNECTED)) {
-                if (mLowBatteryDialog != null) {
-                    mLowBatteryDialog.dismiss();
+                final int oldBatteryLevel = mBatteryLevel;
+                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
+                final int oldBatteryStatus = mBatteryStatus;
+                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+                        BatteryManager.BATTERY_STATUS_UNKNOWN);
+                final int oldPlugType = mPlugType;
+                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
+                final int oldInvalidCharger = mInvalidCharger;
+                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
+
+                final boolean plugged = mPlugType != 0;
+                final boolean oldPlugged = oldPlugType != 0;
+
+                int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
+                int bucket = findBatteryLevelBucket(mBatteryLevel);
+
+                if (false) {
+                    Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
+                            + " .. " + mLowBatteryReminderLevels[0]
+                            + " .. " + mLowBatteryReminderLevels[1]);
+                    Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
+                    Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
+                    Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
+                    Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
+                    Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
+                    Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
+                }
+
+                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
+                    Slog.d(TAG, "showing invalid charger warning");
+                    showInvalidChargerDialog();
+                    return;
+                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
+                    Slog.d(TAG, "closing invalid charger warning");
+                    dismissInvalidChargerDialog();
+                } else if (mInvalidChargerDialog != null) {
+                    // if invalid charger is showing, don't show low battery
+                    return;
+                }
+
+                if (!plugged
+                        && (bucket < oldBucket || oldPlugged)
+                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                        && bucket < 0) {
+                    Slog.d(TAG, "showing low battery warning: level=" + mBatteryLevel);
+                    showLowBatteryWarning();
+                } else if (plugged || (bucket > oldBucket && bucket > 0)) {
+                    Slog.d(TAG, "closing low battery warning: level=" + mBatteryLevel);
+                    dismissLowBatteryWarning();
+                } else if (mBatteryLevelTextView != null) {
+                    showLowBatteryWarning();
                 }
             } else {
                 Slog.w(TAG, "unknown intent: " + intent);
@@ -79,6 +164,12 @@
         }
     };
 
+    void dismissLowBatteryWarning() {
+        if (mLowBatteryDialog != null) {
+            mLowBatteryDialog.dismiss();
+        }
+    }
+
     void showLowBatteryWarning() {
         CharSequence levelText = mContext.getString(
                 R.string.battery_low_percent_format, mBatteryLevel);
@@ -98,25 +189,30 @@
                 b.setIcon(android.R.drawable.ic_dialog_alert);
                 b.setPositiveButton(android.R.string.ok, null);
 
-                final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                        | Intent.FLAG_ACTIVITY_NO_HISTORY);
-                if (intent.resolveActivity(mContext.getPackageManager()) != null) {
-                    b.setNegativeButton(R.string.battery_low_why,
-                            new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            mContext.startActivity(intent);
-                            if (mLowBatteryDialog != null) {
-                                mLowBatteryDialog.dismiss();
-                            }
+            final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                    | Intent.FLAG_ACTIVITY_NO_HISTORY);
+            if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+                b.setNegativeButton(R.string.battery_low_why,
+                        new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        mContext.startActivity(intent);
+                        if (mLowBatteryDialog != null) {
+                            mLowBatteryDialog.dismiss();
                         }
-                    });
-                }
+                    }
+                });
+            }
 
             AlertDialog d = b.create();
-            d.setOnDismissListener(mLowBatteryListener);
+            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    public void onDismiss(DialogInterface dialog) {
+                        mLowBatteryDialog = null;
+                        mBatteryLevelTextView = null;
+                    }
+                });
             d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
             d.show();
             mLowBatteryDialog = d;
@@ -141,45 +237,53 @@
 }
     }
 
-    private DialogInterface.OnDismissListener mLowBatteryListener
-            = new DialogInterface.OnDismissListener() {
-        public void onDismiss(DialogInterface dialog) {
-            mLowBatteryDialog = null;
-            mBatteryLevelTextView = null;
+    void dismissInvalidChargerDialog() {
+        if (mInvalidChargerDialog != null) {
+            mInvalidChargerDialog.dismiss();
         }
-    };
+    }
 
+    void showInvalidChargerDialog() {
+        dismissLowBatteryWarning();
+
+        AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+            b.setCancelable(true);
+            b.setMessage(mContext.getString(R.string.invalid_charger));
+            b.setIcon(android.R.drawable.ic_dialog_alert);
+            b.setPositiveButton(android.R.string.ok, null);
+
+        AlertDialog d = b.create();
+            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    public void onDismiss(DialogInterface dialog) {
+                        mInvalidChargerDialog = null;
+                        mBatteryLevelTextView = null;
+                    }
+                });
+
+        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        d.show();
+        mInvalidChargerDialog = d;
+    }
     
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (false) {
-            pw.println("args=" + Arrays.toString(args));
-        }
-        if (args == null || args.length == 0) {
-            pw.print("mLowBatteryDialog=");
-            pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
-            pw.print("mBatteryLevel=");
-            pw.println(Integer.toString(mBatteryLevel));
-        }
-
-        // DO NOT SUBMIT with this turned on.
-        if (false) {
-            if (args.length == 3 && "level".equals(args[1])) {
-                try {
-                    final int level = Integer.parseInt(args[2]);
-                    Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
-                    intent.putExtra("level", level);
-                    mIntentReceiver.onReceive(mContext, intent);
-                } catch (NumberFormatException ex) {
-                    pw.println(ex);
-                }
-            } else if (args.length == 2 && "low".equals(args[1])) {
-                Intent intent = new Intent(Intent.ACTION_BATTERY_LOW);
-                mIntentReceiver.onReceive(mContext, intent);
-            } else if (args.length == 2 && "ok".equals(args[1])) {
-                Intent intent = new Intent(Intent.ACTION_BATTERY_OKAY);
-                mIntentReceiver.onReceive(mContext, intent);
-            }
-        }
+        pw.print("mLowBatteryAlertCloseLevel=");
+        pw.println(mLowBatteryAlertCloseLevel);
+        pw.print("mLowBatteryReminderLevels=");
+        pw.println(Arrays.toString(mLowBatteryReminderLevels));
+        pw.print("mInvalidChargerDialog=");
+        pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
+        pw.print("mLowBatteryDialog=");
+        pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
+        pw.print("mBatteryLevel=");
+        pw.println(Integer.toString(mBatteryLevel));
+        pw.print("mBatteryStatus=");
+        pw.println(Integer.toString(mBatteryStatus));
+        pw.print("mPlugType=");
+        pw.println(Integer.toString(mPlugType));
+        pw.print("mInvalidCharger=");
+        pw.println(Integer.toString(mInvalidCharger));
+        pw.print("bucket: ");
+        pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
     }
 }
 
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 23bd5c8..40883bd 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -43,6 +43,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Arrays;
 
 
 /**
@@ -76,7 +77,7 @@
 
     // Used locally for determining when to make a last ditch effort to log
     // discharge stats before the device dies.
-    private static final int CRITICAL_BATTERY_LEVEL = 4;
+    private int mCriticalBatteryLevel;
 
     private static final int DUMP_MAX_LENGTH = 24 * 1024;
     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
@@ -100,7 +101,7 @@
     private int mBatteryTemperature;
     private String mBatteryTechnology;
     private boolean mBatteryLevelCritical;
-    private boolean mInvalidCharger;
+    private int mInvalidCharger;
 
     private int mLastBatteryStatus;
     private int mLastBatteryHealth;
@@ -109,7 +110,7 @@
     private int mLastBatteryVoltage;
     private int mLastBatteryTemperature;
     private boolean mLastBatteryLevelCritical;
-    private boolean mLastInvalidCharger;
+    private int mLastInvalidCharger;
 
     private int mLowBatteryWarningLevel;
     private int mLowBatteryCloseWarningLevel;
@@ -129,6 +130,8 @@
         mLed = new Led(context, lights);
         mBatteryStats = BatteryStatsService.getService();
 
+        mCriticalBatteryLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
         mLowBatteryWarningLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
         mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
@@ -183,7 +186,7 @@
     private UEventObserver mInvalidChargerObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
-            boolean invalidCharger = "1".equals(event.get("SWITCH_STATE"));
+            int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
             if (mInvalidCharger != invalidCharger) {
                 mInvalidCharger = invalidCharger;
                 update();
@@ -228,11 +231,14 @@
 
     private synchronized final void update() {
         native_update();
+        processValues();
+    }
 
+    private void processValues() {
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
-        mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
+        mBatteryLevelCritical = mBatteryLevel <= mCriticalBatteryLevel;
         if (mAcOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
         } else if (mUsbOnline) {
@@ -384,8 +390,8 @@
         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
 
-        if (false) {
-            Slog.d(TAG, "updateBattery level:" + mBatteryLevel +
+        if (true) {
+            Slog.d(TAG, "level:" + mBatteryLevel +
                     " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
                     " health:" + mBatteryHealth +  " present:" + mBatteryPresent +
                     " voltage: " + mBatteryVoltage +
@@ -487,18 +493,47 @@
             return;
         }
 
-        synchronized (this) {
-            pw.println("Current Battery Service state:");
-            pw.println("  AC powered: " + mAcOnline);
-            pw.println("  USB powered: " + mUsbOnline);
-            pw.println("  status: " + mBatteryStatus);
-            pw.println("  health: " + mBatteryHealth);
-            pw.println("  present: " + mBatteryPresent);
-            pw.println("  level: " + mBatteryLevel);
-            pw.println("  scale: " + BATTERY_SCALE);
-            pw.println("  voltage:" + mBatteryVoltage);
-            pw.println("  temperature: " + mBatteryTemperature);
-            pw.println("  technology: " + mBatteryTechnology);
+        if (args == null || args.length == 0) {
+            synchronized (this) {
+                pw.println("Current Battery Service state:");
+                pw.println("  AC powered: " + mAcOnline);
+                pw.println("  USB powered: " + mUsbOnline);
+                pw.println("  status: " + mBatteryStatus);
+                pw.println("  health: " + mBatteryHealth);
+                pw.println("  present: " + mBatteryPresent);
+                pw.println("  level: " + mBatteryLevel);
+                pw.println("  scale: " + BATTERY_SCALE);
+                pw.println("  voltage:" + mBatteryVoltage);
+                pw.println("  temperature: " + mBatteryTemperature);
+                pw.println("  technology: " + mBatteryTechnology);
+            }
+        } else if (false) {
+            // DO NOT SUBMIT WITH THIS TURNED ON
+            if (args.length == 3 && "set".equals(args[0])) {
+                String key = args[1];
+                String value = args[2];
+                try {
+                    boolean update = true;
+                    if ("ac".equals(key)) {
+                        mAcOnline = Integer.parseInt(value) != 0;
+                    } else if ("usb".equals(key)) {
+                        mUsbOnline = Integer.parseInt(value) != 0;
+                    } else if ("status".equals(key)) {
+                        mBatteryStatus = Integer.parseInt(value);
+                    } else if ("level".equals(key)) {
+                        mBatteryLevel = Integer.parseInt(value);
+                    } else if ("invalid".equals(key)) {
+                        mInvalidCharger = Integer.parseInt(value);
+                    } else {
+                        update = false;
+                    }
+                    if (update) {
+                        processValues();
+                    }
+                } catch (NumberFormatException ex) {
+                    pw.println("Bad value: " + value);
+                }
+            }
         }
     }