Merge "Idle timebase" into mnc-dev
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index abfc435..81c7422 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -46,6 +46,13 @@
     public long mLastTimeUsed;
 
     /**
+     * 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;
@@ -74,6 +81,7 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
+        mBeginIdleTime = stats.mBeginIdleTime;
     }
 
     public String getPackageName() {
@@ -110,6 +118,15 @@
     }
 
     /**
+     * @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() {
@@ -133,6 +150,7 @@
             mLastEvent = right.mLastEvent;
             mEndTimeStamp = right.mEndTimeStamp;
             mLastTimeUsed = right.mLastTimeUsed;
+            mBeginIdleTime = right.mBeginIdleTime;
         }
         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
@@ -153,6 +171,7 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
+        dest.writeLong(mBeginIdleTime);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -166,6 +185,7 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
+            stats.mBeginIdleTime = in.readLong();
             return stats;
         }
 
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 8b3fc2e..7bcc038 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -69,14 +69,6 @@
     public abstract boolean isAppIdle(String packageName, int userId);
 
     /**
-     * Returns the most recent time that the specified package was active for the given user.
-     * @param packageName The package to search.
-     * @param userId The user id of the user of interest.
-     * @return The timestamp of when the package was last used, or -1 if it hasn't been used.
-     */
-    public abstract long getLastPackageAccessTime(String packageName, int userId);
-
-    /**
      * Sets up a listener for changes to packages being accessed.
      * @param listener A listener within the system process.
      */
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 3cb29ff..602bfea 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -23,4 +23,5 @@
     String[] getSystemPowerWhitelist();
     String[] getFullPowerWhitelist();
     int[] getAppIdWhitelist();
+    boolean isPowerSaveWhitelistApp(String name);
 }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 76465d4..0b33812 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -313,6 +313,10 @@
             return getAppIdWhitelistInternal();
         }
 
+        @Override public boolean isPowerSaveWhitelistApp(String name) {
+            return isPowerSaveWhitelistAppInternal(name);
+        }
+
         @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             DeviceIdleController.this.dump(fd, pw, args);
         }
@@ -452,6 +456,13 @@
         }
     }
 
+    public boolean isPowerSaveWhitelistAppInternal(String packageName) {
+        synchronized (this) {
+            return mPowerSaveWhitelistApps.containsKey(packageName)
+                    || mPowerSaveWhitelistUserApps.containsKey(packageName);
+        }
+    }
+
     public int[] getAppIdWhitelistInternal() {
         synchronized (this) {
             return mPowerSaveWhitelistAppIdArray;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 76ee3bc..d035c92 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19490,7 +19490,7 @@
             mStackSupervisor.resumeTopActivitiesLocked();
         }
         EventLogTags.writeAmSwitchUser(newUserId);
-        getUserManagerLocked().userForeground(newUserId);
+        getUserManagerLocked().onUserForeground(newUserId);
         sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8dccfdf..e463fad 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -241,7 +241,7 @@
                 }
             }
         }
