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));