summaryrefslogtreecommitdiff
path: root/core/java/android/content/ContentProvider.java
diff options
context:
space:
mode:
authorNicolas Prevot <nprevot@google.com>2014-04-16 19:52:08 +0100
committerNicolas Prevot <nprevot@google.com>2014-05-13 14:59:52 +0100
commitd85fc72fb810858f7502e7e7f1bad53e1bf03edd (patch)
tree01c4624393d93312774716cf3233bcdcacf8243f /core/java/android/content/ContentProvider.java
parent2ac997917924161ed2eed5f4387246beb58c456e (diff)
Resolving resources across users.
When an intent is sent to another profile: For content uris contained in this intent: The userId of the source user is added to the userInfo part. The ActivityManagerService has been modified to resolve resources in the user specified by the uri. The user id to which the uri belongs to is stored in the UriPermission. Change-Id: I43dc76895aba692bf148d276253aeaf9c75fce34
Diffstat (limited to 'core/java/android/content/ContentProvider.java')
-rw-r--r--core/java/android/content/ContentProvider.java114
1 files changed, 108 insertions, 6 deletions
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 02c850bc47c4..be9782fb1f07 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -36,6 +36,7 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
+import android.text.TextUtils;
import java.io.File;
import java.io.FileDescriptor;
@@ -195,6 +196,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.query(
@@ -207,6 +209,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public String getType(Uri uri) {
+ uri = getUriWithoutUserId(uri);
return ContentProvider.this.getType(uri);
}
@@ -215,9 +218,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return rejectInsert(uri, initialValues);
}
+ int userId = getUserIdFromUri(uri);
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
- return ContentProvider.this.insert(uri, initialValues);
+ return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId);
} finally {
setCallingPackage(original);
}
@@ -228,6 +233,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.bulkInsert(uri, initialValues);
@@ -240,24 +246,39 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public ContentProviderResult[] applyBatch(String callingPkg,
ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
- for (ContentProviderOperation operation : operations) {
+ int numOperations = operations.size();
+ final int[] userIds = new int[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ ContentProviderOperation operation = operations.get(i);
+ userIds[i] = getUserIdFromUri(operation.getUri());
if (operation.isReadOperation()) {
if (enforceReadPermission(callingPkg, operation.getUri())
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
-
if (operation.isWriteOperation()) {
if (enforceWritePermission(callingPkg, operation.getUri())
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
+ if (userIds[i] != UserHandle.USER_CURRENT) {
+ // Removing the user id from the uri.
+ operation = new ContentProviderOperation(operation, true);
+ }
+ operations.set(i, operation);
}
final String original = setCallingPackage(callingPkg);
try {
- return ContentProvider.this.applyBatch(operations);
+ ContentProviderResult[] results = ContentProvider.this.applyBatch(operations);
+ for (int i = 0; i < results.length ; i++) {
+ if (userIds[i] != UserHandle.USER_CURRENT) {
+ // Adding the userId to the uri.
+ results[i] = new ContentProviderResult(results[i], userIds[i]);
+ }
+ }
+ return results;
} finally {
setCallingPackage(original);
}
@@ -268,6 +289,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.delete(uri, selection, selectionArgs);
@@ -282,6 +304,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.update(uri, values, selection, selectionArgs);
@@ -295,6 +318,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, mode);
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.openFile(
@@ -309,6 +333,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, mode);
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.openAssetFile(
@@ -330,6 +355,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ uri = getUriWithoutUserId(uri);
return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
}
@@ -337,6 +363,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, "r");
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.openTypedAssetFile(
@@ -356,9 +383,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return null;
}
+ int userId = getUserIdFromUri(uri);
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
- return ContentProvider.this.canonicalize(uri);
+ return maybeAddUserId(ContentProvider.this.canonicalize(uri), userId);
} finally {
setCallingPackage(original);
}
@@ -369,9 +398,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return null;
}
+ int userId = getUserIdFromUri(uri);
+ uri = getUriWithoutUserId(uri);
final String original = setCallingPackage(callingPkg);
try {
- return ContentProvider.this.uncanonicalize(uri);
+ return maybeAddUserId(ContentProvider.this.uncanonicalize(uri), userId);
} finally {
setCallingPackage(original);
}
@@ -1680,4 +1711,75 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
writer.println("nothing to dump");
}
+
+ /** @hide */
+ public static int getUserIdFromAuthority(String auth, int defaultUserId) {
+ if (auth == null) return defaultUserId;
+ int end = auth.indexOf('@');
+ if (end == -1) return defaultUserId;
+ String userIdString = auth.substring(0, end);
+ try {
+ return Integer.parseInt(userIdString);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Error parsing userId.", e);
+ return UserHandle.USER_NULL;
+ }
+ }
+
+ /** @hide */
+ public static int getUserIdFromAuthority(String auth) {
+ return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
+ }
+
+ /** @hide */
+ public static int getUserIdFromUri(Uri uri, int defaultUserId) {
+ if (uri == null) return defaultUserId;
+ return getUserIdFromAuthority(uri.getAuthority(), defaultUserId);
+ }
+
+ /** @hide */
+ public static int getUserIdFromUri(Uri uri) {
+ return getUserIdFromUri(uri, UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Removes userId part from authority string. Expects format:
+ * userId@some.authority
+ * If there is no userId in the authority, it symply returns the argument
+ * @hide
+ */
+ public static String getAuthorityWithoutUserId(String auth) {
+ if (auth == null) return null;
+ int end = auth.indexOf('@');
+ return auth.substring(end+1);
+ }
+
+ /** @hide */
+ public static Uri getUriWithoutUserId(Uri uri) {
+ if (uri == null) return null;
+ Uri.Builder builder = uri.buildUpon();
+ builder.authority(getAuthorityWithoutUserId(uri.getAuthority()));
+ return builder.build();
+ }
+
+ /** @hide */
+ public static boolean uriHasUserId(Uri uri) {
+ if (uri == null) return false;
+ return !TextUtils.isEmpty(uri.getUserInfo());
+ }
+
+ /** @hide */
+ public static Uri maybeAddUserId(Uri uri, int userId) {
+ if (uri == null) return null;
+ if (userId != UserHandle.USER_CURRENT
+ && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ if (!uriHasUserId(uri)) {
+ //We don't add the user Id if there's already one
+ Uri.Builder builder = uri.buildUpon();
+ builder.encodedAuthority("" + userId + "@" + uri.getEncodedAuthority());
+ return builder.build();
+ }
+ }
+ return uri;
+ }
}