More work on device idle mode and other power stuff.

Add idle mode support to the alarm manager.  Introduce
a new concept of flags associated with alarms to tell
the alarm manager how to treat the alarm -- they allow
everything from the alarm that will bring us out of idle
mode, to alarms that are allowed when idle or should
also bring us out of idle.  The standalone boolean is
now also a flag.

(Note there is currently no protection from user space
setting the flags however it wants; I will be working
on that in a follow-up change.)

When in idle mode, the alarm manager pushes all alarms
that shouldn't execute during that time over to a
separate list that is not executed until out of idle.
To help with this, I reworked a bit how Alarm objects
are managed, so that when rebatching or moving between
lists we don't have to allocated new objects but can
just use the same existing instance.

Also tweaked the sync manager to deal with idle mode,
which currently just means doing the same thing as when
low on storage -- turning off sync.

Add new ACTION_CHARGING and ACTION_DISCHARGING broadcasts
that apps can listen for to know when the device is actively
charging and discharging.  These are better than the old
POWER_CONNECTED and POWER_DISCONNECTED ones because we only
report charging when we actually see that there is enough
power being provided to charge the battery (and will report
discharging if there is not enough power).

The job controller uses these new actions for scheduling
jobs that want to run while plugged in.  Removed the
"stable charging" stuff while doing so, since the new
charging state serves as an even better signal for that.

Introduced two new process states: FOREGROUND_SERVICE and
TOP_SLEEPING.  This will allow us to treat foreground services
specially (such as still allowing network access to them for
background music playback) while not mixing them together with
whatever happens to be the top activity while the device is
asleep.

Also some other small cleanup here and there.

Change-Id: I7a9808b578bad6f50deb8e1baf919298512a0d3a
diff --git a/api/current.txt b/api/current.txt
index 0c319d2..f0770c0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22247,6 +22247,9 @@
   public class BatteryManager {
     method public int getIntProperty(int);
     method public long getLongProperty(int);
+    method public boolean isCharging();
+    field public static final java.lang.String ACTION_CHARGING = "android.os.action.CHARGING";
+    field public static final java.lang.String ACTION_DISCHARGING = "android.os.action.DISCHARGING";
     field public static final int BATTERY_HEALTH_COLD = 7; // 0x7
     field public static final int BATTERY_HEALTH_DEAD = 4; // 0x4
     field public static final int BATTERY_HEALTH_GOOD = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 18ccc77..9342d7a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24118,6 +24118,9 @@
   public class BatteryManager {
     method public int getIntProperty(int);
     method public long getLongProperty(int);
+    method public boolean isCharging();
+    field public static final java.lang.String ACTION_CHARGING = "android.os.action.CHARGING";
+    field public static final java.lang.String ACTION_DISCHARGING = "android.os.action.DISCHARGING";
     field public static final int BATTERY_HEALTH_COLD = 7; // 0x7
     field public static final int BATTERY_HEALTH_DEAD = 4; // 0x4
     field public static final int BATTERY_HEALTH_GOOD = 2; // 0x2
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8f125d7..2b35cd4 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -269,45 +269,51 @@
      * all activities that are visible to the user. */
     public static final int PROCESS_STATE_TOP = 2;
 
+    /** @hide Process is hosting a foreground service. */
+    public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
+
+    /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
+    public static final int PROCESS_STATE_TOP_SLEEPING = 4;
+
     /** @hide Process is important to the user, and something they are aware of. */
-    public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
+    public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
 
     /** @hide Process is important to the user, but not something they are aware of. */
-    public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
+    public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 6;
 
     /** @hide Process is in the background running a backup/restore operation. */
-    public static final int PROCESS_STATE_BACKUP = 5;
+    public static final int PROCESS_STATE_BACKUP = 7;
 
     /** @hide Process is in the background, but it can't restore its state so we want
      * to try to avoid killing it. */
-    public static final int PROCESS_STATE_HEAVY_WEIGHT = 6;
+    public static final int PROCESS_STATE_HEAVY_WEIGHT = 8;
 
     /** @hide Process is in the background running a service.  Unlike oom_adj, this level
      * is used for both the normal running in background state and the executing
      * operations state. */
-    public static final int PROCESS_STATE_SERVICE = 7;
+    public static final int PROCESS_STATE_SERVICE = 9;
 
     /** @hide Process is in the background running a receiver.   Note that from the
      * perspective of oom_adj receivers run at a higher foreground level, but for our
      * prioritization here that is not necessary and putting them below services means
      * many fewer changes in some process states as they receive broadcasts. */
-    public static final int PROCESS_STATE_RECEIVER = 8;
+    public static final int PROCESS_STATE_RECEIVER = 10;
 
     /** @hide Process is in the background but hosts the home activity. */
-    public static final int PROCESS_STATE_HOME = 9;
+    public static final int PROCESS_STATE_HOME = 11;
 
     /** @hide Process is in the background but hosts the last shown activity. */
-    public static final int PROCESS_STATE_LAST_ACTIVITY = 10;
+    public static final int PROCESS_STATE_LAST_ACTIVITY = 12;
 
     /** @hide Process is being cached for later use and contains activities. */
-    public static final int PROCESS_STATE_CACHED_ACTIVITY = 11;
+    public static final int PROCESS_STATE_CACHED_ACTIVITY = 13;
 
     /** @hide Process is being cached for later use and is a client of another cached
      * process that contains activities. */
-    public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
+    public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 14;
 
     /** @hide Process is being cached for later use and is empty. */
-    public static final int PROCESS_STATE_CACHED_EMPTY = 13;
+    public static final int PROCESS_STATE_CACHED_EMPTY = 15;
 
     /** @hide requestType for assist context: only basic information. */
     public static final int ASSIST_CONTEXT_BASIC = 0;
@@ -2064,7 +2070,7 @@
                 return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
             } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
                 return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
-            } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+            } else if (procState >= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
                 return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
             } else {
                 return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 5dd02ae..179957d 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -115,6 +115,40 @@
     /** @hide */
     public static final long WINDOW_HEURISTIC = -1;
 
+    /**
+     * Flag for alarms: this is to be a stand-alone alarm, that should not be batched with
+     * other alarms.
+     * @hide
+     */
+    public static final int FLAG_STANDALONE = 1<<0;
+
+    /**
+     * Flag for alarms: this alarm would like to wake the device even if it is idle.  This
+     * is, for example, an alarm for an alarm clock.
+     * @hide
+     */
+    public static final int FLAG_WAKE_FROM_IDLE = 1<<1;
+
+    /**
+     * Flag for alarms: this alarm would like to still execute even if the device is
+     * idle.  This won't bring the device out of idle, just allow this specific alarm to
+     * run.  Note that this means the actual time this alarm goes off can be inconsistent
+     * with the time of non-allow-while-idle alarms (it could go earlier than the time
+     * requested by another alarm).
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2;
+
+    /**
+     * Flag for alarms: this alarm marks the point where we would like to come out of idle
+     * mode.  It may be moved by the alarm manager to match the first wake-from-idle alarm.
+     * Scheduling an alarm with this flag puts the alarm manager in to idle mode, where it
+     * avoids scheduling any further alarms until the marker alarm is executed.
+     * @hide
+     */
+    public static final int FLAG_IDLE_UNTIL = 1<<3;
+
     private final IAlarmManager mService;
     private final boolean mAlwaysExact;
 
@@ -204,7 +238,7 @@
      * @see #RTC_WAKEUP
      */
     public void set(int type, long triggerAtMillis, PendingIntent operation) {
-        setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null, null);
+        setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null);
     }
 
     /**
@@ -265,7 +299,8 @@
      */
     public void setRepeating(int type, long triggerAtMillis,
             long intervalMillis, PendingIntent operation) {
-        setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, operation, null, null);
+        setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation, null,
+                null);
     }
 
     /**
@@ -315,7 +350,7 @@
      */
     public void setWindow(int type, long windowStartMillis, long windowLengthMillis,
             PendingIntent operation) {
-        setImpl(type, windowStartMillis, windowLengthMillis, 0, operation, null, null);
+        setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation, null, null);
     }
 
     /**
@@ -353,7 +388,16 @@
      * @see #RTC_WAKEUP
      */
     public void setExact(int type, long triggerAtMillis, PendingIntent operation) {
-        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null, null);
+        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null);
+    }
+
+    /**
+     * Schedule an idle-until alarm, which will keep the alarm manager idle until
+     * the given time.
+     * @hide
+     */
+    public void setIdleUntil(int type, long triggerAtMillis, PendingIntent operation) {
+        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, operation, null, null);
     }
 
     /**
@@ -381,18 +425,19 @@
      * @see android.content.Intent#filterEquals
      */
     public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
