Merge "Resolving resources across users."
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ec2868a..296e9d3 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1114,7 +1114,8 @@
             int pid = data.readInt();
             int uid = data.readInt();
             int mode = data.readInt();
-            int res = checkUriPermission(uri, pid, uid, mode);
+            int userId = data.readInt();
+            int res = checkUriPermission(uri, pid, uid, mode, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -1139,7 +1140,8 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            grantUriPermission(app, targetPkg, uri, mode);
+            int userId = data.readInt();
+            grantUriPermission(app, targetPkg, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1150,7 +1152,8 @@
             IApplicationThread app = ApplicationThreadNative.asInterface(b);
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            revokeUriPermission(app, uri, mode);
+            int userId = data.readInt();
+            revokeUriPermission(app, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1159,7 +1162,8 @@
             data.enforceInterface(IActivityManager.descriptor);
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            takePersistableUriPermission(uri, mode);
+            int userId = data.readInt();
+            takePersistableUriPermission(uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1168,7 +1172,8 @@
             data.enforceInterface(IActivityManager.descriptor);
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            releasePersistableUriPermission(uri, mode);
+            int userId = data.readInt();
+            releasePersistableUriPermission(uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1602,7 +1607,8 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode);
+            int userId = data.readInt();
+            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1612,10 +1618,11 @@
             IBinder owner = data.readStrongBinder();
             Uri uri = null;
             if (data.readInt() != 0) {
-                Uri.CREATOR.createFromParcel(data);
+                uri = Uri.CREATOR.createFromParcel(data);
             }
             int mode = data.readInt();
-            revokeUriPermissionFromOwner(owner, uri, mode);
+            int userId = data.readInt();
+            revokeUriPermissionFromOwner(owner, uri, mode, userId);
             reply.writeNoException();
             return true;
         }
@@ -1626,7 +1633,8 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int modeFlags = data.readInt();
-            int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags);
+            int userId = data.readInt();
+            int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -3540,7 +3548,7 @@
         reply.recycle();
         return res;
     }
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode) 
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -3549,6 +3557,7 @@
         data.writeInt(pid);
         data.writeInt(uid);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
@@ -3557,7 +3566,7 @@
         return res;
     }
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
-            Uri uri, int mode) throws RemoteException {
+            Uri uri, int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3565,19 +3574,21 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
     }
     public void revokeUriPermission(IApplicationThread caller, Uri uri,
-            int mode) throws RemoteException {
+            int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller.asBinder());
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -3585,12 +3596,14 @@
     }
 
     @Override
-    public void takePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+    public void takePersistableUriPermission(Uri uri, int mode, int userId)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -3598,12 +3611,14 @@
     }
 
     @Override
-    public void releasePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+    public void releasePersistableUriPermission(Uri uri, int mode, int userId)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -4157,7 +4172,7 @@
     }
 
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
-            Uri uri, int mode) throws RemoteException {
+            Uri uri, int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4166,6 +4181,7 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -4173,7 +4189,7 @@
     }
 
     public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
-            int mode) throws RemoteException {
+            int mode, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4185,6 +4201,7 @@
             data.writeInt(0);
         }
         data.writeInt(mode);
+        data.writeInt(userId);
         mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -4192,7 +4209,7 @@
     }
 
     public int checkGrantUriPermission(int callingUid, String targetPkg,
-            Uri uri, int modeFlags) throws RemoteException {
+            Uri uri, int modeFlags, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4200,6 +4217,7 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(modeFlags);
+        data.writeInt(userId);
         mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 801182d..b4d8942 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -26,6 +26,7 @@
 import android.bluetooth.BluetoothManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -1818,8 +1819,8 @@
     public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
          try {
             ActivityManagerNative.getDefault().grantUriPermission(
-                    mMainThread.getApplicationThread(), toPackage, uri,
-                    modeFlags);
+                    mMainThread.getApplicationThread(), toPackage,
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -1828,8 +1829,8 @@
     public void revokeUriPermission(Uri uri, int modeFlags) {
          try {
             ActivityManagerNative.getDefault().revokeUriPermission(
-                    mMainThread.getApplicationThread(), uri,
-                    modeFlags);
+                    mMainThread.getApplicationThread(),
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -1838,12 +1839,17 @@
     public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
         try {
             return ActivityManagerNative.getDefault().checkUriPermission(
-                    uri, pid, uid, modeFlags);
+                    ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
+                    resolveUserId(uri));
         } catch (RemoteException e) {
             return PackageManager.PERMISSION_DENIED;
         }
     }
 
+    private int resolveUserId(Uri uri) {
+        return ContentProvider.getUserIdFromUri(uri, getUserId());
+    }
+
     @Override
     public int checkCallingUriPermission(Uri uri, int modeFlags) {
         int pid = Binder.getCallingPid();
@@ -2280,12 +2286,16 @@
 
         @Override
         protected IContentProvider acquireProvider(Context context, String auth) {
-            return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true);
+            return mMainThread.acquireProvider(context,
+                    ContentProvider.getAuthorityWithoutUserId(auth),
+                    resolveUserIdFromAuthority(auth), true);
         }
 
         @Override
         protected IContentProvider acquireExistingProvider(Context context, String auth) {
-            return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true);
+            return mMainThread.acquireExistingProvider(context,
+                    ContentProvider.getAuthorityWithoutUserId(auth),
+                    resolveUserIdFromAuthority(auth), true);
         }
 
         @Override
@@ -2295,7 +2305,9 @@
 
         @Override
         protected IContentProvider acquireUnstableProvider(Context c, String auth) {
-            return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
+            return mMainThread.acquireProvider(c,
+                    ContentProvider.getAuthorityWithoutUserId(auth),
+                    resolveUserIdFromAuthority(auth), false);
         }
 
         @Override
@@ -2312,5 +2324,10 @@
         public void appNotRespondingViaProvider(IContentProvider icp) {
             mMainThread.appNotRespondingViaProvider(icp.asBinder());
         }
+
+        /** @hide */
+        protected int resolveUserIdFromAuthority(String auth) {
+            return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
+        }
     }
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 074b427..a30a64a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -210,14 +210,16 @@
     public int checkPermission(String permission, int pid, int uid)
             throws RemoteException;
 
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode)
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
             throws RemoteException;
