From 4634987668eb7e1fa1434bddbde969ef43de6b40 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 28 Jul 2015 10:49:47 -0700 Subject: [PATCH] Give secondary users read-only physical cards. Long ago, we mounted secondary physical cards as readable by all users on the device, which enabled the use-case of loading media on a card and viewing it from all users. More recently, we started giving write access to these secondary physical cards, but this created a one-directional channel for communication across user boundaries; something that CDD disallows. This change is designed to give us the best of both worlds: the package-specific directories are writable for the user that mounted the card, but access to those "Android" directories are blocked for all other users. Other users remain able to read content elsewhere on the card. Bug: 22787184 Change-Id: Ied8c98995fec1b7b50ff7d930550feabb4398582 --- core/java/android/os/Environment.java | 3 +- .../android/os/storage/IMountService.java | 8 +++-- .../android/os/storage/StorageManager.java | 11 +++--- core/java/android/os/storage/VolumeInfo.java | 35 +++++++++++++++++-- .../java/com/android/server/MountService.java | 12 +++---- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 208085624e3b0..64d6da57718f3 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -80,7 +80,8 @@ public class Environment { } public File[] getExternalDirs() { - final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId); + final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId, + StorageManager.FLAG_FOR_WRITE); final File[] files = new File[volumes.length]; for (int i = 0; i < volumes.length; i++) { files[i] = volumes[i].getPathFile(); diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index c3b098beb4227..fce09dd4c80cc 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -758,7 +758,7 @@ public interface IMountService extends IInterface { return _result; } - public StorageVolume[] getVolumeList(int uid, String packageName) + public StorageVolume[] getVolumeList(int uid, String packageName, int flags) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -767,6 +767,7 @@ public interface IMountService extends IInterface { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(uid); _data.writeString(packageName); + _data.writeInt(flags); mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArray(StorageVolume.CREATOR); @@ -1609,7 +1610,8 @@ public interface IMountService extends IInterface { data.enforceInterface(DESCRIPTOR); int uid = data.readInt(); String packageName = data.readString(); - StorageVolume[] result = getVolumeList(uid, packageName); + int _flags = data.readInt(); + StorageVolume[] result = getVolumeList(uid, packageName, _flags); reply.writeNoException(); reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); return true; @@ -2059,7 +2061,7 @@ public interface IMountService extends IInterface { /** * Returns list of all mountable volumes. */ - public StorageVolume[] getVolumeList(int uid, String packageName) throws RemoteException; + public StorageVolume[] getVolumeList(int uid, String packageName, int flags) throws RemoteException; /** * Gets the path on the filesystem for the ASEC container itself. diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index b2cec60c04125..d1f37436e4ba3 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -86,6 +86,9 @@ public class StorageManager { /** {@hide} */ public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; + /** {@hide} */ + public static final int FLAG_FOR_WRITE = 1 << 0; + private final Context mContext; private final ContentResolver mResolver; @@ -812,7 +815,7 @@ public class StorageManager { /** {@hide} */ public static @Nullable StorageVolume getStorageVolume(File file, int userId) { - return getStorageVolume(getVolumeList(userId), file); + return getStorageVolume(getVolumeList(userId, 0), file); } /** {@hide} */ @@ -852,11 +855,11 @@ public class StorageManager { /** {@hide} */ public @NonNull StorageVolume[] getVolumeList() { - return getVolumeList(mContext.getUserId()); + return getVolumeList(mContext.getUserId(), 0); } /** {@hide} */ - public static @NonNull StorageVolume[] getVolumeList(int userId) { + public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { final IMountService mountService = IMountService.Stub.asInterface( ServiceManager.getService("mount")); try { @@ -877,7 +880,7 @@ public class StorageManager { if (uid <= 0) { return new StorageVolume[0]; } - return mountService.getVolumeList(uid, packageName); + return mountService.getVolumeList(uid, packageName, flags); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index 8d603a17f1862..ef54d84fdebf7 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -46,6 +46,19 @@ import java.util.Objects; * Information about a storage volume that may be mounted. A volume may be a * partition on a physical {@link DiskInfo}, an emulated volume above some other * storage medium, or a standalone container like an ASEC or OBB. + *

+ * Volumes may be mounted with various flags: + *

* * @hide */ @@ -255,8 +268,23 @@ public class VolumeInfo implements Parcelable { return (mountFlags & MOUNT_FLAG_VISIBLE) != 0; } - public boolean isVisibleToUser(int userId) { - if (type == TYPE_PUBLIC && userId == this.mountUserId) { + public boolean isVisibleForRead(int userId) { + if (type == TYPE_PUBLIC) { + if (isPrimary() && mountUserId != userId) { + // Primary physical is only visible to single user + return false; + } else { + return isVisible(); + } + } else if (type == TYPE_EMULATED) { + return isVisible(); + } else { + return false; + } + } + + public boolean isVisibleForWrite(int userId) { + if (type == TYPE_PUBLIC && mountUserId == userId) { return isVisible(); } else if (type == TYPE_EMULATED) { return isVisible(); @@ -276,7 +304,7 @@ public class VolumeInfo implements Parcelable { public File getPathForUser(int userId) { if (path == null) { return null; - } else if (type == TYPE_PUBLIC && userId == this.mountUserId) { + } else if (type == TYPE_PUBLIC) { return new File(path); } else if (type == TYPE_EMULATED) { return new File(path, Integer.toString(userId)); @@ -306,6 +334,7 @@ public class VolumeInfo implements Parcelable { final boolean allowMassStorage = false; final String envState = reportUnmounted ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state); + File userPath = getPathForUser(userId); if (userPath == null) { userPath = new File("/dev/null"); diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 857394f57b12e..6ab2fd72ac995 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -69,7 +69,6 @@ import android.os.storage.IMountServiceListener; import android.os.storage.IMountShutdownObserver; import android.os.storage.IObbActionListener; import android.os.storage.MountServiceInternal; -import android.os.storage.MountServiceInternal.ExternalStorageMountPolicy; import android.os.storage.OnObbStateChangeListener; import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; @@ -809,7 +808,7 @@ class MountService extends IMountService.Stub synchronized (mVolumes) { for (int i = 0; i < mVolumes.size(); i++) { final VolumeInfo vol = mVolumes.valueAt(i); - if (vol.isVisibleToUser(userId) && vol.isMountedReadable()) { + if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) { final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); @@ -1252,7 +1251,7 @@ class MountService extends IMountService.Stub // started after this point will trigger additional // user-specific broadcasts. for (int userId : mStartedUsers) { - if (vol.isVisibleToUser(userId)) { + if (vol.isVisibleForRead(userId)) { final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); @@ -2610,13 +2609,14 @@ class MountService extends IMountService.Stub } @Override - public StorageVolume[] getVolumeList(int uid, String packageName) { + public StorageVolume[] getVolumeList(int uid, String packageName, int flags) { + final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0; + final ArrayList res = new ArrayList<>(); boolean foundPrimary = false; final int userId = UserHandle.getUserId(uid); final boolean reportUnmounted; - final long identity = Binder.clearCallingIdentity(); try { reportUnmounted = !mMountServiceInternal.hasExternalStorage( @@ -2628,7 +2628,7 @@ class MountService extends IMountService.Stub synchronized (mLock) { for (int i = 0; i < mVolumes.size(); i++) { final VolumeInfo vol = mVolumes.valueAt(i); - if (vol.isVisibleToUser(userId)) { + if (forWrite ? vol.isVisibleForWrite(userId) : vol.isVisibleForRead(userId)) { final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, reportUnmounted); if (vol.isPrimary()) {