-        setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, operation, null, info);
+        setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation, null, info);
     }
 
     /** @hide */
     @SystemApi
     public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
             PendingIntent operation, WorkSource workSource) {
-        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource, null);
+        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, workSource,
+                null);
     }
 
     private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
-            PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) {
+            int flags, PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) {
         if (triggerAtMillis < 0) {
             /* NOTYET
             if (mAlwaysExact) {
@@ -405,7 +450,7 @@
         }
 
         try {
-            mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
+            mService.set(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation,
                     workSource, alarmClock);
         } catch (RemoteException ex) {
         }
@@ -506,7 +551,7 @@
      */
     public void setInexactRepeating(int type, long triggerAtMillis,
             long intervalMillis, PendingIntent operation) {
-        setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation, null, null);
+        setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null);
     }
     
     /**
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index 194082e..d5719f5 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -28,7 +28,7 @@
 interface IAlarmManager {
 	/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
     void set(int type, long triggerAtTime, long windowLength,
-            long interval, in PendingIntent operation, in WorkSource workSource,
+            long interval, int flags, in PendingIntent operation, in WorkSource workSource,
             in AlarmManager.AlarmClockInfo alarmClock);
     boolean setTime(long millis);
     void setTimeZone(String zone);
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 7acf5f0..022a62c 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -318,7 +318,7 @@
              * @param label The label that will both be matched against what the user speaks
              * and displayed visually.
              * @param index The location of this option within the overall set of options.
-             * Can be used to help identify which the option when it is returned from the
+             * Can be used to help identify the option when it is returned from the
              * voice interactor.
              */
             public Option(CharSequence label, int index) {
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index bd5a3922..cccc4be 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -16,10 +16,12 @@
 
 package android.os;
 
+import android.content.Context;
 import android.os.BatteryProperty;
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import com.android.internal.app.IBatteryStats;
 
 /**
  * The BatteryManager class contains strings and constants used for values
@@ -128,6 +130,26 @@
     public static final int BATTERY_PLUGGED_ANY =
             BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS;
 
+    /**
+     * Sent when the device's battery has started charging (or has reached full charge
+     * and the device is on power).  This is a good time to do work that you would like to
+     * avoid doing while on battery (that is to avoid draining the user's battery due to
+     * things they don't care enough about).
+     *
+     * This is paired with {@link #ACTION_DISCHARGING}.  The current state can always
+     * be retrieved with {@link #isCharging()}.
+     */
+    public static final String ACTION_CHARGING = "android.os.action.CHARGING";
+
+    /**
+     * Sent when the device's battery may be discharging, so apps should avoid doing
+     * extraneous work that would cause it to discharge faster.
+     *
+     * This is paired with {@link #ACTION_CHARGING}.  The current state can always
+     * be retrieved with {@link #isCharging()}.
+     */
+    public static final String ACTION_DISCHARGING = "android.os.action.DISCHARGING";
+
     /*
      * Battery property identifiers.  These must match the values in
      * frameworks/native/include/batteryservice/BatteryService.h
@@ -162,17 +184,34 @@
      */
     public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5;
 
+    private final IBatteryStats mBatteryStats;
     private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
 
     /**
      * @removed Was previously made visible by accident.
      */
     public BatteryManager() {
+        mBatteryStats = IBatteryStats.Stub.asInterface(
+                ServiceManager.getService(BatteryStats.SERVICE_NAME));
         mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(
                 ServiceManager.getService("batteryproperties"));
     }
 
     /**
+     * Return true if the battery is currently considered to be charging.  This means that
+     * the device is plugged in and is supplying sufficient power that the battery level is
+     * going up (or the battery is fully charged).  Changes in this state are matched by
+     * broadcasts of {@link #ACTION_CHARGING} and {@link #ACTION_DISCHARGING}.
+     */
+    public boolean isCharging() {
+        try {
+            return mBatteryStats.isCharging();
+        } catch (RemoteException e) {
+            return true;
+        }
+    }
+
+    /**
      * Query a battery property from the batteryproperties service.
      *
      * Returns the requested value, or Long.MIN_VALUE if property not
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 3051926..1566985 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1065,6 +1065,7 @@
         public static final int STATE_SCREEN_ON_FLAG = 1<<20;
         public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
         public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
+        public static final int STATE_CHARGING_FLAG = 1<<17;
         public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
 
         public static final int MOST_INTERESTING_STATES =
@@ -1751,6 +1752,7 @@
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
         new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
+        new BitDescription(HistoryItem.STATE_CHARGING_FLAG, "charging", "ch"),
         new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
                 HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1746bed..4f0e29e 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -40,6 +40,9 @@
 
     ParcelFileDescriptor getStatisticsStream();
 
+    // Return true if we see the battery as currently charging.
+    boolean isCharging();
+
     // Return the computed amount of time remaining on battery, in milliseconds.
     // Returns -1 if nothing could be computed.
     long computeBatteryTimeRemaining();
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 75beee9..fe79eff 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -140,6 +140,8 @@
             STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
             STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
             STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
+            STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+            STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP_SLEEPING
             STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
             STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
             STATE_BACKUP,                   // ActivityManager.PROCESS_STATE_BACKUP
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 793d0d3..c5c0ba6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -127,6 +128,7 @@
 
     static final int MSG_UPDATE_WAKELOCKS = 1;
     static final int MSG_REPORT_POWER_CHANGE = 2;
+    static final int MSG_REPORT_CHARGING = 3;
     static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
 
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
@@ -135,6 +137,7 @@
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
         public void batteryPowerChanged(boolean onBattery);
+        public void batterySendBroadcast(Intent intent);
     }
 
     final class MyHandler extends Handler {
@@ -156,6 +159,18 @@
                         cb.batteryPowerChanged(msg.arg1 != 0);
                     }
                     break;
+                case MSG_REPORT_CHARGING:
+                    if (cb != null) {
+                        final String action;
+                        synchronized (BatteryStatsImpl.this) {
+                            action = mCharging ? BatteryManager.ACTION_CHARGING
+                                    : BatteryManager.ACTION_DISCHARGING;
+                        }
+                        Intent intent = new Intent(action);
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                        cb.batterySendBroadcast(intent);
+                    }
+                    break;
             }
         }
     }
@@ -393,6 +408,12 @@
     boolean mOnBattery;
     boolean mOnBatteryInternal;
 
+    /**
+     * External reporting of whether the device is actually charging.
+     */
+    boolean mCharging = true;
+    int mLastChargingStateLevel;
+
     /*
      * These keep track of battery levels (1-100) at the last plug event and the last unplug event.
      */
@@ -7243,6 +7264,10 @@
         return mOnBattery;
     }
 
