Update standby buckets for restored apps.

When apps are restored, set their standby bucket to RARE if
they have been used within the past 90 days, based on the
restored UsageStats data.

Bug: 214580000
Test: atest UsageStatsDatabaseTest
Change-Id: I9d63032ef84fef97734b51323f41b797caa4f554
(cherry picked from commit 89f69113ad)
This commit is contained in:
Varun Shah
2022-05-24 17:55:01 -07:00
parent 9e7c760329
commit dfd04f3547
8 changed files with 99 additions and 8 deletions

View File

@@ -163,6 +163,13 @@ public interface AppStandbyInternal {
int getAppStandbyBucketReason(@NonNull String packageName, @UserIdInt int userId,
@ElapsedRealtimeLong long elapsedRealtime);
/**
* Puts the list of apps in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE}
* bucket.
* @param restoredApps the list of restored apps
*/
void restoreAppsToRare(@NonNull Set<String> restoredApps, int userId);
/**
* Put the specified app in the
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}

View File

@@ -23,6 +23,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION;
@@ -1604,6 +1605,26 @@ public class AppStandbyController
setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
}
@Override
public void restoreAppsToRare(Set<String> restoredApps, int userId) {
final int reason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_RESTORED;
final long nowElapsed = mInjector.elapsedRealtime();
for (String packageName : restoredApps) {
// If the package is not installed, don't allow the bucket to be set.
if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
Slog.e(TAG, "Tried to restore bucket for uninstalled app: " + packageName);
continue;
}
final int standbyBucket = getAppStandbyBucket(packageName, userId, nowElapsed, false);
// Only update the standby bucket to RARE if the app is still in the NEVER bucket.
if (standbyBucket == STANDBY_BUCKET_NEVER) {
setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RARE, reason,
nowElapsed, false);
}
}
}
@Override
public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
int callingUid, int callingPid) {

View File

@@ -292,6 +292,17 @@ public final class UsageStats implements Parcelable {
return mLastTimeComponentUsed;
}
/**
* Returns the last time the package was used - defined by the latest of
* mLastTimeUsed, mLastTimeVisible, mLastTimeForegroundServiceUsed, or mLastTimeComponentUsed.
* @hide
*/
public long getLastTimePackageUsed() {
return Math.max(mLastTimeUsed,
Math.max(mLastTimeVisible,
Math.max(mLastTimeForegroundServiceUsed, mLastTimeComponentUsed)));
}
/**
* Returns the number of times the app was launched as an activity from outside of the app.
* Excludes intra-app activity transitions.

View File

@@ -219,6 +219,11 @@ public final class UsageStatsManager {
* @hide
*/
public static final int REASON_SUB_DEFAULT_APP_UPDATE = 0x0001;
/**
* The app was restored.
* @hide
*/
public static final int REASON_SUB_DEFAULT_APP_RESTORED = 0x0002;
/**
* The app was interacted with in some way by the system.
* @hide
@@ -1209,6 +1214,9 @@ public final class UsageStatsManager {
case REASON_SUB_DEFAULT_APP_UPDATE:
sb.append("-au");
break;
case REASON_SUB_DEFAULT_APP_RESTORED:
sb.append("-ar");
break;
}
break;
case REASON_MAIN_FORCED_BY_SYSTEM:

View File

@@ -22,6 +22,7 @@ import static junit.framework.TestCase.fail;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents.Event;
@@ -440,6 +441,7 @@ public class UsageStatsDatabaseTest {
prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
Set<String> prevDBApps = mIntervalStats.packageStats.keySet();
// Create a backup with a specific version
byte[] blob = prevDB.getBackupPayload(KEY_USAGE_STATS, version);
if (version >= 1 && version <= 3) {
@@ -447,6 +449,11 @@ public class UsageStatsDatabaseTest {
"UsageStatsDatabase shouldn't be able to write backups as XML");
return;
}
if (version < 1 || version > UsageStatsDatabase.BACKUP_VERSION) {
assertFalse(blob != null && blob.length != 0,
"UsageStatsDatabase shouldn't be able to write backups for unknown versions");
return;
}
clearUsageStatsFiles();
@@ -454,9 +461,11 @@ public class UsageStatsDatabaseTest {
newDB.readMappingsLocked();
newDB.init(1);
// Attempt to restore the usage stats from the backup
newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
List<IntervalStats> stats = newDB.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, mEndTime,
mIntervalStatsVerifier);
Set<String> restoredApps = newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
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);
if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) {
assertFalse(stats != null && !stats.isEmpty(),

View File

@@ -16,6 +16,7 @@
package com.android.server.usage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
@@ -24,6 +25,7 @@ import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -55,8 +57,11 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Provides an interface to query for UsageStat data from a Protocol Buffer database.
@@ -1252,6 +1257,10 @@ public class UsageStatsDatabase {
Slog.wtf(TAG, "Attempting to backup UsageStats as XML with version " + version);
return null;
}
if (version < 1 || version > BACKUP_VERSION) {
Slog.wtf(TAG, "Attempting to backup UsageStats with an unknown version: " + version);
return null;
}
synchronized (mLock) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (KEY_USAGE_STATS.equals(key)) {
@@ -1300,14 +1309,26 @@ public class UsageStatsDatabase {
}
return baos.toByteArray();
}
}
/**
* Updates the set of packages given to only include those that have been used within the
* given timeframe (as defined by {@link UsageStats#getLastTimePackageUsed()}).
*/
private void calculatePackagesUsedWithinTimeframe(
IntervalStats stats, Set<String> packagesList, long timeframeMs) {
for (UsageStats stat : stats.packageStats.values()) {
if (stat.getLastTimePackageUsed() > timeframeMs) {
packagesList.add(stat.mPackageName);
}
}
}
/**
* @hide
*/
@VisibleForTesting
public void applyRestoredPayload(String key, byte[] payload) {
public @NonNull Set<String> applyRestoredPayload(String key, byte[] payload) {
synchronized (mLock) {
if (KEY_USAGE_STATS.equals(key)) {
// Read stats files for the current device configs
@@ -1320,12 +1341,15 @@ public class UsageStatsDatabase {
IntervalStats yearlyConfigSource =
getLatestUsageStats(UsageStatsManager.INTERVAL_YEARLY);
final Set<String> packagesRestored = new ArraySet<>();
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
int backupDataVersion = in.readInt();
// Can't handle this backup set
if (backupDataVersion < 1 || backupDataVersion > BACKUP_VERSION) return;
if (backupDataVersion < 1 || backupDataVersion > BACKUP_VERSION) {
return packagesRestored;
}
// Delete all stats files
// Do this after reading version and before actually restoring
@@ -1333,10 +1357,14 @@ public class UsageStatsDatabase {
deleteDirectoryContents(mIntervalDirs[i]);
}
// 90 days before today in epoch
final long timeframe = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(90);
int fileCount = in.readInt();
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
packagesRestored.addAll(stats.packageStats.keySet());
stats = mergeStats(stats, dailyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_DAILY, stats);
}
@@ -1345,6 +1373,7 @@ public class UsageStatsDatabase {
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
stats = mergeStats(stats, weeklyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, stats);
}
@@ -1353,6 +1382,7 @@ public class UsageStatsDatabase {
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
stats = mergeStats(stats, monthlyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, stats);
}
@@ -1361,6 +1391,7 @@ public class UsageStatsDatabase {
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
stats = mergeStats(stats, yearlyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_YEARLY, stats);
}
@@ -1370,7 +1401,9 @@ public class UsageStatsDatabase {
} finally {
indexFilesLocked();
}
return packagesRestored;
}
return Collections.EMPTY_SET;
}
}

View File

@@ -3034,7 +3034,8 @@ public class UsageStatsService extends SystemService implements
if (userStats == null) {
return; // user was stopped or removed
}
userStats.applyRestoredPayload(key, payload);
final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
mAppStandby.restoreAppsToRare(restoredApps, user);
}
}
}

View File

@@ -63,6 +63,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* A per-user UsageStatsService. All methods are meant to be called with the main lock held
@@ -1374,8 +1375,8 @@ class UserUsageStatsService {
return mDatabase.getBackupPayload(key);
}
void applyRestoredPayload(String key, byte[] payload){
Set<String> applyRestoredPayload(String key, byte[] payload) {
checkAndGetTimeLocked();
mDatabase.applyRestoredPayload(key, payload);
return mDatabase.applyRestoredPayload(key, payload);
}
}