Rewrite app standby stats

Don't mix up with usage stats. Keep a separate db and history
based on elapsed time and screen on time.

Unit tests for AppIdleHistory class.

Bug: 26989006
Change-Id: If343785b46da1db67f7c1c1263854c2732a232c6
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index a88aa3125..2937ccc 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -47,20 +47,6 @@
     public long mLastTimeUsed;
 
     /**
-     * The last time the package was used via implicit, non-user initiated actions (service
-     * was bound, etc).
-     * {@hide}
-     */
-    public long mLastTimeSystemUsed;
-
-    /**
-     * Last time the package was used and the beginning of the idle countdown.
-     * This uses a different timebase that is about how much the device has been in use in general.
-     * {@hide}
-     */
-    public long mBeginIdleTime;
-
-    /**
      * {@hide}
      */
     public long mTotalTimeInForeground;
@@ -89,8 +75,6 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
-        mBeginIdleTime = stats.mBeginIdleTime;
-        mLastTimeSystemUsed = stats.mLastTimeSystemUsed;
     }
 
     public String getPackageName() {
@@ -127,25 +111,6 @@
     }
 
     /**
-     * @hide
-     * Get the last time this package was used by the system (not the user). This can be different
-     * from {@link #getLastTimeUsed()} when the system binds to one of this package's services.
-     * See {@link System#currentTimeMillis()}.
-     */
-    public long getLastTimeSystemUsed() {
-        return mLastTimeSystemUsed;
-    }
-
-    /**
-     * @hide
-     * Get the last time this package was active, measured in milliseconds. This timestamp
-     * uses a timebase that represents how much the device was used and not wallclock time.
-     */
-    public long getBeginIdleTime() {
-        return mBeginIdleTime;
-    }
-
-    /**
      * Get the total time this package spent in the foreground, measured in milliseconds.
      */
     public long getTotalTimeInForeground() {
@@ -172,8 +137,6 @@
             // regards to their mEndTimeStamp.
             mLastEvent = right.mLastEvent;
             mLastTimeUsed = right.mLastTimeUsed;
-            mBeginIdleTime = right.mBeginIdleTime;
-            mLastTimeSystemUsed = right.mLastTimeSystemUsed;
         }
         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
@@ -195,8 +158,6 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
-        dest.writeLong(mBeginIdleTime);
-        dest.writeLong(mLastTimeSystemUsed);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -210,8 +171,6 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
-            stats.mBeginIdleTime = in.readLong();
-            stats.mLastTimeSystemUsed = in.readLong();
             return stats;
         }
 
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 3ad26d3..0b87567 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -11,6 +11,7 @@
     services.core \
     services.devicepolicy \
     services.net \
