Have AudioService listen to DISALLOW_UNMUTE_MICROPHONE and

... DISALLOW_ADJUST_VOLUME, instead of UserManager pushing
new settings to AudioService.

Also:
- Allow PO to set these two restrictions.

- Now AS.setMasterMuteInternal() respects mUseFixedVolume to make
it consistent with readPersistedSettings().

- When a user switches and restores the mute state in
AS.readPersistedSettings(), also check the current user restrictions
in addition to system settings. Because of the delay in AudioService
before persisting the mute settings in setMasterMuteInternal() and
setMicrophoneMute(), there's was an edge case
DISALLOW_UNMUTE_MICROPHONE and DISALLOW_ADJUST_VOLUME would be ignored
when the user switches right after they are set.

Bug 24981972

Change-Id: I4d9b709a0a0e6812319204568c6e44d6664bdeb4
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index b50cc79..9178ec6 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -19,6 +19,17 @@
  * @hide Only for use within the system server.
  */
 public abstract class UserManagerInternal {
+    public interface UserRestrictionsListener {
+        /**
+         * Called when a user restriction changes.
+         *
+         * @param userId target user id
+         * @param newRestrictions new user restrictions
+         * @param prevRestrictions user restrictions that were previously set
+         */
+        void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions);
+    }
+
     /**
      * Lock that must be held when calling certain methods in this class.
      *
@@ -60,4 +71,13 @@
      */
     public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId,
             Bundle baseRestrictions);
+
+    /** Return a user restriction. */
+    public abstract boolean getUserRestriction(int userId, String key);
+
+    /** Adds a listener to user restriction changes. */
+    public abstract void addUserRestrictionsListener(UserRestrictionsListener listener);
+
+    /** Remove a {@link UserRestrictionsListener}. */
+    public abstract void removeUserRestrictionsListener(UserRestrictionsListener listener);
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d89f47c..058d681 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -76,6 +76,7 @@
 import android.media.audiopolicy.IAudioPolicyCallback;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
@@ -88,6 +89,8 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.System;
@@ -397,6 +400,12 @@
     // Broadcast receiver for device connections intent broadcasts
     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
 
+    /** Interface for UserManagerService. */
+    private final UserManagerInternal mUserManagerInternal;
+
+    private final UserRestrictionsListener mUserRestrictionsListener =
+            new AudioServiceUserRestrictionsListener();
+
     // Devices currently connected
     // Use makeDeviceListKey() to make a unique key for this list.
     private class DeviceListSpec {
@@ -598,6 +607,8 @@
 
         mPlatformType = AudioSystem.getPlatformType(context);
 
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
 
@@ -694,6 +705,8 @@
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
 
         LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
+
+        mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
     }
 
     public void systemReady() {
@@ -1039,17 +1052,32 @@
                 System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
                 UserHandle.USER_CURRENT);
 
-        boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
-                0, UserHandle.USER_CURRENT) == 1;
+        final int currentUser = getCurrentUserId();
+
+        // In addition to checking the system setting, also check the current user restriction.
+        // Because of the delay before persisting VOLUME_MASTER_MUTE, there's a window where
+        // DISALLOW_ADJUST_VOLUME will be ignored when it's set right before switching users.
+        boolean masterMute = (System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
+                0, UserHandle.USER_CURRENT) == 1)
+                || mUserManagerInternal.getUserRestriction(
+                    currentUser, UserManager.DISALLOW_ADJUST_VOLUME);
         if (mUseFixedVolume) {
             masterMute = false;
             AudioSystem.setMasterVolume(1.0f);
         }
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
+        }
         AudioSystem.setMasterMute(masterMute);
         broadcastMasterMuteStatus(masterMute);
 
         boolean microphoneMute =
-                System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1;
+                (System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1)
+                || mUserManagerInternal.getUserRestriction(
+                        currentUser, UserManager.DISALLOW_UNMUTE_MICROPHONE);
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Mic mute %s, user=%d", microphoneMute, currentUser));
+        }
         AudioSystem.muteMicrophone(microphoneMute);
 
         // Each stream will read its own persisted settings
