summaryrefslogtreecommitdiffstats
path: root/services/usage
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2015-05-08 16:36:21 -0700
committerAmith Yamasani <yamasani@google.com>2015-05-13 11:49:35 -0700
commit0a11e69428d4c00dfcb368c1eb4e60ad8e0dc918 (patch)
tree34407c845b53947ac87ac16fdc02f2fa85619b68 /services/usage
parent05fe90c10b54111a742187935f2890029b348bf5 (diff)
downloadframeworks_base-0a11e69428d4c00dfcb368c1eb4e60ad8e0dc918.zip
frameworks_base-0a11e69428d4c00dfcb368c1eb4e60ad8e0dc918.tar.gz
frameworks_base-0a11e69428d4c00dfcb368c1eb4e60ad8e0dc918.tar.bz2
Track app idle history and dump it
"dumpsys usagestats history" will show the active state of each app for the last 100 hours, if the device hasn't rebooted. Bug: 20066058 Change-Id: I703e5bc121298e4363c202da56fffb0b8534bcaf
Diffstat (limited to 'services/usage')
-rw-r--r--services/usage/java/com/android/server/usage/AppIdleHistory.java95
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java29
2 files changed, 116 insertions, 8 deletions
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
new file mode 100644
index 0000000..9d3db16
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2015 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.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * 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[]>> idleHistory = new SparseArray<>();
+ private long lastPeriod = 0;
+ private static final long ONE_MINUTE = 60 * 1000;
+ private static final int HISTORY_SIZE = 100;
+ private static final int FLAG_LAST_STATE = 2;
+ private static final int FLAG_PARTIAL_ACTIVE = 1;
+ private static final long PERIOD_DURATION = UsageStatsService.DEBUG ? ONE_MINUTE
+ : 60 * ONE_MINUTE;
+
+ public void addEntry(String packageName, int userId, boolean idle, long timeNow) {
+ ArrayMap<String, byte[]> userHistory = idleHistory.get(userId);
+ if (userHistory == null) {
+ userHistory = new ArrayMap<>();
+ idleHistory.put(userId, userHistory);
+ }
+ byte[] packageHistory = userHistory.get(packageName);
+ if (packageHistory == null) {
+ packageHistory = new byte[HISTORY_SIZE];
+ userHistory.put(packageName, packageHistory);
+ }
+ long thisPeriod = timeNow / 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);
+ final int NUSERS = idleHistory.size();
+ for (int u = 0; u < NUSERS; u++) {
+ userHistory = idleHistory.valueAt(u);
+ for (byte[] history : userHistory.values()) {
+ // Shift left
+ System.arraycopy(history, diff, history, 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);
+ }
+ }
+ }
+ }
+ lastPeriod = thisPeriod;
+ if (!idle) {
+ packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
+ } else {
+ packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
+ }
+ }
+
+ public void removeUser(int userId) {
+ idleHistory.remove(userId);
+ }
+
+ public void dump(IndentingPrintWriter idpw, int userId) {
+ ArrayMap<String, byte[]> userHistory = idleHistory.get(userId);
+ 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);
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ idpw.print(history[i] == 0 ? '.' : 'A');
+ }
+ idpw.print(" " + packageName);
+ idpw.println();
+ }
+ }
+} \ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index d18f7c2..bfd6cc0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -64,6 +64,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.DeviceIdleController;
@@ -90,20 +91,21 @@ public class UsageStatsService extends SystemService implements
static final String TAG = "UsageStatsService";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
private static final long TEN_SECONDS = 10 * 1000;
private static final long ONE_MINUTE = 60 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
- static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 4
- : 1L * 24 * 60 * 60 * 1000; // 1 day
- static final long DEFAULT_CHECK_IDLE_INTERVAL = DEBUG ? ONE_MINUTE
- : 8 * 3600 * 1000; // 8 hours
+ static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 4
+ : 1L * 24 * 60 * ONE_MINUTE; // 1 day
+ static final long DEFAULT_CHECK_IDLE_INTERVAL = DEBUG ? ONE_MINUTE / 4
+ : 8 * 60 * ONE_MINUTE; // 8 hours
static final long DEFAULT_PAROLE_INTERVAL = DEBUG ? ONE_MINUTE * 10
- : 24 * 60 * 60 * 1000L; // 24 hours between paroles
- static final long DEFAULT_PAROLE_DURATION = DEBUG ? ONE_MINUTE * 2
- : 10 * 60 * 1000L; // 10 minutes
+ : 24 * 60 * ONE_MINUTE; // 24 hours between paroles
+ static final long DEFAULT_PAROLE_DURATION = DEBUG ? ONE_MINUTE
+ : 10 * ONE_MINUTE; // 10 minutes
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
@@ -137,6 +139,9 @@ public class UsageStatsService extends SystemService implements
long mScreenOnTime;
long mScreenOnSystemTimeSnapshot;
+ @GuardedBy("mLock")
+ private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
+
private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
@@ -346,12 +351,14 @@ public class UsageStatsService extends SystemService implements
| PackageManager.GET_UNINSTALLED_PACKAGES,
userId);
synchronized (mLock) {
+ final long timeNow = checkAndGetTimeLocked();
final int packageCount = packages.size();
for (int p = 0; p < packageCount; p++) {
final String packageName = packages.get(p).packageName;
final boolean isIdle = isAppIdleFiltered(packageName, userId);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
userId, isIdle ? 1 : 0, packageName));
+ mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
}
}
}
@@ -541,6 +548,7 @@ public class UsageStatsService extends SystemService implements
// Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ 0, event.mPackage));
+ mAppIdleHistory.addEntry(event.mPackage, userId, false, timeNow);
}
}
}
@@ -567,6 +575,7 @@ public class UsageStatsService extends SystemService implements
// Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ idle ? 1 : 0, packageName));
+ mAppIdleHistory.addEntry(packageName, userId, idle, timeNow);
}
}
}
@@ -768,10 +777,14 @@ public class UsageStatsService extends SystemService implements
mUserState.valueAt(i).checkin(idpw, screenOnTime);
} else {
mUserState.valueAt(i).dump(idpw, screenOnTime);
+ idpw.println();
+ if (args.length > 0 && "history".equals(args[0])) {
+ mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
+ }
}
idpw.decreaseIndent();
}
- pw.write("Screen On Timestamp:" + mScreenOnTime + "\n");
+ pw.write("Screen On Timebase:" + mScreenOnTime + "\n");
}
}