Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f160735 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

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
parent 5263492d
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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;
        }
+23 −9
Original line number Diff line number Diff line
@@ -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();
        }

        // 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() {
+125 −2
Original line number Diff line number Diff line
@@ -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) {
+1 −77
Original line number Diff line number Diff line
@@ -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);
                }