-    public void grantUriPermission(IApplicationThread caller, String targetPkg,
-            Uri uri, int mode) throws RemoteException;
-    public void revokeUriPermission(IApplicationThread caller, Uri uri,
-            int mode) throws RemoteException;
-    public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
-    public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+    public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
+            int mode, int userId) throws RemoteException;
+    public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode, int userId)
+            throws RemoteException;
+    public void takePersistableUriPermission(Uri uri, int modeFlags, int userId)
+            throws RemoteException;
+    public void releasePersistableUriPermission(Uri uri, int modeFlags, int userId)
+            throws RemoteException;
     public ParceledListSlice<UriPermission> getPersistedUriPermissions(
             String packageName, boolean incoming) throws RemoteException;
 
@@ -323,12 +325,12 @@
     
     public IBinder newUriPermissionOwner(String name) throws RemoteException;
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
-            Uri uri, int mode) throws RemoteException;
+            Uri uri, int mode, int userId) throws RemoteException;
     public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
-            int mode) throws RemoteException;
+            int mode, int userId) throws RemoteException;
 
-    public int checkGrantUriPermission(int callingUid, String targetPkg,
-            Uri uri, int modeFlags) throws RemoteException;
+    public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri,
+            int modeFlags, int userId) throws RemoteException;
 
     // Cause the specified process to dump the specified heap.
     public boolean dumpHeap(String process, int userId, boolean managed, String path,
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 50c4fed..b44abf9 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import static android.content.ContentProvider.maybeAddUserId;
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -186,7 +187,7 @@
         final CharSequence mText;
         final String mHtmlText;
         final Intent mIntent;
-        final Uri mUri;
+        Uri mUri;
 
         /**
          * Create an Item consisting of a single block of (possibly styled) text.
@@ -809,6 +810,24 @@
         }
     }
 
+    /**
+     * Prepare this {@link ClipData} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveUser(int userId) {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                item.mIntent.prepareToLeaveUser(userId);
+            }
+            if (item.mUri != null) {
+                item.mUri = maybeAddUserId(item.mUri, userId);
+            }
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 02c850b..be9782f 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -36,6 +36,7 @@
 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 @@
                 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 @@
 
         @Override
         public String getType(Uri uri) {
+            uri = getUriWithoutUserId(uri);
             return ContentProvider.this.getType(uri);
         }
 
@@ -215,9 +218,11 @@
             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 @@
             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 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 @@
             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 @@
             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 @@
                 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 @@
                 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 @@
 
         @Override
         public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+            uri = getUriWithoutUserId(uri);
             return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
         }
 
@@ -337,6 +363,7 @@
         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 @@
             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 @@
             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 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;
+    }
 }
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 12e9bab..136e54d 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.content.ContentProvider;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Parcel;
@@ -87,6 +88,31 @@
         mYieldAllowed = source.readInt() != 0;
     }
 
+    /** @hide */
+    public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
+        mType = cpo.mType;
+        if (removeUserIdFromUri) {
+            mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
+        } else {
+            mUri = cpo.mUri;
+        }
+        mValues = cpo.mValues;
+        mSelection = cpo.mSelection;
+        mSelectionArgs = cpo.mSelectionArgs;
+        mExpectedCount = cpo.mExpectedCount;
+        mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences;
+        mValuesBackReferences = cpo.mValuesBackReferences;
+        mYieldAllowed = cpo.mYieldAllowed;
+    }
+
+    /** @hide */
+    public ContentProviderOperation getWithoutUserIdInUri() {
+        if (ContentProvider.uriHasUserId(mUri)) {
+            return new ContentProviderOperation(this, true);
+        }
+        return this;
+    }
+
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
         Uri.writeToParcel(dest, mUri);
