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:
@@ -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}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user