+    services.usage \
     easymocklib \
     guava \
     android-support-test \
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
new file mode 100644
index 0000000..9ccb1a6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import android.os.FileUtils;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+public class AppIdleHistoryTests extends AndroidTestCase {
+
+    File mStorageDir;
+
+    final static String PACKAGE_1 = "com.android.testpackage1";
+    final static String PACKAGE_2 = "com.android.testpackage2";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStorageDir = new File(getContext().getFilesDir(), "appidle");
+        mStorageDir.mkdirs();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        FileUtils.deleteContents(mStorageDir);
+        super.tearDown();
+    }
+
+    public void testFilesCreation() {
+        final int userId = 0;
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);
+
+        aih.updateDisplayLocked(true, /* elapsedRealtime= */ 1000);
+        aih.updateDisplayLocked(false, /* elapsedRealtime= */ 2000);
+        // Screen On time file should be written right away
+        assertTrue(aih.getScreenOnTimeFile().exists());
+
+        aih.writeAppIdleTimesLocked(userId);
+        // stats file should be written now
+        assertTrue(new File(new File(mStorageDir, "users/" + userId),
+                AppIdleHistory.APP_IDLE_FILENAME).exists());
+    }
+
+    public void testScreenOnTime() {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.updateDisplayLocked(false, 2000);
+        assertEquals(aih.getScreenOnTimeLocked(2000), 0);
+        aih.updateDisplayLocked(true, 3000);
+        assertEquals(aih.getScreenOnTimeLocked(4000), 1000);
+        assertEquals(aih.getScreenOnTimeLocked(5000), 2000);
+        aih.updateDisplayLocked(false, 6000);
+        // Screen on time should not keep progressing with screen is off
+        assertEquals(aih.getScreenOnTimeLocked(7000), 3000);
+        assertEquals(aih.getScreenOnTimeLocked(8000), 3000);
+        aih.writeElapsedTimeLocked();
+
+        // Check if the screen on time is persisted across instantiations
+        AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0);
+        assertEquals(aih2.getScreenOnTimeLocked(11000), 3000);
+        aih2.updateDisplayLocked(true, 4000);
+        aih2.updateDisplayLocked(false, 5000);
+        assertEquals(aih2.getScreenOnTimeLocked(13000), 4000);
+    }
+
+    public void testPackageEvents() {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.setThresholds(4000, 1000);
+        aih.updateDisplayLocked(true, 1000);
+        // App is not-idle by default
+        assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 1500));
+        // Still not idle
+        assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 3000));
+        // Idle now
+        assertTrue(aih.isIdleLocked(PACKAGE_1, 0, 8000));
+        // Not idle
+        assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 9000));
+
+        // Screen off
+        aih.updateDisplayLocked(false, 9100);
+        // Still idle after 10 seconds because screen hasn't been on long enough
+        assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 20000));
+        aih.updateDisplayLocked(true, 21000);
+        assertTrue(aih.isIdleLocked(PACKAGE_2, 0, 23000));
+    }
+}
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index e3c0868..3e2b43d 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,19 +16,45 @@
 
 package com.android.server.usage;
 
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
 /**
  * Keeps track of recent active state changes in apps.
  * Access should be guarded by a lock by the caller.
  */
 public class AppIdleHistory {
 
-    private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>();
-    private long lastPeriod = 0;
+    private static final String TAG = "AppIdleHistory";
+
+    // History for all users and all packages
+    private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>();
+    private long mLastPeriod = 0;
     private static final long ONE_MINUTE = 60 * 1000;
     private static final int HISTORY_SIZE = 100;
     private static final int FLAG_LAST_STATE = 2;
@@ -36,77 +62,353 @@
     private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE
             : 60 * ONE_MINUTE;
 
-    public void addEntry(String packageName, int userId, boolean idle, long timeNow) {
-        ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
-        byte[] packageHistory = getPackageHistory(userHistory, packageName);
+    @VisibleForTesting
+    static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
+    private static final String TAG_PACKAGES = "packages";
+    private static final String TAG_PACKAGE = "package";
+    private static final String ATTR_NAME = "name";
+    // Screen on timebase time when app was last used
+    private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
+    // Elapsed timebase time when app was last used
+    private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
 
-        long thisPeriod = timeNow / PERIOD_DURATION;
+    // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
+    private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
+    private long mElapsedDuration; // Total device on duration since device was "born"
+
+    // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
+    private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
+    private long mScreenOnDuration; // Total screen on duration since device was "born"
+
+    private long mElapsedTimeThreshold;
+    private long mScreenOnTimeThreshold;
+    private final File mStorageDir;
+
+    private boolean mScreenOn;
+
+    private static class PackageHistory {
+        final byte[] recent = new byte[HISTORY_SIZE];
+        long lastUsedElapsedTime;
+        long lastUsedScreenTime;
+    }
+
+    AppIdleHistory(long elapsedRealtime) {
+        this(Environment.getDataSystemDirectory(), elapsedRealtime);
+    }
+
+    @VisibleForTesting
+    AppIdleHistory(File storageDir, long elapsedRealtime) {
+        mElapsedSnapshot = elapsedRealtime;
+        mScreenOnSnapshot = elapsedRealtime;
+        mStorageDir = storageDir;
+        readScreenOnTimeLocked();
+    }
+
+    public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
+        mElapsedTimeThreshold = elapsedTimeThreshold;
+        mScreenOnTimeThreshold = screenOnTimeThreshold;
+    }
+
+    public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) {
+        if (screenOn == mScreenOn) return;
+
+        mScreenOn = screenOn;
+        if (mScreenOn) {
+            mScreenOnSnapshot = elapsedRealtime;
+        } else {
+            mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
+            mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
+            writeScreenOnTimeLocked();
+            mElapsedSnapshot = elapsedRealtime;
+        }
+    }
+
+    public long getScreenOnTimeLocked(long elapsedRealtime) {
+        long screenOnTime = mScreenOnDuration;
+        if (mScreenOn) {
+            screenOnTime += elapsedRealtime - mScreenOnSnapshot;
+        }
+        return screenOnTime;
+    }
+
+    @VisibleForTesting
+    File getScreenOnTimeFile() {
+        return new File(mStorageDir, "screen_on_time");
+    }
+
+    private void readScreenOnTimeLocked() {
+        File screenOnTimeFile = getScreenOnTimeFile();
+        if (screenOnTimeFile.exists()) {
+            try {
+                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
+                mScreenOnDuration = Long.parseLong(reader.readLine());
+                mElapsedDuration = Long.parseLong(reader.readLine());
+                reader.close();
+            } catch (IOException | NumberFormatException e) {
+            }
+        } else {
+            writeScreenOnTimeLocked();
+        }
+    }
+
+    private void writeScreenOnTimeLocked() {
+        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
+        FileOutputStream fos = null;
+        try {
+            fos = screenOnTimeFile.startWrite();
+            fos.write((Long.toString(mScreenOnDuration) + "\n"
+                    + Long.toString(mElapsedDuration) + "\n").getBytes());
+            screenOnTimeFile.finishWrite(fos);
+        } catch (IOException ioe) {
+            screenOnTimeFile.failWrite(fos);
+        }
+    }
+
+    /**
+     * To be called periodically to keep track of elapsed time when app idle times are written
+     */
+    public void writeElapsedTimeLocked() {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        // Only bump up and snapshot the elapsed time. Don't change screen on duration.
+        mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
+        mElapsedSnapshot = elapsedRealtime;
+        writeScreenOnTimeLocked();
+    }
+
+    public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+
+        shiftHistoryToNow(userHistory, elapsedRealtime);
+
+        packageHistory.lastUsedElapsedTime = mElapsedDuration
+                + (elapsedRealtime - mElapsedSnapshot);
+        packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
+        packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
+    }
+
+    public void setIdle(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+
+        shiftHistoryToNow(userHistory, elapsedRealtime);
+
+        packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
+    }
+
+    private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory,
+            long elapsedRealtime) {
+        long thisPeriod = elapsedRealtime / PERIOD_DURATION;
         // Has the period switched over? Slide all users' package histories
-        if (lastPeriod != 0 && lastPeriod < thisPeriod
-                && (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) {
-            int diff = (int) (thisPeriod - lastPeriod);
+        if (mLastPeriod != 0 && mLastPeriod < thisPeriod
+                && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) {
+            int diff = (int) (thisPeriod - mLastPeriod);
             final int NUSERS = mIdleHistory.size();
             for (int u = 0; u < NUSERS; u++) {
                 userHistory = mIdleHistory.valueAt(u);
-                for (byte[] history : userHistory.values()) {
+                for (PackageHistory idleState : userHistory.values()) {
                     // Shift left
-                    System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff);
+                    System.arraycopy(idleState.recent, diff, idleState.recent, 0,
+                            HISTORY_SIZE - diff);
                     // Replicate last state across the diff
                     for (int i = 0; i < diff; i++) {
-                        history[HISTORY_SIZE - i - 1] =
-                                (byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
+                        idleState.recent[HISTORY_SIZE - i - 1] =
+                            (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
                     }
                 }
             }
         }
-        lastPeriod = thisPeriod;
-        if (!idle) {
-            packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        } else {
-            packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
-        }
+        mLastPeriod = thisPeriod;
     }
 
-    private ArrayMap<String, byte[]> getUserHistory(int userId) {
-        ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
+    private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) {
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
         if (userHistory == null) {
             userHistory = new ArrayMap<>();
             mIdleHistory.put(userId, userHistory);
+            readAppIdleTimesLocked(userId, userHistory);
         }
         return userHistory;
     }
 
-    private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) {
-        byte[] packageHistory = userHistory.get(packageName);
+    private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory,
+            String packageName, long elapsedRealtime) {
+        PackageHistory packageHistory = userHistory.get(packageName);
         if (packageHistory == null) {
-            packageHistory = new byte[HISTORY_SIZE];
+            packageHistory = new PackageHistory();
+            packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime);
+            packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
             userHistory.put(packageName, packageHistory);
         }
         return packageHistory;
     }
 
-    public void removeUser(int userId) {
+    public void onUserRemoved(int userId) {
         mIdleHistory.remove(userId);
     }
 
-    public boolean isIdle(int userId, String packageName) {
-        ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
-        byte[] packageHistory = getPackageHistory(userHistory, packageName);
-        return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0;
+    public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory =
+                getPackageHistoryLocked(userHistory, packageName, elapsedRealtime);
+        if (packageHistory == null) {
+            return false; // Default to not idle
+        } else {
+            return hasPassedThresholdsLocked(packageHistory, elapsedRealtime);
+        }
+    }
+
+    private long getElapsedTimeLocked(long elapsedRealtime) {
+        return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
+    }
+
+    public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+        packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime)
+                - mElapsedTimeThreshold;
+        packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime)
+                - (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
+    }
+
+    private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
+        return (packageHistory.lastUsedScreenTime
+                    <= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
+                && (packageHistory.lastUsedElapsedTime
+                        <= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold);
+    }
+
+    private File getUserFile(int userId) {
+        return new File(new File(new File(mStorageDir, "users"),
+                Integer.toString(userId)), APP_IDLE_FILENAME);
+    }
+
+    private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) {
+        FileInputStream fis = null;
+        try {
+            AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
+            fis = appIdleFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                // Skip
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                Slog.e(TAG, "Unable to read app idle file for user " + userId);
+                return;
+            }
+            if (!parser.getName().equals(TAG_PACKAGES)) {
+                return;
+            }
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG) {
+                    final String name = parser.getName();
+                    if (name.equals(TAG_PACKAGE)) {
+                        final String packageName = parser.getAttributeValue(null, ATTR_NAME);
+                        PackageHistory packageHistory = new PackageHistory();
+                        packageHistory.lastUsedElapsedTime =
+                                Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
+                        packageHistory.lastUsedScreenTime =
+                                Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
+                        userHistory.put(packageName, packageHistory);
+                    }
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Unable to read app idle file for user " + userId);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    public void writeAppIdleTimesLocked(int userId) {
+        FileOutputStream fos = null;
+        AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
+        try {
+            fos = appIdleFile.startWrite();
+            final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+            FastXmlSerializer xml = new FastXmlSerializer();
+            xml.setOutput(bos, StandardCharsets.UTF_8.name());
+            xml.startDocument(null, true);
+            xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            xml.startTag(null, TAG_PACKAGES);
+
+            ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId);
+            final int N = userHistory.size();
+            for (int i = 0; i < N; i++) {
+                String packageName = userHistory.keyAt(i);
+                PackageHistory history = userHistory.valueAt(i);
+                xml.startTag(null, TAG_PACKAGE);
+                xml.attribute(null, ATTR_NAME, packageName);
+                xml.attribute(null, ATTR_ELAPSED_IDLE,
+                        Long.toString(history.lastUsedElapsedTime));
+                xml.attribute(null, ATTR_SCREEN_IDLE,
+                        Long.toString(history.lastUsedScreenTime));
+                xml.endTag(null, TAG_PACKAGE);
+            }
+
+            xml.endTag(null, TAG_PACKAGES);
+            xml.endDocument();
+            appIdleFile.finishWrite(fos);
+        } catch (Exception e) {
+            appIdleFile.failWrite(fos);
+            Slog.e(TAG, "Error writing app idle file for user " + userId);
+        }
     }
 
     public void dump(IndentingPrintWriter idpw, int userId) {
-        ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
+        idpw.println("Package idle stats:");
+        idpw.increaseIndent();
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime);
+        final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime);
         if (userHistory == null) return;
         final int P = userHistory.size();
         for (int p = 0; p < P; p++) {
             final String packageName = userHistory.keyAt(p);
-            final byte[] history = userHistory.valueAt(p);
+            final PackageHistory packageHistory = userHistory.valueAt(p);
+            idpw.print("package=" + packageName);
+            idpw.print(" lastUsedElapsed=");
+            TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw);
+            idpw.print(" lastUsedScreenOn=");
+            TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw);
+            idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
+            idpw.println();
+        }
+        idpw.println();
+        idpw.print("totalElapsedTime=");
+        TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw);
+        idpw.println();
+        idpw.print("totalScreenOnTime=");
+        TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw);
+        idpw.println();
+        idpw.decreaseIndent();
+    }
+
+    public void dumpHistory(IndentingPrintWriter idpw, int userId) {
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        if (userHistory == null) return;
+        final int P = userHistory.size();
+        for (int p = 0; p < P; p++) {
+            final String packageName = userHistory.keyAt(p);
+            final byte[] history = userHistory.valueAt(p).recent;
             for (int i = 0; i < HISTORY_SIZE; i++) {
                 idpw.print(history[i] == 0 ? '.' : 'A');
             }
+            idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
             idpw.print("  " + packageName);
             idpw.println();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 7f379fe..f541f70 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -48,7 +48,6 @@
             usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
-            usageStats.mBeginIdleTime = 0;
             packageStats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
@@ -113,7 +112,6 @@
         if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
             usageStats.mLastTimeUsed = timeStamp;
         }
-        usageStats.mLastTimeSystemUsed = timeStamp;
         usageStats.mEndTimeStamp = timeStamp;
 
         if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
@@ -123,22 +121,6 @@
         endTime = timeStamp;
     }
 
