Merge "Allow acquiring ContentProviders across users." into jb-mr1-dev
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index bd9eb9a..787fbdb 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.text.TextUtils;
 
 /**
@@ -63,7 +64,8 @@
     private static final String USAGE =
         "usage: adb shell content [subcommand] [options]\n"
         + "\n"
-        + "usage: adb shell content insert --uri <URI> --bind <BINDING> [--bind <BINDING>...]\n"
+        + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]"
+                + " --bind <BINDING> [--bind <BINDING>...]\n"
         + "  <URI> a content provider URI.\n"
         + "  <BINDING> binds a typed value to a column and is formatted:\n"
         + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
@@ -75,7 +77,7 @@
         + "  adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
                 + " --bind value:s:new_value\n"
         + "\n"
-        + "usage: adb shell content update --uri <URI> [--where <WHERE>]\n"
+        + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n"
         + "  <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
                 + " - see example below).\n"
         + "  Example:\n"
@@ -83,15 +85,15 @@
         + "  adb shell content update --uri content://settings/secure --bind"
                 + " value:s:newer_value --where \"name=\'new_setting\'\"\n"
         + "\n"
-        + "usage: adb shell content delete --uri <URI> --bind <BINDING>"
+        + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>"
                 + " [--bind <BINDING>...] [--where <WHERE>]\n"
         + "  Example:\n"
         + "  # Remove \"new_setting\" secure setting.\n"
         + "  adb shell content delete --uri content://settings/secure "
                 + "--where \"name=\'new_setting\'\"\n"
         + "\n"
-        + "usage: adb shell content query --uri <URI> [--projection <PROJECTION>]"
-                + " [--where <WHERE>] [--sort <SORT_ORDER>]\n"
+        + "usage: adb shell content query --uri <URI> [--user <USER_ID>]"
+                + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n"
         + "  <PROJECTION> is a list of colon separated column names and is formatted:\n"
         + "  <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
         + "  <SORT_OREDER> is the order in which rows in the result should be sorted.\n"
@@ -110,6 +112,7 @@
         private static final String ARGUMENT_WHERE = "--where";
         private static final String ARGUMENT_BIND = "--bind";
         private static final String ARGUMENT_URI = "--uri";
+        private static final String ARGUMENT_USER = "--user";
         private static final String ARGUMENT_PROJECTION = "--projection";
         private static final String ARGUMENT_SORT = "--sort";
         private static final String TYPE_BOOLEAN = "b";
@@ -150,10 +153,13 @@
 
         private InsertCommand parseInsertCommand() {
             Uri uri = null;
+            int userId = UserHandle.USER_OWNER;
             ContentValues values = new ContentValues();
             for (String argument; (argument = mTokenizer.nextArg()) != null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_USER.equals(argument)) {
+                    userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_BIND.equals(argument)) {
                     parseBindValue(values);
                 } else {
@@ -168,15 +174,18 @@
                 throw new IllegalArgumentException("Bindings not specified."
                         + " Did you specify --bind argument(s)?");
             }
-            return new InsertCommand(uri, values);
+            return new InsertCommand(uri, userId, values);
         }
 
         private DeleteCommand parseDeleteCommand() {
             Uri uri = null;
+            int userId = UserHandle.USER_OWNER;
             String where = null;
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_USER.equals(argument)) {
+                    userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_WHERE.equals(argument)) {
                     where = argumentValueRequired(argument);
                 } else {
@@ -187,16 +196,19 @@
                 throw new IllegalArgumentException("Content provider URI not specified."
                         + " Did you specify --uri argument?");
             }
-            return new DeleteCommand(uri, where);
+            return new DeleteCommand(uri, userId, where);
         }
 
         private UpdateCommand parseUpdateCommand() {
             Uri uri = null;
+            int userId = UserHandle.USER_OWNER;
             String where = null;
             ContentValues values = new ContentValues();
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_USER.equals(argument)) {
+                    userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_WHERE.equals(argument)) {
                     where = argumentValueRequired(argument);
                 } else if (ARGUMENT_BIND.equals(argument)) {
@@ -213,17 +225,20 @@
                 throw new IllegalArgumentException("Bindings not specified."
                         + " Did you specify --bind argument(s)?");
             }
-            return new UpdateCommand(uri, values, where);
+            return new UpdateCommand(uri, userId, values, where);
         }
 
         public QueryCommand parseQueryCommand() {
             Uri uri = null;
+            int userId = UserHandle.USER_OWNER;
             String[] projection = null;
             String sort = null;
             String where = null;
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_USER.equals(argument)) {
+                    userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_WHERE.equals(argument)) {
                     where = argumentValueRequired(argument);
                 } else if (ARGUMENT_SORT.equals(argument)) {
@@ -238,7 +253,7 @@
                 throw new IllegalArgumentException("Content provider URI not specified."
                         + " Did you specify --uri argument?");
             }
-            return new QueryCommand(uri, projection, where, sort);
+            return new QueryCommand(uri, userId, projection, where, sort);
         }
 
         private void parseBindValue(ContentValues values) {
@@ -298,9 +313,11 @@
 
     private static abstract class Command {
         final Uri mUri;
+        final int mUserId;
 
-        public Command(Uri uri) {
+        public Command(Uri uri, int userId) {
             mUri = uri;
+            mUserId = userId;
         }
 
         public final void execute() {
@@ -311,7 +328,7 @@
                 IBinder token = new Binder();
                 try {
                     ContentProviderHolder holder = activityManager.getContentProviderExternal(
-                            providerName, token);
+                            providerName, mUserId, token);
                     if (holder == null) {
                         throw new IllegalStateException("Could not find provider: " + providerName);
                     }
@@ -334,8 +351,8 @@
     private static class InsertCommand extends Command {
         final ContentValues mContentValues;
 
-        public InsertCommand(Uri uri, ContentValues contentValues) {
-            super(uri);
+        public InsertCommand(Uri uri, int userId, ContentValues contentValues) {
+            super(uri, userId);
             mContentValues = contentValues;
         }
 
@@ -348,8 +365,8 @@
     private static class DeleteCommand extends Command {
         final String mWhere;
 
-        public DeleteCommand(Uri uri, String where) {
-            super(uri);
+        public DeleteCommand(Uri uri, int userId, String where) {
+            super(uri, userId);
             mWhere = where;
         }
 
@@ -363,8 +380,9 @@
         final String[] mProjection;
         final String mSortOrder;
 
-        public QueryCommand(Uri uri, String[] projection, String where, String sortOrder) {
-            super(uri, where);
+        public QueryCommand(
+                Uri uri, int userId, String[] projection, String where, String sortOrder) {
+            super(uri, userId, where);
             mProjection = projection;
             mSortOrder = sortOrder;
         }
@@ -426,8 +444,8 @@
     private static class UpdateCommand extends InsertCommand {
         final String mWhere;
 
-        public UpdateCommand(Uri uri, ContentValues contentValues, String where) {
-            super(uri, contentValues);
+        public UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where) {
+            super(uri, userId, contentValues);
             mWhere = where;
         }
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b0df660..c3f57e8 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -632,8 +632,9 @@
             IBinder b = data.readStrongBinder();
             IApplicationThread app = ApplicationThreadNative.asInterface(b);
             String name = data.readString();
+            int userId = data.readInt();
             boolean stable = data.readInt() != 0;
-            ContentProviderHolder cph = getContentProvider(app, name, stable);
+            ContentProviderHolder cph = getContentProvider(app, name, userId, stable);
             reply.writeNoException();
             if (cph != null) {
                 reply.writeInt(1);
@@ -647,8 +648,9 @@
         case GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String name = data.readString();
+            int userId = data.readInt();
             IBinder token = data.readStrongBinder();
-            ContentProviderHolder cph = getContentProviderExternal(name, token);
+            ContentProviderHolder cph = getContentProviderExternal(name, userId, token);
             reply.writeNoException();
             if (cph != null) {
                 reply.writeInt(1);
@@ -2495,12 +2497,13 @@
         reply.recycle();
     }
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
-            String name, boolean stable) throws RemoteException {
+            String name, int userId, boolean stable) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
         data.writeString(name);
+        data.writeInt(userId);
         data.writeInt(stable ? 1 : 0);
         mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
         reply.readException();
@@ -2513,13 +2516,13 @@
         reply.recycle();
         return cph;
     }
-    public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
-            throws RemoteException
-    {
+    public ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeString(name);
+        data.writeInt(userId);
         data.writeStrongBinder(token);
         mRemote.transact(GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION, data, reply, 0);
         reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 67ecf5b..aa8ef21 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -89,6 +89,7 @@
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.SamplingProfilerIntegration;
+import com.android.internal.util.Objects;
 
 import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
 
@@ -214,9 +215,33 @@
             = new ArrayList<ActivityClientRecord>();
     Configuration mPendingConfiguration = null;
 
+    private static final class ProviderKey {
+        final String authority;
+        final int userId;
+
+        public ProviderKey(String authority, int userId) {
+            this.authority = authority;
+            this.userId = userId;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof ProviderKey) {
+                final ProviderKey other = (ProviderKey) o;
+                return Objects.equal(authority, other.authority) && userId == other.userId;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return ((authority != null) ? authority.hashCode() : 0) ^ userId;
+        }
+    }
+
     // The lock of mProviderMap protects the following variables.
-    final HashMap<String, ProviderClientRecord> mProviderMap
-        = new HashMap<String, ProviderClientRecord>();
+    final HashMap<ProviderKey, ProviderClientRecord> mProviderMap
+        = new HashMap<ProviderKey, ProviderClientRecord>();
     final HashMap<IBinder, ProviderRefCount> mProviderRefCountMap
         = new HashMap<IBinder, ProviderRefCount>();
     final HashMap<IBinder, ProviderClientRecord> mLocalProviders
@@ -4360,8 +4385,9 @@
         }
     }
 
-    public final IContentProvider acquireProvider(Context c, String name, boolean stable) {
-        IContentProvider provider = acquireExistingProvider(c, name, stable);
+    public final IContentProvider acquireProvider(
+            Context c, String auth, int userId, boolean stable) {
+        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
         if (provider != null) {
             return provider;
         }
@@ -4375,11 +4401,11 @@
         IActivityManager.ContentProviderHolder holder = null;
         try {
             holder = ActivityManagerNative.getDefault().getContentProvider(
-                    getApplicationThread(), name, stable);
+                    getApplicationThread(), auth, userId, stable);
         } catch (RemoteException ex) {
         }
         if (holder == null) {
-            Slog.e(TAG, "Failed to find provider info for " + name);
+            Slog.e(TAG, "Failed to find provider info for " + auth);
             return null;
         }
 
@@ -4456,10 +4482,11 @@
         }
     }
 
-    public final IContentProvider acquireExistingProvider(Context c, String name,
-            boolean stable) {
+    public final IContentProvider acquireExistingProvider(
+            Context c, String auth, int userId, boolean stable) {
         synchronized (mProviderMap) {
-            ProviderClientRecord pr = mProviderMap.get(name);
+            final ProviderKey key = new ProviderKey(auth, userId);
+            final ProviderClientRecord pr = mProviderMap.get(key);
             if (pr == null) {
                 return null;
             }
@@ -4639,17 +4666,20 @@
     }
 
     private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
-            ContentProvider localProvider,IActivityManager.ContentProviderHolder holder) {
-        String names[] = PATTERN_SEMICOLON.split(holder.info.authority);
-        ProviderClientRecord pcr = new ProviderClientRecord(names, provider,
-                localProvider, holder);
-        for (int i = 0; i < names.length; i++) {
-            ProviderClientRecord existing = mProviderMap.get(names[i]);
+            ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
+        final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);
+        final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
+
+        final ProviderClientRecord pcr = new ProviderClientRecord(
+                auths, provider, localProvider, holder);
+        for (String auth : auths) {
+            final ProviderKey key = new ProviderKey(auth, userId);
+            final ProviderClientRecord existing = mProviderMap.get(key);
             if (existing != null) {
                 Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
-                        + " already published as " + names[i]);
+                        + " already published as " + auth);
             } else {
-                mProviderMap.put(names[i], pcr);
+                mProviderMap.put(key, pcr);
             }
         }
         return pcr;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 56b745f..a6ec9b6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import com.android.internal.policy.PolicyManager;
+import com.android.internal.util.Preconditions;
 
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
@@ -35,6 +36,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -183,6 +185,7 @@
     private Display mDisplay; // may be null if default display
     private Context mReceiverRestrictedContext = null;
     private boolean mRestricted;
+    private UserHandle mUser;
 
     private final Object mSync = new Object();
 
@@ -1676,7 +1679,13 @@
 
     @Override
     public Context createPackageContext(String packageName, int flags)
-        throws PackageManager.NameNotFoundException {
+            throws NameNotFoundException {
+        return createPackageContextAsUser(packageName, flags, Process.myUserHandle());
+    }
+
+    @Override
+    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+            throws NameNotFoundException {
         if (packageName.equals("system") || packageName.equals("android")) {
             final ContextImpl context = new ContextImpl(mMainThread.getSystemContext());
             context.mBasePackageName = mBasePackageName;
@@ -1688,7 +1697,7 @@
         if (pi != null) {
             ContextImpl c = new ContextImpl();
             c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
-            c.init(pi, null, mMainThread, mResources, mBasePackageName);
+            c.init(pi, null, mMainThread, mResources, mBasePackageName, user);
             if (c.mResources != null) {
                 return c;
             }
@@ -1769,8 +1778,8 @@
     }
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
-        ContextImpl context = new ContextImpl();
-        context.init(Resources.getSystem(), mainThread);
+        final ContextImpl context = new ContextImpl();
+        context.init(Resources.getSystem(), mainThread, Process.myUserHandle());
         return context;
     }
 
@@ -1790,18 +1799,17 @@
         mResources = context.mResources;
         mMainThread = context.mMainThread;
         mContentResolver = context.mContentResolver;
+        mUser = context.mUser;
         mDisplay = context.mDisplay;
         mOuterContext = this;
     }
 
-    final void init(LoadedApk packageInfo,
-            IBinder activityToken, ActivityThread mainThread) {
-        init(packageInfo, activityToken, mainThread, null, null);
+    final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) {
+        init(packageInfo, activityToken, mainThread, null, null, Process.myUserHandle());
     }
 
-    final void init(LoadedApk packageInfo,
-                IBinder activityToken, ActivityThread mainThread,
-                Resources container, String basePackageName) {
+    final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,
+            Resources container, String basePackageName, UserHandle user) {
         mPackageInfo = packageInfo;
         mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
         mResources = mPackageInfo.getResources(mainThread);
@@ -1818,16 +1826,18 @@
                     null, container.getCompatibilityInfo());
         }
         mMainThread = mainThread;
-        mContentResolver = new ApplicationContentResolver(this, mainThread);
         mActivityToken = activityToken;
+        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
+        mUser = user;
     }
 
-    final void init(Resources resources, ActivityThread mainThread) {
+    final void init(Resources resources, ActivityThread mainThread, UserHandle user) {
         mPackageInfo = null;
         mBasePackageName = null;
         mResources = resources;
         mMainThread = mainThread;
-        mContentResolver = new ApplicationContentResolver(this, mainThread);
+        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
+        mUser = user;
     }
 
     final void scheduleFinalCleanup(String who, String what) {
@@ -1912,19 +1922,24 @@
     // ----------------------------------------------------------------------
 
     private static final class ApplicationContentResolver extends ContentResolver {
-        public ApplicationContentResolver(Context context, ActivityThread mainThread) {
+        private final ActivityThread mMainThread;
+        private final UserHandle mUser;
+
+        public ApplicationContentResolver(
+                Context context, ActivityThread mainThread, UserHandle user) {
             super(context);
-            mMainThread = mainThread;
+            mMainThread = Preconditions.checkNotNull(mainThread);
+            mUser = Preconditions.checkNotNull(user);
         }
 
         @Override
-        protected IContentProvider acquireProvider(Context context, String name) {
-            return mMainThread.acquireProvider(context, name, true);
+        protected IContentProvider acquireProvider(Context context, String auth) {
+            return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true);
         }
 
         @Override
-        protected IContentProvider acquireExistingProvider(Context context, String name) {
-            return mMainThread.acquireExistingProvider(context, name, true);
+        protected IContentProvider acquireExistingProvider(Context context, String auth) {
+            return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true);
         }
 
         @Override
@@ -1933,8 +1948,8 @@
         }
 
         @Override
-        protected IContentProvider acquireUnstableProvider(Context c, String name) {
-            return mMainThread.acquireProvider(c, name, false);
+        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
+            return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
         }
 
         @Override
@@ -1946,7 +1961,5 @@
         public void unstableProviderDied(IContentProvider icp) {
             mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
         }
-
-        private final ActivityThread mMainThread;
     }
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ed17d0e..2b2679e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -117,8 +117,8 @@
     public void reportThumbnail(IBinder token,
             Bitmap thumbnail, CharSequence description) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
-            String name, boolean stable) throws RemoteException;
-    public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
+            String name, int userId, boolean stable) throws RemoteException;
+    public ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token)
             throws RemoteException;
     public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException;
     public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 17c2c6b..182ebef 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -29,6 +29,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.IntProperty;
 import android.util.Log;
@@ -893,6 +894,19 @@
         return sb.toString();
     }
 
+    /** {@hide} */
+    public void setUser(UserHandle user) {
+        if (tickerView != null) {
+            tickerView.setUser(user);
+        }
+        if (contentView != null) {
+            contentView.setUser(user);
+        }
+        if (bigContentView != null) {
+            bigContentView.setUser(user);
+        }
+    }
+
     /**
      * Builder class for {@link Notification} objects.
      * 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 161670f..524962cb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2574,6 +2574,17 @@
             int flags) throws PackageManager.NameNotFoundException;
 
     /**
+     * Similar to {@link #createPackageContext(String, int)}, but with a
+     * different {@link UserHandle}. For example, {@link #getContentResolver()}
+     * will open any {@link Uri} as the given user.
+     *
+     * @hide
+     */
+    public abstract Context createPackageContextAsUser(
+            String packageName, int flags, UserHandle user)
+            throws PackageManager.NameNotFoundException;
+
+    /**
      * Return a new Context object for the current Context but whose resources
      * are adjusted to match the given Configuration.  Each call to this method
      * returns a new instance of a Context object; Context objects are not
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6101f4e..d824f1e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -586,6 +586,13 @@
         return mBase.createPackageContext(packageName, flags);
     }
 
+    /** @hide */
+    @Override
+    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        return mBase.createPackageContextAsUser(packageName, flags, user);
+    }
+
     @Override
     public Context createConfigurationContext(Configuration overrideConfiguration) {
         return mBase.createConfigurationContext(overrideConfiguration);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1a47ce2..90f55bf 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -35,9 +34,9 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
 import android.view.RemotableViewMethod;
@@ -72,6 +71,13 @@
     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
 
     /**
+     * User that these views should be applied as. Requires
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} when
+     * crossing user boundaries.
+     */
+    private UserHandle mUser = android.os.Process.myUserHandle();
+
+    /**
      * The package name of the package containing the layout
      * resource. (Added to the parcel)
      */
@@ -1446,11 +1452,16 @@
         recalculateMemoryUsage();
     }
 
