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);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 02cacd9..ac3cd1a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -50,6 +50,7 @@
import android.database.ContentObserver;
import android.media.AudioAttributes;
import android.media.AudioManager;
+import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.IRingtonePlayer;
import android.net.Uri;
@@ -179,6 +180,7 @@
private IActivityManager mAm;
AudioManager mAudioManager;
+ AudioManagerInternal mAudioManagerInternal;
StatusBarManagerInternal mStatusBar;
Vibrator mVibrator;
@@ -996,6 +998,7 @@
// Grab our optional AudioService
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mZenModeHelper.onSystemReady();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
@@ -1468,7 +1471,7 @@
@Override
public ZenModeConfig getZenModeConfig() {
- enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
+ enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
}
@@ -1479,6 +1482,17 @@
}
@Override
+ public void setZenMode(int mode) throws RemoteException {
+ enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mZenModeHelper.setZenMode(mode, "NotificationManager");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void notifyConditions(String pkg, IConditionProvider provider,
Condition[] conditions) {
final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
@@ -1493,13 +1507,13 @@
@Override
public void requestZenModeConditions(IConditionListener callback, int relevance) {
- enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
+ enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
mConditionProviders.requestZenModeConditions(callback, relevance);
}
@Override
public void setZenModeCondition(Condition condition) {
- enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
+ enforceSystemOrSystemUIOrVolume("INotificationManager.setZenModeCondition");
final long identity = Binder.clearCallingIdentity();
try {
mConditionProviders.setZenModeCondition(condition, "binderCall");
@@ -1520,6 +1534,16 @@
return mConditionProviders.getAutomaticZenModeConditions();
}
+ private void enforceSystemOrSystemUIOrVolume(String message) {
+ if (mAudioManagerInternal != null) {
+ final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
+ if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
+ return;
+ }
+ }
+ enforceSystemOrSystemUI(message);
+ }
+
private void enforceSystemOrSystemUI(String message) {
if (isCallerSystem()) return;
getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
@@ -1541,7 +1565,7 @@
@Override
public ComponentName getEffectsSuppressor() {
- enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
+ enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
return mEffectsSuppressor;
}
@@ -1558,7 +1582,7 @@
@Override
public boolean isSystemConditionProviderEnabled(String path) {
- enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
+ enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
return mConditionProviders.isSystemConditionProviderEnabled(path);
}
};