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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user