Add an icon to the user information.

Store the icon in the user folder under /data/system,
similar to how the wallpaper is stored.

Change-Id: Id8ccb55b9e2ba7b4c557505a7f69f04eca1518cf
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 191a696..9a50a41 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1206,7 +1206,8 @@
             return mPM.getUsers();
         } catch (RemoteException re) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>();
-            UserInfo primary = new UserInfo(0, "Root!", UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+            UserInfo primary = new UserInfo(0, "Root!", null,
+                    UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
             users.add(primary);
             return users;
         }
@@ -1240,9 +1241,9 @@
      * @hide
      */
     @Override
-    public void updateUserName(int id, String name) {
+    public void setUserName(int id, String name) {
         try {
-            mPM.updateUserName(id, name);
+            mPM.setUserName(id, name);
         } catch (RemoteException re) {
         }
     }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 70c0c48..6f00abd 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -41,6 +41,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
 import android.content.IntentSender;
 
 /**
@@ -359,7 +360,8 @@
 
     UserInfo createUser(in String name, int flags);
     boolean removeUser(int userId);
-    void updateUserName(int userId, String name);
+    void setUserName(int userId, String name);
+    ParcelFileDescriptor setUserIcon(int userId);
 
     void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
             int flags, in String installerPackageName, in Uri verificationURI,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6de69b0..f9f7e2d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2646,7 +2646,7 @@
      * @param name the new name for the user
      * @hide
      */
-    public abstract void updateUserName(int id, String name);
+    public abstract void setUserName(int id, String name);
 
     /**
      * Changes the user's properties specified by the flags.
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index ba5331c..68a7257 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -43,12 +43,18 @@
 
     public int id;
     public String name;
+    public String iconPath;
     public int flags;
 
     public UserInfo(int id, String name, int flags) {
+        this(id, name, null, flags);
+    }
+
+    public UserInfo(int id, String name, String iconPath, int flags) {
         this.id = id;
         this.name = name;
         this.flags = flags;
+        this.iconPath = iconPath;
     }
 
     public boolean isPrimary() {
@@ -68,6 +74,7 @@
 
     public UserInfo(UserInfo orig) {
         name = orig.name;
+        iconPath = orig.iconPath;
         id = orig.id;
         flags = orig.flags;
     }
@@ -84,6 +91,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeInt(id);
         dest.writeString(name);
+        dest.writeString(iconPath);
         dest.writeInt(flags);
     }
 
@@ -100,6 +108,7 @@
     private UserInfo(Parcel source) {
         id = source.readInt();
         name = source.readString();
+        iconPath = source.readString();
         flags = source.readInt();
     }
 }
diff --git a/core/res/res/layout/global_actions_item.xml b/core/res/res/layout/global_actions_item.xml
index 694301e..009f37b 100644
--- a/core/res/res/layout/global_actions_item.xml
+++ b/core/res/res/layout/global_actions_item.xml
@@ -27,11 +27,11 @@
     android:paddingBottom="6dip"
     >
     <ImageView android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
+        android:layout_width="56dp"
+        android:layout_height="56dp"
         android:layout_gravity="center"
         android:layout_marginRight="16dip"
-        />
+        android:scaleType="center"/>
 
     <LinearLayout
         android:orientation="vertical"
diff --git a/core/res/res/layout/preference_holo.xml b/core/res/res/layout/preference_holo.xml
index e574219..8402b36 100644
--- a/core/res/res/layout/preference_holo.xml
+++ b/core/res/res/layout/preference_holo.xml
@@ -33,11 +33,12 @@
         android:orientation="horizontal">
         <ImageView
             android:id="@+android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
             android:layout_gravity="center"
-            android:minWidth="48dp"
-            android:paddingRight="@dimen/preference_item_padding_inner" />
+            android:scaleType="centerCrop"
+            android:layout_marginRight="@dimen/preference_item_padding_inner"
+             />
     </LinearLayout>
 
     <RelativeLayout
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 5094df18..381e65b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -19,6 +19,7 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.TypedValue;
 
 import java.io.BufferedInputStream;
@@ -303,6 +304,7 @@
             /*  do nothing.
                 If the exception happened on open, bm will be null.
             */
+            Log.e("BitmapFactory", "Unable to decode stream: " + e);
         } finally {
             if (stream != null) {
                 try {
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f09c010..6eca3b6 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -246,6 +246,7 @@
                 throw new IllegalStateException(e);
             }
         }
+
     };
 
     public DefaultContainerService() {
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index fc187ce..e9b8267 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -29,6 +29,8 @@
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ScaleDrawable;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.os.Handler;
@@ -43,6 +45,7 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -52,6 +55,7 @@
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
 import android.widget.TextView;
 
 import java.util.ArrayList;
@@ -243,8 +247,10 @@
             for (final UserInfo user : users) {
                 boolean isCurrentUser = currentUser == null
                         ? user.id == 0 : (currentUser.id == user.id);
+                Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
+                        : null;
                 SinglePressAction switchToUser = new SinglePressAction(
-                        com.android.internal.R.drawable.ic_menu_cc,
+                        com.android.internal.R.drawable.ic_menu_cc, icon,
                         (user.name != null ? user.name : "Primary")
                         + (isCurrentUser ? " \u2714" : "")) {
                     public void onPress() {
@@ -439,6 +445,7 @@
      */
     private static abstract class SinglePressAction implements Action {
         private final int mIconResId;
+        private final Drawable mIcon;
         private final int mMessageResId;
         private final CharSequence mMessage;
 
@@ -446,13 +453,23 @@
             mIconResId = iconResId;
             mMessageResId = messageResId;
             mMessage = null;
+            mIcon = null;
+        }
+
+        protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
+            mIconResId = iconResId;
+            mMessageResId = 0;
+            mMessage = message;
+            mIcon = icon;
         }
 
         protected SinglePressAction(int iconResId, CharSequence message) {
             mIconResId = iconResId;
             mMessageResId = 0;
             mMessage = message;
+            mIcon = null;
         }
+
         public boolean isEnabled() {
             return true;
         }
@@ -471,8 +488,12 @@
             TextView messageView = (TextView) v.findViewById(R.id.message);
 
             v.findViewById(R.id.status).setVisibility(View.GONE);
-
-            icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
+            if (mIcon != null) {
+                icon.setImageDrawable(mIcon);
+                icon.setScaleType(ScaleType.CENTER_CROP);
+            } else if (mIconResId != 0) {
+                icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
+            }
             if (mMessage != null) {
                 messageView.setText(mMessage);
             } else {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index e71556e..fee55f3 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -9388,9 +9388,15 @@
     }
 
     @Override
-    public void updateUserName(int userId, String name) {
+    public void setUserName(int userId, String name) {
         enforceSystemOrRoot("Only the system can rename users");
-        sUserManager.updateUserName(userId, name);
+        sUserManager.setUserName(userId, name);
+    }
+
+    @Override
+    public ParcelFileDescriptor setUserIcon(int userId) {
+        enforceSystemOrRoot("Only the system can update users");
+        return sUserManager.setUserIcon(userId);
     }
 
     @Override
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 4e9e666..738ab08 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -16,6 +16,9 @@
 
 package com.android.server.pm;
 
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
@@ -24,6 +27,7 @@
 import android.content.pm.UserInfo;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.os.UserId;
 import android.util.Log;
@@ -34,6 +38,7 @@
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -44,10 +49,15 @@
 import org.xmlpull.v1.XmlSerializer;
 
 public class UserManager {
+
+    private static final String TAG = "UserManager";
+
     private static final String TAG_NAME = "name";
 
     private static final String ATTR_FLAGS = "flags";
 
+    private static final String ATTR_ICON_PATH = "icon";
+
     private static final String ATTR_ID = "id";
 
     private static final String TAG_USERS = "users";
@@ -58,6 +68,7 @@
 
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
+    private static final String USER_PHOTO_FILENAME = "photo.png";
 
     private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
 
@@ -114,7 +125,7 @@
         }
     }
 
-    public void updateUserName(int userId, String name) {
+    public void setUserName(int userId, String name) {
         synchronized (mUsers) {
             UserInfo info = mUsers.get(userId);
             if (name != null && !name.equals(info.name)) {
@@ -124,6 +135,39 @@
         }
     }
 
+    public ParcelFileDescriptor setUserIcon(int userId) {
+        synchronized (mUsers) {
+            UserInfo info = mUsers.get(userId);
+            if (info == null) return null;
+            ParcelFileDescriptor fd = updateIconBitmapLocked(info);
+            if (fd != null) {
+                writeUserLocked(info);
+            }
+            return fd;
+        }
+    }
+
+    private ParcelFileDescriptor updateIconBitmapLocked(UserInfo info) {
+        try {
+            File dir = new File(mUsersDir, Integer.toString(info.id));
+            File file = new File(dir, USER_PHOTO_FILENAME);
+            if (!dir.exists()) {
+                dir.mkdir();
+                FileUtils.setPermissions(
+                        dir.getPath(),
+                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                        -1, -1);
+            }
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
+                    MODE_CREATE|MODE_READ_WRITE);
+            info.iconPath = file.getAbsolutePath();
+            return fd;
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "Error setting photo for user ", e);
+        }
+        return null;
+    }
+
     /**
      * Returns an array of user ids. This array is cached here for quick access, so do not modify or
      * cache it elsewhere.
@@ -187,7 +231,7 @@
 
     private void fallbackToSingleUserLocked() {
         // Create the primary user
-        UserInfo primary = new UserInfo(0, "Primary",
+        UserInfo primary = new UserInfo(0, "Primary", null,
                 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
         mUsers.put(0, primary);
         updateUserIdsLocked();
@@ -219,6 +263,9 @@
             serializer.startTag(null, TAG_USER);
             serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
             serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+            if (userInfo.iconPath != null) {
+                serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);
+            }
 
             serializer.startTag(null, TAG_NAME);
             serializer.text(userInfo.name);
@@ -286,6 +333,7 @@
     private UserInfo readUser(int id) {
         int flags = 0;
         String name = null;
+        String iconPath = null;
 
         FileInputStream fis = null;
         try {
@@ -312,6 +360,7 @@
                 }
                 String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
                 flags = Integer.parseInt(flagString);
+                iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
 
                 while ((type = parser.next()) != XmlPullParser.START_TAG
                         && type != XmlPullParser.END_DOCUMENT) {
@@ -324,7 +373,7 @@
                 }
             }
 
-            UserInfo userInfo = new UserInfo(id, name, flags);
+            UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
             return userInfo;
 
         } catch (IOException ioe) {
@@ -342,7 +391,7 @@
 
     public UserInfo createUser(String name, int flags) {
         int userId = getNextAvailableId();
-        UserInfo userInfo = new UserInfo(userId, name, flags);
+        UserInfo userInfo = new UserInfo(userId, name, null, flags);
         File userPath = new File(mBaseUserPath, Integer.toString(userId));
         if (!createPackageFolders(userId, userPath)) {
             return null;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index c23e868..416900f8 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -179,7 +179,7 @@
                     @Override
                     public List<UserInfo> getUsers() {
                         final ArrayList<UserInfo> users = new ArrayList<UserInfo>();
-                        users.add(new UserInfo(USER_ID, "Primary", UserInfo.FLAG_PRIMARY));
+                        users.add(new UserInfo(USER_ID, "Primary", null, UserInfo.FLAG_PRIMARY));
                         users.add(new UserInfo(USER_ID_GUEST, "Guest", 0));
                         return users;
                     }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 86689f3..0399b3b 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -545,7 +545,7 @@
      * @hide
      */
     @Override
-    public void updateUserName(int id, String name) {
+    public void setUserName(int id, String name) {
         throw new UnsupportedOperationException();
     }