diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-01-10 16:27:19 -0800 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2014-01-15 17:00:56 -0800 |
commit | 4ca728c064aeab644f6d044e0285eaa056818b8a (patch) | |
tree | 338bfa8e27e1ab4a6ac5834bcaba13332c41d71a /core/java/android/os | |
parent | ac6b332764264b1a940a3037434934582b9a9f68 (diff) | |
download | frameworks_base-4ca728c064aeab644f6d044e0285eaa056818b8a.zip frameworks_base-4ca728c064aeab644f6d044e0285eaa056818b8a.tar.gz frameworks_base-4ca728c064aeab644f6d044e0285eaa056818b8a.tar.bz2 |
Detect removable and emulated secondary storage.
Also rename existing secondary storage API to match naming
convention in rest of class.
Bug: 11536709
Change-Id: I2684c817de4982b414893d2d9927a21e3f171d53
Diffstat (limited to 'core/java/android/os')
-rw-r--r-- | core/java/android/os/Environment.java | 180 | ||||
-rw-r--r-- | core/java/android/os/FileUtils.java | 22 |
2 files changed, 124 insertions, 78 deletions
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index b5413db..54e2c0b 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -16,14 +16,13 @@ package android.os; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.storage.IMountService; -import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.text.TextUtils; import android.util.Log; -import com.android.internal.annotations.GuardedBy; import com.google.android.collect.Lists; import java.io.File; @@ -66,33 +65,6 @@ public class Environment { private static UserEnvironment sCurrentUser; private static boolean sUserRequired; - private static final Object sLock = new Object(); - - @GuardedBy("sLock") - private static volatile StorageVolume sPrimaryVolume; - - private static StorageVolume getPrimaryVolume() { - if (SystemProperties.getBoolean("config.disable_storage", false)) { - return null; - } - - if (sPrimaryVolume == null) { - synchronized (sLock) { - if (sPrimaryVolume == null) { - try { - IMountService mountService = IMountService.Stub.asInterface(ServiceManager - .getService("mount")); - final StorageVolume[] volumes = mountService.getVolumeList(); - sPrimaryVolume = StorageManager.getPrimaryVolume(volumes); - } catch (Exception e) { - Log.e(TAG, "couldn't talk to MountService", e); - } - } - } - } - return sPrimaryVolume; - } - static { initForCurrentUser(); } @@ -101,10 +73,6 @@ public class Environment { public static void initForCurrentUser() { final int userId = UserHandle.myUserId(); sCurrentUser = new UserEnvironment(userId); - - synchronized (sLock) { - sPrimaryVolume = null; - } } /** {@hide} */ @@ -603,28 +571,28 @@ public class Environment { * Unknown storage state, such as when a path isn't backed by known storage * media. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_UNKNOWN = "unknown"; /** * Storage state if the media is not present. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_REMOVED = "removed"; /** * Storage state if the media is present but not mounted. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_UNMOUNTED = "unmounted"; /** * Storage state if the media is present and being disk-checked. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_CHECKING = "checking"; @@ -632,7 +600,7 @@ public class Environment { * Storage state if the media is present but is blank or is using an * unsupported filesystem. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_NOFS = "nofs"; @@ -640,7 +608,7 @@ public class Environment { * Storage state if the media is present and mounted at its mount point with * read/write access. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_MOUNTED = "mounted"; @@ -648,7 +616,7 @@ public class Environment { * Storage state if the media is present and mounted at its mount point with * read-only access. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro"; @@ -656,14 +624,14 @@ public class Environment { * Storage state if the media is present not mounted, and shared via USB * mass storage. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_SHARED = "shared"; /** * Storage state if the media was removed before it was unmounted. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_BAD_REMOVAL = "bad_removal"; @@ -671,7 +639,7 @@ public class Environment { * Storage state if the media is present but cannot be mounted. Typically * this happens if the file system on the media is corrupted. * - * @see #getStorageState(File) + * @see #getExternalStorageState(File) */ public static final String MEDIA_UNMOUNTABLE = "unmountable"; @@ -687,7 +655,15 @@ public class Environment { */ public static String getExternalStorageState() { final File externalDir = sCurrentUser.getExternalDirsForApp()[0]; - return getStorageState(externalDir); + return getExternalStorageState(externalDir); + } + + /** + * @deprecated use {@link #getExternalStorageState(File)} + */ + @Deprecated + public static String getStorageState(File path) { + return getExternalStorageState(path); } /** @@ -700,59 +676,81 @@ public class Environment { * {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED}, * {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}. */ - public static String getStorageState(File path) { - final String rawPath; - try { - rawPath = path.getCanonicalPath(); - } catch (IOException e) { - Log.w(TAG, "Failed to resolve target path: " + e); - return Environment.MEDIA_UNKNOWN; - } - - try { + public static String getExternalStorageState(File path) { + final StorageVolume volume = getStorageVolume(path); + if (volume != null) { final IMountService mountService = IMountService.Stub.asInterface( ServiceManager.getService("mount")); - final StorageVolume[] volumes = mountService.getVolumeList(); - for (StorageVolume volume : volumes) { - if (rawPath.startsWith(volume.getPath())) { - return mountService.getVolumeState(volume.getPath()); - } + try { + return mountService.getVolumeState(volume.getPath()); + } catch (RemoteException e) { } - } catch (RemoteException e) { - Log.w(TAG, "Failed to find external storage state: " + e); } + return Environment.MEDIA_UNKNOWN; } /** * Returns whether the primary "external" storage device is removable. - * If true is returned, this device is for example an SD card that the - * user can remove. If false is returned, the storage is built into - * the device and can not be physically removed. * - * <p>See {@link #getExternalStorageDirectory()} for more information. + * @return true if the storage device can be removed (such as an SD card), + * or false if the storage device is built in and cannot be + * physically removed. */ public static boolean isExternalStorageRemovable() { - final StorageVolume primary = getPrimaryVolume(); - return (primary != null && primary.isRemovable()); + if (isStorageDisabled()) return false; + final File externalDir = sCurrentUser.getExternalDirsForApp()[0]; + return isExternalStorageRemovable(externalDir); } /** - * Returns whether the device has an external storage device which is - * emulated. If true, the device does not have real external storage, and the directory - * returned by {@link #getExternalStorageDirectory()} will be allocated using a portion of - * the internal storage system. + * Returns whether the storage device that provides the given path is + * removable. * - * <p>Certain system services, such as the package manager, use this - * to determine where to install an application. + * @return true if the storage device can be removed (such as an SD card), + * or false if the storage device is built in and cannot be + * physically removed. + * @throws IllegalArgumentException if the path is not a valid storage + * device. + */ + public static boolean isExternalStorageRemovable(File path) { + final StorageVolume volume = getStorageVolume(path); + if (volume != null) { + return volume.isRemovable(); + } else { + throw new IllegalArgumentException("Failed to find storage device at " + path); + } + } + + /** + * Returns whether the primary "external" storage device is emulated. If + * true, data stored on this device will be stored on a portion of the + * internal storage system. * - * <p>Emulated external storage may also be encrypted - see - * {@link android.app.admin.DevicePolicyManager#setStorageEncryption( - * android.content.ComponentName, boolean)} for additional details. + * @see DevicePolicyManager#setStorageEncryption(android.content.ComponentName, + * boolean) */ public static boolean isExternalStorageEmulated() { - final StorageVolume primary = getPrimaryVolume(); - return (primary != null && primary.isEmulated()); + if (isStorageDisabled()) return false; + final File externalDir = sCurrentUser.getExternalDirsForApp()[0]; + return isExternalStorageEmulated(externalDir); + } + + /** + * Returns whether the storage device that provides the given path is + * emulated. If true, data stored on this device will be stored on a portion + * of the internal storage system. + * + * @throws IllegalArgumentException if the path is not a valid storage + * device. + */ + public static boolean isExternalStorageEmulated(File path) { + final StorageVolume volume = getStorageVolume(path); + if (volume != null) { + return volume.isEmulated(); + } else { + throw new IllegalArgumentException("Failed to find storage device at " + path); + } } static File getDirectory(String variableName, String defaultPath) { @@ -815,6 +813,32 @@ public class Environment { return cur; } + private static boolean isStorageDisabled() { + return SystemProperties.getBoolean("config.disable_storage", false); + } + + private static StorageVolume getStorageVolume(File path) { + try { + path = path.getCanonicalFile(); + } catch (IOException e) { + return null; + } + + try { + final IMountService mountService = IMountService.Stub.asInterface( + ServiceManager.getService("mount")); + final StorageVolume[] volumes = mountService.getVolumeList(); + for (StorageVolume volume : volumes) { + if (FileUtils.contains(volume.getPathFile(), path)) { + return volume; + } + } + } catch (RemoteException e) { + } + + return null; + } + /** * If the given path exists on emulated external storage, return the * translated backing path hosted on internal storage. This bypasses any diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 2d60df0..15a154a 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -355,4 +355,26 @@ public class FileUtils { } } } + + /** + * Test if a file lives under the given directory, either as a direct child + * or a distant grandchild. + * <p> + * Both files <em>must</em> have been resolved using + * {@link File#getCanonicalFile()} to avoid symlink or path traversal + * attacks. + */ + public static boolean contains(File dir, File file) { + String dirPath = dir.getPath(); + String filePath = file.getPath(); + + if (dirPath.equals(filePath)) { + return true; + } + + if (!dirPath.endsWith("/")) { + dirPath += "/"; + } + return filePath.startsWith(dirPath); + } } |