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

Commit ada0f53c authored by Martijn Coenen's avatar Martijn Coenen Committed by Android (Google) Code Review
Browse files

Merge changes Ib8a00e73,I071487e3,Idda040f2 into main

* changes:
  Introduce ImmutableVolumeInfo.
  Cache StorageManger#getVolumeList.
  Create WatchableVolumeInfo object.
parents 6e5dce67 5d2135b3
Loading
Loading
Loading
Loading
+160 −106
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.PER_USER_RANGE;
@@ -44,6 +45,7 @@ import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -276,7 +278,8 @@ public class StorageManager {
            FLAG_STORAGE_SDK,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StorageFlags {}
    public @interface StorageFlags {
    }

    /** {@hide} */
    public static final int FLAG_FOR_WRITE = 1 << 8;
@@ -309,6 +312,44 @@ public class StorageManager {
    @GuardedBy("mDelegates")
    private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();

    static record VolumeListQuery(int mUserId, String mPackageName, int mFlags) {
    }

    private static final PropertyInvalidatedCache.QueryHandler<VolumeListQuery, StorageVolume[]>
            sVolumeListQuery = new PropertyInvalidatedCache.QueryHandler<>() {
                @androidx.annotation.Nullable
                @Override
                public StorageVolume[] apply(@androidx.annotation.NonNull VolumeListQuery query) {
                    final IStorageManager storageManager = IStorageManager.Stub.asInterface(
                            ServiceManager.getService("mount"));
                    if (storageManager == null) {
                        // negative results won't be cached, so we will just try again next time
                        return null;
                    }
                    try {
                        return storageManager.getVolumeList(
                                query.mUserId, query.mPackageName, query.mFlags);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            };

    // Generally, the userId and packageName parameters stay pretty constant, but flags may change
    // regularly; we have observed some processes hitting 10+ variations.
    private static final int VOLUME_LIST_CACHE_MAX = 16;

    private static final PropertyInvalidatedCache<VolumeListQuery, StorageVolume[]>
            sVolumeListCache = new PropertyInvalidatedCache<>(
                    new PropertyInvalidatedCache.Args(MODULE_SYSTEM).cacheNulls(false)
                    .api("getVolumeList").maxEntries(VOLUME_LIST_CACHE_MAX), "getVolumeList",
                    sVolumeListQuery);

    /** {@hide} */
    public static void invalidateVolumeListCache() {
        sVolumeListCache.invalidateCache();
    }

    private class StorageEventListenerDelegate extends IStorageEventListener.Stub {
        final Executor mExecutor;
        final StorageEventListener mListener;
@@ -395,7 +436,8 @@ public class StorageManager {

    private class ObbActionListener extends IObbActionListener.Stub {
        @SuppressWarnings("hiding")
        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
        private SparseArray<ObbListenerDelegate> mListeners =
                new SparseArray<ObbListenerDelegate>();

        @Override
        public void onObbResult(String filename, int nonce, int status) {
@@ -478,9 +520,9 @@ public class StorageManager {
     * @param looper The {@link android.os.Looper} which events will be received on.
     *
     *               <p>Applications can get instance of this class by calling
     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
     *               {@link android.content.Context#getSystemService(java.lang.String)} with an
     *               argument
     *               of {@link android.content.Context#STORAGE_SERVICE}.
     *
     * @hide
     */
    @UnsupportedAppUsage
@@ -488,15 +530,16 @@ public class StorageManager {
        mContext = context;
        mResolver = context.getContentResolver();
        mLooper = looper;
        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
        mStorageManager = IStorageManager.Stub.asInterface(
                ServiceManager.getServiceOrThrow("mount"));
        mAppOps = mContext.getSystemService(AppOpsManager.class);
    }

    /**
     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
     *
     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
     *
     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener}
     *                 object.
     * @hide
     */
    @UnsupportedAppUsage
@@ -516,8 +559,8 @@ public class StorageManager {
    /**
     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
     *
     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
     *
     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener}
     *                 object.
     * @hide
     */
    @UnsupportedAppUsage
@@ -558,7 +601,8 @@ public class StorageManager {
         * {@link StorageManager#getStorageVolumes()} to observe the latest
         * value.
         */
        public void onStateChanged(@NonNull StorageVolume volume) { }
        public void onStateChanged(@NonNull StorageVolume volume) {
        }
    }

    /**
@@ -628,8 +672,8 @@ public class StorageManager {

    /**
     * Query if a USB Mass Storage (UMS) host is connected.
     * @return true if UMS host is connected.
     *
     * @return true if UMS host is connected.
     * @hide
     */
    @Deprecated
@@ -640,8 +684,8 @@ public class StorageManager {

    /**
     * Query if a USB Mass Storage (UMS) is enabled on the device.
     * @return true if UMS host is enabled.
     *
     * @return true if UMS host is enabled.
     * @hide
     */
    @Deprecated
@@ -1172,8 +1216,8 @@ public class StorageManager {
    /**
     * This is not the API you're looking for.
     *
     * @see PackageManager#getPrimaryStorageCurrentVolume()
     * @hide
     * @see PackageManager#getPrimaryStorageCurrentVolume()
     */
    public String getPrimaryStorageUuid() {
        try {
@@ -1186,8 +1230,8 @@ public class StorageManager {
    /**
     * This is not the API you're looking for.
     *
     * @see PackageManager#movePrimaryStorage(VolumeInfo)
     * @hide
     * @see PackageManager#movePrimaryStorage(VolumeInfo)
     */
    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
        try {
@@ -1275,6 +1319,7 @@ public class StorageManager {

    /**
     * Gets the state of a volume via its mountpoint.
     *
     * @hide
     */
    @Deprecated
@@ -1389,8 +1434,6 @@ public class StorageManager {
    /** {@hide} */
    @UnsupportedAppUsage
    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
                ServiceManager.getService("mount"));
        try {
            String packageName = ActivityThread.currentOpPackageName();
            if (packageName == null) {
@@ -1406,7 +1449,7 @@ public class StorageManager {
                }
                packageName = packageNames[0];
            }
            return storageManager.getVolumeList(userId, packageName, flags);
            return sVolumeListCache.query(new VolumeListQuery(userId, packageName, flags));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -1414,6 +1457,7 @@ public class StorageManager {

    /**
     * Returns list of paths for all mountable volumes.
     *
     * @hide
     */
    @Deprecated
@@ -1711,7 +1755,8 @@ public class StorageManager {
        return false;
    }

    /** {@hide}
    /**
     * {@hide}
     * Is this device encrypted?
     * <p>
     * Note: all devices launching with Android 10 (API level 29) or later are
@@ -1724,8 +1769,10 @@ public class StorageManager {
        return RoSystemProperties.CRYPTO_ENCRYPTED;
    }

    /** {@hide}
    /**
     * {@hide}
     * Does this device have file-based encryption (FBE) enabled?
     *
     * @return true if the device has file-based encryption enabled.
     */
    public static boolean isFileEncrypted() {
@@ -1759,8 +1806,8 @@ public class StorageManager {
    }

    /**
     * @deprecated disabled now that FUSE has been replaced by sdcardfs
     * @hide
     * @deprecated disabled now that FUSE has been replaced by sdcardfs
     */
    @Deprecated
    public static File maybeTranslateEmulatedPathToInternal(File path) {
@@ -1790,6 +1837,7 @@ public class StorageManager {

    /**
     * Check that given app holds both permission and appop.
     *
     * @hide
     */
    public static boolean checkPermissionAndAppOp(Context context, boolean enforce, int pid,
@@ -1800,6 +1848,7 @@ public class StorageManager {

    /**
     * Check that given app holds both permission and appop but do not noteOp.
     *
     * @hide
     */
    public static boolean checkPermissionAndCheckOp(Context context, boolean enforce,
@@ -1810,6 +1859,7 @@ public class StorageManager {

    /**
     * Check that given app holds both permission and appop.
     *
     * @hide
     */
    private static boolean checkPermissionAndAppOp(Context context, boolean enforce, int pid,
@@ -1877,7 +1927,9 @@ public class StorageManager {
                // Legacy apps technically have the access granted by this op,
                // even when the op is denied
                if ((mAppOps.checkOpNoThrow(OP_LEGACY_STORAGE, uid,
                        packageName) == AppOpsManager.MODE_ALLOWED)) return true;
                        packageName) == AppOpsManager.MODE_ALLOWED)) {
                    return true;
                }

                if (enforce) {
                    throw new SecurityException("Op " + AppOpsManager.opToName(op) + " "
@@ -2014,7 +2066,6 @@ public class StorageManager {
     *                 returned file descriptor.
     * @param handler  Handler that invokes callback methods.
     * @return Seekable ParcelFileDescriptor.
     * @throws IOException
     */
    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
            int mode, ProxyFileDescriptorCallback callback, Handler handler)
@@ -2115,16 +2166,19 @@ public class StorageManager {
    })
    @Retention(RetentionPolicy.SOURCE)
    /** @hide */
    public @interface MountMode {}
    public @interface MountMode {
    }

    /**
     * No external storage should be mounted.
     *
     * @hide
     */
    @SystemApi
    public static final int MOUNT_MODE_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
    /**
     * Default external storage should be mounted.
     *
     * @hide
     */
    @SystemApi
@@ -2132,12 +2186,14 @@ public class StorageManager {
    /**
     * Mount mode for package installers which should give them access to
     * all obb dirs in addition to their package sandboxes
     *
     * @hide
     */
    @SystemApi
    public static final int MOUNT_MODE_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
    /**
     * The lower file system should be bind mounted directly on external storage
     *
     * @hide
     */
    @SystemApi
@@ -2146,6 +2202,7 @@ public class StorageManager {
    /**
     * Use the regular scoped storage filesystem, but Android/ should be writable.
     * Used to support the applications hosting DownloadManager and the MTP server.
     *
     * @hide
     */
    @SystemApi
@@ -2164,10 +2221,10 @@ public class StorageManager {
     * this flag to take effect.
     * </p>
     *
     * @hide
     * @see #getAllocatableBytes(UUID, int)
     * @see #allocateBytes(UUID, long, int)
     * @see #allocateBytes(FileDescriptor, long, int)
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
    @SystemApi
@@ -2194,6 +2251,7 @@ public class StorageManager {
     * freeable cached space when determining allocatable space.
     *
     * Intended for use with {@link #getAllocatableBytes()}.
     *
     * @hide
     */
    public static final int FLAG_ALLOCATE_NON_CACHE_ONLY = 1 << 3;
@@ -2203,6 +2261,7 @@ public class StorageManager {
     * cached space when determining allocatable space.
     *
     * Intended for use with {@link #getAllocatableBytes()}.
     *
     * @hide
     */
    public static final int FLAG_ALLOCATE_CACHE_ONLY = 1 << 4;
@@ -2216,7 +2275,8 @@ public class StorageManager {
            FLAG_ALLOCATE_CACHE_ONLY,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface AllocateFlags {}
    public @interface AllocateFlags {
    }

    /**
     * Return the maximum number of new bytes that your app can allocate for
@@ -2332,10 +2392,9 @@ public class StorageManager {
     * These mount modes specify different views and access levels for
     * different apps on external storage.
     *
     * @return {@code MountMode} for the given uid and packageName.
     * @params uid UID of the application
     * @params packageName name of the package
     * @return {@code MountMode} for the given uid and packageName.
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE)
@@ -2505,7 +2564,8 @@ public class StorageManager {
            QUOTA_TYPE_MEDIA_VIDEO,
            QUOTA_TYPE_MEDIA_IMAGE,
    })
    public @interface QuotaType {}
    public @interface QuotaType {
    }

    private static native boolean setQuotaProjectId(String path, long projectId);

@@ -2536,11 +2596,9 @@ public class StorageManager {
     * @param quotaType the quota type of the file; this is based on the
     *                  {@code QuotaType} constants, eg
     *                  {@code StorageManager.QUOTA_TYPE_MEDIA_AUDIO}
     *
     * @throws IllegalArgumentException if {@code quotaType} does not correspond to a valid
     *                                  quota type.
     * @throws IOException              if the quota type could not be updated.
     *
     * @hide
     */
    @SystemApi
@@ -2616,7 +2674,6 @@ public class StorageManager {
     * permissions of a directory to what they should anyway be.
     *
     * @param path the path for which we should fix up the permissions
     *
     * @hide
     */
    public void fixupAppDir(@NonNull File path) {
@@ -2826,7 +2883,8 @@ public class StorageManager {
            APP_IO_BLOCKED_REASON_TRANSCODING,
            APP_IO_BLOCKED_REASON_UNKNOWN,
    })
    public @interface AppIoBlockedReason {}
    public @interface AppIoBlockedReason {
    }

    /**
     * Notify the system that an app with {@code uid} and {@code tid} is blocked on an IO request on
@@ -2842,7 +2900,6 @@ public class StorageManager {
     * @param uid        the UID of the app blocked on IO
     * @param tid        the tid of the app blocked on IO
     * @param reason     the reason the app is blocked on IO
     *
     * @hide
     */
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2869,7 +2926,6 @@ public class StorageManager {
     * @param uid        the UID of the app resuming IO
     * @param tid        the tid of the app resuming IO
     * @param reason     the reason the app is resuming IO
     *
     * @hide
     */
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2893,7 +2949,6 @@ public class StorageManager {
     * @param uid        the UID of the app to check IO blocked status
     * @param tid        the tid of the app to check IO blocked status
     * @param reason     the reason to check IO blocked status for
     *
     * @hide
     */
    @TestApi
@@ -2962,7 +3017,6 @@ public class StorageManager {
     * information is available, -1 is returned.
     *
     * @return Percentage of the remaining useful lifetime of the internal storage device.
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_STORAGE_LIFETIME_API)
+131 −116

File changed.

Preview size limit exceeded, changes collapsed.

+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.storage;

import android.content.Context;
import android.os.storage.DiskInfo;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;

import com.android.internal.util.IndentingPrintWriter;

import java.io.File;

/**
 * An immutable version of {@link VolumeInfo} with only getters.
 *
 * @hide
 */
public final class ImmutableVolumeInfo {
    private final VolumeInfo mVolumeInfo;

    private ImmutableVolumeInfo(VolumeInfo volumeInfo) {
        mVolumeInfo = new VolumeInfo(volumeInfo);
    }

    public static ImmutableVolumeInfo fromVolumeInfo(VolumeInfo info) {
        return new ImmutableVolumeInfo(info);
    }

    public ImmutableVolumeInfo clone() {
        return fromVolumeInfo(mVolumeInfo.clone());
    }

    public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
        return mVolumeInfo.buildStorageVolume(context, userId, reportUnmounted);
    }

    public void dump(IndentingPrintWriter pw) {
        mVolumeInfo.dump(pw);
    }

    public DiskInfo getDisk() {
        return mVolumeInfo.getDisk();
    }

    public String getDiskId() {
        return mVolumeInfo.getDiskId();
    }

    public String getFsLabel() {
        return mVolumeInfo.fsLabel;
    }

    public String getFsPath() {
        return mVolumeInfo.path;
    }

    public String getFsType() {
        return mVolumeInfo.fsType;
    }

    public String getFsUuid() {
        return mVolumeInfo.fsUuid;
    }

    public String getId() {
        return mVolumeInfo.id;
    }

    public File getInternalPath() {
        return mVolumeInfo.getInternalPath();
    }

    public int getMountFlags() {
        return mVolumeInfo.mountFlags;
    }

    public int getMountUserId() {
        return mVolumeInfo.mountUserId;
    }

    public String getPartGuid() {
        return mVolumeInfo.partGuid;
    }

    public File getPath() {
        return mVolumeInfo.getPath();
    }

    public int getState() {
        return mVolumeInfo.state;
    }

    public int getType() {
        return mVolumeInfo.type;
    }

    public VolumeInfo getVolumeInfo() {
        return new VolumeInfo(mVolumeInfo); // Return a copy, not the original
    }

    public boolean isMountedReadable() {
        return mVolumeInfo.isMountedReadable();
    }

    public boolean isMountedWritable() {
        return mVolumeInfo.isMountedWritable();
    }

    public boolean isPrimary() {
        return mVolumeInfo.isPrimary();
    }

    public boolean isVisible() {
        return mVolumeInfo.isVisible();
    }

    public boolean isVisibleForUser(int userId) {
        return mVolumeInfo.isVisibleForUser(userId);
    }

    public boolean isVisibleForWrite(int userId) {
        return mVolumeInfo.isVisibleForWrite(userId);
    }
}
+16 −14
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.server.storage.ImmutableVolumeInfo;

import java.util.Objects;

@@ -79,18 +80,18 @@ public final class StorageSessionController {
     * @param vol for which the storage session has to be started
     * @return userId for connection for this volume
     */
    public int getConnectionUserIdForVolume(VolumeInfo vol) {
    public int getConnectionUserIdForVolume(ImmutableVolumeInfo vol) {
        final Context volumeUserContext = mContext.createContextAsUser(
                UserHandle.of(vol.mountUserId), 0);
                UserHandle.of(vol.getMountUserId()), 0);
        boolean isMediaSharedWithParent = volumeUserContext.getSystemService(
                UserManager.class).isMediaSharedWithParent();

        UserInfo userInfo = mUserManager.getUserInfo(vol.mountUserId);
        UserInfo userInfo = mUserManager.getUserInfo(vol.getMountUserId());
        if (userInfo != null && isMediaSharedWithParent) {
            // Clones use the same connection as their parent
            return userInfo.profileGroupId;
        } else {
            return vol.mountUserId;
            return vol.getMountUserId();
        }
    }

@@ -108,7 +109,7 @@ public final class StorageSessionController {
     * @throws ExternalStorageServiceException if the session fails to start
     * @throws IllegalStateException if a session has already been created for {@code vol}
     */
    public void onVolumeMount(ParcelFileDescriptor deviceFd, VolumeInfo vol)
    public void onVolumeMount(ParcelFileDescriptor deviceFd, ImmutableVolumeInfo vol)
            throws ExternalStorageServiceException {
        if (!shouldHandle(vol)) {
            return;
@@ -144,7 +145,8 @@ public final class StorageSessionController {
     *
     * @throws ExternalStorageServiceException if it fails to connect to ExternalStorageService
     */
    public void notifyVolumeStateChanged(VolumeInfo vol) throws ExternalStorageServiceException {
    public void notifyVolumeStateChanged(ImmutableVolumeInfo vol)
            throws ExternalStorageServiceException {
        if (!shouldHandle(vol)) {
            return;
        }
@@ -214,7 +216,7 @@ public final class StorageSessionController {
     * @return the connection that was removed or {@code null} if nothing was removed
     */
    @Nullable
    public StorageUserConnection onVolumeRemove(VolumeInfo vol) {
    public StorageUserConnection onVolumeRemove(ImmutableVolumeInfo vol) {
        if (!shouldHandle(vol)) {
            return null;
        }
@@ -246,7 +248,7 @@ public final class StorageSessionController {
     *
     * Call {@link #onVolumeRemove} to remove the connection without waiting for exit
     */
    public void onVolumeUnmount(VolumeInfo vol) {
    public void onVolumeUnmount(ImmutableVolumeInfo vol) {
        String sessionId = vol.getId();
        final long token = Binder.clearCallingIdentity();
        try {
@@ -457,9 +459,9 @@ public final class StorageSessionController {
     * Returns {@code true} if {@code vol} is an emulated or visible public volume,
     * {@code false} otherwise
     **/
    public static boolean isEmulatedOrPublic(VolumeInfo vol) {
        return vol.type == VolumeInfo.TYPE_EMULATED
                || (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isVisible());
    public static boolean isEmulatedOrPublic(ImmutableVolumeInfo vol) {
        return vol.getType() == VolumeInfo.TYPE_EMULATED
                || (vol.getType() == VolumeInfo.TYPE_PUBLIC && vol.isVisible());
    }

    /** Exception thrown when communication with the {@link ExternalStorageService} fails. */
@@ -477,11 +479,11 @@ public final class StorageSessionController {
        }
    }

    private static boolean isSupportedVolume(VolumeInfo vol) {
        return isEmulatedOrPublic(vol) || vol.type == VolumeInfo.TYPE_STUB;
    private static boolean isSupportedVolume(ImmutableVolumeInfo vol) {
        return isEmulatedOrPublic(vol) || vol.getType() == VolumeInfo.TYPE_STUB;
    }

    private boolean shouldHandle(@Nullable VolumeInfo vol) {
    private boolean shouldHandle(@Nullable ImmutableVolumeInfo vol) {
        return !mIsResetting && (vol == null || isSupportedVolume(vol));
    }

+206 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.storage;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.storage.DiskInfo;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;

import com.android.internal.util.IndentingPrintWriter;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;

import java.io.File;

/**
 * A wrapper for {@link VolumeInfo}  implementing the {@link Watchable} interface.
 *
 * The {@link VolumeInfo} class itself cannot safely implement Watchable, because it has several
 * UnsupportedAppUsage annotations and public fields, which allow it to be modified without
 * notifying watchers.
 *
 * @hide
 */
public class WatchedVolumeInfo extends WatchableImpl {
    private final VolumeInfo mVolumeInfo;

    private WatchedVolumeInfo(VolumeInfo volumeInfo) {
        mVolumeInfo = volumeInfo;
    }

    public WatchedVolumeInfo(WatchedVolumeInfo watchedVolumeInfo) {
        mVolumeInfo = new VolumeInfo(watchedVolumeInfo.mVolumeInfo);
    }

    public static WatchedVolumeInfo fromVolumeInfo(VolumeInfo info) {
        return new WatchedVolumeInfo(info);
    }

    /**
     * Returns a copy of the embedded VolumeInfo object, to be used by components
     * that just need it for retrieving some state from it.
     *
     * @return A copy of the embedded VolumeInfo object
     */

    public WatchedVolumeInfo clone() {
        return fromVolumeInfo(mVolumeInfo.clone());
    }

    public ImmutableVolumeInfo getImmutableVolumeInfo() {
        return ImmutableVolumeInfo.fromVolumeInfo(mVolumeInfo);
    }

    public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
        return mVolumeInfo.buildStorageVolume(context, userId, reportUnmounted);
    }

    public void dump(IndentingPrintWriter pw) {
        mVolumeInfo.dump(pw);
    }

    public DiskInfo getDisk() {
        return mVolumeInfo.getDisk();
    }

    public String getDiskId() {
        return mVolumeInfo.getDiskId();
    }

    public String getFsLabel() {
        return mVolumeInfo.fsLabel;
    }

    public void setFsLabel(String fsLabel) {
        mVolumeInfo.fsLabel = fsLabel;
        dispatchChange(this);
    }

    public String getFsPath() {
        return mVolumeInfo.path;
    }

    public void setFsPath(String path) {
        mVolumeInfo.path = path;
        dispatchChange(this);
    }

    public String getFsType() {
        return mVolumeInfo.fsType;
    }

    public void setFsType(String fsType) {
        mVolumeInfo.fsType = fsType;
        dispatchChange(this);
    }

    public @Nullable String getFsUuid() {
        return mVolumeInfo.fsUuid;
    }

    public void setFsUuid(String fsUuid) {
        mVolumeInfo.fsUuid = fsUuid;
        dispatchChange(this);
    }

    public @NonNull String getId() {
        return mVolumeInfo.id;
    }

    public File getInternalPath() {
        return mVolumeInfo.getInternalPath();
    }

    public void setInternalPath(String internalPath) {
        mVolumeInfo.internalPath = internalPath;
        dispatchChange(this);
    }

    public int getMountFlags() {
        return mVolumeInfo.mountFlags;
    }

    public void setMountFlags(int mountFlags) {
        mVolumeInfo.mountFlags = mountFlags;
        dispatchChange(this);
    }

    public int getMountUserId() {
        return mVolumeInfo.mountUserId;
    }

    public void setMountUserId(int mountUserId) {
        mVolumeInfo.mountUserId = mountUserId;
        dispatchChange(this);
    }

    public String getPartGuid() {
        return mVolumeInfo.partGuid;
    }

    public File getPath() {
        return mVolumeInfo.getPath();
    }

    public int getState() {
        return mVolumeInfo.state;
    }

    public int getState(int state) {
        return mVolumeInfo.state;
    }

    public void setState(int state) {
        mVolumeInfo.state = state;
        dispatchChange(this);
    }

    public int getType() {
        return mVolumeInfo.type;
    }

    public VolumeInfo getVolumeInfo() {
        return new VolumeInfo(mVolumeInfo);
    }

    public boolean isMountedReadable() {
        return mVolumeInfo.isMountedReadable();
    }

    public boolean isMountedWritable() {
        return mVolumeInfo.isMountedWritable();
    }

    public boolean isPrimary() {
        return mVolumeInfo.isPrimary();
    }

    public boolean isVisible() {
        return mVolumeInfo.isVisible();
    }

    public boolean isVisibleForUser(int userId) {
        return mVolumeInfo.isVisibleForUser(userId);
    }

    public boolean isVisibleForWrite(int userId) {
        return mVolumeInfo.isVisibleForWrite(userId);
    }
}
 No newline at end of file
Loading