Introduce system API to get source of user restriction

Clients can query who has set a particular user restriction on a user.
Currently the result can be a combination of none, system/user, or admin.

Bug: 27830375
Change-Id: I50ea9db0a59ffe3abbdcbb1a436d9ace7a35a851
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7ea5919..8d1abb0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -23,6 +23,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -31,7 +32,9 @@
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -143,6 +146,7 @@
     private static final String TAG_USER = "user";
     private static final String TAG_RESTRICTIONS = "restrictions";
     private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions";
+    private static final String TAG_GLOBAL_RESTRICTION_OWNER_ID = "globalRestrictionOwnerUserId";
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
     private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
@@ -276,6 +280,12 @@
     private Bundle mDevicePolicyGlobalUserRestrictions;
 
     /**
+     * Id of the user that set global restrictions.
+     */
+    @GuardedBy("mRestrictionsLock")
+    private int mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
+
+    /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
      * for each user.
      */
@@ -991,6 +1001,13 @@
                 if (globalChanged) {
                     mDevicePolicyGlobalUserRestrictions = global;
                 }
+                // Remember the global restriction owner userId to be able to make a distinction
+                // in getUserRestrictionSource on who set local policies.
+                mGlobalRestrictionOwnerUserId = userId;
+            } else {
+                // When profile owner sets restrictions it passes null global bundle and we reset 
+                // global restriction owner userId.
+                mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
             }
             {
                 // Update local.
@@ -1074,6 +1091,54 @@
     }
 
     /**
+     * @hide
+     *
+     * Returns who set a user restriction on a user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param restrictionKey the string key representing the restriction
+     * @param userId the id of the user for whom to retrieve the restrictions.
+     * @return The source of user restriction. Any combination of
+     *         {@link UserManager#RESTRICTION_NOT_SET},
+     *         {@link UserManager#RESTRICTION_SOURCE_SYSTEM},
+     *         {@link UserManager#RESTRICTION_SOURCE_DEVICE_OWNER}
+     *         and {@link UserManager#RESTRICTION_SOURCE_PROFILE_OWNER}
+     */
+    @Override
+    public int getUserRestrictionSource(String restrictionKey, int userId) {
+        checkManageUsersPermission("getUserRestrictionSource");
+        int result = UserManager.RESTRICTION_NOT_SET;
+
+        // Shortcut for the most common case
+        if (!hasUserRestriction(restrictionKey, userId)) {
+            return result;
+        }
+
+        if (hasBaseUserRestriction(restrictionKey, userId)) {
+            result |= UserManager.RESTRICTION_SOURCE_SYSTEM;
+        }
+
+        synchronized(mRestrictionsLock) {
+            Bundle localRestrictions = mDevicePolicyLocalUserRestrictions.get(userId);
+            if (!UserRestrictionsUtils.isEmpty(localRestrictions)
+                    && localRestrictions.getBoolean(restrictionKey)) {
+                // Local restrictions may have been set by device owner the userId of which is
+                // stored in mGlobalRestrictionOwnerUserId.
+                if (mGlobalRestrictionOwnerUserId == userId) {
+                    result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
+                } else {
+                    result |= UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+                }
+            }
+            if (!UserRestrictionsUtils.isEmpty(mDevicePolicyGlobalUserRestrictions)
+                    && mDevicePolicyGlobalUserRestrictions.getBoolean(restrictionKey)) {
+                result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
+            }
+        }
+
+        return result;
+    }
+
+    /**
      * @return UserRestrictions that are in effect currently.  This always returns a new
      * {@link Bundle}.
      */
@@ -1470,6 +1535,11 @@
                                 break;
                             }
                         }
+                    } else if (name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
+                        String ownerUserId = parser.getAttributeValue(null, ATTR_ID);
+                        if (ownerUserId != null) {
+                            mGlobalRestrictionOwnerUserId = Integer.parseInt(ownerUserId);
+                        }
                     }
                 }
             }
@@ -1729,6 +1799,9 @@
                 UserRestrictionsUtils.writeRestrictions(serializer,
                         mDevicePolicyGlobalUserRestrictions, TAG_DEVICE_POLICY_RESTRICTIONS);
             }
+            serializer.startTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
+            serializer.attribute(null, ATTR_ID, Integer.toString(mGlobalRestrictionOwnerUserId));
+            serializer.endTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
             int[] userIdsToWrite;
             synchronized (mUsersLock) {
                 userIdsToWrite = new int[mUsers.size()];
@@ -2964,6 +3037,8 @@
                         .dumpRestrictions(pw, "    ", mDevicePolicyGlobalUserRestrictions);
             }
             pw.println();
+            pw.println("  Global restrictions owner id:" + mGlobalRestrictionOwnerUserId);
+            pw.println();
             pw.println("  Guest restrictions:");
             synchronized (mGuestRestrictions) {
                 UserRestrictionsUtils.dumpRestrictions(pw, "    ", mGuestRestrictions);