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
This commit is contained in:
@@ -18453,10 +18453,13 @@ package android.os {
|
||||
method public static java.io.File getExternalStorageDirectory();
|
||||
method public static java.io.File getExternalStoragePublicDirectory(java.lang.String);
|
||||
method public static java.lang.String getExternalStorageState();
|
||||
method public static java.lang.String getExternalStorageState(java.io.File);
|
||||
method public static java.io.File getRootDirectory();
|
||||
method public static java.lang.String getStorageState(java.io.File);
|
||||
method public static deprecated java.lang.String getStorageState(java.io.File);
|
||||
method public static boolean isExternalStorageEmulated();
|
||||
method public static boolean isExternalStorageEmulated(java.io.File);
|
||||
method public static boolean isExternalStorageRemovable();
|
||||
method public static boolean isExternalStorageRemovable(java.io.File);
|
||||
field public static java.lang.String DIRECTORY_ALARMS;
|
||||
field public static java.lang.String DIRECTORY_DCIM;
|
||||
field public static java.lang.String DIRECTORY_DOCUMENTS;
|
||||
|
||||
@@ -728,7 +728,7 @@ public abstract class Context {
|
||||
* Returned paths may be {@code null} if a storage device is unavailable.
|
||||
*
|
||||
* @see #getExternalFilesDir(String)
|
||||
* @see Environment#getStorageState(File)
|
||||
* @see Environment#getExternalStorageState(File)
|
||||
*/
|
||||
public abstract File[] getExternalFilesDirs(String type);
|
||||
|
||||
@@ -792,7 +792,7 @@ public abstract class Context {
|
||||
* Returned paths may be {@code null} if a storage device is unavailable.
|
||||
*
|
||||
* @see #getObbDir()
|
||||
* @see Environment#getStorageState(File)
|
||||
* @see Environment#getExternalStorageState(File)
|
||||
*/
|
||||
public abstract File[] getObbDirs();
|
||||
|
||||
@@ -895,7 +895,7 @@ public abstract class Context {
|
||||
* Returned paths may be {@code null} if a storage device is unavailable.
|
||||
*
|
||||
* @see #getExternalCacheDir()
|
||||
* @see Environment#getStorageState(File)
|
||||
* @see Environment#getExternalStorageState(File)
|
||||
*/
|
||||
public abstract File[] getExternalCacheDirs();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
import com.google.android.collect.Sets;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -33,8 +35,6 @@ import java.io.FileWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
@MediumTest
|
||||
public class FileUtilsTest extends AndroidTestCase {
|
||||
private static final String TEST_DATA =
|
||||
@@ -112,6 +112,23 @@ public class FileUtilsTest extends AndroidTestCase {
|
||||
assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
|
||||
}
|
||||
|
||||
public void testContains() throws Exception {
|
||||
assertTrue(FileUtils.contains(new File("/"), new File("/moo.txt")));
|
||||
assertTrue(FileUtils.contains(new File("/"), new File("/")));
|
||||
|
||||
assertTrue(FileUtils.contains(new File("/sdcard"), new File("/sdcard")));
|
||||
assertTrue(FileUtils.contains(new File("/sdcard/"), new File("/sdcard/")));
|
||||
|
||||
assertTrue(FileUtils.contains(new File("/sdcard"), new File("/sdcard/moo.txt")));
|
||||
assertTrue(FileUtils.contains(new File("/sdcard/"), new File("/sdcard/moo.txt")));
|
||||
|
||||
assertFalse(FileUtils.contains(new File("/sdcard"), new File("/moo.txt")));
|
||||
assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/moo.txt")));
|
||||
|
||||
assertFalse(FileUtils.contains(new File("/sdcard"), new File("/sdcard.txt")));
|
||||
assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/sdcard.txt")));
|
||||
}
|
||||
|
||||
public void testDeleteOlderEmptyDir() throws Exception {
|
||||
FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
|
||||
assertDirContents();
|
||||
|
||||
Reference in New Issue
Block a user