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

Commit bcde3dce authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "More APIs for cache status and behavior."

parents 78b09a00 9bed070b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -30466,14 +30466,22 @@ package android.os.storage {
  }
  public class StorageManager {
    method public long getCacheQuotaBytes();
    method public long getCacheSizeBytes();
    method public long getExternalCacheQuotaBytes();
    method public long getExternalCacheSizeBytes();
    method public java.lang.String getMountedObbPath(java.lang.String);
    method public android.os.storage.StorageVolume getPrimaryStorageVolume();
    method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
    method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
    method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
    method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
    method public boolean isEncrypted(java.io.File);
    method public boolean isObbMounted(java.lang.String);
    method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
    method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
    method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
    field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
  }
+8 −0
Original line number Diff line number Diff line
@@ -33220,14 +33220,22 @@ package android.os.storage {
  }
  public class StorageManager {
    method public long getCacheQuotaBytes();
    method public long getCacheSizeBytes();
    method public long getExternalCacheQuotaBytes();
    method public long getExternalCacheSizeBytes();
    method public java.lang.String getMountedObbPath(java.lang.String);
    method public android.os.storage.StorageVolume getPrimaryStorageVolume();
    method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
    method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
    method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
    method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
    method public boolean isEncrypted(java.io.File);
    method public boolean isObbMounted(java.lang.String);
    method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
    method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
    method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
    field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
  }
+8 −0
Original line number Diff line number Diff line
@@ -30579,14 +30579,22 @@ package android.os.storage {
  }
  public class StorageManager {
    method public long getCacheQuotaBytes();
    method public long getCacheSizeBytes();
    method public long getExternalCacheQuotaBytes();
    method public long getExternalCacheSizeBytes();
    method public java.lang.String getMountedObbPath(java.lang.String);
    method public android.os.storage.StorageVolume getPrimaryStorageVolume();
    method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
    method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
    method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
    method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
    method public boolean isEncrypted(java.io.File);
    method public boolean isObbMounted(java.lang.String);
    method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
    method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
    method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
    field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
  }
