summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapi/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--core/java/android/app/ActivityThread.java94
-rw-r--r--core/java/android/content/ContentResolver.java38
-rw-r--r--core/java/android/os/FileUtils.java20
-rw-r--r--core/java/android/provider/MediaStore.java15
-rw-r--r--core/tests/coretests/src/android/content/ContentResolverTest.java15
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java14
8 files changed, 197 insertions, 3 deletions
diff --git a/api/current.txt b/api/current.txt
index 15392b8fe29a..c8d842ddcfb3 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -36803,10 +36803,12 @@ package android.provider {
method public static android.net.Uri getMediaScannerUri();
method public static android.net.Uri getMediaUri(android.content.Context, android.net.Uri);
method public static java.lang.String getVersion(android.content.Context);
+ method public static java.lang.String getVolumeName(android.net.Uri);
field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
field public static final java.lang.String AUTHORITY = "media";
+ field public static final android.net.Uri AUTHORITY_URI;
field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
field public static final java.lang.String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
field public static final java.lang.String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
diff --git a/api/system-current.txt b/api/system-current.txt
index 4512fc3b9c2b..fb97643129bd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -987,8 +987,8 @@ package android.content {
field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
- field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
+ field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
field public static final java.lang.String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4756bf40bad3..ee7d00208e2c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -23,6 +23,8 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
+import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
@@ -45,6 +47,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.IIntentReceiver;
@@ -84,6 +87,7 @@ import android.os.Bundle;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.GraphicsEnvironment;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -114,6 +118,9 @@ import android.provider.Settings;
import android.renderscript.RenderScriptCacheDir;
import android.security.NetworkSecurityPolicy;
import android.security.net.config.NetworkSecurityConfigProvider;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import android.system.StructStat;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -162,13 +169,16 @@ import dalvik.system.VMRuntime;
import libcore.io.DropBox;
import libcore.io.EventLogger;
+import libcore.io.ForwardingOs;
import libcore.io.IoUtils;
+import libcore.io.Os;
import libcore.net.event.NetworkEventDispatcher;
import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -6749,7 +6759,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- private class DropBoxReporter implements DropBox.Reporter {
+ private static class DropBoxReporter implements DropBox.Reporter {
private DropBoxManager dropBox;
@@ -6769,7 +6779,84 @@ public final class ActivityThread extends ClientTransactionHandler {
private synchronized void ensureInitialized() {
if (dropBox == null) {
- dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE);
+ dropBox = currentActivityThread().getApplication()
+ .getSystemService(DropBoxManager.class);
+ }
+ }
+ }
+
+ private static class AndroidOs extends ForwardingOs {
+ /**
+ * Install selective syscall interception. For example, this is used to
+ * implement special filesystem paths that will be redirected to
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+ */
+ public static void install() {
+ // If feature is disabled, we don't need to install
+ if (!DEPRECATE_DATA_COLUMNS) return;
+
+ // If app is modern enough, we don't need to install
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) return;
+
+ // Install interception and make sure it sticks!
+ Os def = null;
+ do {
+ def = Os.getDefault();
+ } while (!Os.compareAndSetDefault(def, new AndroidOs(def)));
+ }
+
+ private AndroidOs(Os os) {
+ super(os);
+ }
+
+ private FileDescriptor openDeprecatedDataPath(String path, int mode) throws ErrnoException {
+ final Uri uri = ContentResolver.translateDeprecatedDataPath(path);
+ Log.v(TAG, "Redirecting " + path + " to " + uri);
+
+ final ContentResolver cr = currentActivityThread().getApplication()
+ .getContentResolver();
+ try {
+ final FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(cr.openFileDescriptor(uri,
+ FileUtils.translateModePosixToString(mode)).detachFd());
+ return fd;
+ } catch (FileNotFoundException e) {
+ throw new ErrnoException(e.getMessage(), OsConstants.ENOENT);
+ }
+ }
+
+ @Override
+ public boolean access(String path, int mode) throws ErrnoException {
+ if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+ // If we opened it okay, then access check succeeded
+ IoUtils.closeQuietly(
+ openDeprecatedDataPath(path, FileUtils.translateModeAccessToPosix(mode)));
+ return true;
+ } else {
+ return super.access(path, mode);
+ }
+ }
+
+ @Override
+ public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+ if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+ return openDeprecatedDataPath(path, mode);
+ } else {
+ return super.open(path, flags, mode);
+ }
+ }
+
+ @Override
+ public StructStat stat(String path) throws ErrnoException {
+ if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+ final FileDescriptor fd = openDeprecatedDataPath(path, OsConstants.O_RDONLY);
+ try {
+ return android.system.Os.fstat(fd);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ } else {
+ return super.stat(path);
}
}
}
@@ -6777,6 +6864,9 @@ public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
+ // Install selective syscall interception
+ AndroidOs.install();
+
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index a2a6b9b4a762..4de1dfcc12ba 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -52,7 +52,9 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -88,6 +90,30 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public abstract class ContentResolver {
/**
+ * Enables logic that supports deprecation of {@code _data} columns,
+ * typically by replacing values with fake paths that the OS then offers to
+ * redirect to {@link #openFileDescriptor(Uri, String)}, which developers
+ * should be using directly.
+ *
+ * @hide
+ */
+ public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
+ .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+
+ /**
+ * Special filesystem path prefix which indicates that a path should be
+ * treated as a {@code content://} {@link Uri} when
+ * {@link #DEPRECATE_DATA_COLUMNS} is enabled.
+ * <p>
+ * The remainder of the path after this prefix is a
+ * {@link Uri#getSchemeSpecificPart()} value, which includes authority, path
+ * segments, and query parameters.
+ *
+ * @hide
+ */
+ public static final String DEPRECATE_DATA_PREFIX = "/mnt/content/";
+
+ /**
* @deprecated instead use
* {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
*/
@@ -3261,4 +3287,16 @@ public abstract class ContentResolver {
e.rethrowFromSystemServer();
}
}
+
+ /** {@hide} */
+ public static Uri translateDeprecatedDataPath(String path) {
+ final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length());
+ return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT)
+ .encodedOpaquePart(ssp).build().toString());
+ }
+
+ /** {@hide} */
+ public static String translateDeprecatedDataPath(Uri uri) {
+ return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2);
+ }
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index f71fdd7fdac1..0b90f5437826 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -22,16 +22,19 @@ import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.O_TRUNC;
import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
import static android.system.OsConstants.SPLICE_F_MORE;
import static android.system.OsConstants.SPLICE_F_MOVE;
import static android.system.OsConstants.S_ISFIFO;
import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.W_OK;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1300,6 +1303,23 @@ public class FileUtils {
}
/** {@hide} */
+ public static int translateModeAccessToPosix(int mode) {
+ if (mode == F_OK) {
+ // There's not an exact mapping, so we attempt a read-only open to
+ // determine if a file exists
+ return O_RDONLY;
+ } else if ((mode & (R_OK | W_OK)) == (R_OK | W_OK)) {
+ return O_RDWR;
+ } else if ((mode & R_OK) == R_OK) {
+ return O_RDONLY;
+ } else if ((mode & W_OK) == W_OK) {
+ return O_WRONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ }
+
+ /** {@hide} */
@VisibleForTesting
public static class MemoryPipe extends Thread implements AutoCloseable {
private final FileDescriptor[] pipe;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0aab76ebd0e0..1fce8e6c9ac2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -60,7 +60,10 @@ import java.util.List;
public final class MediaStore {
private final static String TAG = "MediaStore";
+ /** The authority for the media provider */
public static final String AUTHORITY = "media";
+ /** A content:// style uri to the authority for the media provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
@@ -2254,6 +2257,18 @@ public final class MediaStore {
}
/**
+ * Return the volume name that the given {@link Uri} references.
+ */
+ public static @NonNull String getVolumeName(@NonNull Uri uri) {
+ final List<String> segments = uri.getPathSegments();
+ if (uri.getAuthority().equals(AUTHORITY) && segments != null && segments.size() > 0) {
+ return segments.get(0);
+ } else {
+ throw new IllegalArgumentException("Not a media Uri: " + uri);
+ }
+ }
+
+ /**
* Uri for querying the state of the media scanner.
*/
public static Uri getMediaScannerUri() {
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 0036186994fe..9940bf7dd692 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -158,4 +158,19 @@ public class ContentResolverTest {
assertImageAspectAndContents(res);
}
+
+ @Test
+ public void testTranslateDeprecatedDataPath() throws Exception {
+ assertTranslate(Uri.parse("content://com.example/path/?foo=bar&baz=meow"));
+ assertTranslate(Uri.parse("content://com.example/path/subpath/12/"));
+ assertTranslate(Uri.parse("content://com.example/path/subpath/12"));
+ assertTranslate(Uri.parse("content://com.example/path/12"));
+ assertTranslate(Uri.parse("content://com.example/"));
+ assertTranslate(Uri.parse("content://com.example"));
+ }
+
+ private static void assertTranslate(Uri uri) {
+ assertEquals(uri, ContentResolver
+ .translateDeprecatedDataPath(ContentResolver.translateDeprecatedDataPath(uri)));
+ }
}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 6966448f7d63..55e21a76f170 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,6 +17,7 @@
package android.os;
import static android.os.FileUtils.roundStorageSize;
+import static android.os.FileUtils.translateModeAccessToPosix;
import static android.os.FileUtils.translateModePfdToPosix;
import static android.os.FileUtils.translateModePosixToPfd;
import static android.os.FileUtils.translateModePosixToString;
@@ -27,12 +28,16 @@ import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.O_TRUNC;
import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
+import static android.system.OsConstants.W_OK;
+import static android.system.OsConstants.X_OK;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
@@ -525,6 +530,15 @@ public class FileUtilsTest {
}
}
+ @Test
+ public void testTranslateMode_Access() throws Exception {
+ assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK));
+ assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK));
+ assertEquals(O_WRONLY, translateModeAccessToPosix(W_OK));
+ assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK));
+ assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK | X_OK));
+ }
+
private static void assertTranslate(String string, int posix, int pfd) {
assertEquals(posix, translateModeStringToPosix(string));
assertEquals(string, translateModePosixToString(posix));