Merge "More work on device idle mode."
diff --git a/api/current.txt b/api/current.txt
index d8d8998..ecac8fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22884,6 +22884,7 @@
   }
 
   public final class PowerManager {
+    method public boolean isDeviceIdleMode();
     method public boolean isInteractive();
     method public boolean isPowerSaveMode();
     method public deprecated boolean isScreenOn();
@@ -22891,6 +22892,7 @@
     method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
     method public void reboot(java.lang.String);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+    field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
     field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
diff --git a/api/system-current.txt b/api/system-current.txt
index 71474a3..e6cd44b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24676,6 +24676,7 @@
   }
 
   public final class PowerManager {
+    method public boolean isDeviceIdleMode();
     method public boolean isInteractive();
     method public boolean isPowerSaveMode();
     method public deprecated boolean isScreenOn();
@@ -24684,6 +24685,7 @@
     method public void reboot(java.lang.String);
     method public void userActivity(long, int, int);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+    field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
     field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cab03da..26e6b850 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -613,6 +613,9 @@
             if ((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
                 out.append('p');
             }
+            if ((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+                out.append('i');
+            }
             switch ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
                 case Display.STATE_OFF: out.append('F'); break;
                 case Display.STATE_ON: out.append('O'); break;
@@ -622,6 +625,9 @@
             if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
                 out.append('P');
             }
+            if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+                out.append('I');
+            }
             out.append('-');
             appendHex(level, 4, out);
             out.append('-');
@@ -648,6 +654,9 @@
                     case 'p': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
                             << STEP_LEVEL_INITIAL_MODE_SHIFT);
                         break;
+                    case 'i': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+                            << STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
                     case 'F': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
                         break;
                     case 'O': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
@@ -660,6 +669,9 @@
                     case 'P': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
                             << STEP_LEVEL_MODIFIED_MODE_SHIFT);
                         break;
+                    case 'I': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+                            << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
                 }
             }
             i++;
@@ -820,11 +832,18 @@
         }
     }
 
+    public static final class PackageChange {
+        public String mPackageName;
+        public boolean mUpdate;
+        public int mVersionCode;
+    }
+
     public static final class DailyItem {
         public long mStartTime;
         public long mEndTime;
         public LevelStepTracker mDischargeSteps;
         public LevelStepTracker mChargeSteps;
+        public ArrayList<PackageChange> mPackageChanges;
     }
 
     public abstract DailyItem getDailyItemLocked(int daysAgo);
@@ -1524,6 +1543,23 @@
     public abstract int getDeviceIdleModeEnabledCount(int which);
 
     /**
+     * Returns the time in microseconds that device has been in idling while on
+     * battery.  This is broader than {@link #getDeviceIdleModeEnabledTime} -- it
+     * counts all of the time that we consider the device to be idle, whether or not
+     * it is currently in the actual device idle mode.
+     *
+     * {@hide}
+     */
+    public abstract long getDeviceIdlingTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the devie has started idling.
+     *
+     * {@hide}
+     */
+    public abstract int getDeviceIdlingCount(int which);
+
+    /**
      * Returns the number of times that connectivity state changed.
      *
      * {@hide}
@@ -2069,45 +2105,44 @@
     // Step duration mode: power save is on.
     public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
 
+    // Step duration mode: device is currently in idle mode.
+    public static final int STEP_LEVEL_MODE_DEVICE_IDLE = 0x08;
+
     public static final int[] STEP_LEVEL_MODES_OF_INTEREST = new int[] {
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
     };
     public static final int[] STEP_LEVEL_MODE_VALUES = new int[] {
             (Display.STATE_OFF-1),
             (Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_OFF-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
             (Display.STATE_ON-1),
             (Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE,
             (Display.STATE_DOZE-1),
             (Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE,
             (Display.STATE_DOZE_SUSPEND-1),
             (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
     };
     public static final String[] STEP_LEVEL_MODE_LABELS = new String[] {
             "screen off",
             "screen off power save",
+            "screen off device idle",
             "screen on",
             "screen on power save",
             "screen doze",
             "screen doze power save",
             "screen doze-suspend",
             "screen doze-suspend power save",
-    };
-    public static final String[] STEP_LEVEL_MODE_TAGS = new String[] {
-            "off",
-            "off-save",
-            "on",
-            "on-save",
-            "doze",
-            "doze-save",
-            "susp",
-            "susp-save",
+            "screen doze-suspend device idle",
     };
 
     /**
@@ -2140,6 +2175,8 @@
      */
     public abstract LevelStepTracker getDailyChargeLevelStepTracker();
 
