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);
+ }
+ }
}