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

Commit 7aeeb341 authored by sailendrabathi's avatar sailendrabathi
Browse files

Add API getStorageVolumesIncludingSharedProfiles()

The existing getStorageVolumes() does not include storage volumes that
belong to another user with which the calling user shares media with.
Added getStorageVolumesIncludingSharedProfiles() to fix this.

StorageManagerService uses the onUnlockUser method to find which users
share media between them and stores these users as key-value pairs.
This is used when getStorageVolumesIncludingSharedProfiles() is called.

Bug: 209973853
Test: atest
AppCloningHostTest#testGetStorageVolumesIncludingSharedProfiles

Change-Id: Ia554623391505e637cfed904c4b5fc358818d981
parent 69455cff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -33167,6 +33167,7 @@ package android.os.storage {
    method @Nullable public android.os.storage.StorageVolume getStorageVolume(@NonNull java.io.File);
    method @NonNull public android.os.storage.StorageVolume getStorageVolume(@NonNull android.net.Uri);
    method @NonNull public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public java.util.List<android.os.storage.StorageVolume> getStorageVolumesIncludingSharedProfiles();
    method @NonNull public java.util.UUID getUuidForPath(@NonNull java.io.File) throws java.io.IOException;
    method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor);
    method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
+19 −0
Original line number Diff line number Diff line
@@ -290,6 +290,8 @@ public class StorageManager {
    public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
    /** {@hide} */
    public static final int FLAG_INCLUDE_RECENT = 1 << 11;
    /** {@hide} */
    public static final int FLAG_INCLUDE_SHARED_PROFILE = 1 << 12;

    /** {@hide} */
    public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM;
@@ -1326,6 +1328,23 @@ public class StorageManager {
        return res;
    }

    /**
     * Return the list of shared/external storage volumes currently available to
     * the calling user and the user it shares media with
     * CDD link : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
     * <p>
     * This is similar to {@link StorageManager#getStorageVolumes()} except that the result also
     * includes the volumes belonging to any user it shares media with
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
    public @NonNull List<StorageVolume> getStorageVolumesIncludingSharedProfiles() {
        final ArrayList<StorageVolume> res = new ArrayList<>();
        Collections.addAll(res,
                getVolumeList(mContext.getUserId(),
                        FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_SHARED_PROFILE));
        return res;
    }

    /**
     * Return the list of shared/external storage volumes both currently and
     * recently available to the calling user.
+59 −6
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ import android.provider.DocumentsContract;
import android.provider.Downloads;
import android.provider.MediaStore;
import android.provider.Settings;
import android.service.storage.ExternalStorageService;
import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -160,8 +161,6 @@ import com.android.server.storage.StorageSessionController.ExternalStorageServic
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;

@@ -173,6 +172,8 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -491,6 +492,8 @@ class StorageManagerService extends IStorageManager.Stub
    @GuardedBy("mAppFuseLock")
    private AppFuseBridge mAppFuseBridge = null;

    private HashMap<Integer, Integer> mUserSharesMediaWith = new HashMap<>();

    /** Matches known application dir paths. The first group contains the generic part of the path,
     * the second group contains the user id (or null if it's a public volume without users), the
     * third group contains the package name, and the fourth group the remainder of the path.
@@ -1235,6 +1238,21 @@ class StorageManagerService extends IStorageManager.Stub
    private void onUnlockUser(int userId) {
        Slog.d(TAG, "onUnlockUser " + userId);

        if (userId != UserHandle.USER_SYSTEM) {
            // Check if this user shares media with another user
            try {
                Context userContext = mContext.createPackageContextAsUser("system", 0,
                        UserHandle.of(userId));
                UserManager um = userContext.getSystemService(UserManager.class);
                if (um != null && um.isMediaSharedWithParent()) {
                    int parentUserId = um.getProfileParent(userId).id;
                    mUserSharesMediaWith.put(userId, parentUserId);
                    mUserSharesMediaWith.put(parentUserId, userId);
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Failed to create user context for user " + userId);
            }
        }
        // We purposefully block here to make sure that user-specific
        // staging area is ready so it's ready for zygote-forked apps to
        // bind mount against.
@@ -3953,6 +3971,29 @@ class StorageManagerService extends IStorageManager.Stub
        final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
        final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
        final boolean includeRecent = (flags & StorageManager.FLAG_INCLUDE_RECENT) != 0;
        final boolean includeSharedProfile =
                (flags & StorageManager.FLAG_INCLUDE_SHARED_PROFILE) != 0;

        // Only Apps with MANAGE_EXTERNAL_STORAGE should call the API with includeSharedProfile
        if (includeSharedProfile) {
            try {
                // Get package name for calling app and
                // verify it has MANAGE_EXTERNAL_STORAGE permission
                final String[] packagesFromUid = mIPackageManager.getPackagesForUid(callingUid);
                if (packagesFromUid == null) {
                    throw new SecurityException("Unknown uid " + callingUid);
                }
                // Checking first entry in packagesFromUid is enough as using "sharedUserId"
                // mechanism is rare and discouraged. Also, Apps that share same UID share the same
                // permissions.
                if (!mStorageManagerInternal.hasExternalStorageAccess(callingUid,
                        packagesFromUid[0])) {
                    throw new SecurityException("Only File Manager Apps permitted");
                }
            } catch (RemoteException re) {
                throw new SecurityException("Unknown uid " + callingUid, re);
            }
        }

        // Report all volumes as unmounted until we've recorded that user 0 has unlocked. There
        // are no guarantees that callers will see a consistent view of the volume before that
@@ -3984,6 +4025,7 @@ class StorageManagerService extends IStorageManager.Stub

        final ArrayList<StorageVolume> res = new ArrayList<>();
        final ArraySet<String> resUuids = new ArraySet<>();
        final int userIdSharingMedia = mUserSharesMediaWith.getOrDefault(userId, -1);
        synchronized (mLock) {
            for (int i = 0; i < mVolumes.size(); i++) {
                final String volId = mVolumes.keyAt(i);
@@ -3996,6 +4038,11 @@ class StorageManagerService extends IStorageManager.Stub
                        if (vol.getMountUserId() == userId) {
                            break;
                        }
                        if (includeSharedProfile && vol.getMountUserId() == userIdSharingMedia) {
                            // If the volume belongs to a user we share media with,
                            // return it too.
                            break;
                        }
                        // Skip if emulated volume not for userId
                    default:
                        continue;
@@ -4003,10 +4050,12 @@ class StorageManagerService extends IStorageManager.Stub

                boolean match = false;
                if (forWrite) {
                    match = vol.isVisibleForWrite(userId);
                    match = vol.isVisibleForWrite(userId)
                            || (includeSharedProfile && vol.isVisibleForWrite(userIdSharingMedia));
                } else {
                    match = vol.isVisibleForUser(userId)
                            || (includeInvisible && vol.getPath() != null);
                            || (includeInvisible && vol.getPath() != null)
                            || (includeSharedProfile && vol.isVisibleForRead(userIdSharingMedia));
                }
                if (!match) continue;

@@ -4027,9 +4076,13 @@ class StorageManagerService extends IStorageManager.Stub
                    reportUnmounted = true;
                }

                final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
                int volUserId = userId;
                if (volUserId != vol.getMountUserId() && vol.getMountUserId() >= 0) {
                    volUserId = vol.getMountUserId();
                }
                final StorageVolume userVol = vol.buildStorageVolume(mContext, volUserId,
                        reportUnmounted);
                if (vol.isPrimary()) {
                if (vol.isPrimary() && vol.getMountUserId() == userId) {
                    res.add(0, userVol);
                    foundPrimary = true;
                } else {