Loading core/api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -32416,6 +32416,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; Loading Loading @@ -32449,6 +32450,7 @@ package android.os.storage { method public String getDescription(android.content.Context); method @Nullable public java.io.File getDirectory(); method @Nullable public String getMediaStoreVolumeName(); method @NonNull public android.os.UserHandle getOwner(); method public String getState(); method @Nullable public java.util.UUID getStorageUuid(); method @Nullable public String getUuid(); core/api/module-lib-current.txt +0 −4 Original line number Diff line number Diff line Loading @@ -541,10 +541,6 @@ package android.os.storage { field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0 } public final class StorageVolume implements android.os.Parcelable { method @NonNull public android.os.UserHandle getOwner(); } } package android.provider { Loading core/java/android/os/storage/StorageManager.java +19 −0 Original line number Diff line number Diff line Loading @@ -291,6 +291,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; Loading Loading @@ -1327,6 +1329,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. Loading core/java/android/os/storage/StorageVolume.java +2 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.os.storage; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; Loading Loading @@ -308,11 +306,9 @@ public final class StorageVolume implements Parcelable { /** * Returns the user that owns this volume * * {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @SystemApi(client = MODULE_LIBRARIES) // TODO(b/193460475) : Android Lint handle API change from systemApi to public Api incorrectly @SuppressLint("NewApi") public @NonNull UserHandle getOwner() { return mOwner; } Loading services/core/java/com/android/server/StorageManagerService.java +57 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -3971,6 +3989,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 Loading Loading @@ -4002,6 +4043,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); Loading @@ -4014,6 +4056,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; Loading @@ -4021,10 +4068,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; Loading @@ -4045,9 +4094,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 { Loading Loading
core/api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -32416,6 +32416,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; Loading Loading @@ -32449,6 +32450,7 @@ package android.os.storage { method public String getDescription(android.content.Context); method @Nullable public java.io.File getDirectory(); method @Nullable public String getMediaStoreVolumeName(); method @NonNull public android.os.UserHandle getOwner(); method public String getState(); method @Nullable public java.util.UUID getStorageUuid(); method @Nullable public String getUuid();
core/api/module-lib-current.txt +0 −4 Original line number Diff line number Diff line Loading @@ -541,10 +541,6 @@ package android.os.storage { field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0 } public final class StorageVolume implements android.os.Parcelable { method @NonNull public android.os.UserHandle getOwner(); } } package android.provider { Loading
core/java/android/os/storage/StorageManager.java +19 −0 Original line number Diff line number Diff line Loading @@ -291,6 +291,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; Loading Loading @@ -1327,6 +1329,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. Loading
core/java/android/os/storage/StorageVolume.java +2 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.os.storage; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; Loading Loading @@ -308,11 +306,9 @@ public final class StorageVolume implements Parcelable { /** * Returns the user that owns this volume * * {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @SystemApi(client = MODULE_LIBRARIES) // TODO(b/193460475) : Android Lint handle API change from systemApi to public Api incorrectly @SuppressLint("NewApi") public @NonNull UserHandle getOwner() { return mOwner; } Loading
services/core/java/com/android/server/StorageManagerService.java +57 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -3971,6 +3989,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 Loading Loading @@ -4002,6 +4043,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); Loading @@ -4014,6 +4056,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; Loading @@ -4021,10 +4068,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; Loading @@ -4045,9 +4094,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 { Loading