-    /**
-     * Updates the last active time for the package. The timestamp uses a timebase that
-     * tracks the device usage time.
-     * @param packageName
-     * @param timeStamp
-     */
-    void updateBeginIdleTime(String packageName, long timeStamp) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.mBeginIdleTime = timeStamp;
-    }
-
-    void updateSystemLastUsedTime(String packageName, long lastUsedTime) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.mLastTimeSystemUsed = lastUsedTime;
-    }
-
     void updateConfigurationStats(Configuration config, long timeStamp) {
         if (activeConfiguration != null) {
             ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 77740387..46ad8a1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -40,6 +40,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.display.DisplayManager;
@@ -62,7 +63,6 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -77,12 +77,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.SystemService;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -106,7 +102,7 @@
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
     private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
 
-    long mAppIdleDurationMillis;
+    long mAppIdleScreenThresholdMillis;
     long mCheckIdleIntervalMillis;
     long mAppIdleWallclockThresholdMillis;
     long mAppIdleParoleIntervalMillis;
@@ -147,11 +143,8 @@
 
     private volatile boolean mPendingOneTimeCheckIdleStates;
 
-    long mScreenOnTime;
-    long mLastScreenOnEventRealtime;
-
     @GuardedBy("mLock")
-    private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
+    private AppIdleHistory mAppIdleHistory;
 
     private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
@@ -191,8 +184,7 @@
 
         synchronized (mLock) {
             cleanUpRemovedUsersLocked();
-            mLastScreenOnEventRealtime = SystemClock.elapsedRealtime();
-            mScreenOnTime = readScreenOnTimeLocked();
+            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
         }
 
         mRealTimeSnapshot = SystemClock.elapsedRealtime();
@@ -221,7 +213,7 @@
 
             mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
             synchronized (mLock) {
-                updateDisplayLocked();
+                mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime());
             }
 
             if (mPendingOneTimeCheckIdleStates) {
@@ -232,6 +224,11 @@
         }
     }
 
