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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user