resolve merge conflicts of 551fc6823a to master.

Change-Id: I2778bf2a364c103fc0d351f2afc4fc73bede2f19
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 72621a4..630367d 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,6 +59,7 @@
     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;
@@ -137,6 +138,8 @@
             initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
                     mDatabase.isFirstUpdate());
         }
+
+        refreshAppIdleRollingWindow(currentTimeMillis);
     }
 
     /**
@@ -170,6 +173,7 @@
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
         loadActiveStats(newTime, resetBeginIdleTime);
+        refreshAppIdleRollingWindow(newTime);
     }
 
     void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -211,6 +215,11 @@
             }
         }
 
+        if (event.mEventType != Event.CONFIGURATION_CHANGE) {
+            mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
+            mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
+        }
+
         notifyStatsChanged();
     }
 
@@ -222,6 +231,7 @@
         for (IntervalStats stats : mCurrentStats) {
             stats.updateBeginIdleTime(packageName, beginIdleTime);
         }
+        mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
         notifyStatsChanged();
     }
 
@@ -229,6 +239,7 @@
         for (IntervalStats stats : mCurrentStats) {
             stats.updateSystemLastUsedTime(packageName, lastUsedTime);
         }
+        mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
         notifyStatsChanged();
     }
 
@@ -387,9 +398,8 @@
     }
 
     long getBeginIdleTime(String packageName) {
-        final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
-        if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
             return -1;
         } else {
             return packageUsage.getBeginIdleTime();
@@ -397,9 +407,8 @@
     }
 
     long getSystemLastUsedTime(String packageName) {
-        final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
-        if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
             return -1;
         } else {
             return packageUsage.getLastTimeSystemUsed();
@@ -461,6 +470,8 @@
         }
         persistActiveStats();
 
+        refreshAppIdleRollingWindow(currentTimeMillis);
+
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
                 + " milliseconds");
@@ -503,6 +514,7 @@
                 }
             }
         }
+
         mStatsChanged = false;
         updateRolloverDeadline();
     }
@@ -516,6 +528,68 @@
                 mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
+    private static void mergePackageStats(IntervalStats dst, IntervalStats src) {
+        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);
+            if (dstStats == null) {
+                dst.packageStats.put(packageName, new UsageStats(srcStats));
+            } else {
+                dstStats.add(src.packageStats.valueAt(i));
+            }
+        }
+    }
+
+    /**
+     * 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.
+     *
+     * 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(long currentTimeMillis) {
+        // Start the rolling window for AppIdle requests.
+        List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
+                currentTimeMillis - (1000 * 60 * 60 * 24 * 2), currentTimeMillis,
+                sPackageStatsMerger);
+
+        if (stats == null || stats.isEmpty()) {
+            mAppIdleRollingWindow = new IntervalStats();
+            mergePackageStats(mAppIdleRollingWindow,
+                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY]);
+        } else {
+            mAppIdleRollingWindow = stats.get(0);
+        }
+    }
+
     //
     // -- DUMP related methods --
     //
@@ -538,6 +612,9 @@
             pw.println(" stats");
             printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
         }
+
+        pw.println("AppIdleRollingWindow cache");
+        printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
     }
 
     private String formatDateTime(long dateTime, boolean pretty) {