+    private boolean isDisplayOn() {
+        return mDisplayManager
+                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+    }
+
     private class UserActionsReceiver extends BroadcastReceiver {
 
         @Override
@@ -274,7 +271,8 @@
         @Override public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
                 synchronized (UsageStatsService.this.mLock) {
-                    updateDisplayLocked();
+                    mAppIdleHistory.updateDisplayLocked(isDisplayOn(),
+                            SystemClock.elapsedRealtime());
                 }
             }
         }
@@ -291,8 +289,25 @@
     }
 
     @Override
-    public long getAppIdleRollingWindowDurationMillis() {
-        return mAppIdleWallclockThresholdMillis * 2;
+    public void onNewUpdate(int userId) {
+        initializeDefaultsForSystemApps(userId);
+    }
+
+    private void initializeDefaultsForSystemApps(int userId) {
+        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackagesAsUser(
+                PackageManager.MATCH_DISABLED_COMPONENTS
+                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                userId);
+        final int packageCount = packages.size();
+        for (int i = 0; i < packageCount; i++) {
+            final PackageInfo pi = packages.get(i);
+            String packageName = pi.packageName;
+            if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+                mAppIdleHistory.reportUsageLocked(packageName, userId, elapsedRealtime);
+            }
+        }
     }
 
     private void cleanUpRemovedUsersLocked() {
@@ -350,7 +365,7 @@
         if (timeLeft < 0) {
             timeLeft = 0;
         }
-        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft / 10);
+        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
     }
 
     private void postParoleEndTimeout() {
@@ -400,28 +415,27 @@
             return;
         }
 
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
         for (int i = 0; i < userIds.length; i++) {
             final int userId = userIds[i];
             List<PackageInfo> packages =
                     getContext().getPackageManager().getInstalledPackagesAsUser(
-                            PackageManager.GET_DISABLED_COMPONENTS
-                                | PackageManager.GET_UNINSTALLED_PACKAGES,
+                            PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
                             userId);
             synchronized (mLock) {
-                final long timeNow = checkAndGetTimeLocked();
-                final long screenOnTime = getScreenOnTimeLocked();
-                UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId,
-                        timeNow);
                 final int packageCount = packages.size();
                 for (int p = 0; p < packageCount; p++) {
                     final PackageInfo pi = packages.get(p);
                     final String packageName = pi.packageName;
                     final boolean isIdle = isAppIdleFiltered(packageName,
                             UserHandle.getAppId(pi.applicationInfo.uid),
-                            userId, service, timeNow, screenOnTime);
+                            userId, elapsedRealtime);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
                             userId, isIdle ? 1 : 0, packageName));
-                    mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
+                    if (isIdle) {
+                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+                    }
                 }
             }
         }
@@ -458,62 +472,6 @@
         }
     }
 
