Show the icon of the personal space.

In an intent disambiguation dialog from a managed profile,
when the intent can be forwarded to the personal space:
show the icon of the parent next to "Personal apps".
And put it at the bottom of the dialog.

Change-Id: I523222aac5dde9653e784eb26cf23cdaf018b86c
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 22b36f3..13b922c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -48,11 +48,13 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
@@ -1490,6 +1492,15 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public Bitmap getUserIcon(int userId) {
+        UserManager um = UserManager.get(mContext);
+        return um.getUserIcon(userId);
+    }
+
     private final ContextImpl mContext;
     private final IPackageManager mPM;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e757b56..d11698c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Environment;
@@ -3618,4 +3619,8 @@
      */
     public abstract void addCrossProfileIntentsForPackage(String packageName,
             int sourceUserId, int targetUserId);
+    /**
+     * @hide
+     */
+    public abstract Bitmap getUserIcon(int userId);
 }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 1ff41c0..1f9d60c 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,8 +19,11 @@
 import android.content.ComponentName;
 import android.content.IntentFilter;
 import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Printer;
 import android.util.Slog;
@@ -127,6 +130,18 @@
     public String resolvePackageName;
 
     /**
+     * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user.
+     * @hide
+     */
+    public int targetUserId;
+
+    /**
+     * If true, then loadIcon will return the icon of the target user.
+     * @hide
+     */
+    public boolean showTargetUserIcon;
+
+    /**
      * @hide Target comes from system process?
      */
     public boolean system;
@@ -202,6 +217,10 @@
                 return dr;
             }
         }
+        if (showTargetUserIcon) {
+            Bitmap bm = pm.getUserIcon(targetUserId);
+            return new BitmapDrawable(bm);
+        }
         return ci.loadIcon(pm);
     }
     
@@ -215,7 +234,9 @@
     public final int getIconResource() {
         if (icon != 0) return icon;
         final ComponentInfo ci = getComponentInfo();
-        if (ci != null) return ci.getIconResource();
+        if (ci != null && !showTargetUserIcon) {
+            return ci.getIconResource();
+        }
         return 0;
     }
 
@@ -250,6 +271,7 @@
     }
     
     public ResolveInfo() {
+        targetUserId = UserHandle.USER_CURRENT;
     }
 
     public ResolveInfo(ResolveInfo orig) {
@@ -266,6 +288,7 @@
         icon = orig.icon;
         resolvePackageName = orig.resolvePackageName;
         system = orig.system;
+        targetUserId = orig.targetUserId;
     }
 
     public String toString() {
@@ -285,6 +308,13 @@
         }
         sb.append(" m=0x");
         sb.append(Integer.toHexString(match));
+        if (targetUserId != UserHandle.USER_CURRENT) {
+            sb.append(" targetUserId=");
+            sb.append(targetUserId);
+        }
+        if (showTargetUserIcon) {
+            sb.append(" [showTargetUserIcon]");
+        }
         sb.append('}');
         return sb.toString();
     }
@@ -320,6 +350,8 @@
         TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
         dest.writeInt(icon);
         dest.writeString(resolvePackageName);
+        dest.writeInt(targetUserId);
+        dest.writeInt(showTargetUserIcon ? 1 : 0);
         dest.writeInt(system ? 1 : 0);
     }
 
@@ -363,6 +395,8 @@
                 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         icon = source.readInt();
         resolvePackageName = source.readString();
+        targetUserId = source.readInt();
+        showTargetUserIcon = source.readInt() != 0;
         system = source.readInt() != 0;
     }
     
@@ -374,6 +408,13 @@
         }
 
         public final int compare(ResolveInfo a, ResolveInfo b) {
+            // We want to put the one targeted to another user at the end of the dialog.
+            if (a.targetUserId != UserHandle.USER_CURRENT) {
+                return 1;
+            }
+            if (b.targetUserId != UserHandle.USER_CURRENT) {
+                return -1;
+            }
             CharSequence  sa = a.loadLabel(mPM);
             if (sa == null) sa = a.activityInfo.name;
             CharSequence  sb = b.loadLabel(mPM);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 41fe327..f40ca3a 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3577,9 +3577,11 @@
 
     private ResolveInfo createForwardingResolveInfo(IntentFilter filter,
             int sourceUserId, int targetUserId) {
+        ResolveInfo forwardingResolveInfo = new ResolveInfo();
         String className;
         if (targetUserId == UserHandle.USER_OWNER) {
             className = FORWARD_INTENT_TO_USER_OWNER;
+            forwardingResolveInfo.showTargetUserIcon = true;
         } else {
             className = FORWARD_INTENT_TO_MANAGED_PROFILE;
         }
@@ -3587,13 +3589,13 @@
                 mAndroidApplication.packageName, className);
         ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
                 sourceUserId);
-        ResolveInfo forwardingResolveInfo = new ResolveInfo();
         forwardingResolveInfo.activityInfo = forwardingActivityInfo;
         forwardingResolveInfo.priority = 0;
         forwardingResolveInfo.preferredOrder = 0;
         forwardingResolveInfo.match = 0;
         forwardingResolveInfo.isDefault = true;
         forwardingResolveInfo.filter = filter;
+        forwardingResolveInfo.targetUserId = targetUserId;
         return forwardingResolveInfo;
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1839259..a0cb098 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -412,13 +412,17 @@
 
     @Override
     public Bitmap getUserIcon(int userId) {
-        checkManageUsersPermission("read users");
         synchronized (mPackagesLock) {
             UserInfo info = mUsers.get(userId);
             if (info == null || info.partial) {
                 Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
                 return null;
             }
+            int callingGroupId = mUsers.get(UserHandle.getCallingUserId()).profileGroupId;
+            if (callingGroupId == UserInfo.NO_PROFILE_GROUP_ID
+                    || callingGroupId != info.profileGroupId) {
+                checkManageUsersPermission("get the icon of a user who is not related");
+            }
             if (info.iconPath == null) {
                 return null;
             }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 2b884ae..1d10729 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -44,6 +44,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.UserHandle;
@@ -733,4 +734,11 @@
             int targetUserId) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    public Bitmap getUserIcon(int userId) {
+        throw new UnsupportedOperationException();
+    }
 }