Merge "Multi-user - wallpaper service"
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index dfea728..5a36466 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -670,8 +670,9 @@
             String resolvedType = data.readString();
             b = data.readStrongBinder();
             int fl = data.readInt();
+            int userId = data.readInt();
             IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
-            int res = bindService(app, token, service, resolvedType, conn, fl);
+            int res = bindService(app, token, service, resolvedType, conn, fl, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -2288,7 +2289,7 @@
     }
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType, IServiceConnection connection,
-            int flags) throws RemoteException {
+            int flags, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -2298,6 +2299,7 @@
         data.writeString(resolvedType);
         data.writeStrongBinder(connection.asBinder());
         data.writeInt(flags);
+        data.writeInt(userId);
         mRemote.transact(BIND_SERVICE_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 37900b6..ebf692a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1125,6 +1125,12 @@
     @Override
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
+        return bindService(service, conn, flags, UserId.getUserId(Process.myUid()));
+    }
+
+    /** @hide */
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
         IServiceConnection sd;
         if (mPackageInfo != null) {
             sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
@@ -1143,7 +1149,7 @@
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(),
                 service, service.resolveTypeIfNeeded(getContentResolver()),
-                sd, flags);
+                sd, flags, userId);
             if (res < 0) {
                 throw new SecurityException(
                         "Not allowed to bind to service " + service);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a62f724..7deb615 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -166,7 +166,7 @@
             int id, Notification notification, boolean keepNotification) throws RemoteException;
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
-            IServiceConnection connection, int flags) throws RemoteException;
+            IServiceConnection connection, int flags, int userId) throws RemoteException;
     public boolean unbindService(IServiceConnection connection) throws RemoteException;
     public void publishService(IBinder token,
             Intent intent, IBinder service) throws RemoteException;
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index abd1c5b..c057d66 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -30,6 +30,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index 170171e..a74a268 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -38,15 +38,23 @@
     private static final boolean DEBUG = false;
 
     // This path must match what the WallpaperManagerService uses
-    private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    public static final String WALLPAPER_IMAGE = "/data/system/users/0/wallpaper";
+    public static final String WALLPAPER_INFO = "/data/system/users/0/wallpaper_info.xml";
+    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
+    public static final String WALLPAPER_IMAGE_KEY =
+            "/data/data/com.android.settings/files/wallpaper";
+    public static final String WALLPAPER_INFO_KEY = "/data/system/wallpaper_info.xml";
 
     // Stage file - should be adjacent to the WALLPAPER_IMAGE location.  The wallpapers
     // will be saved to this file from the restore stream, then renamed to the proper
     // location if it's deemed suitable.
-    private static final String STAGE_FILE = "/data/data/com.android.settings/files/wallpaper-tmp";
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    private static final String STAGE_FILE = "/data/system/users/0/wallpaper-tmp";
 
     Context mContext;
     String[] mFiles;
+    String[] mKeys;
     double mDesiredMinWidth;
     double mDesiredMinHeight;
 
@@ -57,11 +65,12 @@
      * @param context
      * @param files
      */