+    public boolean isCharging() {
+        return mCharging;
+    }
+
     public boolean isScreenOn() {
         return mScreenState == Display.STATE_ON;
     }
@@ -7802,6 +7827,20 @@
         }
     }
 
+    boolean setChargingLocked(boolean charging) {
+        if (mCharging != charging) {
+            mCharging = charging;
+            if (charging) {
+                mHistoryCur.states |= HistoryItem.STATE_CHARGING_FLAG;
+            } else {
+                mHistoryCur.states &= ~HistoryItem.STATE_CHARGING_FLAG;
+            }
+            mHandler.sendEmptyMessage(MSG_REPORT_CHARGING);
+            return true;
+        }
+        return false;
+    }
+
     void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
             final int oldStatus, final int level) {
         boolean doWrite = false;
@@ -7861,6 +7900,10 @@
                 reset = true;
                 mDischargeStepTracker.init();
             }
+            if (mCharging) {
+                setChargingLocked(false);
+            }
+            mLastChargingStateLevel = level;
             mOnBattery = mOnBatteryInternal = true;
             mLastDischargeStepLevel = level;
             mMinDischargeStepLevel = level;
@@ -7890,6 +7933,7 @@
             mDischargeAmountScreenOff = 0;
             updateTimeBasesLocked(true, !screenOn, uptime, realtime);
         } else {
+            mLastChargingStateLevel = level;
             mOnBattery = mOnBatteryInternal = false;
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
@@ -7982,10 +8026,13 @@
                     mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
                 }
             }
+            // Always start out assuming charging, that will be updated later.
+            mHistoryCur.states |= HistoryItem.STATE_CHARGING_FLAG;
             mHistoryCur.batteryStatus = (byte)status;
             mHistoryCur.batteryLevel = (byte)level;
             mMaxChargeStepLevel = mMinDischargeStepLevel =
                     mLastChargeStepLevel = mLastDischargeStepLevel = level;
+            mLastChargingStateLevel = level;
         } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
             recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
         }
@@ -8046,13 +8093,11 @@
                 mHistoryCur.batteryVoltage = (char)volt;
                 changed = true;
             }
-            if (changed) {
-                addHistoryRecordLocked(elapsedRealtime, uptime);
-            }
             long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
                     | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
                     | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
             if (onBattery) {
+                changed |= setChargingLocked(false);
                 if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
                     mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
                             modeBits, elapsedRealtime);
@@ -8064,6 +8109,28 @@
                     mModStepMode = 0;
                 }
             } else {
+                if (level >= 90) {
+                    // If the battery level is at least 90%, always consider the device to be
+                    // charging even if it happens to go down a level.
+                    changed |= setChargingLocked(true);
+                    mLastChargeStepLevel = level;
+                } if (!mCharging) {
+                    if (mLastChargeStepLevel < level) {
+                        // We have not reporting that we are charging, but the level has now
+                        // gone up, so consider the state to be charging.
+                        changed |= setChargingLocked(true);
+                        mLastChargeStepLevel = level;
+                    }
+                } else {
+                    if (mLastChargeStepLevel > level) {
+                        // We had reported that the device was charging, but here we are with
+                        // power connected and the level going down.  Looks like the current
+                        // power supplied isn't enough, so consider the device to now be
+                        // discharging.
+                        changed |= setChargingLocked(false);
+                        mLastChargeStepLevel = level;
+                    }
+                }
                 if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                     mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
                             modeBits, elapsedRealtime);
@@ -8075,6 +8142,9 @@
                     mModStepMode = 0;
                 }
             }
+            if (changed) {
+                addHistoryRecordLocked(elapsedRealtime, uptime);
+            }
         }
         if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
             // We don't record history while we are plugged in and fully charged.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f427f2b..f2f7be2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -56,6 +56,8 @@
     <protected-broadcast android:name="android.intent.action.ACTION_POWER_CONNECTED" />
     <protected-broadcast android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
     <protected-broadcast android:name="android.intent.action.ACTION_SHUTDOWN" />
+    <protected-broadcast android:name="android.intent.action.CHARGING" />
+    <protected-broadcast android:name="android.intent.action.DISCHARGING" />
     <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_LOW" />
     <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
     <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_FULL" />
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 7d156df..34e8b78 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -34,6 +34,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -61,6 +62,7 @@
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Locale;
+import java.util.Random;
 import java.util.TimeZone;
 
 import static android.app.AlarmManager.RTC_WAKEUP;
@@ -128,6 +130,7 @@
     final ResultReceiver mResultReceiver = new ResultReceiver();
     PendingIntent mTimeTickSender;
     PendingIntent mDateChangeSender;
+    Random mRandom;
     boolean mInteractive = true;
     long mNonInteractiveStartTime;
     long mNonInteractiveTime;
@@ -185,18 +188,20 @@
     final class Batch {
         long start;     // These endpoints are always in ELAPSED
         long end;
-        boolean standalone; // certain "batches" don't participate in coalescing
+        int flags;      // Flags for alarms, such as FLAG_STANDALONE.
 
         final ArrayList<Alarm> alarms = new ArrayList<Alarm>();
 
         Batch() {
             start = 0;
             end = Long.MAX_VALUE;
+            flags = 0;
         }
 
         Batch(Alarm seed) {
             start = seed.whenElapsed;
-            end = seed.maxWhen;
+            end = seed.maxWhenElapsed;
+            flags = seed.flags;
             alarms.add(seed);
         }
 
@@ -227,9 +232,10 @@
                 start = alarm.whenElapsed;
                 newStart = true;
             }