@@ -387,7 +413,6 @@
         }
     };
 
-
     /**
      * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
      * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 5d188ef..ec3d002 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -16,7 +16,9 @@
 
 package android.content;
 
+import android.content.ContentProvider;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -50,6 +52,12 @@
         }
     }
 
+    /** @hide */
+    public ContentProviderResult(ContentProviderResult cpr, int userId) {
+        uri = ContentProvider.maybeAddUserId(cpr.uri, userId);
+        count = cpr.count;
+    }
+
     public void writeToParcel(Parcel dest, int flags) {
         if (uri == null) {
             dest.writeInt(1);
@@ -81,4 +89,4 @@
         }
         return "ContentProviderResult(count=" + count + ")";
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5b41394..7642e13 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1643,7 +1643,8 @@
      */
     public void takePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) {
         try {
-            ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags);
+            ActivityManagerNative.getDefault().takePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -1658,7 +1659,8 @@
      */
     public void releasePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) {
         try {
-            ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags);
+            ActivityManagerNative.getDefault().releasePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -2462,4 +2464,9 @@
     private final Context mContext;
     final String mPackageName;
     private static final String TAG = "ContentResolver";
+
+    /** @hide */
+    public int resolveUserId(Uri uri) {
+        return ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+    }
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3cfc56c..076f657 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -26,6 +26,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.pm.ActivityInfo;
+import static android.content.ContentProvider.maybeAddUserId;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -7433,6 +7434,41 @@
     }
 
     /**
+     * Prepare this {@link Intent} to be sent to another user
+     *
+     * @hide
+     */
+    public void prepareToLeaveUser(int userId) {
+        Uri data = getData();
+        if (data != null) {
+            mData = maybeAddUserId(data, userId);
+        }
+        if (mSelector != null) {
+            mSelector.prepareToLeaveUser(userId);
+        }
+        if (mClipData != null) {
+            mClipData.prepareToLeaveUser(userId);
+        }
+        String action = getAction();
+        if (ACTION_SEND.equals(action)) {
+            final Uri stream = getParcelableExtra(EXTRA_STREAM);
+            if (stream != null) {
+                putExtra(EXTRA_STREAM, maybeAddUserId(stream, userId));
+            }
+        }
+        if (ACTION_SEND_MULTIPLE.equals(action)) {
+            final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+            if (streams != null) {
+                ArrayList<Uri> newStreams = new ArrayList<Uri>();
+                for (int i = 0; i < streams.size(); i++) {
+                    newStreams.add(maybeAddUserId(streams.get(i), userId));
+                }
+                putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
+            }
+        }
+    }
+
+    /**
      * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
      * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
      * intents in {@link #ACTION_CHOOSER}.
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 2f74372..47ef65a 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -84,6 +84,7 @@
             Slog.e(TAG, "PackageManagerService is dead?");
         }
         if (canForward) {
+            newIntent.prepareToLeaveUser(callingUserId);
             startActivityAsUser(newIntent, userDest);
         } else {
             Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user "
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index b35de93..5b59599 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -912,6 +912,15 @@
         }
     }
 
+    public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
     public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
         final String value = in.getAttributeValue(null, name);
         try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 344268a..d6457c3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -722,6 +722,8 @@
     private static final String TAG_URI_GRANTS = "uri-grants";
     private static final String TAG_URI_GRANT = "uri-grant";
     private static final String ATTR_USER_HANDLE = "userHandle";
+    private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
+    private static final String ATTR_TARGET_USER_ID = "targetUserId";
     private static final String ATTR_SOURCE_PKG = "sourcePkg";
     private static final String ATTR_TARGET_PKG = "targetPkg";
     private static final String ATTR_URI = "uri";
@@ -739,10 +741,12 @@
             mGrantedUriPermissions = new SparseArray<ArrayMap<GrantUri, UriPermission>>();
 
     public static class GrantUri {
+        public final int sourceUserId;
         public final Uri uri;
-        public final boolean prefix;
+        public boolean prefix;
 
-        public GrantUri(Uri uri, boolean prefix) {
+        public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
+            this.sourceUserId = sourceUserId;
             this.uri = uri;
             this.prefix = prefix;
         }
@@ -756,18 +760,28 @@
         public boolean equals(Object o) {
             if (o instanceof GrantUri) {
                 GrantUri other = (GrantUri) o;
-                return uri.equals(other.uri) && prefix == other.prefix;
+                return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
+                        && prefix == other.prefix;
             }
             return false;
         }
 
         @Override
         public String toString() {
-            if (prefix) {
-                return uri.toString() + " [prefix]";
-            } else {
-                return uri.toString();
-            }
+            String result = Integer.toString(sourceUserId) + " @ " + uri.toString();
+            if (prefix) result += " [prefix]";
+            return result;
+        }
+
+        public String toSafeString() {
+            String result = Integer.toString(sourceUserId) + " @ " + uri.toSafeString();
+            if (prefix) result += " [prefix]";
+            return result;
+        }
+
+        public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
+            return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
+                    ContentProvider.getUriWithoutUserId(uri), false);
         }
     }
 
@@ -5991,9 +6005,9 @@
      * in {@link ContentProvider}.
      */
     private final boolean checkHoldingPermissionsLocked(
-            IPackageManager pm, ProviderInfo pi, Uri uri, int uid, final int modeFlags) {
+            IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid);
+                "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
 
         if (pi.applicationInfo.uid == uid) {
             return true;
@@ -6022,7 +6036,7 @@
             // check if target holds any <path-permission> that match uri
             final PathPermission[] pps = pi.pathPermissions;
             if (pps != null) {
-                final String path = uri.getPath();
+                final String path = grantUri.uri.getPath();
                 int i = pps.length;
                 while (i > 0 && (!readMet || !writeMet)) {
                     i--;
@@ -6087,32 +6101,33 @@
         return pi;
     }
 
-    private UriPermission findUriPermissionLocked(int targetUid, GrantUri uri) {
+    private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
         final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
         if (targetUris != null) {
-            return targetUris.get(uri);
+            return targetUris.get(grantUri);
         }
         return null;
     }
 
     private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
-            String targetPkg, int targetUid, GrantUri uri) {
+            String targetPkg, int targetUid, GrantUri grantUri) {
         ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
         if (targetUris == null) {
             targetUris = Maps.newArrayMap();
             mGrantedUriPermissions.put(targetUid, targetUris);
         }
 
-        UriPermission perm = targetUris.get(uri);
+        UriPermission perm = targetUris.get(grantUri);
         if (perm == null) {
-            perm = new UriPermission(sourcePkg, targetPkg, targetUid, uri);
-            targetUris.put(uri, perm);
+            perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
+            targetUris.put(grantUri, perm);
         }
 
         return perm;
     }
 
-    private final boolean checkUriPermissionLocked(Uri uri, int uid, final int modeFlags) {
+    private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
+            final int modeFlags) {
         final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
         final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
                 : UriPermission.STRENGTH_OWNED;
@@ -6126,7 +6141,7 @@
         if (perms == null) return false;
 
         // First look for exact match
-        final UriPermission exactPerm = perms.get(new GrantUri(uri, false));
+        final UriPermission exactPerm = perms.get(grantUri);
         if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
             return true;
         }
@@ -6135,7 +6150,7 @@
         final int N = perms.size();
         for (int i = 0; i < N; i++) {
             final UriPermission perm = perms.valueAt(i);
-            if (perm.uri.prefix && uri.isPathPrefixMatch(perm.uri.uri)
+            if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
                     && perm.getStrength(modeFlags) >= minStrength) {
                 return true;
             }
@@ -6145,7 +6160,8 @@
     }
 
     @Override
-    public int checkUriPermission(Uri uri, int pid, int uid, final int modeFlags) {
+    public int checkUriPermission(Uri uri, int pid, int uid,
+            final int modeFlags, int userId) {
         enforceNotIsolatedCaller("checkUriPermission");
 
         // Another redirected-binder-call permissions check as in
@@ -6161,7 +6177,7 @@
             return PackageManager.PERMISSION_GRANTED;
         }
         synchronized (this) {
-            return checkUriPermissionLocked(uri, uid, modeFlags)
+            return checkUriPermissionLocked(new GrantUri(userId, uri, false), uid, modeFlags)
                     ? PackageManager.PERMISSION_GRANTED
                     : PackageManager.PERMISSION_DENIED;
         }
@@ -6176,30 +6192,31 @@
      * If you already know the uid of the target, you can supply it in
      * lastTargetUid else set that to -1.
      */
-    int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
-            Uri uri, final int modeFlags, int lastTargetUid) {
+    int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, int lastTargetUid) {
         if (!Intent.isAccessUriMode(modeFlags)) {
             return -1;
         }
 
         if (targetPkg != null) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                    "Checking grant " + targetPkg + " permission to " + uri);
+                    "Checking grant " + targetPkg + " permission to " + grantUri);
         }
         
         final IPackageManager pm = AppGlobals.getPackageManager();
 
         // If this is not a content: uri, we can't do anything with it.
