diff options
author | 2025-02-27 01:39:49 +0000 | |
---|---|---|
committer | 2025-02-27 07:22:00 +0000 | |
commit | 7b76c7015d06d334da838ed0cf2c9b36e50c8092 (patch) | |
tree | 481ff77b387c2e56f890012118fb6610e779bbc1 /apex | |
parent | 1088bc431e850290c7a45ad1f678f5c556901b75 (diff) |
Persist list of restored apps whose buckets need to be elevated.
Keep the list of restored apps which need to be elevated to the RARE
bucket in memory for up to 2 days (increased from 8hrs).
Also persist this list to disk in case the device restarts within the
first 2 days so important apps work as expected.
Bug: 383766428
Test: manual (ensure restore logic isn't affected)
Test: atest UsageStatsDatabaseTest
Test: atest AppIdleHistoryTests
Flag: com.android.server.usage.persist_restore_to_rare_apps_list
Change-Id: I03af37d3da8be4ce405b0abc4c0d0f79bda1574b
Diffstat (limited to 'apex')
3 files changed, 102 insertions, 6 deletions
diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig index 74d2a590086f..899341016148 100644 --- a/apex/jobscheduler/service/aconfig/app_idle.aconfig +++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig @@ -28,3 +28,14 @@ flag { description: "Adjust the default bucket evaluation parameters" bug: "379909479" } + +flag { + name: "persist_restore_to_rare_apps_list" + namespace: "backstage_power" + description: "Persist the list of apps which are put in the RARE bucket upon restore." + is_fixed_read_only: true + bug: "383766428" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index a8641ae43509..4acfebc536eb 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -38,6 +38,7 @@ import android.app.usage.AppStandbyInfo; import android.app.usage.UsageStatsManager; import android.os.SystemClock; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -83,6 +84,9 @@ public class AppIdleHistory { private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>(); private static final long ONE_MINUTE = 60 * 1000; + // Only keep the persisted restore-to-rare apps list for 2 days. + static final long RESTORE_TO_RARE_APPS_LIST_EXPIRY = ONE_MINUTE * 60 * 24 * 2; + static final int STANDBY_BUCKET_UNKNOWN = -1; /** @@ -277,6 +281,58 @@ public class AppIdleHistory { writeScreenOnTime(); } + private File getRestoreToRareAppsListFile(int userId) { + return new File(getUserDirectory(userId), "restore_to_rare_apps_list"); + } + + public ArraySet<String> readRestoreToRareAppsList(int userId) { + File restoreToRareAppsListFile = getRestoreToRareAppsListFile(userId); + if (!restoreToRareAppsListFile.exists()) { + return null; + } + + try (BufferedReader reader = + new BufferedReader(new FileReader(restoreToRareAppsListFile))) { + final ArraySet<String> appsList = new ArraySet<>(); + final long restoreTime = Long.parseLong(reader.readLine()); + if (System.currentTimeMillis() - restoreTime > RESTORE_TO_RARE_APPS_LIST_EXPIRY) { + // the apps list should only be kept around for 2 days + reader.close(); + restoreToRareAppsListFile.delete(); + return null; + } + String pkgName; + while ((pkgName = reader.readLine()) != null) { + appsList.add(pkgName); + } + return appsList; + } catch (IOException | NumberFormatException e) { + return null; + } + } + + public void writeRestoreToRareAppsList(int userId, ArraySet<String> restoreAppsToRare) { + File fileHandle = getRestoreToRareAppsListFile(userId); + if (fileHandle.exists()) { + // don't update the persisted file - it should only be written once. + return; + } + AtomicFile restoreToRareAppsListFile = new AtomicFile(fileHandle); + FileOutputStream fos = null; + try { + fos = restoreToRareAppsListFile.startWrite(); + final StringBuilder sb = new StringBuilder(); + sb.append(System.currentTimeMillis()).append("\n"); + for (String pkgName : restoreAppsToRare) { + sb.append(pkgName).append("\n"); + } + fos.write(sb.toString().getBytes()); + restoreToRareAppsListFile.finishWrite(fos); + } catch (IOException ioe) { + restoreToRareAppsListFile.failWrite(fos); + } + } + /** * Mark the app as used and update the bucket if necessary. If there is a expiry time specified * that's in the future, then the usage event is temporary and keeps the app in the specified @@ -694,10 +750,13 @@ public class AppIdleHistory { return appUsageHistory.bucketExpiryTimesMs.get(bucket, 0); } + private File getUserDirectory(int userId) { + return new File(new File(mStorageDir, "users"), Integer.toString(userId)); + } + @VisibleForTesting File getUserFile(int userId) { - return new File(new File(new File(mStorageDir, "users"), - Integer.toString(userId)), APP_IDLE_FILENAME); + return new File(getUserDirectory(userId), APP_IDLE_FILENAME); } void clearLastUsedTimestamps(String packageName, int userId) { diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index ab8131ba5126..b87b5ceebd98 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1706,10 +1706,18 @@ public class AppStandbyController restoreAppToRare(packageName, userId, nowElapsed, reason); } // Clear out the list of restored apps that need to have their standby buckets adjusted - // if they still haven't been installed eight hours after restore. - // Note: if the device reboots within these first 8 hours, this list will be lost since it's - // not persisted - this is the expected behavior for now and may be updated in the future. - mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), 8 * ONE_HOUR); + // if they still haven't been installed two days after initial restore. + final long delayMillis = Flags.persistRestoreToRareAppsList() + ? AppIdleHistory.RESTORE_TO_RARE_APPS_LIST_EXPIRY : 8 * ONE_HOUR; + mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), delayMillis); + + // Persist the file in case the device reboots within 2 days after the initial restore. + if (Flags.persistRestoreToRareAppsList()) { + synchronized (mAppIdleLock) { + mAppIdleHistory.writeRestoreToRareAppsList( + userId, mAppsToRestoreToRare.get(userId)); + } + } } /** Adjust the standby bucket of the given package for the user to RARE. */ @@ -2272,6 +2280,22 @@ public class AppStandbyController } else if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) { clearAppIdleForPackage(pkgName, userId); } else { + // Do a lazy read of the persisted list, if necessary. + if (Flags.persistRestoreToRareAppsList() + && mAppsToRestoreToRare.get(userId) == null) { + synchronized (mAppIdleLock) { + final ArraySet<String> restoredApps = + mAppIdleHistory.readRestoreToRareAppsList(userId); + if (restoredApps != null) { + mAppsToRestoreToRare.addAll(userId, restoredApps); + // Clear out the list of restored apps if they still haven't been + // installed in two days - at worst, we are allowing for up to + // 4 days for reinstallation (device reboots just before 2 days) + mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), + AppIdleHistory.RESTORE_TO_RARE_APPS_LIST_EXPIRY); + } + } + } // Package was just added and it's not being replaced. if (mAppsToRestoreToRare.contains(userId, pkgName)) { restoreAppToRare(pkgName, userId, mInjector.elapsedRealtime(), @@ -2454,6 +2478,8 @@ public class AppStandbyController + ": " + Flags.avoidIdleCheck()); pw.println(" " + Flags.FLAG_ADJUST_DEFAULT_BUCKET_ELEVATION_PARAMS + ": " + Flags.adjustDefaultBucketElevationParams()); + pw.println(" " + Flags.FLAG_PERSIST_RESTORE_TO_RARE_APPS_LIST + + ": " + Flags.persistRestoreToRareAppsList()); pw.println(); synchronized (mCarrierPrivilegedLock) { |