-            if (alarm.maxWhen < end) {
-                end = alarm.maxWhen;
+            if (alarm.maxWhenElapsed < end) {
+                end = alarm.maxWhenElapsed;
             }
+            flags |= alarm.flags;
 
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "    => now " + this);
@@ -241,6 +247,7 @@
             boolean didRemove = false;
             long newStart = 0;  // recalculate endpoints as we go
             long newEnd = Long.MAX_VALUE;
+            int newFlags = 0;
             for (int i = 0; i < alarms.size(); ) {
                 Alarm alarm = alarms.get(i);
                 if (alarm.operation.equals(operation)) {
@@ -253,9 +260,10 @@
                     if (alarm.whenElapsed > newStart) {
                         newStart = alarm.whenElapsed;
                     }
-                    if (alarm.maxWhen < newEnd) {
-                        newEnd = alarm.maxWhen;
+                    if (alarm.maxWhenElapsed < newEnd) {
+                        newEnd = alarm.maxWhenElapsed;
                     }
+                    newFlags |= alarm.flags;
                     i++;
                 }
             }
@@ -263,6 +271,7 @@
                 // commit the new batch bounds
                 start = newStart;
                 end = newEnd;
+                flags = newFlags;
             }
             return didRemove;
         }
@@ -271,6 +280,7 @@
             boolean didRemove = false;
             long newStart = 0;  // recalculate endpoints as we go
             long newEnd = Long.MAX_VALUE;
+            int newFlags = 0;
             for (int i = 0; i < alarms.size(); ) {
                 Alarm alarm = alarms.get(i);
                 if (alarm.operation.getTargetPackage().equals(packageName)) {
@@ -283,9 +293,10 @@
                     if (alarm.whenElapsed > newStart) {
                         newStart = alarm.whenElapsed;
                     }
-                    if (alarm.maxWhen < newEnd) {
-                        newEnd = alarm.maxWhen;
+                    if (alarm.maxWhenElapsed < newEnd) {
+                        newEnd = alarm.maxWhenElapsed;
                     }
+                    newFlags |= alarm.flags;
                     i++;
                 }
             }
@@ -293,6 +304,7 @@
                 // commit the new batch bounds
                 start = newStart;
                 end = newEnd;
+                flags = newFlags;
             }
             return didRemove;
         }
@@ -313,8 +325,8 @@
                     if (alarm.whenElapsed > newStart) {
                         newStart = alarm.whenElapsed;
                     }
-                    if (alarm.maxWhen < newEnd) {
-                        newEnd = alarm.maxWhen;
+                    if (alarm.maxWhenElapsed < newEnd) {
+                        newEnd = alarm.maxWhenElapsed;
                     }
                     i++;
                 }
@@ -357,8 +369,9 @@
             b.append(" num="); b.append(size());
             b.append(" start="); b.append(start);
             b.append(" end="); b.append(end);
-            if (standalone) {
-                b.append(" STANDALONE");
+            if (flags != 0) {
+                b.append(" flgs=0x");
+                b.append(Integer.toHexString(flags));
             }
             b.append('}');
             return b.toString();
@@ -441,7 +454,12 @@
     // minimum recurrence period or alarm futurity for us to be able to fuzz it
     static final long MIN_FUZZABLE_INTERVAL = 10000;
     static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
-    final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>();
+    final ArrayList<Batch> mAlarmBatches = new ArrayList<>();
+
+    // set to null if in idle mode; while in this mode, any alarms we don't want
+    // to run during this time are placed in mPendingWhileIdleAlarms
+    Alarm mPendingIdleUntil = null;
+    final ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
 
     public AlarmManagerService(Context context) {
         super(context);
@@ -486,7 +504,7 @@
         final int N = mAlarmBatches.size();
         for (int i = 0; i < N; i++) {
             Batch b = mAlarmBatches.get(i);
-            if (!b.standalone && b.canHold(whenElapsed, maxWhen)) {
+            if ((b.flags&AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) {
                 return i;
             }
         }
@@ -503,31 +521,56 @@
     void rebatchAllAlarmsLocked(boolean doValidate) {
         ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
         mAlarmBatches.clear();
+        Alarm oldPendingIdleUntil = mPendingIdleUntil;
         final long nowElapsed = SystemClock.elapsedRealtime();
         final int oldBatches = oldSet.size();
         for (int batchNum = 0; batchNum < oldBatches; batchNum++) {
             Batch batch = oldSet.get(batchNum);
             final int N = batch.size();
             for (int i = 0; i < N; i++) {
-                Alarm a = batch.get(i);
-                long whenElapsed = convertToElapsed(a.when, a.type);
-                final long maxElapsed;
-                if (a.whenElapsed == a.maxWhen) {
-                    // Exact
-                    maxElapsed = whenElapsed;
-                } else {
-                    // Not exact.  Preserve any explicit window, otherwise recalculate
-                    // the window based on the alarm's new futurity.  Note that this
-                    // reflects a policy of preferring timely to deferred delivery.
-                    maxElapsed = (a.windowLength > 0)
-                            ? (whenElapsed + a.windowLength)
-                            : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
-                }
-                setImplLocked(a.type, a.when, whenElapsed, a.windowLength, maxElapsed,
-                        a.repeatInterval, a.operation, batch.standalone, doValidate, a.workSource,
-                        a.alarmClock, a.userId);
+                reAddAlarmLocked(batch.get(i), nowElapsed, doValidate);
             }
         }
+        if (oldPendingIdleUntil != null && oldPendingIdleUntil != mPendingIdleUntil) {
+            Slog.wtf(TAG, "Rebatching: idle until changed from " + oldPendingIdleUntil
+                    + " to " + mPendingIdleUntil);
+            if (mPendingIdleUntil == null) {
+                // Somehow we lost this...  we need to restore all of the pending alarms.
+                restorePendingWhileIdleAlarmsLocked();
+            }
+        }
+        rescheduleKernelAlarmsLocked();
+        updateNextAlarmClockLocked();
+    }
+
+    void reAddAlarmLocked(Alarm a, long nowElapsed, boolean doValidate) {
+        a.when = a.origWhen;
+        long whenElapsed = convertToElapsed(a.when, a.type);
+        final long maxElapsed;
+        if (a.whenElapsed == a.maxWhenElapsed) {
+            // Exact
+            maxElapsed = whenElapsed;
+        } else {
+            // Not exact.  Preserve any explicit window, otherwise recalculate
+            // the window based on the alarm's new futurity.  Note that this
+            // reflects a policy of preferring timely to deferred delivery.
+            maxElapsed = (a.windowLength > 0)
+                    ? (whenElapsed + a.windowLength)
+                    : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
+        }
+        a.whenElapsed = whenElapsed;
+        a.maxWhenElapsed = maxElapsed;
+        setImplLocked(a, true, doValidate);
+    }
+
+    void restorePendingWhileIdleAlarmsLocked() {
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil != null; i --) {
+            Alarm a = mPendingWhileIdleAlarms.remove(i);
+            reAddAlarmLocked(a, nowElapsed, false);
+        }
+        rescheduleKernelAlarmsLocked();
+        updateNextAlarmClockLocked();
     }
 
     static final class InFlight extends Intent {
@@ -687,7 +730,7 @@
     }
 
     void setImpl(int type, long triggerAtTime, long windowLength, long interval,
-            PendingIntent operation, boolean isStandalone, WorkSource workSource,
+            PendingIntent operation, int flags, WorkSource workSource,
             AlarmManager.AlarmClockInfo alarmClock) {
         if (operation == null) {
             Slog.w(TAG, "set/setRepeating ignored because there is no intent");
@@ -745,25 +788,66 @@
                 Slog.v(TAG, "set(" + operation + ") : type=" + type
                         + " triggerAtTime=" + triggerAtTime + " win=" + windowLength
                         + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
-                        + " interval=" + interval + " standalone=" + isStandalone);
+                        + " interval=" + interval + " flags=0x" + Integer.toHexString(flags));
             }
             setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
-                    interval, operation, isStandalone, true, workSource, alarmClock, userId);
+                    interval, operation, flags, true, workSource, alarmClock, userId);
         }
     }
 
     private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
