Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3aa3a007 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Split cache clearing into two phases."

parents 54eb1d49 458428ea
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -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 {
     * </p>
     */
    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();
+3 −0
Original line number Diff line number Diff line
@@ -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;

+78 −50
Original line number Diff line number Diff line
@@ -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) {
        mHandler.post(() -> {
            boolean success = false;
            try {
                        mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
                    } catch (InstallerException e) {
                        Slog.w(TAG, "Couldn't clear application caches: " + e);
                        success = false;
                    }
                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, "RemoveException when invoking call back");
                    }
                    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) {
                android.Manifest.permission.CLEAR_APP_CACHE, TAG);
        mHandler.post(() -> {
            boolean success = false;
            try {
                        mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
                    } catch (InstallerException e) {
                        Slog.w(TAG, "Couldn't clear application caches: " + e);
                        success = false;
                    }
                freeStorage(volumeUuid, freeStorageSize, 0);
                success = true;
            } catch (IOException e) {
                Slog.w(TAG, e);
            }
            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");
                    }
                    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) {
    /**
     * 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, freeStorageSize, 0);
            } catch (InstallerException e) {
                throw new IOException("Failed to free enough space", e);
                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);
    }
    /**