+    public abstract ArrayList<PackageChange> getDailyPackageChanges();
+
     public abstract Map<String, ? extends Timer> getWakeupReasonStats();
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -2338,6 +2375,7 @@
         final long interactiveTime = getInteractiveTime(rawRealtime, which);
         final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
         final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+        final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
         final int connChanges = getNumConnectivityChange(which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
         final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -2410,7 +2448,8 @@
                 0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000,
                 getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
                 powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000,
-                getDeviceIdleModeEnabledCount(which));
+                getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000,
+                getDeviceIdlingCount(which));
         
         // Dump screen brightness stats
         Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -2879,6 +2918,7 @@
         final long interactiveTime = getInteractiveTime(rawRealtime, which);
         final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
         final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+        final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
         final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
         final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -2923,10 +2963,21 @@
                     sb.append(")");
             pw.println(sb.toString());
         }
-        if (deviceIdleModeEnabledTime != 0) {
+        if (deviceIdlingTime != 0) {
             sb.setLength(0);
             sb.append(prefix);
                     sb.append("  Device idling: ");
+                    formatTimeMs(sb, deviceIdlingTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
+                    sb.append(") "); sb.append(getDeviceIdlingCount(which));
+                    sb.append("x");
+            pw.println(sb.toString());
+        }
+        if (deviceIdleModeEnabledTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Idle mode time: ");
                     formatTimeMs(sb, deviceIdleModeEnabledTime / 1000);
                     sb.append("(");
                     sb.append(formatRatioLocked(deviceIdleModeEnabledTime, whichBatteryRealtime));
@@ -4403,6 +4454,11 @@
                 } else {
                     lineArgs[3] = "";
                 }
+                if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                    lineArgs[3] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-";
+                } else {
+                    lineArgs[3] = "";
+                }
                 dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
             } else {
                 pw.print(prefix);
@@ -4427,6 +4483,12 @@
                             ? "power-save-on" : "power-save-off");
                     haveModes = true;
                 }
+                if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                    pw.print(haveModes ? ", " : " (");
+                    pw.print((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+                            ? "device-idle-on" : "device-idle-off");
+                    haveModes = true;
+                }
                 if (haveModes) {
                     pw.print(")");
                 }
@@ -4558,6 +4620,23 @@
         }
     }
 