-        if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+        if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                    "Can't grant URI permission for non-content URI: " + uri);
+                    "Can't grant URI permission for non-content URI: " + grantUri);
             return -1;
         }
 
-        final String authority = uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId);
         if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission check: " + uri.toSafeString());
+            Slog.w(TAG, "No content provider found for permission check: " +
+                    grantUri.uri.toSafeString());
             return -1;
         }
 
@@ -6219,10 +6236,10 @@
 
         if (targetUid >= 0) {
             // First...  does the target actually need this permission?
-            if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
+            if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) {
                 // No need to grant the target this permission.
                 if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                        "Target " + targetPkg + " already has full permission to " + uri);
+                        "Target " + targetPkg + " already has full permission to " + grantUri);
                 return -1;
             }
         } else {
@@ -6248,14 +6265,14 @@
             throw new SecurityException("Provider " + pi.packageName
                     + "/" + pi.name
                     + " does not allow granting of Uri permissions (uri "
-                    + uri + ")");
+                    + grantUri + ")");
         }
         if (pi.uriPermissionPatterns != null) {
             final int N = pi.uriPermissionPatterns.length;
             boolean allowed = false;
             for (int i=0; i<N; i++) {
                 if (pi.uriPermissionPatterns[i] != null
-                        && pi.uriPermissionPatterns[i].match(uri.getPath())) {
+                        && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
                     allowed = true;
                     break;
                 }
@@ -6264,35 +6281,35 @@
                 throw new SecurityException("Provider " + pi.packageName
                         + "/" + pi.name
                         + " does not allow granting of permission to path of Uri "
-                        + uri);
+                        + grantUri);
             }
         }
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        if (callingUid != Process.myUid()) {
-            if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
+        if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
+            if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
                 // Require they hold a strong enough Uri permission
-                if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
+                if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
                     throw new SecurityException("Uid " + callingUid
-                            + " does not have permission to uri " + uri);
+                            + " does not have permission to uri " + grantUri);
                 }
             }
         }
