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/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) {