diff options
author | Amith Yamasani <yamasani@google.com> | 2015-05-08 16:36:21 -0700 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2015-05-13 11:49:35 -0700 |
commit | 0a11e69428d4c00dfcb368c1eb4e60ad8e0dc918 (patch) | |
tree | 34407c845b53947ac87ac16fdc02f2fa85619b68 /services/usage | |
parent | 05fe90c10b54111a742187935f2890029b348bf5 (diff) | |
download | frameworks_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.java | 95 | ||||
-rw-r--r-- | services/usage/java/com/android/server/usage/UsageStatsService.java | 29 |
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"); } } |