From 458428ea6633e6000b453ef272c13823f5d02fe5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 22 Feb 2017 11:47:15 -0700 Subject: [PATCH] Split cache clearing into two phases. Use newly defined "V2_DEFY_QUOTA" flag to split cache clearing into two phases: clearing data for apps above their quotas, and then pushing deeper by clearing data for apps below their quotas. Add placeholder comments for other data types that we're planning to add shortly. Route all clearing behavior through this new method, which remains guarded behind a flag for now. Test: builds, boots Bug: 34692014 Change-Id: I678d7b4e2bf6c837dd8a9adbc36a53015907f75f --- .../android/os/storage/StorageManager.java | 12 +- .../java/com/android/server/pm/Installer.java | 3 + .../server/pm/PackageManagerService.java | 130 +++++++++++------- 3 files changed, 88 insertions(+), 57 deletions(-) diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index e070c6e8423fd..a1f8dfbf5575c 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1677,8 +1677,8 @@ public class StorageManager { "Well this is embarassing; we can't allocate " + bytes + " for " + file); } - private static final String XATTR_ATOMIC = "user.atomic"; - private static final String XATTR_TOMBSTONE = "user.tombstone"; + private static final String XATTR_CACHE_ATOMIC = "user.cache_atomic"; + private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone"; /** {@hide} */ private static void setCacheBehavior(File path, String name, boolean enabled) @@ -1736,7 +1736,7 @@ public class StorageManager { * to all contained files and directories. */ public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException { - setCacheBehavior(path, XATTR_ATOMIC, atomic); + setCacheBehavior(path, XATTR_CACHE_ATOMIC, atomic); } /** @@ -1744,7 +1744,7 @@ public class StorageManager { * {@link #setCacheBehaviorAtomic(File, boolean)}. */ public boolean isCacheBehaviorAtomic(File path) throws IOException { - return isCacheBehavior(path, XATTR_ATOMIC); + return isCacheBehavior(path, XATTR_CACHE_ATOMIC); } /** @@ -1764,7 +1764,7 @@ public class StorageManager { *

*/ public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException { - setCacheBehavior(path, XATTR_TOMBSTONE, tombstone); + setCacheBehavior(path, XATTR_CACHE_TOMBSTONE, tombstone); } /** @@ -1772,7 +1772,7 @@ public class StorageManager { * {@link #setCacheBehaviorTombstone(File, boolean)}. */ public boolean isCacheBehaviorTombstone(File path) throws IOException { - return isCacheBehavior(path, XATTR_TOMBSTONE); + return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE); } private final Object mFuseAppLoopLock = new Object(); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index db0451574e798..5abdb60d10972 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -63,6 +63,9 @@ public class Installer extends SystemService { public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8; public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; public static final int FLAG_USE_QUOTA = 1 << 12; + public static final int FLAG_FREE_CACHE_V2 = 1 << 13; + public static final int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; + public static final int FLAG_FREE_CACHE_NOOP = 1 << 15; private final boolean mIsolated; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 371a062c0c43f..e447072a742e6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -392,8 +392,8 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DISABLE_EPHEMERAL_APPS = false; private static final boolean HIDE_EPHEMERAL_APIS = false; - private static final boolean ENABLE_QUOTA = - SystemProperties.getBoolean("persist.fw.quota", false); + private static final boolean ENABLE_FREE_CACHE_V2 = + SystemProperties.getBoolean("fw.free_cache_v2", false); private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; @@ -3765,25 +3765,19 @@ public class PackageManagerService extends IPackageManager.Stub { final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); - // Queue up an async operation since clearing cache may take a little while. - mHandler.post(new Runnable() { - public void run() { - mHandler.removeCallbacks(this); - boolean success = true; - synchronized (mInstallLock) { - try { - mInstaller.freeCache(volumeUuid, freeStorageSize, 0); - } catch (InstallerException e) { - Slog.w(TAG, "Couldn't clear application caches: " + e); - success = false; - } - } - if (observer != null) { - try { - observer.onRemoveCompleted(null, success); - } catch (RemoteException e) { - Slog.w(TAG, "RemoveException when invoking call back"); - } + mHandler.post(() -> { + boolean success = false; + try { + freeStorage(volumeUuid, freeStorageSize, 0); + success = true; + } catch (IOException e) { + Slog.w(TAG, e); + } + if (observer != null) { + try { + observer.onRemoveCompleted(null, success); + } catch (RemoteException e) { + Slog.w(TAG, e); } } }); @@ -3793,43 +3787,77 @@ public class PackageManagerService extends IPackageManager.Stub { public void freeStorage(final String volumeUuid, final long freeStorageSize, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CLEAR_APP_CACHE, null); - // Queue up an async operation since clearing cache may take a little while. - mHandler.post(new Runnable() { - public void run() { - mHandler.removeCallbacks(this); - boolean success = true; - synchronized (mInstallLock) { - try { - mInstaller.freeCache(volumeUuid, freeStorageSize, 0); - } catch (InstallerException e) { - Slog.w(TAG, "Couldn't clear application caches: " + e); - success = false; - } - } - if(pi != null) { - try { - // Callback via pending intent - int code = success ? 1 : 0; - pi.sendIntent(null, code, null, - null, null); - } catch (SendIntentException e1) { - Slog.i(TAG, "Failed to send pending intent"); - } + android.Manifest.permission.CLEAR_APP_CACHE, TAG); + mHandler.post(() -> { + boolean success = false; + try { + freeStorage(volumeUuid, freeStorageSize, 0); + success = true; + } catch (IOException e) { + Slog.w(TAG, e); + } + if (pi != null) { + try { + pi.sendIntent(null, success ? 1 : 0, null, null, null); + } catch (SendIntentException e) { + Slog.w(TAG, e); } } }); } - public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags) - throws IOException { - synchronized (mInstallLock) { - try { - mInstaller.freeCache(volumeUuid, freeStorageSize, 0); - } catch (InstallerException e) { - throw new IOException("Failed to free enough space", e); + /** + * Blocking call to clear various types of cached data across the system + * until the requested bytes are available. + */ + public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final File file = storage.findPathForUuid(volumeUuid); + + if (ENABLE_FREE_CACHE_V2) { + final boolean aggressive = (storageFlags + & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; + + // 1. Pre-flight to determine if we have any chance to succeed + // 2. Consider preloaded data (after 1w honeymoon, unless aggressive) + + // 3. Consider parsed APK data (aggressive only) + if (aggressive) { + FileUtils.deleteContents(mCacheDir); } + if (file.getUsableSpace() >= bytes) return; + + // 4. Consider cached app data (above quotas) + try { + mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2); + } catch (InstallerException ignored) { + } + if (file.getUsableSpace() >= bytes) return; + + // 5. Consider shared libraries with refcount=0 and age>2h + // 6. Consider dexopt output (aggressive only) + // 7. Consider ephemeral apps not used in last week + + // 8. Consider cached app data (below quotas) + try { + mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2 + | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); + } catch (InstallerException ignored) { + } + if (file.getUsableSpace() >= bytes) return; + + // 9. Consider DropBox entries + // 10. Consider ephemeral cookies + + } else { + try { + mInstaller.freeCache(volumeUuid, bytes, 0); + } catch (InstallerException ignored) { + } + if (file.getUsableSpace() >= bytes) return; } + + throw new IOException("Failed to free " + bytes + " on storage device at " + file); } /**