+    private void dumpDailyPackageChanges(PrintWriter pw, String prefix,
+            ArrayList<PackageChange> changes) {
+        if (changes == null) {
+            return;
+        }
+        pw.print(prefix); pw.println("Package changes:");
+        for (int i=0; i<changes.size(); i++) {
+            PackageChange pc = changes.get(i);
+            if (pc.mUpdate) {
+                pw.print(prefix); pw.print("  Update "); pw.print(pc.mPackageName);
+                pw.print(" vers="); pw.println(pc.mVersionCode);
+            } else {
+                pw.print(prefix); pw.print("  Uninstall "); pw.println(pc.mPackageName);
+            }
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -4688,7 +4767,8 @@
             int[] outInt = new int[1];
             LevelStepTracker dsteps = getDailyDischargeLevelStepTracker();
             LevelStepTracker csteps = getDailyChargeLevelStepTracker();
-            if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0) {
+            ArrayList<PackageChange> pkgc = getDailyPackageChanges();
+            if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0 || pkgc != null) {
                 if ((flags&DUMP_DAILY_ONLY) != 0) {
                     if (dumpDurationSteps(pw, "    ", "  Current daily discharge step durations:",
                             dsteps, false)) {
@@ -4700,6 +4780,7 @@
                         dumpDailyLevelStepSummary(pw, "      ", "Charge", csteps,
                                 sb, outInt);
                     }
+                    dumpDailyPackageChanges(pw, "    ", pkgc);
                 } else {
                     pw.println("  Current daily steps:");
                     dumpDailyLevelStepSummary(pw, "    ", "Discharge", dsteps,
@@ -4731,6 +4812,7 @@
                         dumpDailyLevelStepSummary(pw, "        ", "Charge", dit.mChargeSteps,
                                 sb, outInt);
                     }
+                    dumpDailyPackageChanges(pw, "    ", dit.mPackageChanges);
                 } else {
                     dumpDailyLevelStepSummary(pw, "    ", "Discharge", dit.mDischargeSteps,
                             sb, outInt);
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 16dac7d..418641f 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -43,6 +43,7 @@
     boolean isInteractive();
     boolean isPowerSaveMode();
     boolean setPowerSaveMode(boolean mode);
+    boolean isDeviceIdleMode();
 
     void reboot(boolean confirm, String reason, boolean wait);
     void shutdown(boolean confirm, boolean wait);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index de970cb..81745b3 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -856,6 +856,23 @@
     }
 
     /**
+     * Returns true if the device is currently in idle mode.  This happens when a device
+     * has been sitting unused and unmoving for a sufficiently long period of time, so that
+     * it decides to go into a lower power-use state.  This may involve things like turning
+     * off network access to apps.  You can monitor for changes to this state with
+     * {@link #ACTION_DEVICE_IDLE_MODE_CHANGED}.
+     *
+     * @return Returns true if currently in low power mode, else false.
+     */
+    public boolean isDeviceIdleMode() {
+        try {
+            return mService.isDeviceIdleMode();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Turn off the device.
      *
      * @param confirm If true, shows a shutdown confirmation dialog.
@@ -879,6 +896,14 @@
             = "android.os.action.POWER_SAVE_MODE_CHANGED";
 
     /**
+     * Intent that is broadcast when the state of {@link #isDeviceIdleMode()} changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_IDLE_MODE_CHANGED
+            = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
+
+    /**
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
      * This broadcast is only sent to registered receivers.
      *
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 6f31768..00ab262 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -117,7 +117,7 @@
     /**
      * Used by the dream manager to override certain properties while dozing.
      *
-     * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN}
+     * @param screenState The overridden screen state, or {@link Display#STATE_UNKNOWN}
      * to disable the override.
      * @param screenBrightness The overridden screen brightness, or
      * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
@@ -132,4 +132,6 @@
     public interface LowPowerModeListener {
         public void onLowPowerModeChanged(boolean enabled);
     }
+
+    public abstract void setDeviceIdleMode(boolean enabled);
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 65a970a..eaa0dc7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -109,7 +109,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 120 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 121 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -310,6 +310,9 @@
     boolean mPowerSaveModeEnabled;
     StopwatchTimer mPowerSaveModeEnabledTimer;
 
+    boolean mDeviceIdling;
+    StopwatchTimer mDeviceIdlingTimer;
+
     boolean mDeviceIdleModeEnabled;
     StopwatchTimer mDeviceIdleModeEnabledTimer;
 
@@ -414,6 +417,7 @@
     int mMinDischargeStepLevel;
     final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
     final LevelStepTracker mDailyDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
+    ArrayList<PackageChange> mDailyPackageChanges;
 
     int mLastChargeStepLevel;
     int mMaxChargeStepLevel;
@@ -3417,9 +3421,26 @@
     }
 
     public void noteDeviceIdleModeLocked(boolean enabled, boolean fromActive, boolean fromMotion) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        boolean nowIdling = enabled;
+        if (mDeviceIdling && !enabled && !fromActive && !fromMotion) {
+            // We don't go out of general idling mode until explicitly taken out of
+            // device idle through going active or significant motion.
+            nowIdling = true;
+        }
+        if (mDeviceIdling != nowIdling) {
+            mDeviceIdling = nowIdling;
+            int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0;
+            mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
+            mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
+            if (enabled) {
+                mDeviceIdlingTimer.startRunningLocked(elapsedRealtime);
+            } else {
+                mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime);
+            }
+        }
         if (mDeviceIdleModeEnabled != enabled) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            final long uptime = SystemClock.uptimeMillis();
             mDeviceIdleModeEnabled = enabled;
             if (fromMotion) {
                 addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SIGNIFICANT_MOTION,
@@ -3449,7 +3470,11 @@
         final long uptime = SystemClock.uptimeMillis();
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
                 pkgName, versionCode);
-        mNumConnectivityChange++;
+        PackageChange pc = new PackageChange();
+        pc.mPackageName = pkgName;
+        pc.mUpdate = true;
+        pc.mVersionCode = versionCode;
+        addPackageChange(pc);
     }
 
     public void notePackageUninstalledLocked(String pkgName) {
@@ -3457,7 +3482,17 @@
         final long uptime = SystemClock.uptimeMillis();
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
                 pkgName, 0);
-        mNumConnectivityChange++;
+        PackageChange pc = new PackageChange();
+        pc.mPackageName = pkgName;
+        pc.mUpdate = true;
+        addPackageChange(pc);
+    }
+
+    private void addPackageChange(PackageChange pc) {
+        if (mDailyPackageChanges == null) {
+            mDailyPackageChanges = new ArrayList<>();
+        }
+        mDailyPackageChanges.add(pc);
     }
 
     public void notePhoneOnLocked() {
@@ -4262,6 +4297,14 @@
         return mDeviceIdleModeEnabledTimer.getCountLocked(which);
     }
 
+    @Override public long getDeviceIdlingTime(long elapsedRealtimeUs, int which) {
+        return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+    }
+
+    @Override public int getDeviceIdlingCount(int which) {
+        return mDeviceIdlingTimer.getCountLocked(which);
+    }
+
     @Override public int getNumConnectivityChange(int which) {
         int val = mNumConnectivityChange;
         if (which == STATS_CURRENT) {
@@ -6724,6 +6767,7 @@
         mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase);
         mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
         mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase);
+        mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null,
@@ -6849,6 +6893,11 @@
                     mDailyChargeStepTracker.mNumStepDurations,
                     mDailyChargeStepTracker.mStepDurations);
         }
+        if (mDailyPackageChanges != null) {
+            hasData = true;
+            item.mPackageChanges = mDailyPackageChanges;
+            mDailyPackageChanges = null;
+        }
         mDailyDischargeStepTracker.init();
         mDailyChargeStepTracker.init();
         updateDailyDeadlineLocked();
@@ -6899,6 +6948,21 @@
             out.attribute(null, "end", Long.toString(dit.mEndTime));
             writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
             writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
+            if (dit.mPackageChanges != null) {
+                for (int j=0; j<dit.mPackageChanges.size(); j++) {
+                    PackageChange pc = dit.mPackageChanges.get(j);
+                    if (pc.mUpdate) {
+                        out.startTag(null, "upd");
+                        out.attribute(null, "pkg", pc.mPackageName);
+                        out.attribute(null, "ver", Integer.toString(pc.mVersionCode));
+                        out.endTag(null, "upd");
+                    } else {
+                        out.startTag(null, "rem");
+                        out.attribute(null, "pkg", pc.mPackageName);
+                        out.endTag(null, "rem");
+                    }
+                }
+            }
             out.endTag(null, "item");
         }
         out.endTag(null, "daily-items");
@@ -7011,6 +7075,26 @@
                 readDailyItemTagDetailsLocked(parser, dit, false, "dis");
             } else if (tagName.equals("chg")) {
                 readDailyItemTagDetailsLocked(parser, dit, true, "chg");
+            } else if (tagName.equals("upd")) {
+                if (dit.mPackageChanges == null) {
+                    dit.mPackageChanges = new ArrayList<>();
+                }
+                PackageChange pc = new PackageChange();
+                pc.mUpdate = true;
+                pc.mPackageName = parser.getAttributeValue(null, "pkg");
+                String verStr = parser.getAttributeValue(null, "ver");
+                pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0;
+                dit.mPackageChanges.add(pc);
+                XmlUtils.skipCurrentTag(parser);
+            } else if (tagName.equals("rem")) {
+                if (dit.mPackageChanges == null) {
+                    dit.mPackageChanges = new ArrayList<>();
+                }
+                PackageChange pc = new PackageChange();
+                pc.mUpdate = false;
+                pc.mPackageName = parser.getAttributeValue(null, "pkg");
+                dit.mPackageChanges.add(pc);
+                XmlUtils.skipCurrentTag(parser);
             } else {
                 Slog.w(TAG, "Unknown element under <item>: "
                         + parser.getName());
@@ -7295,6 +7379,7 @@
         mInteractiveTimer.reset(false);
         mPowerSaveModeEnabledTimer.reset(false);
         mDeviceIdleModeEnabledTimer.reset(false);
+        mDeviceIdlingTimer.reset(false);
         mPhoneOnTimer.reset(false);
         mAudioOnTimer.reset(false);
         mVideoOnTimer.reset(false);
@@ -8083,6 +8168,11 @@
         return mDailyChargeStepTracker;
     }
 
+    @Override
+    public ArrayList<PackageChange> getDailyPackageChanges() {
+        return mDailyPackageChanges;
+    }
+
     long getBatteryUptimeLocked() {
         return mOnBatteryTimeBase.getUptime(SystemClock.uptimeMillis() * 1000);
     }
@@ -8583,6 +8673,20 @@
         mChargeStepTracker.readFromParcel(in);
         mDailyDischargeStepTracker.readFromParcel(in);
         mDailyChargeStepTracker.readFromParcel(in);
+        int NPKG = in.readInt();
+        if (NPKG > 0) {
+            mDailyPackageChanges = new ArrayList<>(NPKG);
+            while (NPKG > 0) {
+                NPKG--;
+                PackageChange pc = new PackageChange();
+                pc.mPackageName = in.readString();
+                pc.mUpdate = in.readInt() != 0;
+                pc.mVersionCode = in.readInt();
+                mDailyPackageChanges.add(pc);
+            }
+        } else {
+            mDailyPackageChanges = null;
+        }
         mDailyStartTime = in.readLong();
         mNextMinDailyDeadline = in.readLong();
         mNextMaxDailyDeadline = in.readLong();
@@ -8599,6 +8703,7 @@
         mPhoneOn = false;
         mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
         mDeviceIdleModeEnabledTimer.readSummaryFromParcelLocked(in);
+        mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
         mPhoneOnTimer.readSummaryFromParcelLocked(in);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
@@ -8890,6 +8995,18 @@
         mChargeStepTracker.writeToParcel(out);
         mDailyDischargeStepTracker.writeToParcel(out);
         mDailyChargeStepTracker.writeToParcel(out);
+        if (mDailyPackageChanges != null) {
+            final int NPKG = mDailyPackageChanges.size();
+            out.writeInt(NPKG);
+            for (int i=0; i<NPKG; i++) {
+                PackageChange pc = mDailyPackageChanges.get(i);
+                out.writeString(pc.mPackageName);
+                out.writeInt(pc.mUpdate ? 1 : 0);
+                out.writeInt(pc.mVersionCode);
+            }
+        } else {
+            out.writeInt(0);
+        }
         out.writeLong(mDailyStartTime);
         out.writeLong(mNextMinDailyDeadline);
         out.writeLong(mNextMaxDailyDeadline);
@@ -8901,6 +9018,7 @@
         mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mDeviceIdleModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -9201,6 +9319,7 @@
         mPhoneOn = false;
         mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
         mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in);
+        mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase, in);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
@@ -9367,6 +9486,7 @@
         mInteractiveTimer.writeToParcel(out, uSecRealtime);
         mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
         mDeviceIdleModeEnabledTimer.writeToParcel(out, uSecRealtime);
+        mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
         mPhoneOnTimer.writeToParcel(out, uSecRealtime);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
@@ -9507,6 +9627,8 @@
             mPowerSaveModeEnabledTimer.logState(pr, "  ");
             pr.println("*** Device idle mode timer:");
             mDeviceIdleModeEnabledTimer.logState(pr, "  ");
+            pr.println("*** Device idling timer:");
+            mDeviceIdlingTimer.logState(pr, "  ");
             pr.println("*** Phone timer:");
             mPhoneOnTimer.logState(pr, "  ");
             for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index fe1260d..53bdbea 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -41,6 +41,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -109,21 +110,22 @@
      * Track Services that have currently active or pending jobs. The index is provided by
      * {@link JobStatus#getServiceToken()}
      */
-    final List<JobServiceContext> mActiveServices = new ArrayList<JobServiceContext>();
+    final List<JobServiceContext> mActiveServices = new ArrayList<>();
     /** List of controllers that will notify this service of updates to jobs. */
     List<StateController> mControllers;
     /**
      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
      * when ready to execute them.
      */
-    final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>();
+    final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
 
-    final ArrayList<Integer> mStartedUsers = new ArrayList();
+    final ArrayList<Integer> mStartedUsers = new ArrayList<>();
 
     final JobHandler mHandler;
     final JobSchedulerStub mJobSchedulerStub;
 
     IBatteryStats mBatteryStats;
+    PowerManager mPowerManager;
 
     /**
      * Set to true once we are allowed to run third party apps.
@@ -131,6 +133,11 @@
     boolean mReadyToRock;
 
     /**
+     * True when in device idle mode, so we don't want to schedule any jobs.
+     */
+    boolean mDeviceIdleMode;
+
+    /**
      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
      * still clean up. On reinstall the package will have a new uid.
      */
@@ -154,6 +161,8 @@
                     Slog.d(TAG, "Removing jobs for user: " + userId);
                 }
                 cancelJobsForUser(userId);
+            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
+                updateIdleMode(mPowerManager != null ? mPowerManager.isDeviceIdleMode() : false);
             }
         }
     };
