Loading apex/jobscheduler/service/aconfig/app_idle.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -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 } } apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +61 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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 Loading Loading @@ -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) { Loading apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +30 −4 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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(), Loading Loading @@ -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) { Loading Loading
apex/jobscheduler/service/aconfig/app_idle.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -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 } }
apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +61 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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 Loading Loading @@ -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) { Loading
apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +30 −4 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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(), Loading Loading @@ -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) { Loading