+    /** {@hide} */
+    public void setUser(UserHandle user) {
+        mUser = user;
+    }
+
     private boolean hasLandscapeAndPortraitLayouts() {
         return (mLandscape != null) && (mPortrait != null);
     }
 
-     /**
+    /**
      * Create a new RemoteViews object that will inflate as the specified
      * landspace or portrait RemoteViews, depending on the current configuration.
      *
@@ -2309,7 +2320,8 @@
 
         if (packageName != null) {
             try {
-                c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
+                c = context.createPackageContextAsUser(
+                        packageName, Context.CONTEXT_RESTRICTED, mUser);
             } catch (NameNotFoundException e) {
                 Log.e(LOG_TAG, "Package name " + packageName + " not found");
                 c = context;
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index cb87ac4..f9a38a5 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -20,8 +20,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
-import android.widget.RemoteViews;
-
 
 /*
 boolean clearable = !n.ongoingEvent && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
@@ -39,19 +37,25 @@
  * Class encapsulating a Notification. Sent by the NotificationManagerService to the IStatusBar (in System UI).
  */
 public class StatusBarNotification implements Parcelable {
-    public String pkg;
-    public int id;
-    public String tag;
-    public int uid;
-    public int initialPid;
-    public Notification notification;
-    public int score;
-    
-    public StatusBarNotification() {
+    public final String pkg;
+    public final int id;
+    public final String tag;
+    public final int uid;
+    public final int initialPid;
+    // TODO: make this field private and move callers to an accessor that
+    // ensures sourceUser is applied.
+    public final Notification notification;
+    public final int score;
+    public final UserHandle user;
+
+    @Deprecated
+    public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score,
+            Notification notification) {
+        this(pkg, id, tag, uid, initialPid, score, notification, UserHandle.OWNER);
     }
 
-    public StatusBarNotification(String pkg, int id, String tag,
-            int uid, int initialPid, int score, Notification notification) {
+    public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score,
+            Notification notification, UserHandle user) {
         if (pkg == null) throw new NullPointerException();
         if (notification == null) throw new NullPointerException();
 
@@ -62,13 +66,11 @@
         this.initialPid = initialPid;
         this.score = score;
         this.notification = notification;
+        this.user = user;
+        this.notification.setUser(user);
     }
 
     public StatusBarNotification(Parcel in) {
-        readFromParcel(in);
-    }
-
-    public void readFromParcel(Parcel in) {
         this.pkg = in.readString();
         this.id = in.readInt();
         if (in.readInt() != 0) {
@@ -80,6 +82,8 @@
         this.initialPid = in.readInt();
         this.score = in.readInt();
         this.notification = new Notification(in);
+        this.user = UserHandle.readFromParcel(in);
+        this.notification.setUser(user);
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -95,6 +99,7 @@
         out.writeInt(this.initialPid);
         out.writeInt(this.score);
         this.notification.writeToParcel(out, flags);
+        user.writeToParcel(out, flags);
     }
 
     public int describeContents() {
@@ -115,14 +120,16 @@
         }
     };
 
+    @Override
     public StatusBarNotification clone() {
-        return new StatusBarNotification(this.pkg, this.id, this.tag,
-                this.uid, this.initialPid, this.score, this.notification.clone());
+        return new StatusBarNotification(this.pkg, this.id, this.tag, this.uid, this.initialPid,
+                this.score, this.notification.clone(), this.user);
     }
 
+    @Override
     public String toString() {
-        return "StatusBarNotification(pkg=" + pkg + " id=" + id + " tag=" + tag
-                + " score=" + score + " notn=" + notification + ")";
+        return "StatusBarNotification(pkg=" + pkg + " id=" + id + " tag=" + tag + " score=" + score
+                + " notn=" + notification + " user=" + user + ")";
     }
 
     public boolean isOngoing() {
@@ -139,5 +146,3 @@
         return UserHandle.getUserId(this.uid);
     }
 }