-
         return targetUid;
     }
 
     @Override
-    public int checkGrantUriPermission(int callingUid, String targetPkg,
-            Uri uri, final int modeFlags) {
+    public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri,
+            final int modeFlags, int userId) {
         enforceNotIsolatedCaller("checkGrantUriPermission");
         synchronized(this) {
-            return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags, -1);
+            return checkGrantUriPermissionLocked(callingUid, targetPkg,
+                    new GrantUri(userId, uri, false), modeFlags, -1);
         }
     }
 
-    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, Uri uri,
+    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri,
             final int modeFlags, UriPermissionOwner owner) {
         if (!Intent.isAccessUriMode(modeFlags)) {
             return;
@@ -6303,36 +6320,40 @@
         // the target.
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
 
-        final String authority = uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(targetUid));
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId);
         if (pi == null) {
-            Slog.w(TAG, "No content provider found for grant: " + uri.toSafeString());
+            Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
             return;
         }
 
-        final boolean prefix = (modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
+        if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
+            grantUri.prefix = true;
+        }
         final UriPermission perm = findOrCreateUriPermissionLocked(
-                pi.packageName, targetPkg, targetUid, new GrantUri(uri, prefix));
+                pi.packageName, targetPkg, targetUid, grantUri);
         perm.grantModes(modeFlags, owner);
     }
 
-    void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri,
+    void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
             final int modeFlags, UriPermissionOwner owner) {
         if (targetPkg == null) {
             throw new NullPointerException("targetPkg");
         }
 
-        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags, -1);
+        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
+                -1);
         if (targetUid < 0) {
             return;
         }
 
-        grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, grantUri, modeFlags,
+                owner);
     }
 
-    static class NeededUriGrants extends ArrayList<Uri> {
+    static class NeededUriGrants extends ArrayList<GrantUri> {
         final String targetPkg;
         final int targetUid;
         final int flags;
@@ -6369,13 +6390,14 @@
         }
 
         if (data != null) {
-            int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
-                mode, needed != null ? needed.targetUid : -1);
+            GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), data);
+            int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
+                    needed != null ? needed.targetUid : -1);
             if (targetUid > 0) {
                 if (needed == null) {
                     needed = new NeededUriGrants(targetPkg, targetUid, mode);
                 }
-                needed.add(data);
+                needed.add(grantUri);
             }
         }
         if (clip != null) {
@@ -6383,13 +6405,14 @@
                 Uri uri = clip.getItemAt(i).getUri();
                 if (uri != null) {
                     int targetUid = -1;
-                    targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri,
-                            mode, needed != null ? needed.targetUid : -1);
+                    GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), uri);
+                    targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
+                            needed != null ? needed.targetUid : -1);
                     if (targetUid > 0) {
                         if (needed == null) {
                             needed = new NeededUriGrants(targetPkg, targetUid, mode);
                         }
-                        needed.add(uri);
+                        needed.add(grantUri);
                     }
                 } else {
                     Intent clipIntent = clip.getItemAt(i).getIntent();
@@ -6414,8 +6437,9 @@
             UriPermissionOwner owner) {
         if (needed != null) {
             for (int i=0; i<needed.size(); i++) {
+                GrantUri grantUri = needed.get(i);
                 grantUriPermissionUncheckedLocked(needed.targetUid, needed.targetPkg,
-                        needed.get(i), needed.flags, owner);
+                        grantUri, needed.flags, owner);
             }
         }
     }
