summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Xin Guan <guanxin@google.com> 2023-08-04 09:52:39 -0500
committer Xin Guan <guanxin@google.com> 2023-09-25 11:55:43 -0500
commit4d5a155c45d1d000303bbb3b21bae26cac3d514a (patch)
treef5798d9141c148a8894cacc49e16e6f1897c9436
parentf8e61cf3e75ae2879decb0196b1d2ad1284d241f (diff)
Don't load usage events into memory for usage statistic queries
Avoid unnecessary IOs and additional memory overhead for usage statistica queries. Also dump the pending usage events for unlocked user(s). Bug: 301449929 Test: atest CtsUsageStatsTestCases atest FrameworksCoreTests:android.app.usage.UsageStatsPersistenceTest atest FrameworksServicesTests:com.android.server.usage.UsageStatsDatabaseTest Change-Id: Ia68e78a96dd1f66d19744acfb85b74e890608df7
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java14
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java39
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProtoV2.java6
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java12
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java22
-rw-r--r--tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java2
6 files changed, 57 insertions, 38 deletions
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 83fa29a5dd66..6ae26585fab2 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -382,7 +382,7 @@ public class UsageStatsDatabaseTest {
void runWriteReadTest(int interval) throws IOException {
mUsageStatsDatabase.putUsageStats(interval, mIntervalStats);
List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime,
- mIntervalStatsVerifier);
+ mIntervalStatsVerifier, false);
assertEquals(1, stats.size());
compareIntervalStats(mIntervalStats, stats.get(0), MAX_TESTED_VERSION);
@@ -421,7 +421,7 @@ public class UsageStatsDatabaseTest {
newDB.readMappingsLocked();
newDB.init(mEndTime);
List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
- mIntervalStatsVerifier);
+ mIntervalStatsVerifier, false);
assertEquals(1, stats.size());
@@ -465,7 +465,7 @@ public class UsageStatsDatabaseTest {
assertTrue(restoredApps.containsAll(prevDBApps),
"List of restored apps does not match list backed-up apps list.");
List<IntervalStats> stats = newDB.queryUsageStats(
- UsageStatsManager.INTERVAL_DAILY, 0, mEndTime, mIntervalStatsVerifier);
+ UsageStatsManager.INTERVAL_DAILY, 0, mEndTime, mIntervalStatsVerifier, false);
if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) {
assertFalse(stats != null && !stats.isEmpty(),
@@ -593,7 +593,7 @@ public class UsageStatsDatabaseTest {
newDB.readMappingsLocked();
newDB.init(mEndTime);
List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
- mIntervalStatsVerifier);
+ mIntervalStatsVerifier, false);
assertEquals(1, stats.size());
// The written and read IntervalStats should match
@@ -620,7 +620,7 @@ public class UsageStatsDatabaseTest {
db.onPackageRemoved(removedPackage, System.currentTimeMillis());
List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
- mIntervalStatsVerifier);
+ mIntervalStatsVerifier, false);
assertEquals(1, stats.size(),
"Only one interval stats object should exist for the given time range.");
final IntervalStats stat = stats.get(0);
@@ -648,7 +648,7 @@ public class UsageStatsDatabaseTest {
private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval,
String removedPackage) {
List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
- mIntervalStatsVerifier);
+ mIntervalStatsVerifier, false);
assertEquals(1, stats.size(),
"Only one interval stats object should exist for the given time range.");
final IntervalStats stat = stats.get(0);
@@ -668,7 +668,7 @@ public class UsageStatsDatabaseTest {
private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval,
Set<String> installedPackages) {
List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
- mIntervalStatsVerifier);
+ mIntervalStatsVerifier, false);
assertEquals(1, stats.size(),
"Only one interval stats object should exist for the given time range.");
final IntervalStats stat = stats.get(0);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index b028b474e96b..a8a90170ae4d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -243,7 +243,7 @@ public class UsageStatsDatabase {
try {
for (int i = start; i < fileCount - 1; i++) {
final IntervalStats stats = new IntervalStats();
- readLocked(files.valueAt(i), stats);
+ readLocked(files.valueAt(i), stats, false);
if (!checkinAction.checkin(stats)) {
return false;
}
@@ -542,7 +542,8 @@ public class UsageStatsDatabase {
}
try {
IntervalStats stats = new IntervalStats();
- readLocked(new AtomicFile(files[j]), stats, version, mPackagesTokenData);
+ readLocked(new AtomicFile(files[j]), stats, version, mPackagesTokenData,
+ false);
// Upgrade to version 5+.
// Future version upgrades should add additional logic here to upgrade.
if (mCurrentVersion >= 5) {
@@ -602,7 +603,8 @@ public class UsageStatsDatabase {
try {
final IntervalStats stats = new IntervalStats();
final AtomicFile atomicFile = new AtomicFile(files[j]);
- if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData)) {
+ if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData,
+ false)) {
continue; // no data was omitted when read so no need to rewrite
}
// Any data related to packages that have been removed would have failed
@@ -648,7 +650,7 @@ public class UsageStatsDatabase {
try {
final IntervalStats stats = new IntervalStats();
final AtomicFile atomicFile = new AtomicFile(files[j]);
- readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+ readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData, false);
if (!pruneStats(installedPackages, stats)) {
continue; // no stats were pruned so no need to rewrite
}
@@ -755,7 +757,7 @@ public class UsageStatsDatabase {
try {
final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1);
IntervalStats stats = new IntervalStats();
- readLocked(f, stats);
+ readLocked(f, stats, false);
return stats;
} catch (Exception e) {
Slog.e(TAG, "Failed to read usage stats file", e);
@@ -821,7 +823,7 @@ public class UsageStatsDatabase {
*/
@Nullable
public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
- StatCombiner<T> combiner) {
+ StatCombiner<T> combiner, boolean skipEvents) {
// mIntervalDirs is final. Accessing its size without holding the lock should be fine.
if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
throw new IllegalArgumentException("Bad interval type " + intervalType);
@@ -875,7 +877,7 @@ public class UsageStatsDatabase {
}
try {
- readLocked(f, stats);
+ readLocked(f, stats, skipEvents);
if (beginTime < stats.endTime
&& !combiner.combine(stats, false, results)) {
break;
@@ -990,7 +992,7 @@ public class UsageStatsDatabase {
try {
final AtomicFile af = new AtomicFile(f);
final IntervalStats stats = new IntervalStats();
- readLocked(af, stats);
+ readLocked(af, stats, false);
final int pkgCount = stats.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
UsageStats pkgStats = stats.packageStats.valueAt(i);
@@ -1014,7 +1016,7 @@ public class UsageStatsDatabase {
private static long parseBeginTime(File file) throws IOException {
String name = file.getName();
- // Parse out the digits from the the front of the file name
+ // Parse out the digits from the front of the file name
for (int i = 0; i < name.length(); i++) {
final char c = name.charAt(i);
if (c < '0' || c > '9') {
@@ -1092,13 +1094,13 @@ public class UsageStatsDatabase {
* method. It is up to the caller to ensure that this is the desired behavior - if not, the
* caller should ensure that the data in the reused object is being cleared.
*/
- private void readLocked(AtomicFile file, IntervalStats statsOut)
+ private void readLocked(AtomicFile file, IntervalStats statsOut, boolean skipEvents)
throws IOException, RuntimeException {
if (mCurrentVersion <= 3) {
Slog.wtf(TAG, "Reading UsageStats as XML; current database version: "
+ mCurrentVersion);
}
- readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData);
+ readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData, skipEvents);
}
/**
@@ -1109,13 +1111,14 @@ public class UsageStatsDatabase {
* caller should ensure that the data in the reused object is being cleared.
*/
private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version,
- PackagesTokenData packagesTokenData) throws IOException, RuntimeException {
+ PackagesTokenData packagesTokenData, boolean skipEvents)
+ throws IOException, RuntimeException {
boolean dataOmitted = false;
try {
FileInputStream in = file.openRead();
try {
statsOut.beginTime = parseBeginTime(file);
- dataOmitted = readLocked(in, statsOut, version, packagesTokenData);
+ dataOmitted = readLocked(in, statsOut, version, packagesTokenData, skipEvents);
statsOut.lastTimeSaved = file.getLastModifiedTime();
} finally {
try {
@@ -1139,7 +1142,7 @@ public class UsageStatsDatabase {
* caller should ensure that the data in the reused object is being cleared.
*/
private static boolean readLocked(InputStream in, IntervalStats statsOut, int version,
- PackagesTokenData packagesTokenData) throws RuntimeException {
+ PackagesTokenData packagesTokenData, boolean skipEvents) throws RuntimeException {
boolean dataOmitted = false;
switch (version) {
case 1:
@@ -1161,7 +1164,7 @@ public class UsageStatsDatabase {
break;
case 5:
try {
- UsageStatsProtoV2.read(in, statsOut);
+ UsageStatsProtoV2.read(in, statsOut, skipEvents);
} catch (Exception e) {
Slog.e(TAG, "Unable to read interval stats from proto.", e);
}
@@ -1436,7 +1439,7 @@ public class UsageStatsDatabase {
throws IOException {
IntervalStats stats = new IntervalStats();
try {
- readLocked(statsFile, stats);
+ readLocked(statsFile, stats, false);
} catch (IOException e) {
Slog.e(TAG, "Failed to read usage stats file", e);
out.writeInt(0);
@@ -1481,7 +1484,7 @@ public class UsageStatsDatabase {
IntervalStats stats = new IntervalStats();
try {
stats.beginTime = in.readLong();
- readLocked(in, stats, version, mPackagesTokenData);
+ readLocked(in, stats, version, mPackagesTokenData, false);
} catch (Exception e) {
Slog.d(TAG, "DeSerializing IntervalStats Failed", e);
stats = null;
@@ -1579,7 +1582,7 @@ public class UsageStatsDatabase {
synchronized (mLock) {
final IntervalStats stats = new IntervalStats();
try {
- readLocked(mSortedStatFiles[interval].get(fileName, null), stats);
+ readLocked(mSortedStatFiles[interval].get(fileName, null), stats, false);
return stats;
} catch (Exception e) {
return null;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index 076eaf84de6a..81387471f4d6 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -438,7 +438,8 @@ final class UsageStatsProtoV2 {
* @param in the input stream from which to read events.
* @param stats the interval stats object which will be populated.
*/
- public static void read(InputStream in, IntervalStats stats) throws IOException {
+ public static void read(InputStream in, IntervalStats stats, boolean skipEvents)
+ throws IOException {
final ProtoInputStream proto = new ProtoInputStream(in);
while (true) {
switch (proto.nextField()) {
@@ -492,6 +493,9 @@ final class UsageStatsProtoV2 {
}
break;
case (int) IntervalStatsObfuscatedProto.EVENT_LOG:
+ if (skipEvents) {
+ break;
+ }
try {
final long eventsToken = proto.start(
IntervalStatsObfuscatedProto.EVENT_LOG);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 7db32a9f1ad9..dc0ce2c16f03 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1886,6 +1886,18 @@ public class UsageStatsService extends SystemService implements
mUserState.valueAt(i).dump(idpw, pkgs, compact);
idpw.println();
}
+ } else {
+ final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
+ if (pendingEvents != null && !pendingEvents.isEmpty()) {
+ final int eventCount = pendingEvents.size();
+ idpw.println("Pending events: count=" + eventCount);
+ idpw.increaseIndent();
+ for (int idx = 0; idx < eventCount; idx++) {
+ UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), true);
+ }
+ idpw.decreaseIndent();
+ idpw.println();
+ }
}
idpw.decreaseIndent();
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 7d2e1a4ed86a..4f66eaa1e884 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -448,7 +448,7 @@ class UserUsageStatsService {
*/
@Nullable
private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
- StatCombiner<T> combiner) {
+ StatCombiner<T> combiner, boolean skipEvents) {
if (intervalType == INTERVAL_BEST) {
intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
if (intervalType < 0) {
@@ -488,7 +488,7 @@ class UserUsageStatsService {
// Get the stats from disk.
List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
- truncatedEndTime, combiner);
+ truncatedEndTime, combiner, skipEvents);
if (DEBUG) {
Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
@@ -518,21 +518,21 @@ class UserUsageStatsService {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
- return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
+ return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner, true);
}
List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
- return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
+ return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner, true);
}
List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
- return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
+ return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner, true);
}
UsageEvents queryEvents(final long beginTime, final long endTime, int flags) {
@@ -587,7 +587,7 @@ class UserUsageStatsService {
}
return true;
}
- });
+ }, false);
if (results == null || results.isEmpty()) {
return null;
@@ -637,7 +637,7 @@ class UserUsageStatsService {
}
}
return true;
- });
+ }, false);
if (results == null || results.isEmpty()) {
return null;
@@ -684,7 +684,7 @@ class UserUsageStatsService {
accumulatedResult.add(event);
}
return true;
- });
+ }, false);
if (results == null || results.isEmpty()) {
return null;
@@ -770,7 +770,7 @@ class UserUsageStatsService {
}
}
return true;
- });
+ }, false);
if (results == null || results.isEmpty()) {
// There won't be any new events added earlier than endTime, so we can use endTime to
@@ -1059,7 +1059,7 @@ class UserUsageStatsService {
return Long.toString(elapsedTime);
}
- void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
+ static void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
pw.printPair("type", eventToString(event.mEventType));
pw.printPair("package", event.mPackage);
@@ -1124,7 +1124,7 @@ class UserUsageStatsService {
}
return true;
}
- });
+ }, false);
pw.print("Last 24 hour events (");
if (prettyDates) {
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index f695cbd5daf9..5160df8e9b6d 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -122,7 +122,7 @@ public class UsageStatsDatabasePerfTest {
while (benchmarkState.keepRunning(elapsedTimeNs)) {
final long startTime = SystemClock.elapsedRealtimeNanos();
List<UsageEvents.Event> temp = sUsageStatsDatabase.queryUsageStats(
- UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner);
+ UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner, false);
final long endTime = SystemClock.elapsedRealtimeNanos();
elapsedTimeNs = endTime - startTime;
assertEquals(packageCount * eventsPerPackage, temp.size());