Multi-user - wallpaper service

- Allow each user to have their own wallpaper (live or static).
- Migrate old wallpaper on upgrade.
- Update SystemBackupAgent to backup/restore from primary user's
  new wallpaper directory.

Reduce dependency on Binder.getOrigCallingUser() by passing the
userId for bindService.

Change-Id: I19c8c3296d3d2efa7f28f951d4b84407489e2166
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 c1e28b0..f6d2612 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);