Per-app media directories on external storage.

This change defines per-app directories on external storage that
will be scanned and included in MediaStore.  This gives apps a way
to write content to secondary shared storage in a way that can
easily be surfaced to other apps.

Bug: 14382377
Change-Id: I4cb367c870509e76f0c2c598f01e2f699780030a
This commit is contained in:
Jeff Sharkey
2014-05-30 15:38:35 -07:00
parent b2f4bc8727
commit 2ee3c1e189
8 changed files with 84 additions and 0 deletions

View File

@ -6902,6 +6902,7 @@ package android.content {
method public abstract java.io.File[] getExternalCacheDirs();
method public abstract java.io.File getExternalFilesDir(java.lang.String);
method public abstract java.io.File[] getExternalFilesDirs(java.lang.String);
method public abstract java.io.File[] getExternalMediaDirs();
method public abstract java.io.File getFileStreamPath(java.lang.String);
method public abstract java.io.File getFilesDir();
method public abstract android.os.Looper getMainLooper();
@ -7070,6 +7071,7 @@ package android.content {
method public java.io.File[] getExternalCacheDirs();
method public java.io.File getExternalFilesDir(java.lang.String);
method public java.io.File[] getExternalFilesDirs(java.lang.String);
method public java.io.File[] getExternalMediaDirs();
method public java.io.File getFileStreamPath(java.lang.String);
method public java.io.File getFilesDir();
method public android.os.Looper getMainLooper();
@ -28323,6 +28325,7 @@ package android.test.mock {
method public java.io.File[] getExternalCacheDirs();
method public java.io.File getExternalFilesDir(java.lang.String);
method public java.io.File[] getExternalFilesDirs(java.lang.String);
method public java.io.File[] getExternalMediaDirs();
method public java.io.File getFileStreamPath(java.lang.String);
method public java.io.File getFilesDir();
method public android.os.Looper getMainLooper();

View File

@ -249,6 +249,8 @@ class ContextImpl extends Context {
private File[] mExternalFilesDirs;
@GuardedBy("mSync")
private File[] mExternalCacheDirs;
@GuardedBy("mSync")
private File[] mExternalMediaDirs;
private static final String[] EMPTY_FILE_LIST = {};
@ -1031,6 +1033,18 @@ class ContextImpl extends Context {
}
}
@Override
public File[] getExternalMediaDirs() {
synchronized (mSync) {
if (mExternalMediaDirs == null) {
mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
}
// Create dirs if needed
return ensureDirsExistOrFilter(mExternalMediaDirs);
}
}
@Override
public File getFileStreamPath(String name) {
return makeFilename(getFilesDir(), name);

View File

@ -40,6 +40,7 @@ import android.os.Looper;
import android.os.StatFs;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.view.DisplayAdjustments;
import android.view.Display;
@ -928,6 +929,40 @@ public abstract class Context {
*/
public abstract File[] getExternalCacheDirs();
/**
* Returns absolute paths to application-specific directories on all
* external storage devices where the application can place media files.
* These files are scanned and made available to other apps through
* {@link MediaStore}.
* <p>
* This is like {@link #getExternalFilesDirs} in that these files will be
* deleted when the application is uninstalled, however there are some
* important differences:
* <ul>
* <li>External files are not always available: they will disappear if the
* user mounts the external storage on a computer or removes it.
* <li>There is no security enforced with these files.
* </ul>
* <p>
* External storage devices returned here are considered a permanent part of
* the device, including both emulated external storage and physical media
* slots, such as SD cards in a battery compartment. The returned paths do
* not include transient devices, such as USB flash drives.
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
* No permissions are required to read or write to the returned paths; they
* are always accessible to the calling app. Write access outside of these
* paths on secondary external storage devices is not available.
* <p>
* Returned paths may be {@code null} if a storage device is unavailable.
*
* @see Environment#getExternalStorageState(File)
*/
public abstract File[] getExternalMediaDirs();
/**
* Returns an array of strings naming the private files associated with
* this Context's application package.

View File

@ -236,6 +236,11 @@ public class ContextWrapper extends Context {
return mBase.getExternalCacheDirs();
}
@Override
public File[] getExternalMediaDirs() {
return mBase.getExternalMediaDirs();
}
@Override
public File getDir(String name, int mode) {
return mBase.getDir(name, mode);

View File

@ -191,6 +191,10 @@ public class Environment {
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
}
public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
}
public File[] buildExternalStorageAppObbDirs(String packageName) {
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
}

View File

@ -2372,6 +2372,18 @@ class MountService extends IMountService.Stub
}
}
voldPath = maybeTranslatePathForVold(appPath,
userEnv.buildExternalStorageAppMediaDirs(callingPkg),
userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
if (voldPath != null) {
try {
mConnector.execute("volume", "mkdirs", voldPath);
return 0;
} catch (NativeDaemonConnectorException e) {
return e.getCode();
}
}
throw new SecurityException("Invalid mkdirs path: " + appPath);
}

View File

@ -608,4 +608,9 @@ public class MockContext extends Context {
public File[] getExternalCacheDirs() {
throw new UnsupportedOperationException();
}
@Override
public File[] getExternalMediaDirs() {
throw new UnsupportedOperationException();
}
}

View File

@ -1466,4 +1466,10 @@ public final class BridgeContext extends Context {
// pass
return new File[0];
}
@Override
public File[] getExternalMediaDirs() {
// pass
return new File[0];
}
}