diff options
author | 2023-08-04 09:52:39 -0500 | |
---|---|---|
committer | 2023-09-25 11:55:43 -0500 | |
commit | 4d5a155c45d1d000303bbb3b21bae26cac3d514a (patch) | |
tree | f5798d9141c148a8894cacc49e16e6f1897c9436 | |
parent | f8e61cf3e75ae2879decb0196b1d2ad1284d241f (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
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()); |