-
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 7153ec7..3335dfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -1370,9 +1370,8 @@
                 iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0);
 
                 mNotificationDNDDummyEntry = new NotificationData.Entry(
-                        null,
-                        new StatusBarNotification("", 0, "", 0, 0, Notification.PRIORITY_MAX, dndNotification),
-                        iconView);
+                        null, new StatusBarNotification("", 0, "", 0, 0, Notification.PRIORITY_MAX,
+                                dndNotification, android.os.Process.myUserHandle()), iconView);
 
                 mIconLayout.addView(iconView, params);
             }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 3caba1f..71e6e66 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -63,6 +63,7 @@
 import android.util.Xml;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.RemoteViews;
 import android.widget.Toast;
 
 import com.android.internal.statusbar.StatusBarNotification;
@@ -877,7 +878,6 @@
         return (x < low) ? low : ((x > high) ? high : x);
     }
 
-    
     // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
     // uid/pid of another application)
     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
@@ -992,8 +992,9 @@
             }
 
             if (notification.icon != 0) {
-                StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
-                        r.uid, r.initialPid, score, notification);
+                final UserHandle user = new UserHandle(userId);
+                final StatusBarNotification n = new StatusBarNotification(
+                        pkg, id, tag, r.uid, r.initialPid, score, notification, user);
                 if (old != null && old.statusBarKey != null) {
                     r.statusBarKey = old.statusBarKey;
                     long identity = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f162dae..a6f2974 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6414,10 +6414,6 @@
                           + " (pid=" + Binder.getCallingPid()
                           + ") when getting content provider " + name);
                 }