-    void updateDisplayLocked() {
-        boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
-                == Display.STATE_ON;
-
-        if (screenOn == mScreenOn) return;
-
-        mScreenOn = screenOn;
-        long now = SystemClock.elapsedRealtime();
-        if (mScreenOn) {
-            mLastScreenOnEventRealtime = now;
-        } else {
-            mScreenOnTime += now - mLastScreenOnEventRealtime;
-            writeScreenOnTimeLocked(mScreenOnTime);
-        }
-    }
-
-    long getScreenOnTimeLocked() {
-        long screenOnTime = mScreenOnTime;
-        if (mScreenOn) {
-            screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime;
-        }
-        return screenOnTime;
-    }
-
-    private File getScreenOnTimeFile() {
-        return new File(mUsageStatsDir, UserHandle.USER_SYSTEM + "/screen_on_time");
-    }
-
-    private long readScreenOnTimeLocked() {
-        long screenOnTime = 0;
-        File screenOnTimeFile = getScreenOnTimeFile();
-        if (screenOnTimeFile.exists()) {
-            try {
-                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
-                screenOnTime = Long.parseLong(reader.readLine());
-                reader.close();
-            } catch (IOException | NumberFormatException e) {
-            }
-        } else {
-            writeScreenOnTimeLocked(screenOnTime);
-        }
-        return screenOnTime;
-    }
-
-    private void writeScreenOnTimeLocked(long screenOnTime) {
-        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
-        FileOutputStream fos = null;
-        try {
-            fos = screenOnTimeFile.startWrite();
-            fos.write(Long.toString(screenOnTime).getBytes());
-            screenOnTimeFile.finishWrite(fos);
-        } catch (IOException ioe) {
-            screenOnTimeFile.failWrite(fos);
-        }
-    }
-
     void onDeviceIdleModeChanged() {
         final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
         if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
@@ -549,7 +507,7 @@
         if (service == null) {
             service = new UserUsageStatsService(getContext(), userId,
                     new File(mUsageStatsDir, Integer.toString(userId)), this);
-            service.init(currentTimeMillis, getScreenOnTimeLocked());
+            service.init(currentTimeMillis);
             mUserState.put(userId, service);
         }
         return service;
@@ -569,8 +527,7 @@
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
                 final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(),
-                        false);
+                service.onTimeChanged(expectedSystemTime, actualSystemTime);
             }
             mRealTimeSnapshot = actualRealtime;
             mSystemTimeSnapshot = actualSystemTime;
@@ -602,26 +559,26 @@
     void reportEvent(UsageEvents.Event event, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked();
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             convertToSystemTimeLocked(event);
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long beginIdleTime = service.getBeginIdleTime(event.mPackage);
-            final long lastUsedTime = service.getSystemLastUsedTime(event.mPackage);
-            final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
-                    lastUsedTime, screenOnTime, timeNow);
-            service.reportEvent(event, screenOnTime);
+            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+            // about apps that are on some kind of whitelist anyway.
+            final boolean previouslyIdle = mAppIdleHistory.isIdleLocked(
+                    event.mPackage, userId, elapsedRealtime);
+            service.reportEvent(event);
             // Inform listeners if necessary
             if ((event.mEventType == Event.MOVE_TO_FOREGROUND
                     || event.mEventType == Event.MOVE_TO_BACKGROUND
                     || event.mEventType == Event.SYSTEM_INTERACTION
                     || event.mEventType == Event.USER_INTERACTION)) {
+                mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime);
                 if (previouslyIdle) {
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                             /* idle = */ 0, event.mPackage));
                     notifyBatteryStats(event.mPackage, userId, false);
-                    mAppIdleHistory.addEntry(event.mPackage, userId, false, timeNow);
                 }
             }
         }
@@ -655,28 +612,23 @@
      * the threshold for idle.
      */
     void forceIdleState(String packageName, int userId, boolean idle) {
+        final int appId = getAppId(packageName);
+        if (appId < 0) return;
         synchronized (mLock) {
-            final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked();
-            final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long beginIdleTime = service.getBeginIdleTime(packageName);
-            final long lastUsedTime = service.getSystemLastUsedTime(packageName);
-            final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
-                    lastUsedTime, screenOnTime, timeNow);
-            service.setBeginIdleTime(packageName, deviceUsageTime);
-            service.setSystemLastUsedTime(packageName,
-                    timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
+            final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+                    userId, elapsedRealtime);
+            mAppIdleHistory.setIdleLocked(packageName, userId, idle, elapsedRealtime);
+            final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+                    userId, elapsedRealtime);
             // Inform listeners if necessary
-            if (previouslyIdle != idle) {
+            if (previouslyIdle != stillIdle) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                        /* idle = */ idle ? 1 : 0, packageName));
-                if (!idle) {
+                        /* idle = */ stillIdle ? 1 : 0, packageName));
+                if (!stillIdle) {
                     notifyBatteryStats(packageName, userId, idle);
                 }
-                mAppIdleHistory.addEntry(packageName, userId, idle, timeNow);
             }
         }
     }
@@ -693,10 +645,11 @@
     /**
      * Called by the Binder stub.
      */
-    void removeUser(int userId) {
+    void onUserRemoved(int userId) {
         synchronized (mLock) {
             Slog.i(TAG, "Removing user " + userId + " and all data.");
             mUserState.remove(userId);
+            mAppIdleHistory.onUserRemoved(userId);
             cleanUpRemovedUsersLocked();
         }
     }
@@ -750,29 +703,12 @@
         }
     }
 
-    private boolean isAppIdleUnfiltered(String packageName, UserUsageStatsService userService,
-            long timeNow, long screenOnTime) {
+    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
         synchronized (mLock) {
-            long beginIdleTime = userService.getBeginIdleTime(packageName);
-            long lastUsedTime = userService.getSystemLastUsedTime(packageName);
-            return hasPassedIdleTimeoutLocked(beginIdleTime, lastUsedTime, screenOnTime,
-                    timeNow);
+            return mAppIdleHistory.isIdleLocked(packageName, userId, elapsedRealtime);
         }
     }
 
