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

Commit 5d2135b3 authored by Martijn Coenen's avatar Martijn Coenen
Browse files

Introduce ImmutableVolumeInfo.

Now that we're caching VolumeInfo objects, make sure that they cannot
easily be changed without the cache knowing about it. Use immutable
versions when we can.

Bug: 323574186
Flag: EXEMPT refactor
Test: N/A
Change-Id: Ib8a00e7307e6466d64a8b3fdfc5a2272a1f4d308
parent 824ab708
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -1549,7 +1549,7 @@ class StorageManagerService extends IStorageManager.Stub
            }

            if (vol != null) {
                mStorageSessionController.onVolumeRemove(vol.getVolumeInfo());
                mStorageSessionController.onVolumeRemove(vol.getImmutableVolumeInfo());
                try {
                    if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
                        mInstaller.onPrivateVolumeRemoved(vol.getFsUuid());
@@ -1787,7 +1787,7 @@ class StorageManagerService extends IStorageManager.Stub
        // before notifying other listeners.
        // Intentionally called without the mLock to avoid deadlocking from the Storage Service.
        try {
            mStorageSessionController.notifyVolumeStateChanged(vol.getVolumeInfo());
            mStorageSessionController.notifyVolumeStateChanged(vol.getImmutableVolumeInfo());
        } catch (ExternalStorageServiceException e) {
            Log.e(TAG, "Failed to notify volume state changed to the Storage Service", e);
        }
@@ -2388,7 +2388,7 @@ class StorageManagerService extends IStorageManager.Stub
                    try {
                        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
                                "SMS.startFuseFileSystem: " + vol.getId());
                        mStorageSessionController.onVolumeMount(pfd, vol.getVolumeInfo());
                        mStorageSessionController.onVolumeMount(pfd, vol.getImmutableVolumeInfo());
                        return true;
                    } catch (ExternalStorageServiceException e) {
                        Slog.e(TAG, "Failed to mount volume " + vol, e);
@@ -2437,7 +2437,7 @@ class StorageManagerService extends IStorageManager.Stub
                Slog.e(TAG, "Failed unmount mirror data", e);
            }
            mVold.unmount(vol.getId());
            mStorageSessionController.onVolumeUnmount(vol.getVolumeInfo());
            mStorageSessionController.onVolumeUnmount(vol.getImmutableVolumeInfo());
        } catch (Exception e) {
            Slog.wtf(TAG, e);
        }
+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);
    }
}
+9 −8
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,7 +80,7 @@ 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.getMountUserId()), 0);
        boolean isMediaSharedWithParent = volumeUserContext.getSystemService(
@@ -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,7 @@ public final class StorageSessionController {
     *
     * @throws ExternalStorageServiceException if it fails to connect to ExternalStorageService
     */
    public void notifyVolumeStateChanged(VolumeInfo vol)
    public void notifyVolumeStateChanged(ImmutableVolumeInfo vol)
            throws ExternalStorageServiceException {
        if (!shouldHandle(vol)) {
            return;
@@ -215,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;
        }
@@ -247,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 {
@@ -458,7 +459,7 @@ 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) {
    public static boolean isEmulatedOrPublic(ImmutableVolumeInfo vol) {
        return vol.getType() == VolumeInfo.TYPE_EMULATED
                || (vol.getType() == VolumeInfo.TYPE_PUBLIC && vol.isVisible());
    }
@@ -478,11 +479,11 @@ public final class StorageSessionController {
        }
    }

    private static boolean isSupportedVolume(VolumeInfo vol) {
    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));
    }

+4 −0
Original line number Diff line number Diff line
@@ -64,6 +64,10 @@ public class WatchedVolumeInfo extends WatchableImpl {
        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);
    }