Allow sysui-managed remote volume controllers.

- Relax restriction on audio service calls that assume the volume
  ui is systemui, allow calls from a blessed component app.
- Blessed component app service saved in secure settings.
- SystemUI mediates requests to replace the volume dialog, prompts
  the user on activation.
- Show a low pri ongoing notification when the volume dialog is
  being replaced, to allow user restoration at any time.
- Replace the controller management code in VolumeUI to use a
  ServiceMonitor, backed by the new blessed app component setting.
- Add proper zen-related noman client wrappers, make avail to the
  registered volume controller.
- Everything is still @hidden, no api impact.

Bug: 19260237
Change-Id: Ie1383f57659090318a7eda737fdad5b8f88737d4
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index eaece09..4301427 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -182,6 +182,7 @@
 
     /** The controller for the volume UI. */
     private final VolumeController mVolumeController = new VolumeController();
+    private final ControllerService mControllerService = new ControllerService();
 
     // sendMsg() flags
     /** If the msg is already queued, replace it with this one. */
@@ -708,6 +709,7 @@
                 SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
 
         StreamOverride.init(mContext);
+        mControllerService.init();
     }
 
     private void createAudioSystemThread() {
@@ -1833,7 +1835,7 @@
     }
 
     public void setRingerModeInternal(int ringerMode, String caller) {
-        enforceSelfOrSystemUI("setRingerModeInternal");
+        enforceVolumeController("setRingerModeInternal");
         setRingerMode(ringerMode, caller, false /*external*/);
     }
 
@@ -5013,7 +5015,7 @@
 
     @Override
     public void setRemoteStreamVolume(int index) {
-        enforceSelfOrSystemUI("set the remote stream volume");
+        enforceVolumeController("set the remote stream volume");
         mMediaFocusControl.setRemoteStreamVolume(index);
     }
 
@@ -5333,7 +5335,7 @@
 
     @Override
     public void disableSafeMediaVolume() {
-        enforceSelfOrSystemUI("disable the safe media volume");
+        enforceVolumeController("disable the safe media volume");
         synchronized (mSafeMediaVolumeState) {
             setSafeMediaVolumeEnabled(false);
             if (mPendingVolumeCommand != null) {
@@ -5505,6 +5507,7 @@
         pw.print("  mMusicActiveMs="); pw.println(mMusicActiveMs);
         pw.print("  mMcc="); pw.println(mMcc);
         pw.print("  mHasVibrator="); pw.println(mHasVibrator);
+        pw.print("  mControllerService="); pw.println(mControllerService);
 
         dumpAudioPolicies(pw);
     }
@@ -5528,14 +5531,17 @@
         }
     }
 
-    private void enforceSelfOrSystemUI(String action) {
+    private void enforceVolumeController(String action) {
+        if (mControllerService.mUid != 0 && Binder.getCallingUid() == mControllerService.mUid) {
+            return;
+        }
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
                 "Only SystemUI can " + action);
     }
 
     @Override
     public void setVolumeController(final IVolumeController controller) {
-        enforceSelfOrSystemUI("set the volume controller");
+        enforceVolumeController("set the volume controller");
 
         // return early if things are not actually changing
         if (mVolumeController.isSameBinder(controller)) {
@@ -5566,7 +5572,7 @@
 
     @Override
     public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) {
-        enforceSelfOrSystemUI("notify about volume controller visibility");
+        enforceVolumeController("notify about volume controller visibility");
 
         // return early if the controller is not current
         if (!mVolumeController.isSameBinder(controller)) {
@@ -5751,6 +5757,11 @@
         public void setRingerModeInternal(int ringerMode, String caller) {
             AudioService.this.setRingerModeInternal(ringerMode, caller);
         }
+
+        @Override
+        public int getVolumeControllerUid() {
+            return mControllerService.mUid;
+        }
     }
 
     //==========================================================================================
@@ -5915,4 +5926,42 @@
     private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
             new HashMap<IBinder, AudioPolicyProxy>();
     private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies
+
+    private class ControllerService extends ContentObserver {
+        private int mUid;
+        private ComponentName mComponent;
+
+        public ControllerService() {
+            super(null);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{mUid=%d,mComponent=%s}", mUid, mComponent);
+        }
+
+        public void init() {
+            onChange(true);
+            mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT), false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mUid = 0;
+            mComponent = null;
+            final String setting = Settings.Secure.getString(mContentResolver,
+                    Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
+            if (setting == null) return;
+            try {
+                mComponent = ComponentName.unflattenFromString(setting);
+                if (mComponent == null) return;
+                mUid = mContext.getPackageManager()
+                        .getApplicationInfo(mComponent.getPackageName(), 0).uid;
+            } catch (Exception e) {
+                Log.w(TAG, "Error loading controller service", e);
+            }
+            if (DEBUG_VOL) Log.d(TAG, "Reloaded controller service: " + this);
+        }
+    }
 }