@@ -1767,6 +1795,16 @@
                 != PackageManager.PERMISSION_GRANTED) {
             return;
         }
+        setMasterMuteInternalNoCallerCheck(mute, flags, userId);
+    }
+
+    private void setMasterMuteInternalNoCallerCheck(boolean mute, int flags, int userId) {
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Master mute %s, %d, user=%d", mute, flags, userId));
+        }
+        if (mUseFixedVolume) {
+            return; // If using fixed volume, we don't mute.
+        }
         if (getCurrentUserId() == userId) {
             if (mute != AudioSystem.getMasterMute()) {
                 setSystemAudioMute(mute);
@@ -1841,7 +1879,8 @@
         return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
     }
 
-    /** @see AudioManager#setMicrophoneMute(boolean, int) */
+    /** @see AudioManager#setMicrophoneMute(boolean) */
+    @Override
     public void setMicrophoneMute(boolean on, String callingPackage, int userId) {
         // If we are being called by the system check for user we are going to change
         // so we handle user restrictions correctly.
@@ -1863,7 +1902,13 @@
                 != PackageManager.PERMISSION_GRANTED) {
             return;
         }
+        setMicrophoneMuteNoCallerCheck(on, userId);
+    }
 
+    private void setMicrophoneMuteNoCallerCheck(boolean on, int userId) {
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Mic mute %s, user=%d", on, userId));
+        }
         // If mute is for current user actually mute, else just persist the setting
         // which will be loaded on user switch.
         if (getCurrentUserId() == userId) {
@@ -5116,6 +5161,35 @@
         }
     } // end class AudioServiceBroadcastReceiver
 
+    private class AudioServiceUserRestrictionsListener implements UserRestrictionsListener {
+
+        @Override
+        public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
+                Bundle prevRestrictions) {
+            // Update mic mute state.
+            {
+                final boolean wasRestricted =
+                        prevRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+                final boolean isRestricted =
+                        newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+                if (wasRestricted != isRestricted) {
+                    setMicrophoneMuteNoCallerCheck(isRestricted, userId);
+                }
+            }
+
+            // Update speaker mute state.
+            {
+                final boolean wasRestricted =
+                        prevRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+                final boolean isRestricted =
+                        newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME);
+                if (wasRestricted != isRestricted) {
+                    setMasterMuteInternalNoCallerCheck(isRestricted, /* flags =*/ 0, userId);
+                }
+            }
+        }
+    } // end class AudioServiceUserRestrictionsListener
+
     private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) {
         PackageManager pm = mContext.getPackageManager();
         // Find the home activity of the user. It should not be killed to avoid expensive restart,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c41d493..4725592 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -52,6 +52,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.system.ErrnoException;
@@ -232,6 +233,10 @@
 
     private final LocalService mLocalService;
 
+    @GuardedBy("mUserRestrictionsListeners")
+    private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners =
+            new ArrayList<>();
+
     private static UserManagerService sInstance;
 
     public static UserManagerService getInstance() {
@@ -781,7 +786,14 @@
 
     @GuardedBy("mRestrictionsLock")
     private void applyUserRestrictionsLR(int userId, Bundle newRestrictions) {
-        final Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+        if (newRestrictions == null) {
+            newRestrictions = Bundle.EMPTY;
+        }
+
+        Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+        if (prevRestrictions == null) {
+            prevRestrictions = Bundle.EMPTY;
+        }
 
         if (DBG) {
             Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId
@@ -797,12 +809,36 @@
             Binder.restoreCallingIdentity(token);
         }
 
-        UserRestrictionsUtils.applyUserRestrictions(
+        UserRestrictionsUtils.applyUserRestrictionsLR(
                 mContext, userId, newRestrictions, prevRestrictions);
 
+        notifyUserRestrictionsListeners(userId, newRestrictions, prevRestrictions);
+
         mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions));
     }
 
+    private void notifyUserRestrictionsListeners(final int userId,
+            Bundle newRestrictions, Bundle prevRestrictions) {
+
+        final Bundle newRestrictionsFinal = new Bundle(newRestrictions);
+        final Bundle prevRestrictionsFinal = new Bundle(prevRestrictions);
+
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                final UserRestrictionsListener[] listeners;
+                synchronized (mUserRestrictionsListeners) {
+                    listeners = new UserRestrictionsListener[mUserRestrictionsListeners.size()];
+                    mUserRestrictionsListeners.toArray(listeners);
+                }
+                for (int i = 0; i < listeners.length; i++) {
+                    listeners[i].onUserRestrictionsChanged(userId,
+                            newRestrictionsFinal, prevRestrictionsFinal);
+                }
+            }
+        });
+    }
+
     @GuardedBy("mRestrictionsLock")
     private void updateEffectiveUserRestrictionsLR(int userId) {
         updateUserRestrictionsInternalLR(null, userId);
@@ -2388,6 +2424,25 @@
                 }
             }
         }