-            long maxWhen, long interval, PendingIntent operation, boolean isStandalone,
+            long maxWhen, long interval, PendingIntent operation, int flags,
             boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
             int userId) {
         Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
-                operation, workSource, alarmClock, userId);
+                operation, workSource, flags, alarmClock, userId);
         removeLocked(operation);
+        setImplLocked(a, false, doValidate);
+    }
 
-        int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);
+    private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
+        if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+            // This is a special alarm that will put the system idle until it goes off.
+            // The caller has given the time they want this to happen at, however we need
+            // to pull that earlier if there are existing alarms that have requested to
+            // bring us out of idle.
+            final int N = mAlarmBatches.size();
+            for (int i = 0; i < N; i++) {
+                Batch b = mAlarmBatches.get(i);
+                if (a.whenElapsed > b.end) {
+                    // There are no interesting things happening before our idle until,
+                    // so keep the requested time.
+                    break;
+                }
+                if ((b.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+                    a.when = a.whenElapsed = a.maxWhenElapsed = b.end;
+                    break;
+                }
+            }
+            // Add fuzz to make the alarm go off some time before the actual desired time.
+            final long nowElapsed = SystemClock.elapsedRealtime();
+            long fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
+            if (fuzz > 0) {
+                if (mRandom == null) {
+                    mRandom = new Random();
+                }
+                a.whenElapsed -= mRandom.nextLong() % fuzz;
+            }
+
+        } else if (mPendingIdleUntil != null) {
+            // We currently have an idle until alarm scheduled; if the new alarm has
+            // not explicitly stated it wants to run while idle, then put it on hold.
+            if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE|AlarmManager.FLAG_WAKE_FROM_IDLE))
+                    == 0) {
+                mPendingWhileIdleAlarms.add(a);
+                return;
+            }
+        }
+
+        int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)
+                ? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed);
         if (whichBatch < 0) {
             Batch batch = new Batch(a);
-            batch.standalone = isStandalone;
             addBatchLocked(mAlarmBatches, batch);
         } else {
             Batch batch = mAlarmBatches.get(whichBatch);
@@ -775,28 +859,48 @@
             }
         }
 
-        if (alarmClock != null) {
+        if (a.alarmClock != null) {
             mNextAlarmClockMayChange = true;
-            updateNextAlarmClockLocked();
         }
 
-        if (DEBUG_VALIDATE) {
-            if (doValidate && !validateConsistencyLocked()) {
-                Slog.v(TAG, "Tipping-point operation: type=" + type + " when=" + when
-                        + " when(hex)=" + Long.toHexString(when)
-                        + " whenElapsed=" + whenElapsed + " maxWhen=" + maxWhen
-                        + " interval=" + interval + " op=" + operation
-                        + " standalone=" + isStandalone);
+        boolean needRebatch = false;
+
+        if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+            mPendingIdleUntil = a;
+            needRebatch = true;
+        } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0 && mPendingIdleUntil != null) {
+            // If we are adding an alarm that asks to wake from idle, and we are currently
+            // idling, then we need to rebatch alarms in case the idle until time needs to
+            // be updated.
+            needRebatch = true;
+        }
+
+        if (!rebatching) {
+            if (DEBUG_VALIDATE) {
+                if (doValidate && !validateConsistencyLocked()) {
+                    Slog.v(TAG, "Tipping-point operation: type=" + a.type + " when=" + a.when
+                            + " when(hex)=" + Long.toHexString(a.when)
+                            + " whenElapsed=" + a.whenElapsed
+                            + " maxWhenElapsed=" + a.maxWhenElapsed
+                            + " interval=" + a.repeatInterval + " op=" + a.operation
+                            + " flags=0x" + Integer.toHexString(a.flags));
+                    rebatchAllAlarmsLocked(false);
+                    needRebatch = false;
+                }
+            }
+
+            if (needRebatch) {
                 rebatchAllAlarmsLocked(false);
             }
-        }
 
-        rescheduleKernelAlarmsLocked();
+            rescheduleKernelAlarmsLocked();
+            updateNextAlarmClockLocked();
+        }
     }
 
     private final IBinder mService = new IAlarmManager.Stub() {
         @Override
-        public void set(int type, long triggerAtTime, long windowLength, long interval,
+        public void set(int type, long triggerAtTime, long windowLength, long interval, int flags,
                 PendingIntent operation, WorkSource workSource,
                 AlarmManager.AlarmClockInfo alarmClock) {
             if (workSource != null) {
@@ -805,8 +909,17 @@
                         "AlarmManager.set");
             }
 
+            if (windowLength == AlarmManager.WINDOW_EXACT) {
+                flags |= AlarmManager.FLAG_STANDALONE;
+            }
+            if (alarmClock != null) {
+                flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
+            }
+            if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+                flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+            }
             setImpl(type, triggerAtTime, windowLength, interval, operation,
-                    windowLength == AlarmManager.WINDOW_EXACT, workSource, alarmClock);
+                    flags, workSource, alarmClock);
         }
 
         @Override
@@ -912,6 +1025,14 @@
                     dumpAlarmList(pw, b.alarms, "  ", nowELAPSED, nowRTC, sdf);
                 }
             }
+            if (mPendingIdleUntil != null) {
+                pw.println();
+                pw.println("Idle mode state:");
+                pw.print("  Idling until: "); pw.println(mPendingIdleUntil);
+                mPendingIdleUntil.dump(pw, "    ", nowELAPSED, nowRTC, sdf);
+                pw.println("  Pending alarms:");
+                dumpAlarmList(pw, mPendingWhileIdleAlarms, "    ", nowELAPSED, nowRTC, sdf);
+            }
 
             pw.println();
             pw.print("Past-due non-wakeup alarms: ");
@@ -1224,6 +1345,15 @@
     }
 
     void rescheduleKernelAlarmsLocked() {
+        if (mPendingIdleUntil != null) {
+            // If we have a pending "idle until" alarm, we will just blindly wait until
+            // it is time for that alarm to go off.  We don't want to wake up for any
+            // other reasons.
+            mNextWakeup = mNextNonWakeup = mPendingIdleUntil.whenElapsed;
+            setLocked(ELAPSED_REALTIME_WAKEUP, mNextWakeup);
+            setLocked(ELAPSED_REALTIME, mNextNonWakeup);
+            return;
+        }
         // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
         // prior to that which contains no wakeups, we schedule that as well.
         long nextNonWakeup = 0;
@@ -1260,13 +1390,26 @@
                 mAlarmBatches.remove(i);
             }
         }
