Make UserManager enforce user restrictions, not DPM.
- Now even if a user restriction is set via UserManager, it'll be correctly
enforced.
- Changed the way AudioService enforces the OP_MUTE_MICROPHONE and
OP_AUDIO_MASTER_VOLUME app ops -- previously, when they're set, even a muting
call would be rejected. This was why DPMS.setUserRestriction() used different
calling orders for DISALLOW_UNMUTE_MICROPHONE/DISALLOW_ADJUST_VOLUME depending
on setting them or clearing them.
Now, even when the app ops are set, we still allow muting calls.
Bug 23902097
Bug 24981972
Change-Id: I865b5de43e15f5955f94006475a5ec6254904d31
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 66e731a..d89f47c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1756,7 +1756,8 @@
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
+ // If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
+ if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -1848,7 +1849,8 @@
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
+ // If OP_MUTE_MICROPHONE is set, disallow unmuting.
+ if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c1f827f..62ced52 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -199,6 +199,14 @@
@GuardedBy("mRestrictionsLock")
private final SparseArray<Bundle> mCachedEffectiveUserRestrictions = new SparseArray<>();
+ /**
+ * User restrictions that have already been applied in {@link #applyUserRestrictionsRL}. We
+ * use it to detect restrictions that have changed since the last
+ * {@link #applyUserRestrictionsRL} call.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private final SparseArray<Bundle> mAppliedUserRestrictions = new SparseArray<>();
+
private final Bundle mGuestRestrictions = new Bundle();
/**
@@ -727,8 +735,6 @@
Log.d(LOG_TAG, "updateUserRestrictionsInternalLocked userId=" + userId
+ " bundle=" + newRestrictions);
}
- final Bundle prevRestrictions = getEffectiveUserRestrictions(userId);
-
// Update system restrictions.
if (newRestrictions != null) {
// If newRestrictions == the current one, it's probably a bug.
@@ -742,12 +748,18 @@
mCachedEffectiveUserRestrictions.put(userId, effective);
- applyUserRestrictionsRL(userId, effective, prevRestrictions);
+ applyUserRestrictionsRL(userId, effective);
}
@GuardedBy("mRestrictionsLock")
- private void applyUserRestrictionsRL(int userId,
- Bundle newRestrictions, Bundle prevRestrictions) {
+ private void applyUserRestrictionsRL(int userId, Bundle newRestrictions) {
+ final Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+
+ if (DBG) {
+ Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId
+ + " new=" + newRestrictions + " prev=" + prevRestrictions);
+ }
+
final long token = Binder.clearCallingIdentity();
try {
mAppOpsService.setUserRestrictions(newRestrictions, userId);
@@ -757,7 +769,10 @@
Binder.restoreCallingIdentity(token);
}
- // TODO Move the code from DPMS.setUserRestriction().
+ UserRestrictionsUtils.applyUserRestrictions(
+ mContext, userId, newRestrictions, prevRestrictions);
+
+ mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions));
}
@GuardedBy("mRestrictionsLock")
@@ -768,9 +783,8 @@
@GuardedBy("mRestrictionsLock")
private void updateEffectiveUserRestrictionsForAllUsersRL() {
// First, invalidate all cached values.
- synchronized (mRestrictionsLock) {
- mCachedEffectiveUserRestrictions.clear();
- }
+ mCachedEffectiveUserRestrictions.clear();
+
// We don't want to call into ActivityManagerNative while taking a lock, so we'll call
// it on a handler.
final Runnable r = new Runnable() {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 23e3b35..28df9f6 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -18,10 +18,19 @@
import com.google.android.collect.Sets;
-import com.android.internal.util.Preconditions;
-
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.IAudioService;
+import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Slog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -31,6 +40,8 @@
import java.util.Set;
public class UserRestrictionsUtils {
+ private static final String TAG = "UserRestrictionsUtils";
+
private UserRestrictionsUtils() {
}
@@ -115,6 +126,118 @@
}
}
+ /**
+ * Takes a new use restriction set and the previous set, and apply the restrictions that have
+ * changed.
+ */
+ public static void applyUserRestrictions(Context context, int userId,
+ @Nullable Bundle newRestrictions, @Nullable Bundle prevRestrictions) {
+ if (newRestrictions == null) {
+ newRestrictions = Bundle.EMPTY;
+ }
+ if (prevRestrictions == null) {
+ prevRestrictions = Bundle.EMPTY;
+ }
+ for (String key : USER_RESTRICTIONS) {
+ final boolean newValue = newRestrictions.getBoolean(key);
+ final boolean prevValue = prevRestrictions.getBoolean(key);
+
+ if (newValue != prevValue) {
+ applyUserRestriction(context, userId, key, newValue);
+ }
+ }
+ }
+
+ private static void applyUserRestriction(Context context, int userId, String key,
+ boolean newValue) {
+ // When certain restrictions are cleared, we don't update the system settings,
+ // because these settings are changeable on the Settings UI and we don't know the original
+ // value -- for example LOCATION_MODE might have been off already when the restriction was
+ // set, and in that case even if the restriction is lifted, changing it to ON would be
+ // wrong. So just don't do anything in such a case. If the user hopes to enable location
+ // later, they can do it on the Settings UI.
+
+ final ContentResolver cr = context.getContentResolver();
+ final long id = Binder.clearCallingIdentity();
+ try {
+ switch (key) {
+ case UserManager.DISALLOW_UNMUTE_MICROPHONE:
+ IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE))
+ .setMicrophoneMute(newValue, context.getPackageName(), userId);
+ break;
+ case UserManager.DISALLOW_ADJUST_VOLUME:
+ IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE))
+ .setMasterMute(newValue, 0, context.getPackageName(), userId);
+ break;
+ case UserManager.DISALLOW_CONFIG_WIFI:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure
+ .WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
+ }
+ break;
+ case UserManager.DISALLOW_SHARE_LOCATION:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF,
+ userId);
+ android.provider.Settings.Secure.putStringForUser(cr,
+ android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+ userId);
+ }
+ // Send out notifications as some clients may want to reread the
+ // value which actually changed due to a restriction having been
+ // applied.
+ final String property =
+ android.provider.Settings.Secure.SYS_PROP_SETTING_VERSION;
+ long version = SystemProperties.getLong(property, 0) + 1;
+ SystemProperties.set(property, Long.toString(version));
+
+ final String name = android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
+ final Uri url = Uri.withAppendedPath(
+ android.provider.Settings.Secure.CONTENT_URI, name);
+ context.getContentResolver().notifyChange(url, null, true, userId);
+
+ break;
+ case UserManager.DISALLOW_DEBUGGING_FEATURES:
+ if (newValue) {
+ // Only disable adb if changing for system user, since it is global
+ // TODO: should this be admin user?
+ if (userId == UserHandle.USER_SYSTEM) {
+ android.provider.Settings.Global.putStringForUser(cr,
+ android.provider.Settings.Global.ADB_ENABLED, "0",
+ userId);
+ }
+ }
+ break;
+ case UserManager.ENSURE_VERIFY_APPS:
+ if (newValue) {
+ android.provider.Settings.Global.putStringForUser(
+ context.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+ userId);
+ android.provider.Settings.Global.putStringForUser(
+ context.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+ userId);
+ }
+ break;
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+ userId);
+ }
+ break;
+ }
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Failed to talk to AudioService.", re);
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ }
+
public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) {
boolean noneSet = true;
if (restrictions != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2e7f609..1602c12 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5618,9 +5618,6 @@
final long id = mInjector.binderClearCallingIdentity();
try {
- // Original value.
- final boolean alreadyRestricted = mUserManager.hasUserRestriction(key, user);
-
// Save the restriction to ActiveAdmin.
// TODO When DO sets a restriction, it'll always be treated as device-wide.
// If there'll be a policy that can be set by both, we'll need scoping support,
@@ -5629,85 +5626,12 @@
activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
saveSettingsLocked(userHandle);
- // Tell UserManager the new value. Note this needs to be done before calling
- // into AudioService, because AS will check AppOps that'll be updated by UM.
+ // Tell UserManager the new value.
if (isDeviceOwner) {
mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersRL();
} else {
mUserManagerInternal.updateEffectiveUserRestrictionsRL(userHandle);
}
-
- // New value.
- final boolean enabled = mUserManager.hasUserRestriction(key, user);
-
- // TODO The rest of the code should move to UserManagerService.
-
- if (enabled && !alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- mInjector.getIAudioService()
- .setMicrophoneMute(true, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- mInjector.getIAudioService()
- .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
- userHandle);
- } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF,
- userHandle);
- mInjector.settingsSecurePutStringForUser(
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
- userHandle);
- } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
- // Only disable adb if changing for system user, since it is global
- // TODO: should this be admin user?
- if (userHandle == UserHandle.USER_SYSTEM) {
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.ADB_ENABLED, "0", userHandle);
- }
- } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
- userHandle);
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
- userHandle);
- } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
- userHandle);
- }
- }
-
- if (enabled != alreadyRestricted) {
- if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- // Send out notifications however as some clients may want to reread the
- // value which actually changed due to a restriction having been
- // applied.
- final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
- long version = mInjector.systemPropertiesGetLong(property, 0) + 1;
- mInjector.systemPropertiesSet(property, Long.toString(version));
-
- final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
- Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
- mContext.getContentResolver().notifyChange(url, null, true, userHandle);
- }
- }
- if (!enabled && alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- mInjector.getIAudioService()
- .setMicrophoneMute(false, mContext.getPackageName(),
- userHandle);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- mInjector.getIAudioService()
- .setMasterMute(false, 0, mContext.getPackageName(), userHandle);
- }
- }
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}