-    public WallpaperBackupHelper(Context context, String... files) {
+    public WallpaperBackupHelper(Context context, String[] files, String[] keys) {
         super(context);
 
         mContext = context;
         mFiles = files;
+        mKeys = keys;
 
         WallpaperManager wpm;
         wpm = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
@@ -89,7 +98,7 @@
      */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) {
-        performBackup_checked(oldState, data, newState, mFiles, mFiles);
+        performBackup_checked(oldState, data, newState, mFiles, mKeys);
     }
 
     /**
@@ -99,8 +108,8 @@
      */
     public void restoreEntity(BackupDataInputStream data) {
         final String key = data.getKey();
-        if (isKeyInList(key, mFiles)) {
-            if (key.equals(WALLPAPER_IMAGE)) {
+        if (isKeyInList(key, mKeys)) {
+            if (key.equals(WALLPAPER_IMAGE_KEY)) {
                 // restore the file to the stage for inspection
                 File f = new File(STAGE_FILE);
                 if (writeFile(f, data)) {
@@ -135,9 +144,9 @@
                         f.delete();
                     }
                 }
-            } else {
-                // Some other normal file; just decode it to its destination
-                File f = new File(key);
+            } else if (key.equals(WALLPAPER_INFO_KEY)) {
+                // XML file containing wallpaper info
+                File f = new File(WALLPAPER_INFO);
                 writeFile(f, data);
             }
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3d4e354..6d4cdae 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1307,6 +1307,15 @@
             int flags);
 
     /**
+     * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userId
+     * argument for use by system server and other multi-user aware code.
+     * @hide
+     */
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Disconnect from an application service.  You will no longer receive
      * calls as the service is restarted, and the service is now allowed to
      * stop at any time.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3928aaf..cd8d87f 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -370,6 +370,12 @@
         return mBase.bindService(service, conn, flags);
     }
 
+    /** @hide */
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+        return mBase.bindService(service, conn, flags, userId);
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         mBase.unbindService(conn);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2023f82..8029bd5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3380,7 +3380,8 @@
 
     public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
         if (s == null) return null;
-        if (!copyNeeded(flags, s.owner, s.metaData) && userId == 0) {
+        if (!copyNeeded(flags, s.owner, s.metaData)
+                && userId == UserId.getUserId(s.info.applicationInfo.uid)) {
             return s.info;
         }
         // Make shallow copies so we can store the metadata safely
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
index 286b674..0da67d63 100644
--- a/core/java/android/os/UserId.java
+++ b/core/java/android/os/UserId.java
@@ -73,6 +73,10 @@
         }
     }
 
+    public static final int getCallingUserId() {
+        return getUserId(Binder.getCallingUid());
+    }
+
     /**
      * Returns the uid that is composed from the userId and the appId.
      * @hide
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index f4b5dca..abe0104 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -52,7 +52,6 @@
     if (keyUTF == NULL) {
         return -1;
     }
-
     err = writer->WriteEntityHeader(String8(keyUTF), dataSize);
 
     env->ReleaseStringUTFChars(key, keyUTF);
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 250386f..41ede2e 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -573,12 +573,13 @@
                 mBoundRemoteViewsServices.remove(key);
             }
 
+            int userId = UserId.getUserId(id.provider.uid);
             // Bind to the RemoteViewsService (which will trigger a callback to the
             // RemoteViewsAdapter.onServiceConnected())
             final long token = Binder.clearCallingIdentity();
             try {
                 conn = new ServiceConnectionProxy(key, connection);
-                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
                 mBoundRemoteViewsServices.put(key, conn);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -638,11 +639,11 @@
 
         // Check if we need to destroy any services (if no other app widgets are
         // referencing the same service)
-        decrementAppWidgetServiceRefCount(appWidgetId);
+        decrementAppWidgetServiceRefCount(id);
     }
 
     // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
-    private void destroyRemoteViewsService(final Intent intent) {
+    private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
         final ServiceConnection conn = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
@@ -663,11 +664,12 @@
             }
         };
 
+        int userId = UserId.getUserId(id.provider.uid);
         // Bind to the service and remove the static intent->factory mapping in the
         // RemoteViewsService.
         final long token = Binder.clearCallingIdentity();
         try {
-            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -687,16 +689,16 @@
 
     // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
     // the ref-count reaches zero.
-    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+    private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
         Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
         while (it.hasNext()) {
             final FilterComparison key = it.next();
             final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
-            if (ids.remove(appWidgetId)) {
+            if (ids.remove(id.appWidgetId)) {
                 // If we have removed the last app widget referencing this service, then we
                 // should destroy it and remove it from this set
                 if (ids.isEmpty()) {
-                    destroyRemoteViewsService(key.getIntent());
+                    destroyRemoteViewsService(key.getIntent(), id);
                     it.remove();
                 }
             }
@@ -888,10 +890,11 @@
                             }
                         };
 
+                        int userId = UserId.getUserId(id.provider.uid);
                         // Bind to the service and call onDataSetChanged()
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
@@ -1343,7 +1346,6 @@
 
     void readStateFromFileLocked(FileInputStream stream) {
         boolean success = false;
-
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(stream, null);
@@ -1475,7 +1477,7 @@
     }
 
     AtomicFile savedStateFile() {
-        int userId = Binder.getOrigCallingUser();
+        int userId = UserId.getCallingUserId();
         File dir = new File("/data/system/users/" + userId);
         File settingsFile = new File(dir, SETTINGS_FILENAME);
         if (!dir.exists()) {
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index da97089..a7a583c 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -44,12 +44,16 @@
     private static final String WALLPAPER_IMAGE_FILENAME = "wallpaper";
     private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
 
-    private static final String WALLPAPER_IMAGE_DIR = "/data/data/com.android.settings/files";
-    private static final String WALLPAPER_IMAGE = WALLPAPER_IMAGE_DIR + "/" + WALLPAPER_IMAGE_FILENAME;
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    private static final String WALLPAPER_IMAGE_DIR = "/data/system/users/0";
+    private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
 
-    private static final String WALLPAPER_INFO_DIR = "/data/system";
-    private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/" +  WALLPAPER_INFO_FILENAME;
-
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    private static final String WALLPAPER_INFO_DIR = "/data/system/users/0";
+    private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
+    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
+    private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
+    private static final String WALLPAPER_INFO_KEY = WallpaperBackupHelper.WALLPAPER_INFO_KEY;
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
@@ -58,13 +62,15 @@
         WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
                 Context.WALLPAPER_SERVICE);
         String[] files = new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO };
-        if (wallpaper != null && wallpaper.mName != null && wallpaper.mName.length() > 0) {
+        String[] keys = new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY };
+        if (wallpaper != null && wallpaper.getName() != null && wallpaper.getName().length() > 0) {
             // When the wallpaper has a name, back up the info by itself.
             // TODO: Don't rely on the innards of the service object like this!
             // TODO: Send a delete for any stored wallpaper image in this case?
             files = new String[] { WALLPAPER_INFO };
+            keys = new String[] { WALLPAPER_INFO_KEY };
         }
-        addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files));
+        addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys));
         super.onBackup(oldState, data, newState);
     }
 
@@ -90,9 +96,11 @@
             throws IOException {
         // On restore, we also support a previous data schema "system_files"
         addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this,
-                new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }));
+                new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO },
+                new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} ));
         addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this,
-                new String[] { WALLPAPER_IMAGE }));
+                new String[] { WALLPAPER_IMAGE },
+                new String[] { WALLPAPER_IMAGE_KEY} ));
 
         try {
             super.onRestore(data, appVersionCode, newState);
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 4925a4e..8ee12bc 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -24,6 +24,7 @@
 import android.app.PendingIntent;
 import android.app.WallpaperInfo;
 import android.app.backup.BackupManager;
+import android.app.backup.WallpaperBackupHelper;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -43,11 +44,13 @@
 import android.os.RemoteCallbackList;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
 import android.service.wallpaper.WallpaperService;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.Xml;
 import android.view.Display;
 import android.view.IWindowManager;
@@ -70,6 +73,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
+import com.android.server.am.ActivityManagerService;
 
 class WallpaperManagerService extends IWallpaperManager.Stub {
     static final String TAG = "WallpaperService";
@@ -83,17 +87,9 @@
      */
     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
     
-    static final File WALLPAPER_DIR = new File(
-            "/data/data/com.android.settings/files");
+    static final File WALLPAPER_BASE_DIR = new File("/data/system/users");
     static final String WALLPAPER = "wallpaper";
-    static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
-
-    /**
-     * List of callbacks registered they should each be notified
-     * when the wallpaper is changed.
-     */
-    private final RemoteCallbackList<IWallpaperManagerCallback> mCallbacks
-            = new RemoteCallbackList<IWallpaperManagerCallback>();
+    static final String WALLPAPER_INFO = "wallpaper_info.xml";
 
     /**
      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -101,97 +97,135 @@
      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
      * everytime the wallpaper is changed.
      */
-    private final FileObserver mWallpaperObserver = new FileObserver(
-            WALLPAPER_DIR.getAbsolutePath(), CLOSE_WRITE | DELETE | DELETE_SELF) {
-                @Override
-                public void onEvent(int event, String path) {
-                    if (path == null) {
-                        return;
-                    }
-                    synchronized (mLock) {
-                        // changing the wallpaper means we'll need to back up the new one
-                        long origId = Binder.clearCallingIdentity();
-                        BackupManager bm = new BackupManager(mContext);
-                        bm.dataChanged();
-                        Binder.restoreCallingIdentity(origId);
+    private class WallpaperObserver extends FileObserver {
 
-                        File changedFile = new File(WALLPAPER_DIR, path);
-                        if (WALLPAPER_FILE.equals(changedFile)) {
-                            notifyCallbacksLocked();
-                            if (mWallpaperComponent == null || event != CLOSE_WRITE
-                                    || mImageWallpaperPending) {
-                                if (event == CLOSE_WRITE) {
-                                    mImageWallpaperPending = false;
-                                }
-                                bindWallpaperComponentLocked(mImageWallpaperComponent,
-                                        true, false);
-                                saveSettingsLocked();
-                            }
+        final WallpaperData mWallpaper;
+        final File mWallpaperDir;
+        final File mWallpaperFile;
+
+        public WallpaperObserver(WallpaperData wallpaper) {
+            super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
+                    CLOSE_WRITE | DELETE | DELETE_SELF);
+            mWallpaperDir = getWallpaperDir(wallpaper.userId);
+            mWallpaper = wallpaper;
+            mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
+        }
+
+        @Override
+        public void onEvent(int event, String path) {
+            if (path == null) {
+                return;
+            }
+            synchronized (mLock) {
+                // changing the wallpaper means we'll need to back up the new one
+                long origId = Binder.clearCallingIdentity();
+                BackupManager bm = new BackupManager(mContext);
+                bm.dataChanged();
+                Binder.restoreCallingIdentity(origId);
+
+                File changedFile = new File(mWallpaperDir, path);
+                if (mWallpaperFile.equals(changedFile)) {
+                    notifyCallbacksLocked(mWallpaper);
+                    if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE
+                            || mWallpaper.imageWallpaperPending) {
+                        if (event == CLOSE_WRITE) {
+                            mWallpaper.imageWallpaperPending = false;
                         }
+                        bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true,
+                                false, mWallpaper);
+                        saveSettingsLocked(mWallpaper);
                     }
                 }
-            };
-    
+            }
+        }
+    }
+
     final Context mContext;
     final IWindowManager mIWindowManager;
     final MyPackageMonitor mMonitor;
+    WallpaperData mLastWallpaper;
 
-    int mWidth = -1;
-    int mHeight = -1;
+    SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
 
-    /**
-     * Client is currently writing a new image wallpaper.
-     */
-    boolean mImageWallpaperPending;
+    int mCurrentUserId;
 
-    /**
-     * Resource name if using a picture from the wallpaper gallery
-     */
-    String mName = "";
-    
-    /**
-     * The component name of the currently set live wallpaper.
-     */
-    ComponentName mWallpaperComponent;
-    
-    /**
-     * The component name of the wallpaper that should be set next.
-     */
-    ComponentName mNextWallpaperComponent;
-    
-    /**
-     * Name of the component used to display bitmap wallpapers from either the gallery or
-     * built-in wallpapers.
-     */
-    ComponentName mImageWallpaperComponent = new ComponentName("com.android.systemui",
-            "com.android.systemui.ImageWallpaper");
-    
-    WallpaperConnection mWallpaperConnection;
-    long mLastDiedTime;
-    boolean mWallpaperUpdating;
-    
+    static class WallpaperData {
+
+        int userId;
+
+        File wallpaperFile;
+
+        /**
+         * Client is currently writing a new image wallpaper.
+         */
+        boolean imageWallpaperPending;
+
+        /**
+         * Resource name if using a picture from the wallpaper gallery
+         */
+        String name = "";
+
+        /**
+         * The component name of the currently set live wallpaper.
+         */
+        ComponentName wallpaperComponent;
+
+        /**
+         * The component name of the wallpaper that should be set next.
+         */
+        ComponentName nextWallpaperComponent;
+
+        /**
+         * Name of the component used to display bitmap wallpapers from either the gallery or
+         * built-in wallpapers.
+         */
+        ComponentName imageWallpaperComponent = new ComponentName("com.android.systemui",
+                "com.android.systemui.ImageWallpaper");
+
+        WallpaperConnection connection;
+        long lastDiedTime;
+        boolean wallpaperUpdating;
+        WallpaperObserver wallpaperObserver;
+
+        /**
+         * List of callbacks registered they should each be notified when the wallpaper is changed.
+         */
+        private RemoteCallbackList<IWallpaperManagerCallback> callbacks
+                = new RemoteCallbackList<IWallpaperManagerCallback>();
+
+        int width = -1;
+        int height = -1;
+
+        WallpaperData(int userId) {
+            this.userId = userId;
+            wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
+        }
+    }
+
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
         final WallpaperInfo mInfo;
         final Binder mToken = new Binder();
         IWallpaperService mService;
         IWallpaperEngine mEngine;
+        WallpaperData mWallpaper;
 
-        public WallpaperConnection(WallpaperInfo info) {
+        public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
             mInfo = info;
+            mWallpaper = wallpaper;
         }
         
         public void onServiceConnected(ComponentName name, IBinder service) {
             synchronized (mLock) {
-                if (mWallpaperConnection == this) {
-                    mLastDiedTime = SystemClock.uptimeMillis();
+                if (mWallpaper.connection == this) {
+                    mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
                     mService = IWallpaperService.Stub.asInterface(service);
-                    attachServiceLocked(this);
+                    attachServiceLocked(this, mWallpaper);
                     // XXX should probably do saveSettingsLocked() later
                     // when we have an engine, but I'm not sure about
                     // locking there and anyway we always need to be able to
                     // recover if there is something wrong.
-                    saveSettingsLocked();
+                    saveSettingsLocked(mWallpaper);
                 }
             }
         }
@@ -200,43 +234,50 @@
             synchronized (mLock) {
                 mService = null;
                 mEngine = null;
-                if (mWallpaperConnection == this) {
-                    Slog.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
-                    if (!mWallpaperUpdating && (mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
-                                > SystemClock.uptimeMillis()) {
+                if (mWallpaper.connection == this) {
+                    Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
+                    if (!mWallpaper.wallpaperUpdating
+                            && (mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME)
+                                > SystemClock.uptimeMillis()
+                            && mWallpaper.userId == mCurrentUserId) {
                         Slog.w(TAG, "Reverting to built-in wallpaper!");
-                        clearWallpaperLocked(true);
+                        clearWallpaperLocked(true, mWallpaper.userId);
                     }
                 }
             }
         }
-        
+
         public void attachEngine(IWallpaperEngine engine) {
             mEngine = engine;
         }
-        
+
         public ParcelFileDescriptor setWallpaper(String name) {
             synchronized (mLock) {
-                if (mWallpaperConnection == this) {
-                    return updateWallpaperBitmapLocked(name);
+                if (mWallpaper.connection == this) {
+                    return updateWallpaperBitmapLocked(name, mWallpaper);
                 }
                 return null;
             }
         }
     }
-    
+
     class MyPackageMonitor extends PackageMonitor {
         @Override
         public void onPackageUpdateFinished(String packageName, int uid) {
             synchronized (mLock) {
-                if (mWallpaperComponent != null &&
-                        mWallpaperComponent.getPackageName().equals(packageName)) {
-                    mWallpaperUpdating = false;
-                    ComponentName comp = mWallpaperComponent;
-                    clearWallpaperComponentLocked();
-                    if (!bindWallpaperComponentLocked(comp, false, false)) {
-                        Slog.w(TAG, "Wallpaper no longer available; reverting to default");
-                        clearWallpaperLocked(false);
+                for (int i = 0; i < mWallpaperMap.size(); i++) {
+                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                    if (wallpaper.wallpaperComponent != null
+                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        wallpaper.wallpaperUpdating = false;
+                        ComponentName comp = wallpaper.wallpaperComponent;
+                        clearWallpaperComponentLocked(wallpaper);
+                        // Do this only for the current user's wallpaper
+                        if (wallpaper.userId == mCurrentUserId
+                                && !bindWallpaperComponentLocked(comp, false, false, wallpaper)) {
+                            Slog.w(TAG, "Wallpaper no longer available; reverting to default");
+                            clearWallpaperLocked(false, wallpaper.userId);
+                        }
                     }
                 }
             }
@@ -245,72 +286,91 @@
         @Override
         public void onPackageModified(String packageName) {
             synchronized (mLock) {
-                if (mWallpaperComponent == null ||
-                        !mWallpaperComponent.getPackageName().equals(packageName)) {
-                    return;
+                for (int i = 0; i < mWallpaperMap.size(); i++) {
+                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                    if (wallpaper.wallpaperComponent == null
+                            || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        continue;
+                    }
+                    doPackagesChanged(true, wallpaper);
                 }
             }
-            doPackagesChanged(true);
         }
 
         @Override
         public void onPackageUpdateStarted(String packageName, int uid) {
             synchronized (mLock) {
-                if (mWallpaperComponent != null &&
-                        mWallpaperComponent.getPackageName().equals(packageName)) {
-                    mWallpaperUpdating = true;
+                for (int i = 0; i < mWallpaperMap.size(); i++) {
+                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                    if (wallpaper.wallpaperComponent != null
+                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        wallpaper.wallpaperUpdating = true;
+                    }
                 }
             }
         }
 
         @Override
         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
-            return doPackagesChanged(doit);
+            boolean changed = false;
+            for (int i = 0; i < mWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                boolean res = doPackagesChanged(doit, wallpaper);
+                changed |= res;
+            }
+            return changed;
         }
 
         @Override
         public void onSomePackagesChanged() {
-            doPackagesChanged(true);
+            for (int i = 0; i < mWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                doPackagesChanged(true, wallpaper);
+            }
         }
-        
-        boolean doPackagesChanged(boolean doit) {
+
+        boolean doPackagesChanged(boolean doit, WallpaperData wallpaper) {
             boolean changed = false;
             synchronized (mLock) {
-                if (mWallpaperComponent != null) {
-                    int change = isPackageDisappearing(mWallpaperComponent.getPackageName());
+                if (wallpaper.wallpaperComponent != null) {
+                    int change = isPackageDisappearing(wallpaper.wallpaperComponent
+                            .getPackageName());
                     if (change == PACKAGE_PERMANENT_CHANGE
                             || change == PACKAGE_TEMPORARY_CHANGE) {
                         changed = true;
                         if (doit) {
-                            Slog.w(TAG, "Wallpaper uninstalled, removing: " + mWallpaperComponent);
-                            clearWallpaperLocked(false);
+                            Slog.w(TAG, "Wallpaper uninstalled, removing: "
+                                    + wallpaper.wallpaperComponent);
+                            clearWallpaperLocked(false, wallpaper.userId);
                         }
                     }
                 }
-                if (mNextWallpaperComponent != null) {
-                    int change = isPackageDisappearing(mNextWallpaperComponent.getPackageName());
+                if (wallpaper.nextWallpaperComponent != null) {
+                    int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
+                            .getPackageName());
                     if (change == PACKAGE_PERMANENT_CHANGE
                             || change == PACKAGE_TEMPORARY_CHANGE) {
-                        mNextWallpaperComponent = null;
+                        wallpaper.nextWallpaperComponent = null;
                     }
                 }
-                if (mWallpaperComponent != null
-                        && isPackageModified(mWallpaperComponent.getPackageName())) {
+                if (wallpaper.wallpaperComponent != null
+                        && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
                     try {
                         mContext.getPackageManager().getServiceInfo(
-                                mWallpaperComponent, 0);
+                                wallpaper.wallpaperComponent, 0);
                     } catch (NameNotFoundException e) {
-                        Slog.w(TAG, "Wallpaper component gone, removing: " + mWallpaperComponent);
-                        clearWallpaperLocked(false);
+                        Slog.w(TAG, "Wallpaper component gone, removing: "
+                                + wallpaper.wallpaperComponent);
+                        clearWallpaperLocked(false, wallpaper.userId);
                     }
                 }
-                if (mNextWallpaperComponent != null
-                        && isPackageModified(mNextWallpaperComponent.getPackageName())) {
+                if (wallpaper.nextWallpaperComponent != null
+                        && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
                     try {
                         mContext.getPackageManager().getServiceInfo(
-                                mNextWallpaperComponent, 0);
+                                wallpaper.nextWallpaperComponent, 0);
                     } catch (NameNotFoundException e) {
-                        mNextWallpaperComponent = null;
+                        wallpaper.nextWallpaperComponent = null;
                     }
                 }
             }
@@ -325,51 +385,110 @@
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mMonitor = new MyPackageMonitor();
         mMonitor.register(context, true);
-        WALLPAPER_DIR.mkdirs();
-        loadSettingsLocked();
-        mWallpaperObserver.startWatching();
+        WALLPAPER_BASE_DIR.mkdirs();
+        loadSettingsLocked(0);
     }
     
+    private static File getWallpaperDir(int userId) {
+        return new File(WALLPAPER_BASE_DIR + "/" + userId);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         super.finalize();
-        mWallpaperObserver.stopWatching();
+        for (int i = 0; i < mWallpaperMap.size(); i++) {
+            WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+            wallpaper.wallpaperObserver.stopWatching();
+        }
     }
     
     public void systemReady() {
         if (DEBUG) Slog.v(TAG, "systemReady");
+        WallpaperData wallpaper = mWallpaperMap.get(0);
+        switchWallpaper(wallpaper);
+        wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+        wallpaper.wallpaperObserver.startWatching();
+        ActivityManagerService ams = (ActivityManagerService) ServiceManager
+                .getService(Context.ACTIVITY_SERVICE);
+        ams.addUserListener(new ActivityManagerService.UserListener() {
+
+            @Override
+            public void onUserChanged(int userId) {
+                switchUser(userId);
+            }
+
+            @Override
+            public void onUserAdded(int userId) {
+            }
+
+            @Override
+            public void onUserRemoved(int userId) {
+            }
+
+            @Override
+            public void onUserLoggedOut(int userId) {
+            }
+
+        });
+    }
+
+    String getName() {
+        return mWallpaperMap.get(0).name;
+    }
+
+    void switchUser(int userId) {
+        synchronized (mLock) {
+            mCurrentUserId = userId;
+            WallpaperData wallpaper = mWallpaperMap.get(userId);
+            if (wallpaper == null) {
+                wallpaper = new WallpaperData(userId);
+                mWallpaperMap.put(userId, wallpaper);
+                loadSettingsLocked(userId);
+                wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+                wallpaper.wallpaperObserver.startWatching();
+            }
+            switchWallpaper(wallpaper);
+        }
+    }
+
+    void switchWallpaper(WallpaperData wallpaper) {
         synchronized (mLock) {
             RuntimeException e = null;
             try {
-                if (bindWallpaperComponentLocked(mNextWallpaperComponent, false, false)) {
+                ComponentName cname = wallpaper.wallpaperComponent != null ?
+                        wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
+                if (bindWallpaperComponentLocked(cname, true, false, wallpaper)) {
                     return;
                 }
             } catch (RuntimeException e1) {
                 e = e1;
             }
             Slog.w(TAG, "Failure starting previous wallpaper", e);
-            clearWallpaperLocked(false);
-        }
-    }
-    
-    public void clearWallpaper() {
-        if (DEBUG) Slog.v(TAG, "clearWallpaper");
-        synchronized (mLock) {
-            clearWallpaperLocked(false);
+            clearWallpaperLocked(false, wallpaper.userId);
         }
     }
 
-    public void clearWallpaperLocked(boolean defaultFailed) {
-        File f = WALLPAPER_FILE;
+    public void clearWallpaper() {
+        if (DEBUG) Slog.v(TAG, "clearWallpaper");
+        synchronized (mLock) {
+            clearWallpaperLocked(false, UserId.getCallingUserId());
+        }
+    }
+
+    void clearWallpaperLocked(boolean defaultFailed, int userId) {
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        File f = new File(getWallpaperDir(userId), WALLPAPER);
         if (f.exists()) {
             f.delete();
         }
         final long ident = Binder.clearCallingIdentity();
         RuntimeException e = null;
         try {
-            mImageWallpaperPending = false;
+            wallpaper.imageWallpaperPending = false;
+            if (userId != mCurrentUserId) return;
             if (bindWallpaperComponentLocked(defaultFailed
-                    ? mImageWallpaperComponent : null, true, false)) {
+                    ? wallpaper.imageWallpaperComponent
+                    : null, true, false, wallpaper)) {
                 return;
             }
         } catch (IllegalArgumentException e1) {
@@ -383,29 +502,35 @@
         // let's not let it crash the system and just live with no
         // wallpaper.
         Slog.e(TAG, "Default wallpaper component not found!", e);
-        clearWallpaperComponentLocked();
+        clearWallpaperComponentLocked(wallpaper);
     }
 
     public void setDimensionHints(int width, int height) throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
 
+        int userId = UserId.getCallingUserId();
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+        }
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
 
         synchronized (mLock) {
-            if (width != mWidth || height != mHeight) {
-                mWidth = width;
-                mHeight = height;
-                saveSettingsLocked();
-                if (mWallpaperConnection != null) {
-                    if (mWallpaperConnection.mEngine != null) {
+            if (width != wallpaper.width || height != wallpaper.height) {
+                wallpaper.width = width;
+                wallpaper.height = height;
+                saveSettingsLocked(wallpaper);
+                if (mCurrentUserId != userId) return; // Don't change the properties now
+                if (wallpaper.connection != null) {
+                    if (wallpaper.connection.mEngine != null) {
                         try {
-                            mWallpaperConnection.mEngine.setDesiredSize(
+                            wallpaper.connection.mEngine.setDesiredSize(
                                     width, height);
                         } catch (RemoteException e) {
                         }
-                        notifyCallbacksLocked();
+                        notifyCallbacksLocked(wallpaper);
                     }
                 }
             }
@@ -414,26 +539,38 @@
 
     public int getWidthHint() throws RemoteException {
         synchronized (mLock) {
-            return mWidth;
+            WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+            return wallpaper.width;
         }
     }
 
     public int getHeightHint() throws RemoteException {
         synchronized (mLock) {
-            return mHeight;
+            WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+            return wallpaper.height;
         }
     }
 
     public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
             Bundle outParams) {
         synchronized (mLock) {
+            // This returns the current user's wallpaper, if called by a system service. Else it
+            // returns the wallpaper for the calling user.
+            int callingUid = Binder.getCallingUid();
+            int wallpaperUserId = 0;
+            if (callingUid == android.os.Process.SYSTEM_UID) {
+                wallpaperUserId = mCurrentUserId;
+            } else {
+                wallpaperUserId = UserId.getUserId(callingUid);
+            }
+            WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
             try {
                 if (outParams != null) {
-                    outParams.putInt("width", mWidth);
-                    outParams.putInt("height", mHeight);
+                    outParams.putInt("width", wallpaper.width);
+                    outParams.putInt("height", wallpaper.height);
                 }
-                mCallbacks.register(cb);
-                File f = WALLPAPER_FILE;
+                wallpaper.callbacks.register(cb);
+                File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER);
                 if (!f.exists()) {
                     return null;
                 }
@@ -447,24 +584,30 @@
     }
 
     public WallpaperInfo getWallpaperInfo() {
+        int userId = UserId.getCallingUserId();
         synchronized (mLock) {
-            if (mWallpaperConnection != null) {
-                return mWallpaperConnection.mInfo;
+            WallpaperData wallpaper = mWallpaperMap.get(userId);
+            if (wallpaper.connection != null) {
+                return wallpaper.connection.mInfo;
             }
             return null;
         }
     }
-    
+
     public ParcelFileDescriptor setWallpaper(String name) {
         if (DEBUG) Slog.v(TAG, "setWallpaper");
-        
+        int userId = UserId.getCallingUserId();
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+        }
         checkPermission(android.Manifest.permission.SET_WALLPAPER);
         synchronized (mLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
+                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
                 if (pfd != null) {
-                    mImageWallpaperPending = true;
+                    wallpaper.imageWallpaperPending = true;
                 }
                 return pfd;
             } finally {
@@ -473,19 +616,20 @@
         }
     }
 
-    ParcelFileDescriptor updateWallpaperBitmapLocked(String name) {
+    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) {
         if (name == null) name = "";
         try {
-            if (!WALLPAPER_DIR.exists()) {
-                WALLPAPER_DIR.mkdir();
+            File dir = getWallpaperDir(wallpaper.userId);
+            if (!dir.exists()) {
+                dir.mkdir();
                 FileUtils.setPermissions(
-                        WALLPAPER_DIR.getPath(),
+                        dir.getPath(),
                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                         -1, -1);
             }
-            ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(dir, WALLPAPER),
                     MODE_CREATE|MODE_READ_WRITE);
-            mName = name;
+            wallpaper.name = name;
             return fd;
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "Error setting wallpaper", e);
@@ -495,31 +639,36 @@
 
     public void setWallpaperComponent(ComponentName name) {
         if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
+        int userId = UserId.getCallingUserId();
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+        }
         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
         synchronized (mLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mImageWallpaperPending = false;
-                bindWallpaperComponentLocked(name, false, true);
+                wallpaper.imageWallpaperPending = false;
+                bindWallpaperComponentLocked(name, false, true, wallpaper);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
     }
     
-    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser) {
+    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
+            boolean fromUser, WallpaperData wallpaper) {
         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
-        
         // Has the component changed?
         if (!force) {
-            if (mWallpaperConnection != null) {
-                if (mWallpaperComponent == null) {
+            if (wallpaper.connection != null) {
+                if (wallpaper.wallpaperComponent == null) {
                     if (componentName == null) {
                         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
                         // Still using default wallpaper.
                         return true;
                     }
-                } else if (mWallpaperComponent.equals(componentName)) {
+                } else if (wallpaper.wallpaperComponent.equals(componentName)) {
                     // Changing to same wallpaper.
                     if (DEBUG) Slog.v(TAG, "same wallpaper");
                     return true;
@@ -538,7 +687,7 @@
                 }
                 if (componentName == null) {
                     // Fall back to static image wallpaper
-                    componentName = mImageWallpaperComponent;
+                    componentName = wallpaper.imageWallpaperComponent;
                     //clearWallpaperComponentLocked();
                     //return;
                     if (DEBUG) Slog.v(TAG, "Using image wallpaper");
@@ -560,7 +709,7 @@
             WallpaperInfo wi = null;
             
             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
-            if (componentName != null && !componentName.equals(mImageWallpaperComponent)) {
+            if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) {
                 // Make sure the selected service is actually a wallpaper service.
                 List<ResolveInfo> ris = mContext.getPackageManager()
                         .queryIntentServices(intent, PackageManager.GET_META_DATA);
@@ -599,8 +748,13 @@
             
             // Bind the service!
             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
-            WallpaperConnection newConn = new WallpaperConnection(wi);
+            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
             intent.setComponent(componentName);
+            int serviceUserId = wallpaper.userId;
+            // Because the image wallpaper is running in the system ui
+            if (componentName.equals(wallpaper.imageWallpaperComponent)) {
+                serviceUserId = 0;
+            }
             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                     com.android.internal.R.string.wallpaper_binding_label);
             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
@@ -608,8 +762,7 @@
                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
                             0));
-            if (!mContext.bindService(intent, newConn,
-                    Context.BIND_AUTO_CREATE)) {
+            if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
                 String msg = "Unable to bind service: "
                         + componentName;
                 if (fromUser) {
@@ -618,18 +771,22 @@
                 Slog.w(TAG, msg);
                 return false;
             }
-            
-            clearWallpaperComponentLocked();
-            mWallpaperComponent = componentName;
-            mWallpaperConnection = newConn;
-            mLastDiedTime = SystemClock.uptimeMillis();
+            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
+                detachWallpaperLocked(mLastWallpaper);
+            }
+            wallpaper.wallpaperComponent = componentName;
+            wallpaper.connection = newConn;
+            wallpaper.lastDiedTime = SystemClock.uptimeMillis();
             try {
-                if (DEBUG) Slog.v(TAG, "Adding window token: " + newConn.mToken);
-                mIWindowManager.addWindowToken(newConn.mToken,
-                        WindowManager.LayoutParams.TYPE_WALLPAPER);
+                if (wallpaper.userId == mCurrentUserId) {
+                    if (DEBUG)
+                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
+                    mIWindowManager.addWindowToken(newConn.mToken,
+                            WindowManager.LayoutParams.TYPE_WALLPAPER);
+                    mLastWallpaper = wallpaper;
+                }
             } catch (RemoteException e) {
             }
-            
         } catch (PackageManager.NameNotFoundException e) {
             String msg = "Unknown component " + componentName;
             if (fromUser) {
@@ -640,54 +797,58 @@
         }
         return true;
     }
-    
-    void clearWallpaperComponentLocked() {
-        mWallpaperComponent = null;
-        if (mWallpaperConnection != null) {
-            if (mWallpaperConnection.mEngine != null) {
+
+    void detachWallpaperLocked(WallpaperData wallpaper) {
+        if (wallpaper.connection != null) {
+            if (wallpaper.connection.mEngine != null) {
                 try {
-                    mWallpaperConnection.mEngine.destroy();
+                    wallpaper.connection.mEngine.destroy();
                 } catch (RemoteException e) {
                 }
             }
-            mContext.unbindService(mWallpaperConnection);
+            mContext.unbindService(wallpaper.connection);
             try {
-                if (DEBUG) Slog.v(TAG, "Removing window token: "
-                        + mWallpaperConnection.mToken);
-                mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
+                if (DEBUG)
+                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
+                mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
             } catch (RemoteException e) {
             }
-            mWallpaperConnection.mService = null;
-            mWallpaperConnection.mEngine = null;
-            mWallpaperConnection = null;
+            wallpaper.connection.mService = null;
+            wallpaper.connection.mEngine = null;
+            wallpaper.connection = null;
         }
     }
-    
-    void attachServiceLocked(WallpaperConnection conn) {
+
+    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
+        wallpaper.wallpaperComponent = null;
+        detachWallpaperLocked(wallpaper);
+    }
+
+    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
         try {
             conn.mService.attach(conn, conn.mToken,
                     WindowManager.LayoutParams.TYPE_WALLPAPER, false,
-                    mWidth, mHeight);
+                    wallpaper.width, wallpaper.height);
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
-            if (!mWallpaperUpdating) {
-                bindWallpaperComponentLocked(null, false, false);
+            if (!wallpaper.wallpaperUpdating) {
+                bindWallpaperComponentLocked(null, false, false, wallpaper);
             }
         }
     }
-    
-    private void notifyCallbacksLocked() {
-        final int n = mCallbacks.beginBroadcast();
+
+    private void notifyCallbacksLocked(WallpaperData wallpaper) {
+        final int n = wallpaper.callbacks.beginBroadcast();
         for (int i = 0; i < n; i++) {
             try {
-                mCallbacks.getBroadcastItem(i).onWallpaperChanged();
+                wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
             } catch (RemoteException e) {
 
                 // The RemoteCallbackList will take care of removing
                 // the dead object for us.
             }
         }
-        mCallbacks.finishBroadcast();
+        wallpaper.callbacks.finishBroadcast();
         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
         mContext.sendBroadcast(intent);
     }
@@ -699,13 +860,13 @@
         }
     }
 
-    private static JournaledFile makeJournaledFile() {
-        final String base = "/data/system/wallpaper_info.xml";
+    private static JournaledFile makeJournaledFile(int userId) {
+        final String base = "/data/system/users/" + userId + "/" + WALLPAPER_INFO;
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
-    private void saveSettingsLocked() {
-        JournaledFile journal = makeJournaledFile();
+    private void saveSettingsLocked(WallpaperData wallpaper) {
+        JournaledFile journal = makeJournaledFile(wallpaper.userId);
         FileOutputStream stream = null;
         try {
             stream = new FileOutputStream(journal.chooseForWrite(), false);
@@ -714,13 +875,13 @@
             out.startDocument(null, true);
 
             out.startTag(null, "wp");
-            out.attribute(null, "width", Integer.toString(mWidth));
-            out.attribute(null, "height", Integer.toString(mHeight));
-            out.attribute(null, "name", mName);
-            if (mWallpaperComponent != null &&
-                    !mWallpaperComponent.equals(mImageWallpaperComponent)) {
+            out.attribute(null, "width", Integer.toString(wallpaper.width));
+            out.attribute(null, "height", Integer.toString(wallpaper.height));
+            out.attribute(null, "name", wallpaper.name);
+            if (wallpaper.wallpaperComponent != null
+                    && !wallpaper.wallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
                 out.attribute(null, "component",
-                        mWallpaperComponent.flattenToShortString());
+                        wallpaper.wallpaperComponent.flattenToShortString());
             }
             out.endTag(null, "wp");
 
@@ -739,12 +900,34 @@
         }
     }
 
-    private void loadSettingsLocked() {
+    private void migrateFromOld() {
+        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
+        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
+        if (oldWallpaper.exists()) {
+            File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
+            oldWallpaper.renameTo(newWallpaper);
+        }
+        if (oldInfo.exists()) {
+            File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
+            oldInfo.renameTo(newInfo);
+        }
+    }
+
+    private void loadSettingsLocked(int userId) {
         if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
         
-        JournaledFile journal = makeJournaledFile();
+        JournaledFile journal = makeJournaledFile(userId);
         FileInputStream stream = null;
         File file = journal.chooseForRead();
+        if (!file.exists()) {
+            // This should only happen one time, when upgrading from a legacy system
+            migrateFromOld();
+        }
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            wallpaper = new WallpaperData(userId);
+            mWallpaperMap.put(userId, wallpaper);
+        }
         boolean success = false;
         try {
             stream = new FileInputStream(file);
@@ -757,23 +940,26 @@
                 if (type == XmlPullParser.START_TAG) {
                     String tag = parser.getName();
                     if ("wp".equals(tag)) {
-                        mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
-                        mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
-                        mName = parser.getAttributeValue(null, "name");
+                        wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
+                        wallpaper.height = Integer.parseInt(parser
+                                .getAttributeValue(null, "height"));
+                        wallpaper.name = parser.getAttributeValue(null, "name");
                         String comp = parser.getAttributeValue(null, "component");
-                        mNextWallpaperComponent = comp != null
+                        wallpaper.nextWallpaperComponent = comp != null
                                 ? ComponentName.unflattenFromString(comp)
                                 : null;
-                        if (mNextWallpaperComponent == null ||
-                                "android".equals(mNextWallpaperComponent.getPackageName())) {
-                            mNextWallpaperComponent = mImageWallpaperComponent;
+                        if (wallpaper.nextWallpaperComponent == null
+                                || "android".equals(wallpaper.nextWallpaperComponent
+                                        .getPackageName())) {
+                            wallpaper.nextWallpaperComponent = wallpaper.imageWallpaperComponent;
                         }
                           
                         if (DEBUG) {
-                            Slog.v(TAG, "mWidth:" + mWidth);
-                            Slog.v(TAG, "mHeight:" + mHeight);
-                            Slog.v(TAG, "mName:" + mName);
-                            Slog.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent);
+                            Slog.v(TAG, "mWidth:" + wallpaper.width);
+                            Slog.v(TAG, "mHeight:" + wallpaper.height);
+                            Slog.v(TAG, "mName:" + wallpaper.name);
+                            Slog.v(TAG, "mNextWallpaperComponent:"
+                                    + wallpaper.nextWallpaperComponent);
                         }
                     }
                 }
@@ -799,70 +985,75 @@
         }
 
         if (!success) {
-            mWidth = -1;
-            mHeight = -1;
-            mName = "";
+            wallpaper.width = -1;
+            wallpaper.height = -1;
+            wallpaper.name = "";
         }
 
         // We always want to have some reasonable width hint.
         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
         Display d = wm.getDefaultDisplay();
         int baseSize = d.getMaximumSizeDimension();
-        if (mWidth < baseSize) {
-            mWidth = baseSize;
+        if (wallpaper.width < baseSize) {
+            wallpaper.width = baseSize;
         }
-        if (mHeight < baseSize) {
-            mHeight = baseSize;
+        if (wallpaper.height < baseSize) {
+            wallpaper.height = baseSize;
         }
     }
 
     // Called by SystemBackupAgent after files are restored to disk.
     void settingsRestored() {
+        // TODO: If necessary, make it work for secondary users as well. This currently assumes
+        // restores only to the primary user
         if (DEBUG) Slog.v(TAG, "settingsRestored");
-
+        WallpaperData wallpaper = null;
         boolean success = false;
         synchronized (mLock) {
-            loadSettingsLocked();
-            if (mNextWallpaperComponent != null && 
-                    !mNextWallpaperComponent.equals(mImageWallpaperComponent)) {
-                if (!bindWallpaperComponentLocked(mNextWallpaperComponent, false, false)) {
+            loadSettingsLocked(0);
+            wallpaper = mWallpaperMap.get(0);
+            if (wallpaper.nextWallpaperComponent != null
+                    && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
+                if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
+                        wallpaper)) {
                     // No such live wallpaper or other failure; fall back to the default
                     // live wallpaper (since the profile being restored indicated that the
                     // user had selected a live rather than static one).
-                    bindWallpaperComponentLocked(null, false, false);
+                    bindWallpaperComponentLocked(null, false, false, wallpaper);
                 }
                 success = true;
             } else {
                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
                 // use the default.
-                if ("".equals(mName)) {
+                if ("".equals(wallpaper.name)) {
                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
                     success = true;
                 } else {
                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
-                    success = restoreNamedResourceLocked();
+                    success = restoreNamedResourceLocked(wallpaper);
                 }
                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
                 if (success) {
-                    bindWallpaperComponentLocked(mNextWallpaperComponent, false, false);
+                    bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
+                            wallpaper);
                 }
             }
         }
 
         if (!success) {
-            Slog.e(TAG, "Failed to restore wallpaper: '" + mName + "'");
-            mName = "";
-            WALLPAPER_FILE.delete();
+            Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
+            wallpaper.name = "";
+            getWallpaperDir(0).delete();
         }
 
         synchronized (mLock) {
-            saveSettingsLocked();
+            saveSettingsLocked(wallpaper);
         }
     }
 
-    boolean restoreNamedResourceLocked() {
-        if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) {
-            String resName = mName.substring(4);
+    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
+        if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
+            String resName = wallpaper.name.substring(4);
 
             String pkg = null;
             int colon = resName.indexOf(':');
@@ -896,10 +1087,10 @@
                     }
 
                     res = r.openRawResource(resId);
-                    if (WALLPAPER_FILE.exists()) {
-                        WALLPAPER_FILE.delete();
+                    if (wallpaper.wallpaperFile.exists()) {
+                        wallpaper.wallpaperFile.delete();
                     }
-                    fos = new FileOutputStream(WALLPAPER_FILE);
+                    fos = new FileOutputStream(wallpaper.wallpaperFile);
 
                     byte[] buffer = new byte[32768];
                     int amt;
@@ -933,7 +1124,7 @@
         }
         return false;
     }
-    
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -947,20 +1138,35 @@
 
         synchronized (mLock) {
             pw.println("Current Wallpaper Service state:");
-            pw.print("  mWidth="); pw.print(mWidth);
-                    pw.print(" mHeight="); pw.println(mHeight);
-            pw.print("  mName="); pw.println(mName);
-            pw.print("  mWallpaperComponent="); pw.println(mWallpaperComponent);
-            if (mWallpaperConnection != null) {
-                WallpaperConnection conn = mWallpaperConnection;
-                pw.print("  Wallpaper connection ");
-                        pw.print(conn); pw.println(":");
-                pw.print("    mInfo.component="); pw.println(conn.mInfo.getComponent());
-                pw.print("    mToken="); pw.println(conn.mToken);
-                pw.print("    mService="); pw.println(conn.mService);
-                pw.print("    mEngine="); pw.println(conn.mEngine);
-                pw.print("    mLastDiedTime=");
-                        pw.println(mLastDiedTime - SystemClock.uptimeMillis());
+            for (int i = 0; i < mWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                pw.println(" User " + wallpaper.userId + ":");
+                pw.print("  mWidth=");
+                pw.print(wallpaper.width);
+                pw.print(" mHeight=");
+                pw.println(wallpaper.height);
+                pw.print("  mName=");
+                pw.println(wallpaper.name);
+                pw.print("  mWallpaperComponent=");
+                pw.println(wallpaper.wallpaperComponent);
+                if (wallpaper.connection != null) {
+                    WallpaperConnection conn = wallpaper.connection;
+                    pw.print("  Wallpaper connection ");
+                    pw.print(conn);
+                    pw.println(":");
+                    if (conn.mInfo != null) {
+                        pw.print("    mInfo.component=");
+                        pw.println(conn.mInfo.getComponent());
+                    }
+                    pw.print("    mToken=");
+                    pw.println(conn.mToken);
+                    pw.print("    mService=");
+                    pw.println(conn.mService);
+                    pw.print("    mEngine=");
+                    pw.println(conn.mEngine);
+                    pw.print("    mLastDiedTime=");
+                    pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4e1e9d9..7bf26cc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -49,6 +49,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
+import android.app.WallpaperManager;
 import android.app.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -1368,24 +1369,6 @@
      */
     final ArrayList mCancelledThumbnails = new ArrayList();
 
-    /**
-     * All of the currently running global content providers.  Keys are a
-     * string containing the provider name and values are a
-     * ContentProviderRecord object containing the data about it.  Note
-     * that a single provider may be published under multiple names, so
-     * there may be multiple entries here for a single one in mProvidersByClass.
-     */
-    final HashMap<String, ContentProviderRecord> mProvidersByName
-            = new HashMap<String, ContentProviderRecord>();
-
-    /**
-     * All of the currently running global content providers.  Keys are a
-     * string containing the provider's implementation class and values are a
-     * ContentProviderRecord object containing the data about it.
-     */
-    final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
-            = new HashMap<ComponentName, ContentProviderRecord>();
-
     final ProviderMap mProviderMap = new ProviderMap();
 
     /**
@@ -4445,7 +4428,7 @@
         }
 
         ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
-        for (ContentProviderRecord provider : mProvidersByClass.values()) {
+        for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(-1).values()) {
             if (provider.info.packageName.equals(name)
                     && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
                 if (!doit) {
@@ -11372,18 +11355,18 @@
     }
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
-            String resolvedType, int callingPid, int callingUid) {
+            String resolvedType, int callingPid, int callingUid, int userId) {
         ServiceRecord r = null;
         if (DEBUG_SERVICE)
             Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
-                    + " origCallingUid=" + callingUid);
+                    + " callingUid=" + callingUid);
 
         if (service.getComponent() != null) {
-            r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
+            r = mServiceMap.getServiceByName(service.getComponent(), userId);
         }
         if (r == null) {
             Intent.FilterComparison filter = new Intent.FilterComparison(service);
-            r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
+            r = mServiceMap.getServiceByIntent(filter, userId);
         }
         if (r == null) {
             try {
@@ -11397,13 +11380,12 @@
                           ": not found");
                     return null;
                 }
-                if (Binder.getOrigCallingUser() > 0) {
-                    sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
-                            Binder.getOrigCallingUser());
+                if (userId > 0) {
+                    sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo, userId);
                 }
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
+                r = mServiceMap.getServiceByName(name, userId);
                 if (r == null) {
                     Intent.FilterComparison filter = new Intent.FilterComparison(
                             service.cloneFilter());
@@ -11967,7 +11949,7 @@
 
             ServiceLookupResult res =
                 retrieveServiceLocked(service, resolvedType,
-                        callingPid, callingUid);
+                        callingPid, callingUid, UserId.getUserId(callingUid));
             if (res == null) {
                 return null;
             }
@@ -12217,13 +12199,15 @@
     
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
-            IServiceConnection connection, int flags) {
+            IServiceConnection connection, int flags, int userId) {
         enforceNotIsolatedCaller("bindService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        checkValidCaller(Binder.getCallingUid(), userId);
+
         synchronized(this) {
             if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
                     + " type=" + resolvedType + " conn=" + connection.asBinder()
@@ -12273,7 +12257,7 @@
             
             ServiceLookupResult res =
                 retrieveServiceLocked(service, resolvedType,
-                        Binder.getCallingPid(), Binder.getOrigCallingUid());
+                        Binder.getCallingPid(), Binder.getCallingUid(), userId);
             if (res == null) {
                 return 0;
             }
@@ -15287,6 +15271,25 @@
 
     private int mCurrentUserId;
     private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+    private ArrayList<UserListener> mUserListeners = new ArrayList<UserListener>(3);
+
+    public interface UserListener {
+        public void onUserChanged(int userId);
+
+        public void onUserAdded(int userId);
+
+        public void onUserRemoved(int userId);
+
+        public void onUserLoggedOut(int userId);
+    }
+
+    public void addUserListener(UserListener listener) {
+        synchronized (this) {
+            if (!mUserListeners.contains(listener)) {
+                mUserListeners.add(listener);
+            }
+        }
+    }
 
     public boolean switchUser(int userId) {
         final int callingUid = Binder.getCallingUid();
@@ -15297,6 +15300,8 @@
         if (mCurrentUserId == userId)
             return true;
 
+        ArrayList<UserListener> listeners;
+
         synchronized (this) {
             // Check if user is already logged in, otherwise check if user exists first before
             // adding to the list of logged in users.
@@ -15312,6 +15317,12 @@
             if (!haveActivities) {
                 startHomeActivityLocked(userId);
             }
+
+            listeners = (ArrayList<UserListener>) mUserListeners.clone();
+        }
+        // Inform the listeners
+        for (UserListener listener : listeners) {
+            listener.onUserChanged(userId);
         }
         return true;
     }
@@ -15331,6 +15342,12 @@
         return false;
     }
 
+    private void checkValidCaller(int uid, int userId) {
+        if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
+
+        throw new SecurityException("Caller uid=" + uid
+                + " is not privileged to communicate with user=" + userId);
+    }
 
     private int applyUserId(int uid, int userId) {
         return UserId.getUid(userId, uid);
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index 06353f6..2021e0d 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -158,7 +158,7 @@
         }
     }
 
-    private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+    HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
         final int userId = optionalUserId >= 0
                 ? optionalUserId : Binder.getOrigCallingUser();
         final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 0b4fc51..674c0b7 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -333,6 +333,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         throw new UnsupportedOperationException();