-    /**
-     * @param beginIdleTime when the app was last used in device usage timebase
-     * @param lastUsedTime wallclock time of when the app was last used
-     * @param screenOnTime screen-on timebase time
-     * @param currentTime current time in device usage timebase
-     * @return whether it's been used far enough in the past to be considered inactive
-     */
-    boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime,
-            long screenOnTime, long currentTime) {
-        return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis)
-                && (lastUsedTime <= currentTime - mAppIdleWallclockThresholdMillis);
-    }
-
     void addListener(AppIdleStateChangeListener listener) {
         synchronized (mLock) {
             if (!mPackageAccessListeners.contains(listener)) {
@@ -787,32 +723,22 @@
         }
     }
 
-    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) {
+    int getAppId(String packageName) {
+        try {
+            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
+            return ai.uid;
+        } catch (NameNotFoundException re) {
+            return -1;
+        }
+    }
+
+    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) {
         if (mAppIdleParoled) {
             return false;
         }
-        try {
-            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_UNINSTALLED_PACKAGES
-                            | PackageManager.GET_DISABLED_COMPONENTS);
-            return isAppIdleFiltered(packageName, ai.uid, userId, timeNow);
-        } catch (PackageManager.NameNotFoundException e) {
-        }
-        return false;
-    }
-
-    boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) {
-        final UserUsageStatsService userService;
-        final long screenOnTime;
-        synchronized (mLock) {
-            if (timeNow == -1) {
-                timeNow = checkAndGetTimeLocked();
-            }
-            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked();
-        }
-        return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
-                userService, timeNow, screenOnTime);
+        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
     }
 
     /**
@@ -822,7 +748,7 @@
      * Called by interface impls.
      */
     private boolean isAppIdleFiltered(String packageName, int appId, int userId,
-            UserUsageStatsService userService, long timeNow, long screenOnTime) {
+            long elapsedRealtime) {
         if (packageName == null) return false;
         // If not enabled at all, of course nobody is ever idle.
         if (!mAppIdleEnabled) {
@@ -864,7 +790,7 @@
             return false;
         }
 
-        return isAppIdleUnfiltered(packageName, userService, timeNow, screenOnTime);
+        return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
     }
 
     int[] getIdleUidsForUser(int userId) {
@@ -872,14 +798,7 @@
             return new int[0];
         }
 
-        final long timeNow;
-        final UserUsageStatsService userService;
-        final long screenOnTime;
-        synchronized (mLock) {
-            timeNow = checkAndGetTimeLocked();
-            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked();
-        }
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
 
         List<ApplicationInfo> apps;
         try {
@@ -899,12 +818,12 @@
 
         // Now resolve all app state.  Iterating over all apps, keeping track of how many
         // we find for each uid and how many of those are idle.
-        for (int i = apps.size()-1; i >= 0; i--) {
+        for (int i = apps.size() - 1; i >= 0; i--) {
             ApplicationInfo ai = apps.get(i);
 
             // Check whether this app is idle.
             boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
-                    userId, userService, timeNow, screenOnTime);
+                    userId, elapsedRealtime);
 
             int index = uidStates.indexOfKey(ai.uid);
             if (index < 0) {
@@ -990,8 +909,11 @@
         for (int i = 0; i < userCount; i++) {
             UserUsageStatsService service = mUserState.valueAt(i);
             service.persistActiveStats();
+            mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
         }
-
+        // Persist elapsed time periodically, in case screen doesn't get toggled
+        // until the next boot
+        mAppIdleHistory.writeElapsedTimeLocked();
         mHandler.removeMessages(MSG_FLUSH_TO_DISK);
     }
 
@@ -1000,7 +922,6 @@
      */
     void dump(String[] args, PrintWriter pw) {
         synchronized (mLock) {
-            final long screenOnTime = getScreenOnTimeLocked();
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
             ArraySet<String> argSet = new ArraySet<>();
             argSet.addAll(Arrays.asList(args));
@@ -1011,27 +932,28 @@
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
-                    mUserState.valueAt(i).checkin(idpw, screenOnTime);
+                    mUserState.valueAt(i).checkin(idpw);
                 } else {
-                    mUserState.valueAt(i).dump(idpw, screenOnTime);
+                    mUserState.valueAt(i).dump(idpw);
                     idpw.println();
-                    if (args.length > 0 && "history".equals(args[0])) {
-                        mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
+                    if (args.length > 0) {
+                        if ("history".equals(args[0])) {
+                            mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
+                        } else if ("flush".equals(args[0])) {
+                            UsageStatsService.this.flushToDiskLocked();
+                            pw.println("Flushed stats to disk");
+                        }
                     }
                 }
+                mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
                 idpw.decreaseIndent();
             }
-            pw.print("Screen On Timebase: ");
-            pw.print(screenOnTime);
-            pw.print(" (");
-            TimeUtils.formatDuration(screenOnTime, pw);
-            pw.println(")");
 
             pw.println();
             pw.println("Settings:");
 
             pw.print("  mAppIdleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleDurationMillis, pw);
+            TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
             pw.println();
 
             pw.print("  mAppIdleWallclockThresholdMillis=");
@@ -1057,11 +979,6 @@
             pw.print("mLastAppIdleParoledTime=");
             TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
             pw.println();
-            pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw);
-            pw.println();
-            pw.print("mLastScreenOnEventRealtime=");
-            TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw);
-            pw.println();
         }
     }
 
@@ -1082,7 +999,7 @@
                     break;
 
                 case MSG_REMOVE_USER:
-                    removeUser(msg.arg1);
+                    onUserRemoved(msg.arg1);
                     break;
 
                 case MSG_INFORM_LISTENERS:
@@ -1179,13 +1096,13 @@
                 }
 
                 // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleDurationMillis = mParser.getLong(KEY_IDLE_DURATION,
+                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
 
                 mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
                         COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
 
-                mCheckIdleIntervalMillis = Math.min(mAppIdleDurationMillis / 4,
+                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
                         COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
 
                 // Default: 24 hours between paroles
@@ -1194,6 +1111,8 @@
 
                 mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+                        mAppIdleScreenThresholdMillis);
             }
         }
     }
@@ -1284,7 +1203,8 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, -1);
+                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+                        SystemClock.elapsedRealtime());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1304,11 +1224,9 @@
                     "No permission to change app idle state");
             final long token = Binder.clearCallingIdentity();
             try {
-                PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(packageName,
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-                if (pi == null) return;
+                final int appId = getAppId(packageName);
+                if (appId < 0) return;
                 UsageStatsService.this.setAppIdle(packageName, idle, userId);
-            } catch (RemoteException re) {
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1335,8 +1253,6 @@
             }
             UsageStatsService.this.dump(args, pw);
         }