-        userForeground(UserHandle.USER_OWNER);
+        onUserForeground(UserHandle.USER_OWNER);
         mAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
         for (int i = 0; i < mUserIds.length; ++i) {
@@ -1784,7 +1784,7 @@
      * Make a note of the last started time of a user and do some cleanup.
      * @param userId the user that was just foregrounded
      */
-    public void userForeground(int userId) {
+    public void onUserForeground(int userId) {
         synchronized (mPackagesLock) {
             UserInfo user = mUsers.get(userId);
             long now = System.currentTimeMillis();
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 869d6e1..d6ff475 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -48,6 +48,7 @@
             usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
+            usageStats.mBeginIdleTime = endTime;
             packageStats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
@@ -119,6 +120,17 @@
         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 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 48b127a..197daed 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -40,31 +40,39 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.Display;
 
-import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.SystemConfig;
+import com.android.server.DeviceIdleController;
 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;
@@ -76,6 +84,7 @@
  */
 public class UsageStatsService extends SystemService implements
         UserUsageStatsService.StatsUpdatedListener {
+
     static final String TAG = "UsageStatsService";
 
     static final boolean DEBUG = false;
@@ -91,7 +100,7 @@
     static final int MSG_FLUSH_TO_DISK = 1;
     static final int MSG_REMOVE_USER = 2;
     static final int MSG_INFORM_LISTENERS = 3;
-    static final int MSG_RESET_LAST_TIMESTAMP = 4;
+    static final int MSG_FORCE_IDLE_STATE = 4;
     static final int MSG_CHECK_IDLE_STATES = 5;
 
     private final Object mLock = new Object();
@@ -99,16 +108,20 @@
     AppOpsManager mAppOps;
     UserManager mUserManager;
     AppWidgetManager mAppWidgetManager;
+    IDeviceIdleController mDeviceIdleController;
+    private DisplayManager mDisplayManager;
 
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
     private File mUsageStatsDir;
     long mRealTimeSnapshot;
     long mSystemTimeSnapshot;
     boolean mAppIdleParoled;
+    private boolean mScreenOn;
 
     long mAppIdleDurationMillis;
-
     long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL;
+    long mScreenOnTime;
+    long mScreenOnSystemTimeSnapshot;
 
     private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
@@ -162,6 +175,18 @@
             // Observe changes to the threshold
             new SettingsObserver(mHandler).registerObserver();
             mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
+            mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                    ServiceManager.getService(DeviceIdleController.SERVICE_NAME));
+            mDisplayManager = (DisplayManager) getContext().getSystemService(
+                    Context.DISPLAY_SERVICE);
+            mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
+            synchronized (this) {
+                mScreenOnTime = readScreenOnTimeLocked();
+            }
+            mDisplayManager.registerDisplayListener(mDisplayListener, null);
+            synchronized (this) {
+                updateDisplayLocked();
+            }
         } else if (phase == PHASE_BOOT_COMPLETED) {
             setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
         }
@@ -195,6 +220,24 @@
         }
     }
 
+    private final DisplayManager.DisplayListener mDisplayListener
+            = new DisplayManager.DisplayListener() {
+
+        @Override public void onDisplayAdded(int displayId) {
+        }
+
+        @Override public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                synchronized (UsageStatsService.this.mLock) {
+                    updateDisplayLocked();
+                }
+            }
+        }
+    };
+
     @Override
     public void onStatsUpdated() {
         mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
@@ -261,7 +304,7 @@
                 final int packageCount = packages.size();
                 for (int p = 0; p < packageCount; p++) {
                     final String packageName = packages.get(p).packageName;
-                    final boolean isIdle = isAppIdle(packageName, userId);
+                    final boolean isIdle = isAppIdleFiltered(packageName, userId);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
                             userId, isIdle ? 1 : 0, packageName));
                 }
@@ -270,6 +313,61 @@
         mHandler.sendEmptyMessageDelayed(MSG_CHECK_IDLE_STATES, mCheckIdleIntervalMillis);
     }
 
+    void updateDisplayLocked() {
+        boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
+                != Display.STATE_OFF;
+        if (screenOn == mScreenOn) return;
+
+        mScreenOn = screenOn;
+        long now = System.currentTimeMillis();
+        if (mScreenOn) {
+            mScreenOnSystemTimeSnapshot = now;
+        } else {
+            mScreenOnTime += now - mScreenOnSystemTimeSnapshot;
+            writeScreenOnTimeLocked(mScreenOnTime);
+        }
+    }
+
+    private long getScreenOnTimeLocked(long now) {
+        if (mScreenOn) {
+            return now - mScreenOnSystemTimeSnapshot + mScreenOnTime;
+        } else {
+            return mScreenOnTime;
+        }
+    }
+
+    private File getScreenOnTimeFile() {
+        return new File(mUsageStatsDir, UserHandle.USER_OWNER + "/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);
+        }
+    }
+
     private static void deleteRecursively(File f) {
         File[] files = f.listFiles();
         if (files != null) {
@@ -289,7 +387,7 @@
         if (service == null) {
             service = new UserUsageStatsService(getContext(), userId,
                     new File(mUsageStatsDir, Integer.toString(userId)), this);
-            service.init(currentTimeMillis);
+            service.init(currentTimeMillis, getScreenOnTimeLocked(currentTimeMillis));
             mUserState.put(userId, service);
         }
         return service;
@@ -343,9 +441,10 @@
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long lastUsed = service.getLastPackageAccessTime(event.mPackage);
-            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
-            service.reportEvent(event);
+            final long lastUsed = service.getBeginIdleTime(event.mPackage);
+            final long screenOnTime = getScreenOnTimeLocked(timeNow);
+            final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed, screenOnTime);
+            service.reportEvent(event, screenOnTime);
             // Inform listeners if necessary
             if ((event.mEventType == Event.MOVE_TO_FOREGROUND
                     || event.mEventType == Event.MOVE_TO_BACKGROUND
@@ -360,19 +459,21 @@
     }
 
     /**
-     * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the
-     * last used timestamp to a point in time thats behind the threshold for idle.
+     * Forces the app's beginIdleTime to reflect idle or active. If idle, then it rolls back the
+     * beginIdleTime to a point in time thats behind the threshold for idle.
      */
-    void resetLastTimestamp(String packageName, int userId, boolean idle) {
+    void forceIdleState(String packageName, int userId, boolean idle) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0) - 5000;
+            final long screenOnTime = getScreenOnTimeLocked(timeNow);
+            final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long lastUsed = service.getLastPackageAccessTime(packageName);
-            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
-            service.setLastTimestamp(packageName, lastTimestamp);
+            final long lastUsed = service.getBeginIdleTime(packageName);
+            final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed,
+                    getScreenOnTimeLocked(timeNow));
+            service.setBeginIdleTime(packageName, deviceUsageTime);
             // Inform listeners if necessary
             if (previouslyIdle != idle) {
                 // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
@@ -451,23 +552,25 @@
         }
     }
 
