Disable multi-user background recording

On user switch, kill existing processes of the background user with
android.permission.RECORD_AUDIO permission. Home activity should not be
killed to avoid an expensive restart of the home launcher, when the
user switches back.

Introduced DISALLOW_RECORD_AUDIO user restriction, which is enabled for the
background user, and removed for the foreground user.

Introduced a concept of system controlled user restriction, which can only
be set by the system, rather than device administrator.

Bug: 20346194
Change-Id: Ic942fd565e80d14424230dae612965a8e229c4ef
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 51b26a8..8dccfdf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -50,6 +50,8 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.google.android.collect.Sets;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
@@ -70,6 +72,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import libcore.io.IoUtils;
 
@@ -126,6 +129,10 @@
     // without first making sure that the rest of the framework is prepared for it.
     private static final int MAX_MANAGED_PROFILES = 1;
 
+    // Set of user restrictions, which can only be enforced by the system
+    private static final Set<String> SYSTEM_CONTROLLED_RESTRICTIONS = Sets.newArraySet(
+            UserManager.DISALLOW_RECORD_AUDIO);
+
     static final int WRITE_USER_MSG = 1;
     static final int WRITE_USER_DELAY = 2*1000;  // 2 seconds
 
@@ -500,7 +507,7 @@
     public boolean hasUserRestriction(String restrictionKey, int userId) {
         synchronized (mPackagesLock) {
             Bundle restrictions = mUserRestrictions.get(userId);
-            return restrictions != null ? restrictions.getBoolean(restrictionKey) : false;
+            return restrictions != null && restrictions.getBoolean(restrictionKey);
         }
     }
 
@@ -515,25 +522,59 @@
     }
 
     @Override
+    public void setUserRestriction(String key, boolean value, int userId) {
+        synchronized (mPackagesLock) {
+            if (!SYSTEM_CONTROLLED_RESTRICTIONS.contains(key)) {
+                Bundle restrictions = getUserRestrictions(userId);
+                restrictions.putBoolean(key, value);
+                setUserRestrictionsInternalLocked(restrictions, userId);
+            }
+        }
+    }
+
+    @Override
+    public void setSystemControlledUserRestriction(String key, boolean value, int userId) {
+        checkSystemOrRoot("setSystemControlledUserRestriction");
+        synchronized (mPackagesLock) {
+            Bundle restrictions = getUserRestrictions(userId);
+            restrictions.putBoolean(key, value);
+            setUserRestrictionsInternalLocked(restrictions, userId);
+        }
+    }
+
+    @Override
     public void setUserRestrictions(Bundle restrictions, int userId) {
         checkManageUsersPermission("setUserRestrictions");
         if (restrictions == null) return;
 
         synchronized (mPackagesLock) {
-            mUserRestrictions.get(userId).clear();
-            mUserRestrictions.get(userId).putAll(restrictions);
-            long token = Binder.clearCallingIdentity();
-            try {
-                mAppOpsService.setUserRestrictions(mUserRestrictions.get(userId), userId);
-            } catch (RemoteException e) {
-                Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
-            } finally {
-                Binder.restoreCallingIdentity(token);
+            final Bundle oldUserRestrictions = mUserRestrictions.get(userId);
+            // Restore the original state of system controlled restrictions from oldUserRestrictions
+            for (String key : SYSTEM_CONTROLLED_RESTRICTIONS) {
+                restrictions.remove(key);
+                if (oldUserRestrictions.containsKey(key)) {
+                    restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key));
+                }
             }
-            scheduleWriteUserLocked(mUsers.get(userId));
+            setUserRestrictionsInternalLocked(restrictions, userId);
         }
     }
 
+    private void setUserRestrictionsInternalLocked(Bundle restrictions, int userId) {
+        final Bundle userRestrictions = mUserRestrictions.get(userId);
+        userRestrictions.clear();
+        userRestrictions.putAll(restrictions);
+        long token = Binder.clearCallingIdentity();
+        try {
+        mAppOpsService.setUserRestrictions(userRestrictions, userId);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        scheduleWriteUserLocked(mUsers.get(userId));
+    }
+
     /**
      * Check if we've hit the limit of how many users can be created.
      */
@@ -569,6 +610,13 @@
         }
     }
 
+    private static void checkSystemOrRoot(String message) {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID && uid != 0) {
+            throw new SecurityException("Only system may call: " + message);
+        }
+    }
+
     private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
         try {
             File dir = new File(mUsersDir, Integer.toString(info.id));