-
-
     }
 
     /**
@@ -1411,7 +1327,8 @@
 
         @Override
         public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
-            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1);
+            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
+                    SystemClock.elapsedRealtime());
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index f2ca3a4..c95ff23 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,7 +26,6 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
-import android.text.TextUtils;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -55,13 +54,11 @@
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
-    private static final String LAST_TIME_ACTIVE_SYSTEM_ATTR = "lastTimeActiveSystem";
-    private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime";
     private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
 
     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
-            throws XmlPullParserException, IOException {
+            throws IOException {
         final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
         if (pkg == null) {
             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
@@ -72,20 +69,6 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-
-        final String lastTimeUsedSystem = parser.getAttributeValue(null,
-                LAST_TIME_ACTIVE_SYSTEM_ATTR);
-        if (TextUtils.isEmpty(lastTimeUsedSystem)) {
-            // If the field isn't present, use the old one.
-            stats.mLastTimeSystemUsed = stats.mLastTimeUsed;
-        } else {
-            stats.mLastTimeSystemUsed = statsOut.beginTime + Long.parseLong(lastTimeUsedSystem);
-        }
-
-        final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR);
-        if (!TextUtils.isEmpty(beginIdleTime)) {
-            stats.mBeginIdleTime = Long.parseLong(beginIdleTime);
-        }
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
     }
@@ -141,13 +124,10 @@
         // Write the time offset.
         XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
                 usageStats.mLastTimeUsed - stats.beginTime);
-        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_SYSTEM_ATTR,
-                usageStats.mLastTimeSystemUsed - stats.beginTime);
 
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
-        XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime);
 
         xml.endTag(null, PACKAGE_TAG);
     }
@@ -255,7 +235,6 @@
         }
         xml.endTag(null, PACKAGES_TAG);
 
-
         xml.startTag(null, CONFIGURATIONS_TAG);
         final int configCount = stats.configurations.size();
         for (int i = 0; i < configCount; i++) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f2045d3..7d003f3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,7 +59,6 @@
     private final Context mContext;
     private final UsageStatsDatabase mDatabase;
     private final IntervalStats[] mCurrentStats;
-    private IntervalStats mAppIdleRollingWindow;
     private boolean mStatsChanged = false;
     private final UnixCalendar mDailyExpiryDate;
     private final StatsUpdatedListener mListener;
@@ -74,7 +73,11 @@
     interface StatsUpdatedListener {
         void onStatsUpdated();
         void onStatsReloaded();
-        long getAppIdleRollingWindowDurationMillis();
+        /**
+         * Callback that a system update was detected
+         * @param mUserId user that needs to be initialized
+         */
+        void onNewUpdate(int mUserId);
     }
 
     UserUsageStatsService(Context context, int userId, File usageStatsDir,
@@ -88,7 +91,7 @@
         mUserId = userId;
     }
 
-    void init(final long currentTimeMillis, final long deviceUsageTime) {
+    void init(final long currentTimeMillis) {
         mDatabase.init(currentTimeMillis);
 
         int nullCount = 0;
@@ -112,7 +115,7 @@
 
             // By calling loadActiveStats, we will
             // generate new stats for each bucket.
-            loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
+            loadActiveStats(currentTimeMillis);
         } else {
             // Set up the expiry date to be one day from the latest daily stat.
             // This may actually be today and we will rollover on the first event
@@ -136,54 +139,18 @@
             stat.updateConfigurationStats(null, stat.lastTimeSaved);
         }
 
-        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
-
         if (mDatabase.isNewUpdate()) {
-            initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
-                    mDatabase.isFirstUpdate());
+            notifyNewUpdate();
         }
     }
 
-    /**
-     * If any of the apps don't have a last-used entry, add one now.
-     * @param currentTimeMillis the current time
-     * @param firstUpdate if it is the first update, touch all installed apps, otherwise only
-     *        touch the system apps
-     */
-    private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
-            boolean firstUpdate) {
-        PackageManager pm = mContext.getPackageManager();
-        List<PackageInfo> packages = pm.getInstalledPackagesAsUser(0, mUserId);
-        final int packageCount = packages.size();
-        for (int i = 0; i < packageCount; i++) {
-            final PackageInfo pi = packages.get(i);
-            String packageName = pi.packageName;
-            if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
-                    && getBeginIdleTime(packageName) == -1) {
-                for (IntervalStats stats : mCurrentStats) {
-                    stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
-                    stats.updateBeginIdleTime(packageName, deviceUsageTime);
-                }
-
-                mAppIdleRollingWindow.update(packageName, currentTimeMillis,
-                        Event.SYSTEM_INTERACTION);
-                mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime);
-                mStatsChanged = true;
-            }
-        }
-        // Persist the new OTA-related access stats.
-        persistActiveStats();
-    }
-
-    void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
-                       boolean resetBeginIdleTime) {
+    void onTimeChanged(long oldTime, long newTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
-        loadActiveStats(newTime, resetBeginIdleTime);
-        refreshAppIdleRollingWindow(newTime, deviceUsageTime);
+        loadActiveStats(newTime);
     }
 
-    void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
+    void reportEvent(UsageEvents.Event event) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                     + "[" + event.mTimeStamp + "]: "
@@ -192,7 +159,7 @@
 
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
-            rolloverStats(event.mTimeStamp, deviceUsageTime);
+            rolloverStats(event.mTimeStamp);
         }
 
         final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
@@ -218,35 +185,9 @@
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
-                stats.updateBeginIdleTime(event.mPackage, deviceUsageTime);
             }
         }
 
-        if (event.mEventType != Event.CONFIGURATION_CHANGE) {
-            mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
-            mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
-        }
-
-        notifyStatsChanged();
-    }
-
-    /**
-     * Sets the beginIdleTime for each of the intervals.
-     * @param beginIdleTime
-     */
-    void setBeginIdleTime(String packageName, long beginIdleTime) {
-        for (IntervalStats stats : mCurrentStats) {
-            stats.updateBeginIdleTime(packageName, beginIdleTime);
-        }
-        mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
-        notifyStatsChanged();
-    }
-
-    void setSystemLastUsedTime(String packageName, long lastUsedTime) {
-        for (IntervalStats stats : mCurrentStats) {
-            stats.updateSystemLastUsedTime(packageName, lastUsedTime);
-        }
-        mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
         notifyStatsChanged();
     }
 