+
+        @Override
+        public boolean getUserRestriction(int userId, String key) {
+            return getUserRestrictions(userId).getBoolean(key);
+        }
+
+        @Override
+        public void addUserRestrictionsListener(UserRestrictionsListener listener) {
+            synchronized (mUserRestrictionsListeners) {
+                mUserRestrictionsListeners.add(listener);
+            }
+        }
+
+        @Override
+        public void removeUserRestrictionsListener(UserRestrictionsListener listener) {
+            synchronized (mUserRestrictionsListeners) {
+                mUserRestrictionsListeners.remove(listener);
+            }
+        }
     }
 
     private class Shell extends ShellCommand {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 28df9f6..56e8b3e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -18,19 +18,14 @@
 
 import com.google.android.collect.Sets;
 
-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;
@@ -39,6 +34,11 @@
 import java.io.PrintWriter;
 import java.util.Set;
 
+/**
+ * Utility methods for uesr restrictions.
+ *
+ * <p>See {@link UserManagerService} for the method suffixes.
+ */
 public class UserRestrictionsUtils {
     private static final String TAG = "UserRestrictionsUtils";
 
@@ -129,26 +129,31 @@
     /**
      * Takes a new use restriction set and the previous set, and apply the restrictions that have
      * changed.
+     *
+     * <p>Note this method is called by {@link UserManagerService} while holding
+     * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
+     * a deadlock.
      */
-    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;
-        }
+    public static void applyUserRestrictionsLR(Context context, int userId,
+            Bundle newRestrictions, Bundle prevRestrictions) {
         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);
+                applyUserRestrictionLR(context, userId, key, newValue);
             }
         }
     }
 
-    private static void applyUserRestriction(Context context, int userId, String key,
+    /**
+     * Apply each user restriction.
+     *
+     * <p>Note this method is called by {@link UserManagerService} while holding
+     * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
+     * a deadlock.
+     */
+    private static void applyUserRestrictionLR(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
@@ -161,14 +166,6 @@
         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,
@@ -231,8 +228,6 @@
                     }
                     break;
             }
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Failed to talk to AudioService.", re);
         } finally {
             Binder.restoreCallingIdentity(id);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aea2ecf..4534345 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -212,8 +212,6 @@
         DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
         DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
         DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
-        DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_UNMUTE_MICROPHONE);
-        DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADJUST_VOLUME);
         DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SMS);
         DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FUN);
         DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SAFE_BOOT);
@@ -4648,7 +4646,6 @@
             mIPackageManager.updatePermissionFlagsForAllApps(
                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
                     0  /* flagValues */, userHandle.getIdentifier());
-            // TODO This will not revert audio mute restrictions if they were set.  b/24981972
             synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
                 mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle.getIdentifier());
             }
@@ -5599,7 +5596,6 @@
     public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
-        final UserHandle user = new UserHandle(userHandle);
         synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
             synchronized (this) {
                 ActiveAdmin activeAdmin =