-                if (r.userId != userId) {
-                    throw new SecurityException("Calling requested user " + userId
-                            + " but app is user " + r.userId);
-                }
             }
 
             // First check if this content provider has been published...
@@ -6666,7 +6662,7 @@
     }
 
     public final ContentProviderHolder getContentProvider(
-            IApplicationThread caller, String name, boolean stable) {
+            IApplicationThread caller, String name, int userId, boolean stable) {
         enforceNotIsolatedCaller("getContentProvider");
         if (caller == null) {
             String msg = "null IApplicationThread when getting content provider "
@@ -6675,14 +6671,18 @@
             throw new SecurityException(msg);
         }
 
-        return getContentProviderImpl(caller, name, null, stable,
-                UserHandle.getCallingUserId());
+        userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                false, true, "getContentProvider", null);
+        return getContentProviderImpl(caller, name, null, stable, userId);
     }
 
-    public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
+    public ContentProviderHolder getContentProviderExternal(
+            String name, int userId, IBinder token) {
         enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
             "Do not have permission in call getContentProviderExternal()");
-        return getContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
+        userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                false, true, "getContentProvider", null);
+        return getContentProviderExternalUnchecked(name, token, userId);
     }
 
     private ContentProviderHolder getContentProviderExternalUnchecked(String name,
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 5c9282e..1f815e7 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -523,6 +523,13 @@
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    @Override
+    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public Context createConfigurationContext(Configuration overrideConfiguration) {
         throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 428c4c2..80478ba 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -920,6 +920,12 @@
     }
 
     @Override
+    public Context createPackageContextAsUser(String arg0, int arg1, UserHandle user) {
+        // pass
+        return null;
+    }
+
+    @Override
     public Context createConfigurationContext(Configuration overrideConfiguration) {
         // pass
         return null;