@@ -6432,20 +6456,21 @@
     }
 
     @Override
-    public void grantUriPermission(IApplicationThread caller, String targetPkg,
-            Uri uri, final int modeFlags) {
+    public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
+            final int modeFlags, int userId) {
         enforceNotIsolatedCaller("grantUriPermission");
+        GrantUri grantUri = new GrantUri(userId, uri, false);
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (r == null) {
                 throw new SecurityException("Unable to find app for caller "
                         + caller
-                        + " when granting permission to uri " + uri);
+                        + " when granting permission to uri " + grantUri);
             }
             if (targetPkg == null) {
                 throw new IllegalArgumentException("null target");
             }
-            if (uri == null) {
+            if (grantUri == null) {
                 throw new IllegalArgumentException("null uri");
             }
 
@@ -6454,7 +6479,7 @@
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
 
-            grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags, null);
+            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null);
         }
     }
 
@@ -6474,24 +6499,25 @@
         }
     }
 
-    private void revokeUriPermissionLocked(int callingUid, Uri uri, final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri);
+    private void revokeUriPermissionLocked(int callingUid, GrantUri grantUri, final int modeFlags) {
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
 
         final IPackageManager pm = AppGlobals.getPackageManager();
-        final String authority = uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId);
         if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission revoke: " + uri.toSafeString());
+            Slog.w(TAG, "No content provider found for permission revoke: "
+                    + grantUri.toSafeString());
             return;
         }
 
         // Does the caller have this permission on the URI?
-        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
+        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
             // Right now, if you are not the original owner of the permission,
             // you are not allowed to revoke it.
             //if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
                 throw new SecurityException("Uid " + callingUid
-                        + " does not have permission to uri " + uri);
+                        + " does not have permission to uri " + grantUri);
             //}
         }
 
@@ -6505,7 +6531,8 @@
 
             for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
                 final UriPermission perm = it.next();
-                if (perm.uri.uri.isPathPrefixMatch(uri)) {
+                if (perm.uri.sourceUserId == grantUri.sourceUserId
+                        && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
                     if (DEBUG_URI_PERMISSION)
                         Slog.v(TAG,
                                 "Revoking " + perm.targetUid + " permission to " + perm.uri);
@@ -6530,8 +6557,8 @@
     }
 
     @Override
-    public void revokeUriPermission(IApplicationThread caller, Uri uri,
-            final int modeFlags) {
+    public void revokeUriPermission(IApplicationThread caller, Uri uri, final int modeFlags,
+            int userId) {
         enforceNotIsolatedCaller("revokeUriPermission");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
@@ -6551,14 +6578,14 @@
 
             final IPackageManager pm = AppGlobals.getPackageManager();
             final String authority = uri.getAuthority();
-            final ProviderInfo pi = getProviderInfoLocked(authority, r.userId);
+            final ProviderInfo pi = getProviderInfoLocked(authority, userId);
             if (pi == null) {
                 Slog.w(TAG, "No content provider found for permission revoke: "
                         + uri.toSafeString());
                 return;
             }
 
-            revokeUriPermissionLocked(r.uid, uri, modeFlags);
+            revokeUriPermissionLocked(r.uid, new GrantUri(userId, uri, false), modeFlags);
         }
     }
 
@@ -6628,8 +6655,8 @@
     }
 
     @Override
-    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
-            Uri uri, final int modeFlags) {
+    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
+            final int modeFlags, int userId) {
         synchronized(this) {
             UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
             if (owner == null) {
@@ -6649,12 +6676,13 @@
                 throw new IllegalArgumentException("null uri");
             }
 
-            grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner);
+            grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(userId, uri, false),
+                    modeFlags, owner);
         }
     }
 
     @Override
