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
This commit is contained in:
Makoto Onuki
2015-10-27 17:15:38 -07:00
parent 5263492de6
commit 4f16073556
4 changed files with 153 additions and 90 deletions

View File

@@ -1756,7 +1756,8 @@ public class AudioService extends IAudioService.Stub {
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 @@ public class AudioService extends IAudioService.Stub {
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;
}

View File

@@ -199,6 +199,14 @@ public class UserManagerService extends IUserManager.Stub {
@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 @@ public class UserManagerService extends IUserManager.Stub {
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 @@ public class UserManagerService extends IUserManager.Stub {
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 @@ public class UserManagerService extends IUserManager.Stub {
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 @@ public class UserManagerService extends IUserManager.Stub {
@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() {

View File

@@ -18,10 +18,19 @@ package com.android.server.pm;
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.io.PrintWriter;
import java.util.Set;
public class UserRestrictionsUtils {
private static final String TAG = "UserRestrictionsUtils";
private UserRestrictionsUtils() {
}
@@ -115,6 +126,118 @@ public class UserRestrictionsUtils {
}
}
/**
* 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) {

View File

@@ -5618,9 +5618,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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);
}