+        for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+            if (mPendingWhileIdleAlarms.get(i).operation.equals(operation)) {
+                // Don't set didRemove, since this doesn't impact the scheduled alarms.
+                mPendingWhileIdleAlarms.remove(i);
+            }
+        }
 
         if (didRemove) {
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "remove(operation) changed bounds; rebatching");
             }
+            boolean restorePending = false;
+            if (mPendingIdleUntil != null && mPendingIdleUntil.operation.equals(operation)) {
+                mPendingIdleUntil = null;
+                restorePending = true;
+            }
             rebatchAllAlarmsLocked(true);
-            rescheduleKernelAlarmsLocked();
+            if (restorePending) {
+                restorePendingWhileIdleAlarmsLocked();
+            }
             updateNextAlarmClockLocked();
         }
     }
@@ -1280,6 +1423,12 @@
                 mAlarmBatches.remove(i);
             }
         }
+        for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+            if (mPendingWhileIdleAlarms.get(i).operation.getTargetPackage().equals(packageName)) {
+                // Don't set didRemove, since this doesn't impact the scheduled alarms.
+                mPendingWhileIdleAlarms.remove(i);
+            }
+        }
 
         if (didRemove) {
             if (DEBUG_BATCH) {
@@ -1300,6 +1449,13 @@
                 mAlarmBatches.remove(i);
             }
         }
+        for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+            if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).operation.getCreatorUid())
+                    == userHandle) {
+                // Don't set didRemove, since this doesn't impact the scheduled alarms.
+                mPendingWhileIdleAlarms.remove(i);
+            }
+        }
 
         if (didRemove) {
             if (DEBUG_BATCH) {
@@ -1344,6 +1500,11 @@
                 return true;
             }
         }
+        for (int i = 0; i < mPendingWhileIdleAlarms.size(); i++) {
+            if (mPendingWhileIdleAlarms.get(i).operation.getTargetPackage().equals(packageName)) {
+                return true;
+            }
+        }
         return false;
     }
 
@@ -1413,6 +1574,13 @@
     boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
             final long nowRTC) {
         boolean hasWakeup = false;
+        if (mPendingIdleUntil != null) {
+            // If we have a pending "idle until" alarm, don't trigger any alarms
+            // until we are past the idle period.
+            if (nowELAPSED < mPendingIdleUntil.whenElapsed) {
+                return false;
+            }
+        }
         // batches are temporally sorted, so we need only pull from the
         // start of the list until we either empty it or hit a batch
         // that is not yet deliverable
@@ -1432,6 +1600,11 @@
                 Alarm alarm = batch.get(i);
                 alarm.count = 1;
                 triggerList.add(alarm);
+                if (mPendingIdleUntil == alarm) {
+                    mPendingIdleUntil = null;
+                    rebatchAllAlarmsLocked(false);
+                    restorePendingWhileIdleAlarmsLocked();
+                }
 
                 // Recurring alarms may have passed several alarm intervals while the
                 // phone was asleep or off, so pass a trigger count when sending them.
@@ -1445,7 +1618,7 @@
                     final long nextElapsed = alarm.whenElapsed + delta;
                     setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                             maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
-                            alarm.repeatInterval, alarm.operation, batch.standalone, true,
+                            alarm.repeatInterval, alarm.operation, alarm.flags, true,
                             alarm.workSource, alarm.alarmClock, alarm.userId);
                 }
 
@@ -1494,34 +1667,38 @@
     
     private static class Alarm {
         public final int type;
+        public final long origWhen;
         public final boolean wakeup;
         public final PendingIntent operation;
         public final String  tag;
         public final WorkSource workSource;
+        public final int flags;
         public int count;
         public long when;
         public long windowLength;
         public long whenElapsed;    // 'when' in the elapsed time base
-        public long maxWhen;        // also in the elapsed time base
+        public long maxWhenElapsed; // also in the elapsed time base
         public long repeatInterval;
         public final AlarmManager.AlarmClockInfo alarmClock;
         public final int userId;
         public PriorityClass priorityClass;
 
         public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
-                long _interval, PendingIntent _op, WorkSource _ws,
+                long _interval, PendingIntent _op, WorkSource _ws, int _flags,
                 AlarmManager.AlarmClockInfo _info, int _userId) {
             type = _type;
+            origWhen = _when;
             wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
                     || _type == AlarmManager.RTC_WAKEUP;
             when = _when;
             whenElapsed = _whenElapsed;
             windowLength = _windowLength;
-            maxWhen = _maxWhen;
+            maxWhenElapsed = _maxWhen;
             repeatInterval = _interval;
             operation = _op;
             tag = makeTag(_op, _type);
             workSource = _ws;
+            flags = _flags;
             alarmClock = _info;
             userId = _userId;
         }
@@ -1561,7 +1738,8 @@
                     pw.println();
             pw.print(prefix); pw.print("window="); pw.print(windowLength);
                     pw.print(" repeatInterval="); pw.print(repeatInterval);
-                    pw.print(" count="); pw.println(count);
+                    pw.print(" count="); pw.print(count);
+                    pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
             pw.print(prefix); pw.print("operation="); pw.println(operation);
         }
     }
@@ -1599,6 +1777,20 @@
         }
     }
 
+    static long fuzzForDuration(long duration) {
+        if (duration < 15*60*1000) {
+            // If the duration until the time is less than 15 minutes, the maximum fuzz
+            // is the duration.
+            return duration;
+        } else if (duration < 90*60*1000) {
+            // If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes,
+            return 15*60*1000;
+        } else {
+            // Otherwise, we will fuzz by at most half an hour.
+            return 30*60*1000;
+        }
+    }
+
     boolean checkAllowNonWakeupDelayLocked(long nowELAPSED) {
         if (mInteractive) {
             return false;
@@ -1886,7 +2078,7 @@
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
             setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
-                    0, mTimeTickSender, true, workSource, null);
+                    0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null);
         }
 
         public void scheduleDateChangedEvent() {
@@ -1899,8 +2091,8 @@
             calendar.add(Calendar.DAY_OF_MONTH, 1);
 
             final WorkSource workSource = null; // Let system take blame for date change events.
-            setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true, workSource,
-                    null);
+            setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender,
+                    AlarmManager.FLAG_STANDALONE, workSource, null);
         }
     }
     
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d2f52b4..14be293 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -986,6 +986,12 @@
     private boolean mSleeping = false;
 
     /**
+     * The process state used for processes that are running the top activities.
+     * This changes between TOP and TOP_SLEEPING to following mSleeping.
+     */
+    int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+
+    /**
      * Set while we are running a voice interaction.  This overrides
      * sleeping while it is active.
      */
@@ -2466,6 +2472,13 @@
         }
     }
 
