summaryrefslogtreecommitdiffstats
path: root/services/usage
diff options
context:
space:
mode:
authorAdam Lesinski <adamlesinski@google.com>2016-01-07 18:24:53 -0800
committerThe Android Automerger <android-build@google.com>2016-02-24 13:21:09 -0800
commitbfe0b64f562869c30f9bbc7292910e2380d54b25 (patch)
tree273a90e8c7d3543d205e2f14596bf9692c7f415f /services/usage
parent1e24c428efa7b0836154fa909bdcf2d3e7210462 (diff)
downloadframeworks_base-bfe0b64f562869c30f9bbc7292910e2380d54b25.zip
frameworks_base-bfe0b64f562869c30f9bbc7292910e2380d54b25.tar.gz
frameworks_base-bfe0b64f562869c30f9bbc7292910e2380d54b25.tar.bz2
DO NOT MERGE ANYWHERE: UsageStatsService: Fix app idle issue at rollover time
App Idle queries are very frequent and so they only check in memory stats. However, in memory stats can be missing some entries, especially after a rollover, but also due to a larger bug fixed in master (too risky to take now). The fix is to do a deep query (reading older files from disk) and maintain a parallel cache of stats for app idle. That way the rolling window of data required to serve app idle queries stays in memory. Bug:26355386 Change-Id: I6a29bbc25214f6a3c2f24c8c079936e66f99e42e
Diffstat (limited to 'services/usage')
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java85
1 files changed, 81 insertions, 4 deletions
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b07b815..d8f26ed 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 @@ class UserUsageStatsService {
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;
@@ -138,6 +139,8 @@ class UserUsageStatsService {
initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
mDatabase.isFirstUpdate());
}
+
+ refreshAppIdleRollingWindow(currentTimeMillis);
}
/**
@@ -171,6 +174,7 @@ class UserUsageStatsService {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
loadActiveStats(newTime, /* force= */ true, resetBeginIdleTime);
+ refreshAppIdleRollingWindow(newTime);
}
void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -212,6 +216,11 @@ class UserUsageStatsService {
}
}
+ if (event.mEventType != Event.CONFIGURATION_CHANGE) {
+ mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
+ }
+
notifyStatsChanged();
}
@@ -223,6 +232,7 @@ class UserUsageStatsService {
for (IntervalStats stats : mCurrentStats) {
stats.updateBeginIdleTime(packageName, beginIdleTime);
}
+ mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
notifyStatsChanged();
}
@@ -230,6 +240,7 @@ class UserUsageStatsService {
for (IntervalStats stats : mCurrentStats) {
stats.updateSystemLastUsedTime(packageName, lastUsedTime);
}
+ mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
notifyStatsChanged();
}
@@ -388,9 +399,8 @@ class UserUsageStatsService {
}
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();
@@ -398,9 +408,8 @@ class UserUsageStatsService {
}
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();
@@ -462,6 +471,8 @@ class UserUsageStatsService {
}
persistActiveStats();
+ refreshAppIdleRollingWindow(currentTimeMillis);
+
final long totalTime = SystemClock.elapsedRealtime() - startTime;
Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
+ " milliseconds");
@@ -521,6 +532,7 @@ class UserUsageStatsService {
}
}
}
+
mStatsChanged = false;
mDailyExpiryDate.setTimeInMillis(currentTimeMillis);
mDailyExpiryDate.addDays(1);
@@ -530,6 +542,68 @@ class UserUsageStatsService {
tempCal.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 --
//
@@ -552,6 +626,9 @@ class UserUsageStatsService {
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) {