Add concepts of related users and managed profiles to user manager.

Related users are a group that will share things like notifications.
Managed profiles are a profile of another user.

Change-Id: I2d0532f1abf939810f0fa3fc7c77ad13fa567833
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c33134a..38a1949 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -92,6 +92,7 @@
     private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
     private static final String ATTR_PARTIAL = "partial";
     private static final String ATTR_USER_VERSION = "version";
+    private static final String ATTR_RELATED_GROUP_ID = "relatedGroupId";
     private static final String TAG_USERS = "users";
     private static final String TAG_USER = "user";
     private static final String TAG_RESTRICTIONS = "restrictions";
@@ -258,6 +259,29 @@
     }
 
     @Override
+    public List<UserInfo> getRelatedUsers(int userId) {
+        checkManageUsersPermission("query users");
+        synchronized (mPackagesLock) {
+            UserInfo user = getUserInfoLocked(userId);
+            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+            for (int i = 0; i < mUsers.size(); i++) {
+                UserInfo ui = mUsers.valueAt(i);
+                if (!areRelatedUsers(user, ui)) {
+                    continue;
+                }
+                users.add(ui);
+            }
+            return users;
+        }
+    }
+
+    private boolean areRelatedUsers(UserInfo user1, UserInfo user2) {
+        return user1.relatedGroupId != UserInfo.NO_RELATED_GROUP_ID &&
+                user1.relatedGroupId == user2.relatedGroupId &&
+                user1.id != user2.id;
+    }
+
+    @Override
     public UserInfo getUserInfo(int userId) {
         checkManageUsersPermission("query user");
         synchronized (mPackagesLock) {
@@ -662,6 +686,10 @@
             if (userInfo.partial) {
                 serializer.attribute(null, ATTR_PARTIAL, "true");
             }
+            if (userInfo.relatedGroupId != UserInfo.NO_RELATED_GROUP_ID) {
+                serializer.attribute(null, ATTR_RELATED_GROUP_ID,
+                        Integer.toString(userInfo.relatedGroupId));
+            }
 
             serializer.startTag(null, TAG_NAME);
             serializer.text(userInfo.name);
@@ -745,6 +773,7 @@
         long salt = 0L;
         String pinHash = null;
         int failedAttempts = 0;
+        int relatedGroupId = UserInfo.NO_RELATED_GROUP_ID;
         long lastAttemptTime = 0L;
         boolean partial = false;
         Bundle restrictions = new Bundle();
@@ -782,6 +811,8 @@
                 pinHash = parser.getAttributeValue(null, ATTR_PIN_HASH);
                 failedAttempts = readIntAttribute(parser, ATTR_FAILED_ATTEMPTS, 0);
                 lastAttemptTime = readLongAttribute(parser, ATTR_LAST_RETRY_MS, 0L);
+                relatedGroupId = readIntAttribute(parser, ATTR_RELATED_GROUP_ID,
+                        UserInfo.NO_RELATED_GROUP_ID);
                 String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
                 if ("true".equals(valueString)) {
                     partial = true;
@@ -820,6 +851,7 @@
             userInfo.creationTime = creationTime;
             userInfo.lastLoggedInTime = lastLoggedInTime;
             userInfo.partial = partial;
+            userInfo.relatedGroupId = relatedGroupId;
             mUserRestrictions.append(id, restrictions);
             if (salt != 0L) {
                 RestrictionsPinState pinState = mRestrictionsPinStates.get(id);
@@ -934,10 +966,40 @@
         }
     }
 
+    private int getNextRelatedGroupIdLocked() {
+        int maxGroupId = UserInfo.NO_RELATED_GROUP_ID;
+        for (int i = 0; i < mUsers.size(); i++) {
+            UserInfo ui = mUsers.valueAt(i);
+            if (maxGroupId < ui.relatedGroupId) {
+                maxGroupId = ui.relatedGroupId;
+            }
+        }
+        return maxGroupId + 1;
+    }
+
+    @Override
+    public UserInfo createRelatedUser(String name, int flags, int relatedUserId) {
+        checkManageUsersPermission("Only the system can create users");
+        if (relatedUserId != UserHandle.USER_OWNER) {
+            Slog.w(LOG_TAG, "Only user owner can have related users");
+            return null;
+        }
+        synchronized (mPackagesLock) {
+            UserInfo relatedUser = getUserInfoLocked(relatedUserId);
+            if (relatedUser == null) {
+                return null;
+            }
+            return createUserInternal(name, flags, relatedUser);
+        }
+    }
+
     @Override
     public UserInfo createUser(String name, int flags) {
         checkManageUsersPermission("Only the system can create users");
+        return createUserInternal(name, flags, null);
+    }
 
+    private UserInfo createUserInternal(String name, int flags, UserInfo relatedUser) {
         final long ident = Binder.clearCallingIdentity();
         final UserInfo userInfo;
         try {
@@ -954,6 +1016,13 @@
                     Environment.getUserSystemDirectory(userInfo.id).mkdirs();
                     mUsers.put(userId, userInfo);
                     writeUserListLocked();
+                    if (relatedUser != null) {
+                        if (relatedUser.relatedGroupId == UserInfo.NO_RELATED_GROUP_ID) {
+                            relatedUser.relatedGroupId = getNextRelatedGroupIdLocked();
+                        }
+                        userInfo.relatedGroupId = relatedUser.relatedGroupId;
+                        writeUserLocked(relatedUser);
+                    }
                     writeUserLocked(userInfo);
                     mPm.createNewUserLILPw(userId, userPath);
                     userInfo.partial = false;