From c408e9c011fa285fa9f3aa13fdc32458914b3641 Mon Sep 17 00:00:00 2001 From: Daniel Nishi Date: Tue, 17 May 2016 11:20:53 -0700 Subject: [PATCH] Don't show app in deletion helper if it was installed recently. This solves several problems. The first is that if a user installs a large app which triggers the deletion helper, the deletion helper may clear out the recently installed large app to free up space. The second is that it appears that the usage stats may not be synced from device to device currently using Backup and Restore. By considering the installation to be a use for deletion helper filtering, this should avoid the problem for 60 days (and if the user hasn't used the app at that point, it should be fair game to clear). Bug: 28800057 Change-Id: Idb8545aa45a42e45dc673dc5c179c0197204edfd --- .../AppStateUsageStatsBridge.java | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/deletionhelper/AppStateUsageStatsBridge.java b/src/com/android/settings/deletionhelper/AppStateUsageStatsBridge.java index 013c801989c..7bbe8bf2eb9 100644 --- a/src/com/android/settings/deletionhelper/AppStateUsageStatsBridge.java +++ b/src/com/android/settings/deletionhelper/AppStateUsageStatsBridge.java @@ -20,6 +20,9 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.util.Log; import com.android.settings.applications.AppStateBaseBridge; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -33,7 +36,9 @@ import java.util.concurrent.TimeUnit; * Connects data from the UsageStatsManager to the ApplicationsState. */ public class AppStateUsageStatsBridge extends AppStateBaseBridge { + private static final String TAG = "AppStateUsageStatsBridge"; private UsageStatsManager mUsageStatsManager; + private PackageManager mPm; public static final long NEVER_USED = -1; public static final long UNKNOWN_LAST_USE = -2; @@ -42,6 +47,7 @@ public class AppStateUsageStatsBridge extends AppStateBaseBridge { super(appState, callback); mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + mPm = context.getPackageManager(); } @Override @@ -53,7 +59,8 @@ public class AppStateUsageStatsBridge extends AppStateBaseBridge { System.currentTimeMillis()); for (AppEntry entry : apps) { UsageStats usageStats = map.get(entry.info.packageName); - entry.extraInfo = getDaysSinceLastUse(usageStats); + entry.extraInfo = new UsageStatsState(getDaysSinceLastUse(usageStats), + getDaysSinceInstalled(entry.info.packageName)); } } @@ -62,7 +69,8 @@ public class AppStateUsageStatsBridge extends AppStateBaseBridge { Map map = mUsageStatsManager.queryAndAggregateUsageStats(0, System.currentTimeMillis()); UsageStats usageStats = map.get(app.info.packageName); - app.extraInfo = getDaysSinceLastUse(usageStats); + app.extraInfo = new UsageStatsState(getDaysSinceLastUse(usageStats), + getDaysSinceInstalled(app.info.packageName)); } private long getDaysSinceLastUse(UsageStats stats) { @@ -75,7 +83,21 @@ public class AppStateUsageStatsBridge extends AppStateBaseBridge { return UNKNOWN_LAST_USE; } return TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - lastUsed); + } + private long getDaysSinceInstalled(String packageName) { + PackageInfo pi = null; + try { + pi = mPm.getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, packageName + " was not found."); + } + + if (pi == null) { + return NEVER_USED; + } + + return (TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - pi.firstInstallTime)); } /** @@ -92,18 +114,36 @@ public class AppStateUsageStatsBridge extends AppStateBaseBridge { @Override public boolean filterApp(AppEntry info) { if (info == null) return false; - boolean isBundled = (info.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - return isExtraInfoValid(info.extraInfo) && !isBundled; + return isExtraInfoValid(info.extraInfo) && !isBundled(info) + && !isPersistentProcess(info); } private boolean isExtraInfoValid(Object extraInfo) { - if (extraInfo == null || !(extraInfo instanceof Long)) { + if (extraInfo == null || !(extraInfo instanceof UsageStatsState)) { return false; } - long daysSinceLastUse = (long) extraInfo; - return daysSinceLastUse >= UNUSED_DAYS_DELETION_THRESHOLD || - daysSinceLastUse == NEVER_USED; + UsageStatsState state = (UsageStatsState) extraInfo; + long mostRecentUse = Math.max(state.daysSinceFirstInstall, state.daysSinceLastUse); + return mostRecentUse >= UNUSED_DAYS_DELETION_THRESHOLD || mostRecentUse == NEVER_USED; + } + + private boolean isBundled(AppEntry info) { + return (info.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + private boolean isPersistentProcess(AppEntry info) { + return (info.info.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; } }; + + private class UsageStatsState { + public long daysSinceLastUse; + public long daysSinceFirstInstall; + + public UsageStatsState(long daysSinceLastUse, long daysSinceFirstInstall) { + this.daysSinceLastUse = daysSinceLastUse; + this.daysSinceFirstInstall = daysSinceFirstInstall; + } + } }