Merge "UsageStats: Fix issue where initializing data for first time would cause crash" into mnc-dr1.5-dev am: f2cc98bc96
am: 112e26646f

* commit '112e26646f334bed6ee202765b09e6687dde1436':
  UsageStats: Fix issue where initializing data for first time would cause crash
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 54d9cd9..c9710be 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -547,7 +547,8 @@
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
                 final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime, resetBeginIdleTime);
+                service.onTimeChanged(expectedSystemTime, actualSystemTime, mScreenOnTime,
+                        resetBeginIdleTime);
             }
             mRealTimeSnapshot = actualRealtime;
             mSystemTimeSnapshot = actualSystemTime;
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d8f26ed..addb1a1 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -135,12 +135,12 @@
             stat.updateConfigurationStats(null, stat.lastTimeSaved);
         }
 
+        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
+
         if (mDatabase.isNewUpdate()) {
             initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
                     mDatabase.isFirstUpdate());
         }
-
-        refreshAppIdleRollingWindow(currentTimeMillis);
     }
 
     /**
@@ -162,19 +162,23 @@
                 for (IntervalStats stats : mCurrentStats) {
                     stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
                     stats.updateBeginIdleTime(packageName, deviceUsageTime);
-                    mStatsChanged = true;
                 }
+                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, boolean resetBeginIdleTime) {
+    void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
+                       boolean resetBeginIdleTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
         loadActiveStats(newTime, /* force= */ true, resetBeginIdleTime);
-        refreshAppIdleRollingWindow(newTime);
+        refreshAppIdleRollingWindow(newTime, deviceUsageTime);
     }
 
     void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -186,7 +190,7 @@
 
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
-            rolloverStats(event.mTimeStamp);
+            rolloverStats(event.mTimeStamp, deviceUsageTime);
         }
 
         final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
@@ -430,7 +434,7 @@
         }
     }
 
-    private void rolloverStats(final long currentTimeMillis) {
+    private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
         final long startTime = SystemClock.elapsedRealtime();
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
 
@@ -471,7 +475,7 @@
         }
         persistActiveStats();
 
-        refreshAppIdleRollingWindow(currentTimeMillis);
+        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
 
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
@@ -542,44 +546,30 @@
                 tempCal.getTimeInMillis() + ")");
     }
 
-    private static void mergePackageStats(IntervalStats dst, IntervalStats src) {
+    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);
-            final UsageStats dstStats = dst.packageStats.get(packageName);
+            UsageStats dstStats = dst.packageStats.get(packageName);
             if (dstStats == null) {
-                dst.packageStats.put(packageName, new UsageStats(srcStats));
+                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;
+            }
         }
     }
 
     /**
-     * Merges all the stats into the first element of the resulting list.
-     */
-    private static final StatCombiner<IntervalStats> sPackageStatsMerger =
-            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);
-        }
-    };
-
-    /**
      * 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.
@@ -589,16 +579,31 @@
      *
      * @param currentTimeMillis
      */
-    void refreshAppIdleRollingWindow(long currentTimeMillis) {
+    void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
         // Start the rolling window for AppIdle requests.
         List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
                 currentTimeMillis - (1000 * 60 * 60 * 24 * 2), currentTimeMillis,
-                sPackageStatsMerger);
+                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]);
+                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
         } else {
             mAppIdleRollingWindow = stats.get(0);
         }