+    @Override
+    public void batterySendBroadcast(Intent intent) {
+        broadcastIntentLocked(null, null, intent, null,
+                null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1,
+                Process.SYSTEM_UID, UserHandle.USER_ALL);
+    }
+
     /**
      * Initialize the application bind args. These are passed to each
      * process when the bindApplication() IPC is sent to the process. They're
@@ -9726,10 +9739,14 @@
     void updateSleepIfNeededLocked() {
         if (mSleeping && !shouldSleepLocked()) {
             mSleeping = false;
+            mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
             mStackSupervisor.comeOutOfSleepIfNeededLocked();
+            updateOomAdjLocked();
         } else if (!mSleeping && shouldSleepLocked()) {
             mSleeping = true;
+            mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
             mStackSupervisor.goingToSleepLocked();
+            updateOomAdjLocked();
 
             // Initialize the wake times of all processes.
             checkExcessivePowerUsageLocked(false);
@@ -10700,7 +10717,8 @@
             for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
                 ProcessRecord proc = mLruProcesses.get(i);
                 if (proc.notCachedSinceIdle) {
-                    if (proc.setProcState > ActivityManager.PROCESS_STATE_TOP
+                    if (proc.setProcState != ActivityManager.PROCESS_STATE_TOP
+                            && proc.setProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
                             && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
                         if (doKilling && proc.initialIdlePss != 0
                                 && proc.lastPss > ((proc.initialIdlePss*3)/2)) {
@@ -12893,7 +12911,7 @@
                     StringBuilder sb = new StringBuilder();
                     sb.append("    ").append(proc).append('/');
                     UserHandle.formatUid(sb, uids.keyAt(j));
-                    Pair<Long, String> val = uids.valueAt(i);
+                    Pair<Long, String> val = uids.valueAt(j);
                     sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
                     if (val.second != null) {
                         sb.append(", report to ").append(val.second);
@@ -16824,6 +16842,8 @@
 
         app.systemNoUi = false;
 
+        final int PROCESS_STATE_TOP = mTopProcessState;
+
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
         int adj;
@@ -16837,7 +16857,7 @@
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "top-activity";
             foregroundActivities = true;
-            procState = ActivityManager.PROCESS_STATE_TOP;
+            procState = PROCESS_STATE_TOP;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -16890,8 +16910,8 @@
                         adj = ProcessList.VISIBLE_APP_ADJ;
                         app.adjType = "visible";
                     }
-                    if (procState > ActivityManager.PROCESS_STATE_TOP) {
-                        procState = ActivityManager.PROCESS_STATE_TOP;
+                    if (procState > PROCESS_STATE_TOP) {
+                        procState = PROCESS_STATE_TOP;
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
@@ -16903,8 +16923,8 @@
                         adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                         app.adjType = "pausing";
                     }
-                    if (procState > ActivityManager.PROCESS_STATE_TOP) {
-                        procState = ActivityManager.PROCESS_STATE_TOP;
+                    if (procState > PROCESS_STATE_TOP) {
+                        procState = PROCESS_STATE_TOP;
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
@@ -16943,7 +16963,7 @@
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
                 app.cached = false;
                 app.adjType = "fg-service";
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -17472,7 +17492,7 @@
                                 IApplicationThread thread = myProc.thread;
                                 if (thread != null) {
                                     try {
-                                        if (true || DEBUG_PSS) Slog.d(TAG_PSS,
+                                        if (DEBUG_PSS) Slog.d(TAG_PSS,
                                                 "Requesting dump heap from "
                                                 + myProc + " to " + heapdumpFile);
                                         thread.dumpHeap(true, heapdumpFile.toString(), fd);
@@ -18757,7 +18777,7 @@
                         + " does not match last path " + mMemWatchDumpFile);
                 return;
             }
-            if (true || DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
             mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ddba1eb..2362d28 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1909,7 +1909,7 @@
                 next.sleeping = false;
                 mService.showAskCompatModeDialogLocked(next);
                 next.app.pendingUiClean = true;
-                next.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
+                next.app.forceProcessStateUpTo(mService.mTopProcessState);
                 next.clearOptionsLocked();
                 next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                         mService.isNextTransitionForward(), resumeAnimOptions);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d08cddc..c2f6bfd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1229,7 +1229,7 @@
                 app.hasShownUi = true;
                 app.pendingUiClean = true;
             }
-            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
+            app.forceProcessStateUpTo(mService.mTopProcessState);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                     new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ac70d88..ed108c2 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -260,6 +260,12 @@
         }
     }
 
+    public boolean isCharging() {
+        synchronized (mStats) {
+            return mStats.isCharging();
+        }
+    }
+
     public long computeBatteryTimeRemaining() {
         synchronized (mStats) {
             long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c7aa94c..cdfcd0c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -368,6 +368,12 @@
             case ActivityManager.PROCESS_STATE_TOP:
                 procState = "T ";
                 break;
+            case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
+                procState = "FS";
+                break;
+            case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
+                procState = "TS";
+                break;
             case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
                 procState = "IF";
                 break;
@@ -475,6 +481,8 @@
         PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT
         PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PROC_MEM_TOP,                   // ActivityManager.PROCESS_STATE_TOP
+        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PROC_MEM_TOP,                   // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
         PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_BACKUP
@@ -492,6 +500,8 @@
         PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_FIRST_TOP_INTERVAL,         // ActivityManager.PROCESS_STATE_TOP
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_BACKUP
@@ -509,6 +519,8 @@
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_TOP
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BACKUP
@@ -526,6 +538,8 @@
         PSS_TEST_FIRST_TOP_INTERVAL,        // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_TEST_FIRST_TOP_INTERVAL,        // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_TEST_FIRST_TOP_INTERVAL,        // ActivityManager.PROCESS_STATE_TOP
+        PSS_FIRST_BACKGROUND_INTERVAL,      // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PSS_FIRST_BACKGROUND_INTERVAL,      // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
         PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP
@@ -543,6 +557,8 @@
         PSS_TEST_SAME_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_TEST_SAME_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_TOP
+        PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_TOP_SLEEPING
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         PSS_TEST_SAME_IMPORTANT_INTERVAL,   // ActivityManager.PROCESS_STATE_BACKUP
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 191df2f..7866ddc 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -176,6 +176,7 @@
     volatile private PowerManager.WakeLock mSyncManagerWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
+    volatile private boolean mDeviceIsIdle = false;
 
     private final NotificationManager mNotificationMgr;
     private AlarmManager mAlarmService = null;
@@ -221,6 +222,20 @@
                 }
             };
 
+    private BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
+        @Override public void onReceive(Context context, Intent intent) {
+            boolean idle = mPowerManager.isDeviceIdleMode();
+            mDeviceIsIdle = idle;
+            if (idle) {
+                cancelActiveSync(
+                        SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
+                        null /* any sync */);
+            } else {
+                sendCheckAlarmsMessage();
+            }
+        }
+    };
+
     private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -425,6 +440,9 @@
         intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
         context.registerReceiver(mStorageIntentReceiver, intentFilter);
 