-    /**
-     * Called by LocalService stub.
-     */
-    long getLastPackageAccessTime(String packageName, int userId) {
+    private boolean isAppIdleUnfiltered(String packageName, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            // android package is always considered non-idle.
-            // TODO: Add a generic whitelisting mechanism
-            if (packageName.equals("android")) {
-                return timeNow;
-            }
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            return service.getLastPackageAccessTime(packageName);
+            long beginIdleTime = service.getBeginIdleTime(packageName);
+            return hasPassedIdleTimeout(beginIdleTime, getScreenOnTimeLocked(timeNow));
         }
     }
 
+    /**
+     * @param timestamp when the app was last used in device usage timebase
+     * @param currentTime current time in device usage timebase
+     * @return whether it's been used far enough in the past to be considered inactive
+     */
+    boolean hasPassedIdleTimeout(long timestamp, long currentTime) {
+        return timestamp <= currentTime - mAppIdleDurationMillis;
+    }
+
     void addListener(AppIdleStateChangeListener listener) {
         synchronized (mLock) {
             if (!mPackageAccessListeners.contains(listener)) {
@@ -482,12 +585,13 @@
         }
     }
 
-    private boolean hasPassedIdleDuration(long lastUsed) {
-        final long now = System.currentTimeMillis();
-        return lastUsed <= now - mAppIdleDurationMillis;
-    }
-
-    boolean isAppIdle(String packageName, int userId) {
+    /**
+     * Checks if an app has been idle for a while and filters out apps that are excluded.
+     * It returns false if the current system state allows all apps to be considered active.
+     * This happens if the device is plugged in or temporarily allowed to make exceptions.
+     * Called by interface impls.
+     */
+    boolean isAppIdleFiltered(String packageName, int userId) {
         if (packageName == null) return false;
         synchronized (mLock) {
             // Temporary exemption, probably due to device charging or occasional allowance to
@@ -496,8 +600,12 @@
                 return false;
             }
         }
-        if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
-            return false;
+        if (packageName.equals("android")) return false;
+        try {
+            if (mDeviceIdleController.isPowerSaveWhitelistApp(packageName)) {
+                return false;
+            }
+        } catch (RemoteException re) {
         }
         // TODO: Optimize this check
         if (isActiveDeviceAdmin(packageName, userId)) {
@@ -509,14 +617,13 @@
             return false;
         }
 
-        final long lastUsed = getLastPackageAccessTime(packageName, userId);
-        return hasPassedIdleDuration(lastUsed);
+        return isAppIdleUnfiltered(packageName, userId);
     }
 
     void setAppIdle(String packageName, boolean idle, int userId) {
         if (packageName == null) return;
 
-        mHandler.obtainMessage(MSG_RESET_LAST_TIMESTAMP, userId, idle ? 1 : 0, packageName)
+        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
                 .sendToTarget();
     }
 
@@ -559,6 +666,7 @@
      */
     void dump(String[] args, PrintWriter pw) {
         synchronized (mLock) {
+            final long screenOnTime = getScreenOnTimeLocked(checkAndGetTimeLocked());
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
             ArraySet<String> argSet = new ArraySet<>();
             argSet.addAll(Arrays.asList(args));
@@ -569,12 +677,13 @@
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
-                    mUserState.valueAt(i).checkin(idpw);
+                    mUserState.valueAt(i).checkin(idpw, screenOnTime);
                 } else {
-                    mUserState.valueAt(i).dump(idpw);
+                    mUserState.valueAt(i).dump(idpw, screenOnTime);
                 }
                 idpw.decreaseIndent();
             }
+            pw.write("Screen On Timestamp:" + mScreenOnTime + "\n");
         }
     }
 
@@ -602,8 +711,8 @@
                     informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
                     break;
 
-                case MSG_RESET_LAST_TIMESTAMP:
-                    resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
+                case MSG_FORCE_IDLE_STATE:
+                    forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
                     break;
 
                 case MSG_CHECK_IDLE_STATES:
@@ -727,7 +836,7 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdle(packageName, userId);
+                return UsageStatsService.this.isAppIdleFiltered(packageName, userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -832,12 +941,7 @@
 
         @Override
         public boolean isAppIdle(String packageName, int userId) {
-            return UsageStatsService.this.isAppIdle(packageName, userId);
-        }
-
-        @Override
-        public long getLastPackageAccessTime(String packageName, int userId) {
-            return UsageStatsService.this.getLastPackageAccessTime(packageName, userId);
+            return UsageStatsService.this.isAppIdleFiltered(packageName, userId);
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index bfb71c5..0111201 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,6 +26,7 @@
 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;
@@ -54,6 +55,7 @@
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+    private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime";
     private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
 
@@ -69,7 +71,10 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-
+        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);
     }
@@ -129,6 +134,7 @@
         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);
     }
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d94759d..b638c8e 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -80,7 +80,7 @@
         mUserId = userId;
     }
 
-    void init(final long currentTimeMillis) {
+    void init(final long currentTimeMillis, final long deviceUsageTime) {
         mDatabase.init(currentTimeMillis);
 
         int nullCount = 0;
@@ -135,7 +135,8 @@
         }
 
         if (mDatabase.isNewUpdate()) {
-            initializeDefaultsForApps(currentTimeMillis, mDatabase.isFirstUpdate());
+            initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
+                    mDatabase.isFirstUpdate());
         }
     }
 
@@ -145,7 +146,8 @@
      * @param firstUpdate if it is the first update, touch all installed apps, otherwise only
      *        touch the system apps
      */
-    private void initializeDefaultsForApps(long currentTimeMillis, boolean firstUpdate) {
+    private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
+            boolean firstUpdate) {
         PackageManager pm = mContext.getPackageManager();
         List<PackageInfo> packages = pm.getInstalledPackages(0, mUserId);
         final int packageCount = packages.size();
@@ -153,9 +155,10 @@
             final PackageInfo pi = packages.get(i);
             String packageName = pi.packageName;
             if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
-                    && getLastPackageAccessTime(packageName) == -1) {
+                    && getBeginIdleTime(packageName) == -1) {
                 for (IntervalStats stats : mCurrentStats) {
                     stats.update(packageName, currentTimeMillis, Event.INTERACTION);
+                    stats.updateBeginIdleTime(packageName, deviceUsageTime);
                     mStatsChanged = true;
                 }
             }
@@ -170,7 +173,7 @@
         loadActiveStats(newTime, true);
     }
 
-    void reportEvent(UsageEvents.Event event) {
+    void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                     + "[" + event.mTimeStamp + "]: "
@@ -205,6 +208,7 @@
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+                stats.updateBeginIdleTime(event.mPackage, deviceUsageTime);
             }
         }
 
@@ -215,9 +219,9 @@
      * Sets the last timestamp for each of the intervals.
      * @param lastTimestamp
      */
-    void setLastTimestamp(String packageName, long lastTimestamp) {
+    void setBeginIdleTime(String packageName, long deviceUsageTime) {
         for (IntervalStats stats : mCurrentStats) {
-            stats.update(packageName, lastTimestamp, UsageEvents.Event.NONE);
+            stats.updateBeginIdleTime(packageName, deviceUsageTime);
         }
         notifyStatsChanged();
     }
@@ -376,13 +380,13 @@
         return new UsageEvents(results, table);
     }
 
-    long getLastPackageAccessTime(String packageName) {
+    long getBeginIdleTime(String packageName) {
         final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
         if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
             return -1;
         } else {
-            return packageUsage.getLastTimeUsed();
+            return packageUsage.getBeginIdleTime();
         }
     }
 
@@ -506,23 +510,23 @@
     // -- DUMP related methods --
     //
 
-    void checkin(final IndentingPrintWriter pw) {
+    void checkin(final IndentingPrintWriter pw, final long screenOnTime) {
         mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
             @Override
             public boolean checkin(IntervalStats stats) {
-                printIntervalStats(pw, stats, false);
+                printIntervalStats(pw, stats, screenOnTime, false);
                 return true;
             }
         });
     }
 
-    void dump(IndentingPrintWriter pw) {
+    void dump(IndentingPrintWriter pw, final long screenOnTime) {
         // 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], true);
+            printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
         }
     }
 
@@ -540,7 +544,8 @@
         return Long.toString(elapsedTime);
     }
 
-    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) {
+    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime,
+            boolean prettyDates) {
         if (prettyDates) {
             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
                     stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
@@ -559,6 +564,8 @@
             pw.printPair("package", usageStats.mPackageName);
             pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
             pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+            pw.printPair("inactiveTime",
+                    formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates));
             pw.println();
         }
         pw.decreaseIndent();