@@ -404,24 +345,6 @@
         return new UsageEvents(results, table);
     }
 
-    long getBeginIdleTime(String packageName) {
-        UsageStats packageUsage;
-        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
-            return -1;
-        } else {
-            return packageUsage.getBeginIdleTime();
-        }
-    }
-
-    long getSystemLastUsedTime(String packageName) {
-        UsageStats packageUsage;
-        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
-            return -1;
-        } else {
-            return packageUsage.getLastTimeSystemUsed();
-        }
-    }
-
     void persistActiveStats() {
         if (mStatsChanged) {
             Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
@@ -436,7 +359,7 @@
         }
     }
 
-    private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
+    private void rolloverStats(final long currentTimeMillis) {
         final long startTime = SystemClock.elapsedRealtime();
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
 
@@ -463,7 +386,7 @@
 
         persistActiveStats();
         mDatabase.prune(currentTimeMillis);
-        loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
+        loadActiveStats(currentTimeMillis);
 
         final int continueCount = continuePreviousDay.size();
         for (int i = 0; i < continueCount; i++) {
@@ -477,8 +400,6 @@
         }
         persistActiveStats();
 
-        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
-
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
                 + " milliseconds");
@@ -491,7 +412,11 @@
         }
     }
 
-    private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
+    private void notifyNewUpdate() {
+        mListener.onNewUpdate(mUserId);
+    }
+
+    private void loadActiveStats(final long currentTimeMillis) {
         for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
             final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
             if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
@@ -514,12 +439,6 @@
                 mCurrentStats[intervalType].beginTime = currentTimeMillis;
                 mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
             }
-
-            if (resetBeginIdleTime) {
-                for (UsageStats usageStats : mCurrentStats[intervalType].packageStats.values()) {
-                    usageStats.mBeginIdleTime = 0;
-                }
-            }
         }
 
         mStatsChanged = false;
@@ -538,96 +457,28 @@
                 mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
-    private static void mergePackageStats(IntervalStats dst, IntervalStats src,
-                                          final long deviceUsageTime) {
-        dst.endTime = Math.max(dst.endTime, src.endTime);
-
-        final int srcPackageCount = src.packageStats.size();
-        for (int i = 0; i < srcPackageCount; i++) {
-            final String packageName = src.packageStats.keyAt(i);
-            final UsageStats srcStats = src.packageStats.valueAt(i);
-            UsageStats dstStats = dst.packageStats.get(packageName);
-            if (dstStats == null) {
-                dstStats = new UsageStats(srcStats);
-                dst.packageStats.put(packageName, dstStats);
-            } else {
-                dstStats.add(src.packageStats.valueAt(i));
-            }
-
-            // App idle times can not begin in the future. This happens if we had a time change.
-            if (dstStats.mBeginIdleTime > deviceUsageTime) {
-                dstStats.mBeginIdleTime = deviceUsageTime;
-            }
-        }
-    }
-
-    /**
-     * App idle operates on a rolling window of time. When we roll over time, we end up with a
-     * period of time where in-memory stats are empty and we don't hit the disk for older stats
-     * for performance reasons. Suddenly all apps will become idle.
-     *
-     * Instead, at times we do a deep query to find all the apps that have run in the past few
-     * days and keep the cached data up to date.
-     *
-     * @param currentTimeMillis
-     */
-    void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
-        // Start the rolling window for AppIdle requests.
-        final long startRangeMillis = currentTimeMillis -
-                mListener.getAppIdleRollingWindowDurationMillis();
-
-        List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
-                startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() {
-                    @Override
-                    public void combine(IntervalStats stats, boolean mutable,
-                                        List<IntervalStats> accumulatedResult) {
-                        IntervalStats accum;
-                        if (accumulatedResult.isEmpty()) {
-                            accum = new IntervalStats();
-                            accum.beginTime = stats.beginTime;
-                            accumulatedResult.add(accum);
-                        } else {
-                            accum = accumulatedResult.get(0);
-                        }
-
-                        mergePackageStats(accum, stats, deviceUsageTime);
-                    }
-                });
-
-        if (stats == null || stats.isEmpty()) {
-            mAppIdleRollingWindow = new IntervalStats();
-            mergePackageStats(mAppIdleRollingWindow,
-                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
-        } else {
-            mAppIdleRollingWindow = stats.get(0);
-        }
-    }
-
     //
     // -- DUMP related methods --
     //
 
-    void checkin(final IndentingPrintWriter pw, final long screenOnTime) {
+    void checkin(final IndentingPrintWriter pw) {
         mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
             @Override
             public boolean checkin(IntervalStats stats) {
-                printIntervalStats(pw, stats, screenOnTime, false);
+                printIntervalStats(pw, stats, false);
                 return true;
             }
         });
     }
 
-    void dump(IndentingPrintWriter pw, final long screenOnTime) {
+    void dump(IndentingPrintWriter pw) {
         // This is not a check-in, only dump in-memory stats.
         for (int interval = 0; interval < mCurrentStats.length; interval++) {
             pw.print("In-memory ");
             pw.print(intervalToString(interval));
             pw.println(" stats");
-            printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
+            printIntervalStats(pw, mCurrentStats[interval], true);
         }
-
-        pw.println("AppIdleRollingWindow cache");
-        printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
     }
 
     private String formatDateTime(long dateTime, boolean pretty) {
@@ -644,7 +495,7 @@
         return Long.toString(elapsedTime);
     }
 
-    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime,
+    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
             boolean prettyDates) {
         if (prettyDates) {
             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
@@ -665,10 +516,6 @@
             pw.printPair("totalTime",
                     formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
             pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
-            pw.printPair("lastTimeSystem",
-                    formatDateTime(usageStats.mLastTimeSystemUsed, prettyDates));
-            pw.printPair("inactiveTime",
-                    formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates));
             pw.println();
         }
         pw.decreaseIndent();