+        intentFilter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+        context.registerReceiver(mDeviceIdleReceiver, intentFilter);
+
         intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
         intentFilter.setPriority(100);
         context.registerReceiver(mShutdownIntentReceiver, intentFilter);
@@ -1312,6 +1330,7 @@
             pw.println();
         }
         pw.print("memory low: "); pw.println(mStorageIsLow);
+        pw.print("device idle: "); pw.println(mDeviceIsIdle);
 
         final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
 
@@ -2358,6 +2377,13 @@
                 return Long.MAX_VALUE;
             }
 
+            if (mDeviceIsIdle) {
+                if (isLoggable) {
+                    Log.v(TAG, "maybeStartNextSync: device idle, skipping");
+                }
+                return Long.MAX_VALUE;
+            }
+
             // If the accounts aren't known yet then we aren't ready to run. We will be kicked
             // when the account lookup request does complete.
             if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) {
@@ -2984,6 +3010,7 @@
             // method to be called again
             if (!mDataConnectionIsConnected) return;
             if (mStorageIsLow) return;
+            if (mDeviceIsIdle) return;
 
             // When the status bar notification should be raised
             final long notificationTime =
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 309e034..7c2aead 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -47,10 +47,6 @@
 
     private static final Object sCreationLock = new Object();
     private static volatile BatteryController sController;
-    private static final String ACTION_CHARGING_STABLE =
-            "com.android.server.task.controllers.BatteryController.ACTION_CHARGING_STABLE";
-    /** Wait this long after phone is plugged in before doing any work. */
-    private static final long STABLE_CHARGING_THRESHOLD_MILLIS = 2 * 60 * 1000; // 2 minutes.
 
     private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
     private ChargingTracker mChargeTracker;
@@ -91,9 +87,6 @@
                 taskStatus.chargingConstraintSatisfied.set(isOnStablePower);
             }
         }
-        if (isOnStablePower) {
-            mChargeTracker.setStableChargingAlarm();
-        }
     }
 
     @Override
@@ -131,8 +124,6 @@
     }
 
     public class ChargingTracker extends BroadcastReceiver {
-        private final AlarmManager mAlarm;
-        private final PendingIntent mStableChargingTriggerIntent;
         /**
          * Track whether we're "charging", where charging means that we're ready to commit to
          * doing work.
@@ -142,9 +133,6 @@
         private boolean mBatteryHealthy;
 
         public ChargingTracker() {
-            mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-            Intent intent = new Intent(ACTION_CHARGING_STABLE);
-            mStableChargingTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
         }
 
         public void startTracking() {
@@ -154,10 +142,8 @@
             filter.addAction(Intent.ACTION_BATTERY_LOW);
             filter.addAction(Intent.ACTION_BATTERY_OKAY);
             // Charging/not charging.
-            filter.addAction(Intent.ACTION_POWER_CONNECTED);
-            filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
-            // Charging stable.
-            filter.addAction(ACTION_CHARGING_STABLE);
+            filter.addAction(BatteryManager.ACTION_CHARGING);
+            filter.addAction(BatteryManager.ACTION_DISCHARGING);
             mContext.registerReceiver(this, filter);
 
             // Initialise tracker state.
@@ -195,45 +181,21 @@
                 }
                 mBatteryHealthy = true;
                 maybeReportNewChargingState();
-            } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
+            } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Received charging intent, setting alarm for "
-                            + STABLE_CHARGING_THRESHOLD_MILLIS);
+                    Slog.d(TAG, "Received charging intent, fired @ "
+                            + SystemClock.elapsedRealtime());
                 }
-                // Set up an alarm for ACTION_CHARGING_STABLE - we don't want to kick off tasks
-                // here if the user unplugs the phone immediately.
-                setStableChargingAlarm();
                 mCharging = true;
-            } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
+                maybeReportNewChargingState();
+            } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Disconnected from power, cancelling any set alarms.");
+                    Slog.d(TAG, "Disconnected from power.");
                 }
-                // If an alarm is set, breathe a sigh of relief and cancel it - crisis averted.
-                mAlarm.cancel(mStableChargingTriggerIntent);
                 mCharging = false;
                 maybeReportNewChargingState();
-            }else if (ACTION_CHARGING_STABLE.equals(action)) {
-                // Here's where we actually do the notify for a task being ready.
-                if (DEBUG) {
-                    Slog.d(TAG, "Stable charging fired @ " + SystemClock.elapsedRealtime()
-                            + " charging: " + mCharging);
-                }
-                if (mCharging) {  // Should never receive this intent if mCharging is false.
-                    maybeReportNewChargingState();
-                }
             }
         }
-
-        void setStableChargingAlarm() {
-            final long alarmTriggerElapsed =
-                    SystemClock.elapsedRealtime() + STABLE_CHARGING_THRESHOLD_MILLIS;
-            if (DEBUG) {
-                Slog.d(TAG, "Setting stable alarm to go off in " +
-                        (STABLE_CHARGING_THRESHOLD_MILLIS / 1000) + "s");
-            }
-            mAlarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTriggerElapsed,
-                    mStableChargingTriggerIntent);
-        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/power/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
index dd00446..a23a87b 100644
--- a/services/core/java/com/android/server/power/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -377,7 +377,7 @@
         }
     }
 
-    void scheduleAlarmLocked(long delay, boolean wakeup) {
+    void scheduleAlarmLocked(long delay, boolean idleUntil) {
         if (mSigMotionSensor == null) {
             // If there is no significant motion sensor on this device, then we won't schedule
             // alarms, because we can't determine if the device is not moving.  This effectively
@@ -386,8 +386,13 @@
             return;
         }
         mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
-        mAlarmManager.set(wakeup ? AlarmManager.ELAPSED_REALTIME_WAKEUP
-                : AlarmManager.ELAPSED_REALTIME, mNextAlarmTime, mAlarmIntent);
+        if (idleUntil) {
+            mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    mNextAlarmTime, mAlarmIntent);
+        } else {
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    mNextAlarmTime, mAlarmIntent);
+        }
     }
 
     private void dumpHelp(PrintWriter pw) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 7383478..7ea5aa7 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -39,6 +39,7 @@
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
+import static org.easymock.EasyMock.anyInt;
 import static org.easymock.EasyMock.anyLong;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
@@ -879,7 +880,7 @@
         expectLastCall().anyTimes();
 
         mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
-                isA(PendingIntent.class), isA(WorkSource.class),
+                anyInt(), isA(PendingIntent.class), isA(WorkSource.class),
                 isA(AlarmManager.AlarmClockInfo.class));
         expectLastCall().atLeastOnce();
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 607df2d..fb83956 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -162,8 +162,8 @@
         mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
         mBindIntent.setComponent(mSessionComponentName);
         mBound = mContext.bindServiceAsUser(mBindIntent, this,
-                Context.BIND_AUTO_CREATE|Context.BIND_WAIVE_PRIORITY
-                        |Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
+                Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
+                        | Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
         if (mBound) {
             try {
                 mIWindowManager.addWindowToken(mToken,