@@ -199,7 +208,7 @@
         return outList;
     }
 
-    private void cancelJobsForUser(int userHandle) {
+    void cancelJobsForUser(int userHandle) {
         List<JobStatus> jobsForUser;
         synchronized (mJobs) {
             jobsForUser = mJobs.getJobsByUser(userHandle);
@@ -257,6 +266,40 @@
         }
     }
 
+    void updateIdleMode(boolean enabled) {
+        boolean changed = false;
+        boolean rocking;
+        synchronized (mJobs) {
+            if (mDeviceIdleMode != enabled) {
+                changed = true;
+            }
+            rocking = mReadyToRock;
+        }
+        if (changed) {
+            if (rocking) {
+                for (int i=0; i<mControllers.size(); i++) {
+                    mControllers.get(i).deviceIdleModeChanged(enabled);
+                }
+            }
+            synchronized (mJobs) {
+                mDeviceIdleMode = enabled;
+                if (enabled) {
+                    // When becoming idle, make sure no jobs are actively running.
+                    for (int i=0; i<mActiveServices.size(); i++) {
+                        JobServiceContext jsc = mActiveServices.get(i);
+                        final JobStatus executing = jsc.getRunningJob();
+                        if (executing != null) {
+                            jsc.cancelExecutingJob();
+                        }
+                    }
+                } else {
+                    // When coming out of idle, allow thing to start back up.
+                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+                }
+            }
+        }
+    }
+
     /**
      * Initializes the system service.
      * <p>
@@ -294,8 +337,10 @@
             getContext().registerReceiverAsUser(
                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+            userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
             getContext().registerReceiverAsUser(
                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+            mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mJobs) {
                 // Let's go!
@@ -313,6 +358,7 @@
                 for (int i=0; i<jobs.size(); i++) {
                     JobStatus job = jobs.valueAt(i);
                     for (int controller=0; controller<mControllers.size(); controller++) {
+                        mControllers.get(i).deviceIdleModeChanged(mDeviceIdleMode);
                         mControllers.get(controller).maybeStartTrackingJob(job);
                     }
                 }
@@ -667,6 +713,10 @@
          */
         private void maybeRunPendingJobsH() {
             synchronized (mJobs) {
+                if (mDeviceIdleMode) {
+                    // If device is idle, we will not schedule jobs to run.
+                    return;
+                }
                 Iterator<JobStatus> it = mPendingJobs.iterator();
                 if (DEBUG) {
                     Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
@@ -878,6 +928,7 @@
             }
             pw.println();
             pw.print("mReadyToRock="); pw.println(mReadyToRock);
+            pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
         }
         pw.println();
     }
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 7d76fc0..efd1928 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -31,12 +31,17 @@
     protected static final boolean DEBUG = false;
     protected Context mContext;
     protected StateChangedListener mStateChangedListener;
+    protected boolean mDeviceIdleMode;
 
     public StateController(StateChangedListener stateChangedListener, Context context) {
         mStateChangedListener = stateChangedListener;
         mContext = context;
     }
 
+    public void deviceIdleModeChanged(boolean enabled) {
+        mDeviceIdleMode = enabled;
+    }
+
     /**
      * Implement the logic here to decide whether a job should be tracked by this controller.
      * This logic is put here so the JobManger can be completely agnostic of Controller logic.
@@ -50,5 +55,4 @@
     public abstract void maybeStopTrackingJob(JobStatus jobStatus);
 
     public abstract void dumpControllerState(PrintWriter pw);
-
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8d46775..5de7d42 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -71,7 +71,9 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.Manifest;
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
@@ -83,6 +85,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
@@ -2021,6 +2024,17 @@
     void updateRulesForUidLocked(int uid) {
         if (!isUidValidForRules(uid)) return;
 
+        // quick check: if this uid doesn't have INTERNET permission, it doesn't have
+        // network access anyway, so it is a waste to mess with it here.
+        final IPackageManager ipm = AppGlobals.getPackageManager();
+        try {
+            if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+        } catch (RemoteException e) {
+        }
+
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
         final boolean uidForeground = isUidForegroundLocked(uid);
 
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
similarity index 84%
rename from services/core/java/com/android/server/DeviceIdleController.java
rename to services/core/java/com/android/server/power/DeviceIdleController.java
index 062992d..dd00446 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.power;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -30,12 +30,16 @@
 import android.hardware.display.DisplayManager;
 import android.net.INetworkPolicyManager;
 import android.os.Binder;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.TimeUtils;
 import android.view.Display;
 import com.android.internal.app.IBatteryStats;
+import com.android.server.SystemService;
 import com.android.server.am.BatteryStatsService;
 
 import java.io.FileDescriptor;
@@ -60,6 +64,11 @@
      */
     private static final long DEFAULT_INACTIVE_TIMEOUT = 30*60*1000L;
     /**
+     * This is the time, after seeing motion, that we wait after becoming inactive from
+     * that until we start looking for motion again.
+     */
+    private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = 10*60*1000L;
+    /**
      * This is the time, after the inactive timeout elapses, that we will wait looking
      * for significant motion until we truly consider the device to be idle.
      */
@@ -94,11 +103,13 @@
 
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
+    private PowerManagerInternal mLocalPowerManager;
     private INetworkPolicyManager mNetworkPolicyManager;
     private DisplayManager mDisplayManager;
     private SensorManager mSensorManager;
     private Sensor mSigMotionSensor;
     private PendingIntent mAlarmIntent;
+    private Intent mIdleIntent;
     private Display mCurDisplay;
     private boolean mScreenOn;
     private boolean mCharging;
@@ -124,6 +135,7 @@
 
     private int mState;
 
+    private long mInactiveTimeout;
     private long mNextAlarmTime;
     private long mNextIdlePendingDelay;
     private long mNextIdleDelay;
@@ -181,6 +193,7 @@
         synchronized (this) {
             mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
             mBatteryStats = BatteryStatsService.getService();
+            mLocalPowerManager = getLocalService(PowerManagerInternal.class);
             mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                                 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
             mDisplayManager = (DisplayManager) getContext().getSystemService(
@@ -193,6 +206,9 @@
                     .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
 
+            mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+            mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
             filter.addAction(ACTION_STEP_IDLE_STATE);
@@ -205,6 +221,7 @@
             // a battery update the next time the level drops.
             mCharging = true;
             mState = STATE_ACTIVE;
+            mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
             updateDisplayLocked();
         }
 
@@ -238,12 +255,17 @@
 
     void becomeActiveLocked() {
         if (mState != STATE_ACTIVE) {
+            mLocalPowerManager.setDeviceIdleMode(false);
             try {
                 mNetworkPolicyManager.setDeviceIdleMode(false);
                 mBatteryStats.noteDeviceIdleMode(false, true, false);
             } catch (RemoteException e) {
             }
+            if (mState == STATE_IDLE) {
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+            }
             mState = STATE_ACTIVE;
+            mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
             mNextIdlePendingDelay = 0;
             mNextIdleDelay = 0;
             cancelAlarmLocked();
@@ -256,7 +278,9 @@
             // Screen has turned off; we are now going to become inactive and start
             // waiting to see if we will ultimately go idle.
             mState = STATE_INACTIVE;
-            scheduleAlarmLocked(DEFAULT_INACTIVE_TIMEOUT, false);
+            mNextIdlePendingDelay = 0;
+            mNextIdleDelay = 0;
+            scheduleAlarmLocked(mInactiveTimeout, false);
         }
     }
 
@@ -283,11 +307,13 @@
                     mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
                 }
                 mState = STATE_IDLE;
+                mLocalPowerManager.setDeviceIdleMode(true);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(true);
                     mBatteryStats.noteDeviceIdleMode(true, false, false);
                 } catch (RemoteException e) {
                 }
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                 break;
             case STATE_IDLE:
                 // We have been idling long enough, now it is time to do some work.