+2 −0
Original line number Diff line number Diff line
@@ -291,4 +291,6 @@ interface IStorageManager {
    void fstrim(int flags) = 72;
    AppFuseMount mountProxyFileDescriptorBridge() = 73;
    ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74;
    long getCacheQuotaBytes(String volumeUuid, int uid) = 75;
    long getCacheSizeBytes(String volumeUuid, int uid) = 76;
}
+223 −1
Original line number Diff line number Diff line
@@ -24,27 +24,32 @@ import android.annotation.SdkConstant;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.ProxyFileDescriptorCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.AppFuseMount;
@@ -60,6 +65,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1396,6 +1402,222 @@ public class StorageManager {
        }
    }

    /**
     * Return quota size in bytes for cached data belonging to the calling app.
     * <p>
     * If your app goes above this quota, your cached files will be some of the
     * first to be deleted when additional disk space is needed. Conversely, if
     * your app stays under this quota, your cached files will be some of the
     * last to be deleted when additional disk space is needed.
     * <p>
     * This quota may change over time depending on how frequently the user
     * interacts with your app, and depending on how much disk space is used.
     * <p>
     * Cached data tracked by this method always includes
     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
     * it also includes {@link Context#getExternalCacheDir()} if the primary
     * shared/external storage is hosted on the same storage device as your
     * private data.
     * <p class="note">
     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
     * then cached data for all packages in your shared UID is tracked together
     * as a single unit.
     * </p>
     *
     * @see #getCacheQuotaBytes()
     * @see #getCacheSizeBytes()
     * @see #getExternalCacheQuotaBytes()
     * @see #getExternalCacheSizeBytes()
     */
    public long getCacheQuotaBytes() {
        try {
            final ApplicationInfo app = mContext.getApplicationInfo();
            return mStorageManager.getCacheQuotaBytes(app.volumeUuid, app.uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return total size in bytes of cached data belonging to the calling app.
     * <p>
     * Cached data tracked by this method always includes
     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
     * it also includes {@link Context#getExternalCacheDir()} if the primary
     * shared/external storage is hosted on the same storage device as your
     * private data.
     * <p class="note">
     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
     * then cached data for all packages in your shared UID is tracked together
     * as a single unit.
     * </p>
     *
     * @see #getCacheQuotaBytes()
     * @see #getCacheSizeBytes()
     * @see #getExternalCacheQuotaBytes()
     * @see #getExternalCacheSizeBytes()
     */
    public long getCacheSizeBytes() {
        try {
            final ApplicationInfo app = mContext.getApplicationInfo();
            return mStorageManager.getCacheSizeBytes(app.volumeUuid, app.uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return quota size in bytes for cached data on primary shared/external
     * storage belonging to the calling app.
     * <p>
     * If primary shared/external storage is hosted on the same storage device
     * as your private data, this method will return -1, since all data stored
     * under {@link Context#getExternalCacheDir()} will be counted under
     * {@link #getCacheQuotaBytes()}.
     * <p class="note">
     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
     * then cached data for all packages in your shared UID is tracked together
     * as a single unit.
     * </p>
     */
    public long getExternalCacheQuotaBytes() {
        final ApplicationInfo app = mContext.getApplicationInfo();
        final String primaryUuid = getPrimaryStorageUuid();
        if (Objects.equals(app.volumeUuid, primaryUuid)) {
            return -1;
        }
        try {
            return mStorageManager.getCacheQuotaBytes(primaryUuid, app.uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return total size in bytes of cached data on primary shared/external
     * storage belonging to the calling app.
     * <p>
     * If primary shared/external storage is hosted on the same storage device
     * as your private data, this method will return -1, since all data stored
     * under {@link Context#getExternalCacheDir()} will be counted under
     * {@link #getCacheQuotaBytes()}.
     * <p class="note">
     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
     * then cached data for all packages in your shared UID is tracked together
     * as a single unit.
     * </p>
     */
    public long getExternalCacheSizeBytes() {
        final ApplicationInfo app = mContext.getApplicationInfo();
        final String primaryUuid = getPrimaryStorageUuid();
        if (Objects.equals(app.volumeUuid, primaryUuid)) {
            return -1;
        }
        try {
            return mStorageManager.getCacheSizeBytes(primaryUuid, app.uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    private static final String XATTR_ATOMIC = "user.atomic";
    private static final String XATTR_TOMBSTONE = "user.tombstone";

    /** {@hide} */
    private static void setCacheBehavior(File path, String name, boolean enabled)
            throws IOException {
        if (!path.isDirectory()) {
            throw new IOException("Cache behavior can only be set on directories");
        }
        if (enabled) {
            try {
                Os.setxattr(path.getAbsolutePath(), name,
                        "1".getBytes(StandardCharsets.UTF_8), 0);
            } catch (ErrnoException e) {
                throw e.rethrowAsIOException();
            }
        } else {
            try {
                Os.removexattr(path.getAbsolutePath(), name);
            } catch (ErrnoException e) {
                if (e.errno != OsConstants.ENODATA) {
                    throw e.rethrowAsIOException();
                }
            }
        }
    }

    /** {@hide} */
    private static boolean isCacheBehavior(File path, String name) throws IOException {
        try {
            Os.getxattr(path.getAbsolutePath(), name);
            return true;
        } catch (ErrnoException e) {
            if (e.errno != OsConstants.ENODATA) {
                throw e.rethrowAsIOException();
            } else {
                return false;
            }
        }
    }

    /**
     * Enable or disable special cache behavior that treats this directory and
     * its contents as an atomic unit.
     * <p>
     * When enabled and this directory is considered for automatic deletion by
     * the OS, all contained files will either be deleted together, or not at
     * all. This is useful when you have a directory that contains several
     * related metadata files that depend on each other, such as movie file and
     * a subtitle file.
     * <p>
     * When enabled, the <em>newest</em> {@link File#lastModified()} value of
     * any contained files is considered the modified time of the entire
     * directory.
     * <p>
     * This behavior can only be set on a directory, and it applies recursively
     * to all contained files and directories.
     */
    public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
        setCacheBehavior(path, XATTR_ATOMIC, atomic);
    }

    /**
     * Read the current value set by
     * {@link #setCacheBehaviorAtomic(File, boolean)}.
     */
    public boolean isCacheBehaviorAtomic(File path) throws IOException {
        return isCacheBehavior(path, XATTR_ATOMIC);
    }

    /**
     * Enable or disable special cache behavior that leaves deleted cache files
     * intact as tombstones.
     * <p>
     * When enabled and a file contained in this directory is automatically
     * deleted by the OS, the file will be truncated to have a length of 0 bytes
     * instead of being fully deleted. This is useful if you need to distinguish
     * between a file that was deleted versus one that never existed.
     * <p>
     * This behavior can only be set on a directory, and it applies recursively
     * to all contained files and directories.
     * <p class="note">
     * Note: this behavior is ignored completely if the user explicitly requests
     * that all cached data be cleared.
     * </p>
     */
    public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException {
        setCacheBehavior(path, XATTR_TOMBSTONE, tombstone);
    }

    /**
     * Read the current value set by
     * {@link #setCacheBehaviorTombstone(File, boolean)}.
     */
    public boolean isCacheBehaviorTombstone(File path) throws IOException {
        return isCacheBehavior(path, XATTR_TOMBSTONE);
    }

    private final Object mFuseAppLoopLock = new Object();

    @GuardedBy("mFuseAppLoopLock")
Loading