-    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) {
+    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
         synchronized(this) {
             UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
             if (owner == null) {
@@ -6664,7 +6692,7 @@
             if (uri == null) {
                 owner.removeUriPermissionsLocked(mode);
             } else {
-                owner.removeUriPermissionLocked(uri, mode);
+                owner.removeUriPermissionLocked(new GrantUri(userId, uri, false), mode);
             }
         }
     }
@@ -6703,7 +6731,8 @@
             out.startTag(null, TAG_URI_GRANTS);
             for (UriPermission.Snapshot perm : persist) {
                 out.startTag(null, TAG_URI_GRANT);
-                writeIntAttribute(out, ATTR_USER_HANDLE, perm.userHandle);
+                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
+                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
                 out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
                 out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
                 out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
@@ -6739,7 +6768,18 @@
                 final String tag = in.getName();
                 if (type == START_TAG) {
                     if (TAG_URI_GRANT.equals(tag)) {
-                        final int userHandle = readIntAttribute(in, ATTR_USER_HANDLE);
+                        final int sourceUserId;
+                        final int targetUserId;
+                        final int userHandle = readIntAttribute(in,
+                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
+                        if (userHandle != UserHandle.USER_NULL) {
+                            // For backwards compatibility.
+                            sourceUserId = userHandle;
+                            targetUserId = userHandle;
+                        } else {
+                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
+                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
+                        }
                         final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
                         final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
                         final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
@@ -6749,17 +6789,18 @@
 
                         // Sanity check that provider still belongs to source package
                         final ProviderInfo pi = getProviderInfoLocked(
-                                uri.getAuthority(), userHandle);
+                                uri.getAuthority(), sourceUserId);
                         if (pi != null && sourcePkg.equals(pi.packageName)) {
                             int targetUid = -1;
                             try {
                                 targetUid = AppGlobals.getPackageManager()
-                                        .getPackageUid(targetPkg, userHandle);
+                                        .getPackageUid(targetPkg, targetUserId);
                             } catch (RemoteException e) {
                             }
                             if (targetUid != -1) {
                                 final UriPermission perm = findOrCreateUriPermissionLocked(
-                                        sourcePkg, targetPkg, targetUid, new GrantUri(uri, prefix));
+                                        sourcePkg, targetPkg, targetUid,
+                                        new GrantUri(sourceUserId, uri, prefix));
                                 perm.initPersistedModes(modeFlags, createdTime);
                             }
                         } else {
@@ -6781,7 +6822,7 @@
     }
 
     @Override
-    public void takePersistableUriPermission(Uri uri, final int modeFlags) {
+    public void takePersistableUriPermission(Uri uri, final int modeFlags, int userId) {
         enforceNotIsolatedCaller("takePersistableUriPermission");
 
         Preconditions.checkFlagsArgument(modeFlags,
@@ -6790,9 +6831,12 @@
         synchronized (this) {
             final int callingUid = Binder.getCallingUid();
             boolean persistChanged = false;
+            GrantUri grantUri = new GrantUri(userId, uri, false);
 
-            UriPermission exactPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, false));
-            UriPermission prefixPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, true));
+            UriPermission exactPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, false));
+            UriPermission prefixPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, true));
 
             final boolean exactValid = (exactPerm != null)
                     && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
@@ -6801,7 +6845,7 @@
 
             if (!(exactValid || prefixValid)) {
                 throw new SecurityException("No persistable permission grants found for UID "
-                        + callingUid + " and Uri " + uri.toSafeString());
+                        + callingUid + " and Uri " + grantUri.toSafeString());
             }
 
             if (exactValid) {
@@ -6820,7 +6864,7 @@
     }
 
     @Override
-    public void releasePersistableUriPermission(Uri uri, final int modeFlags) {
+    public void releasePersistableUriPermission(Uri uri, final int modeFlags, int userId) {
         enforceNotIsolatedCaller("releasePersistableUriPermission");
 
         Preconditions.checkFlagsArgument(modeFlags,
@@ -6830,8 +6874,10 @@
             final int callingUid = Binder.getCallingUid();
             boolean persistChanged = false;
 
-            UriPermission exactPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, false));
-            UriPermission prefixPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, true));
+            UriPermission exactPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, false));
+            UriPermission prefixPerm = findUriPermissionLocked(callingUid,
+                    new GrantUri(userId, uri, true));
             if (exactPerm == null && prefixPerm == null) {
                 throw new SecurityException("No permission grants found for UID " + callingUid
                         + " and Uri " + uri.toSafeString());
@@ -7676,9 +7722,25 @@
      * in {@link ContentProvider}.
      */
     private final String checkContentProviderPermissionLocked(
-            ProviderInfo cpi, ProcessRecord r) {
+            ProviderInfo cpi, ProcessRecord r, int userId) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
         final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        // Looking for cross-user grants before to enforce the typical cross-users permissions
+        if (userId != UserHandle.getUserId(callingUid)) {
+            if (perms != null) {
+                for (GrantUri grantUri : perms.keySet()) {
+                    if (grantUri.sourceUserId == userId) {
+                        String authority = grantUri.uri.getAuthority();
+                        if (authority.equals(cpi.authority)) {
+                            return null;
+                        }
+                    }
+                }
+            }
+        }
+        userId = handleIncomingUser(callingPid, callingUid, userId,
+                false, true, "checkContentProviderPermissionLocked", null);
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.applicationInfo.uid, cpi.exported)
                 == PackageManager.PERMISSION_GRANTED) {
@@ -7709,10 +7771,9 @@
             }
         }
 
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
         if (perms != null) {
-            for (GrantUri uri : perms.keySet()) {
-                if (uri.uri.getAuthority().equals(cpi.authority)) {
+            for (GrantUri grantUri : perms.keySet()) {
+                if (grantUri.uri.getAuthority().equals(cpi.authority)) {
                     return null;
                 }
             }
@@ -7820,7 +7881,7 @@
             if (providerRunning) {
                 cpi = cpr.info;
                 String msg;
-                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
                     throw new SecurityException(msg);
                 }
 
@@ -7908,7 +7969,7 @@
                 cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
 
                 String msg;
-                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
                     throw new SecurityException(msg);
                 }
 
@@ -8074,6 +8135,7 @@
         return cpr != null ? cpr.newHolder(conn) : null;
     }
 
+    @Override
     public final ContentProviderHolder getContentProvider(
             IApplicationThread caller, String name, int userId, boolean stable) {
         enforceNotIsolatedCaller("getContentProvider");
@@ -8083,9 +8145,8 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                false, true, "getContentProvider", null);
+        // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
+        // with cross-user grant.
         return getContentProviderImpl(caller, name, null, stable, userId);
     }
 
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 4970b8d..284086d 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -44,7 +44,7 @@
     public static final int STRENGTH_GLOBAL = 2;
     public static final int STRENGTH_PERSISTABLE = 3;
 