@@ -297,11 +323,13 @@
                     mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
                 }
                 mState = STATE_IDLE_PENDING;
+                mLocalPowerManager.setDeviceIdleMode(false);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(false);
                     mBatteryStats.noteDeviceIdleMode(false, false, false);
                 } catch (RemoteException e) {
                 }
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                 break;
         }
     }
@@ -313,13 +341,18 @@
         // state to wait again for no motion.  Note that we only monitor for significant
         // motion after moving out of the inactive state, so no need to worry about that.
         if (mState != STATE_ACTIVE) {
-            mState = STATE_INACTIVE;
+            mLocalPowerManager.setDeviceIdleMode(false);
             try {
                 mNetworkPolicyManager.setDeviceIdleMode(false);
                 mBatteryStats.noteDeviceIdleMode(false, false, true);
             } catch (RemoteException e) {
             }
-            stepIdleStateLocked();
+            if (mState == STATE_IDLE) {
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+            }
+            mState = STATE_ACTIVE;
+            mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+            becomeInactiveIfAppropriateLocked();
         }
     }
 
@@ -399,26 +432,30 @@
             }
         }
 
-        pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
-        pw.print("  mCurDisplay="); pw.println(mCurDisplay);
-        pw.print("  mScreenOn="); pw.println(mScreenOn);
-        pw.print("  mCharging="); pw.println(mCharging);
-        pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
-        pw.print("  mState="); pw.println(stateToString(mState));
-        if (mNextAlarmTime != 0) {
-            pw.print("  mNextAlarmTime=");
-            TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
+        synchronized (this) {
+            pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
+            pw.print("  mCurDisplay="); pw.println(mCurDisplay);
+            pw.print("  mScreenOn="); pw.println(mScreenOn);
+            pw.print("  mCharging="); pw.println(mCharging);
+            pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
+            pw.print("  mState="); pw.println(stateToString(mState));
+            pw.print("  mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw);
             pw.println();
-        }
-        if (mNextIdlePendingDelay != 0) {
-            pw.print("  mNextIdlePendingDelay=");
-            TimeUtils.formatDuration(mNextIdlePendingDelay, pw);
-            pw.println();
-        }
-        if (mNextIdleDelay != 0) {
-            pw.print("  mNextIdleDelay=");
-            TimeUtils.formatDuration(mNextIdleDelay, pw);
-            pw.println();
+            if (mNextAlarmTime != 0) {
+                pw.print("  mNextAlarmTime=");
+                TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
+                pw.println();
+            }
+            if (mNextIdlePendingDelay != 0) {
+                pw.print("  mNextIdlePendingDelay=");
+                TimeUtils.formatDuration(mNextIdlePendingDelay, pw);
+                pw.println();
+            }
+            if (mNextIdleDelay != 0) {
+                pw.print("  mNextIdleDelay=");
+                TimeUtils.formatDuration(mNextIdleDelay, pw);
+                pw.println();
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9e373b7..33b451d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -420,6 +420,9 @@
     // True if the battery level is currently considered low.
     private boolean mBatteryLevelLow;
 
+    // True if we are currently in device idle mode.
+    private boolean mDeviceIdleMode;
+
     // True if theater mode is enabled
     private boolean mTheaterModeEnabled;
 
@@ -2178,6 +2181,12 @@
         }
     }
 
+    private boolean isDeviceIdleModeInternal() {
+        synchronized (mLock) {
+            return mDeviceIdleMode;
+        }
+    }
+
     private void handleBatteryStateChangedLocked() {
         mDirty |= DIRTY_BATTERY_STATE;
         updatePowerStateLocked();
@@ -3050,6 +3059,16 @@
             }
         }
 
+        @Override // Binder call
+        public boolean isDeviceIdleMode() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return isDeviceIdleModeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         /**
          * Reboots the device.
          *
@@ -3295,5 +3314,12 @@
                 mLowPowerModeListeners.add(listener);
             }
         }
+
+        @Override
+        public void setDeviceIdleMode(boolean enabled) {
+            synchronized (mLock) {
+                mDeviceIdleMode = enabled;
+            }
+        }
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ae2c54b..afce948 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -66,7 +66,6 @@
 import com.android.server.media.MediaRouterService;
 import com.android.server.media.MediaSessionService;
 import com.android.server.media.projection.MediaProjectionManagerService;
-import com.android.server.MidiService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
 import com.android.server.notification.NotificationManagerService;
@@ -76,6 +75,7 @@
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.UserManagerService;
+import com.android.server.power.DeviceIdleController;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
 import com.android.server.restrictions.RestrictionsManagerService;