-    final int userHandle;
+    final int targetUserId;
     final String sourcePkg;
     final String targetPkg;
 
@@ -86,7 +86,7 @@
     private String stringName;
 
     UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
-        this.userHandle = UserHandle.getUserId(targetUid);
+        this.targetUserId = UserHandle.getUserId(targetUid);
         this.sourcePkg = sourcePkg;
         this.targetPkg = targetPkg;
         this.targetUid = targetUid;
@@ -307,7 +307,7 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
-        pw.print("userHandle=" + userHandle);
+        pw.print("targetUserId=" + targetUserId);
         pw.print(" sourcePkg=" + sourcePkg);
         pw.println(" targetPkg=" + targetPkg);
 
@@ -352,7 +352,7 @@
      * {@link UriPermission#persistedModeFlags} state.
      */
     public static class Snapshot {
-        final int userHandle;
+        final int targetUserId;
         final String sourcePkg;
         final String targetPkg;
         final GrantUri uri;
@@ -360,7 +360,7 @@
         final long persistedCreateTime;
 
         private Snapshot(UriPermission perm) {
-            this.userHandle = perm.userHandle;
+            this.targetUserId = perm.targetUserId;
             this.sourcePkg = perm.sourcePkg;
             this.targetPkg = perm.targetPkg;
             this.uri = perm.uri;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index 65d7047..ae83940 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -70,13 +70,13 @@
         removeUriPermissionLocked(null, mode);
     }
 
-    void removeUriPermissionLocked(Uri uri, int mode) {
+    void removeUriPermissionLocked(ActivityManagerService.GrantUri grantUri, int mode) {
         if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
                 && mReadPerms != null) {
             Iterator<UriPermission> it = mReadPerms.iterator();
             while (it.hasNext()) {
                 UriPermission perm = it.next();
-                if (uri == null || uri.equals(perm.uri)) {
+                if (grantUri == null || grantUri.equals(perm.uri)) {
                     perm.removeReadOwner(this);
                     service.removeUriPermissionIfNeededLocked(perm);
                     it.remove();
@@ -91,7 +91,7 @@
             Iterator<UriPermission> it = mWritePerms.iterator();
             while (it.hasNext()) {
                 UriPermission perm = it.next();
-                if (uri == null || uri.equals(perm.uri)) {
+                if (grantUri == null || grantUri.equals(perm.uri)) {
                     perm.removeWriteOwner(this);
                     service.removeUriPermissionIfNeededLocked(perm);
                     it.remove();
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index f47d66d..15e3e89 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -23,6 +23,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ClipDescription;
+import android.content.ContentProvider;
 import android.content.IClipboard;
 import android.content.IOnPrimaryClipChangedListener;
 import android.content.Context;
@@ -255,7 +256,8 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException for us.
-            mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            mAm.checkGrantUriPermission(uid, null, ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION, resolveUserId(uri, uid));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -282,8 +284,10 @@
     private final void grantUriLocked(Uri uri, String pkg) {
         long ident = Binder.clearCallingIdentity();
         try {
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg,
+                    ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                    resolveUserId(uri, Process.myUid()));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -331,9 +335,10 @@
     private final void revokeUriLocked(Uri uri) {
         long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION
-                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            mAm.revokeUriPermissionFromOwner(mPermissionOwner,
+                    ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    resolveUserId(uri, Process.myUid()));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -361,4 +366,8 @@
             revokeItemLocked(clipboard.primaryClip.getItemAt(i));
         }
     }
+
+    private final int resolveUserId(Uri uri, int uid) {
+        return ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid));
+    }
 }