Add multi user volume management
Notification and Alarm volumes are per user: they are saved and restored
when the foreground user changes.
Media volume is global: it is still saved and restored per user for
implentation reasons but is copied from one user to the next to ensure
media playback volume continuity when switching users.
Ringer mode (silent, vibrate...) is now a global setting.
Bug 7128886.
Change-Id: I9f4f5a0a3985552bca61c2cc3bbe5a144db755a6
diff --git a/api/current.txt b/api/current.txt
index f0148b4..ef5278f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18797,6 +18797,7 @@
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ field public static final java.lang.String MODE_RINGER = "mode_ringer";
field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
field public static final java.lang.String RADIO_CELL = "cell";
@@ -18960,7 +18961,7 @@
field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
field public static final deprecated java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
- field public static final java.lang.String MODE_RINGER = "mode_ringer";
+ field public static final deprecated java.lang.String MODE_RINGER = "mode_ringer";
field public static final java.lang.String MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected";
field public static final java.lang.String MUTE_STREAMS_AFFECTED = "mute_streams_affected";
field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a182234..804f57a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -919,6 +919,7 @@
MOVED_TO_GLOBAL.add(Settings.Global.POWER_SOUNDS_ENABLED);
MOVED_TO_GLOBAL.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SLEEP_POLICY);
+ MOVED_TO_GLOBAL.add(Settings.Global.MODE_RINGER);
}
private static void lazyInitCache() {
@@ -1482,6 +1483,12 @@
@Deprecated
public static final int WIFI_SLEEP_POLICY_NEVER = Global.WIFI_SLEEP_POLICY_NEVER;
+ /**
+ * @deprecated Use {@link android.provider.Settings.Global#MODE_RINGER} instead
+ */
+ @Deprecated
+ public static final String MODE_RINGER = Global.MODE_RINGER;
+
//TODO: deprecate static IP constants
/**
* Whether to use static IP and other static network attributes.
@@ -1651,13 +1658,6 @@
public static final String ALWAYS_FINISH_ACTIVITIES =
"always_finish_activities";
-
- /**
- * Ringer mode. This is used internally, changing this value will not
- * change the ringer mode. See AudioManager.
- */
- public static final String MODE_RINGER = "mode_ringer";
-
/**
* Determines which streams are affected by ringer mode changes. The
* stream type's bit should be set to 1 if it should be muted when going
@@ -5709,7 +5709,11 @@
*/
public static final String WTF_IS_FATAL = "wtf_is_fatal";
-
+ /**
+ * Ringer mode. This is used internally, changing this value will not
+ * change the ringer mode. See AudioManager.
+ */
+ public static final String MODE_RINGER = "mode_ringer";
// Populated lazily, guarded by class object:
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 4459d03..4d58894 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -480,6 +480,7 @@
intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
// Register a configuration change listener only if requested by system properties
// to monitor orientation changes (off by default)
@@ -608,7 +609,7 @@
final ContentResolver cr = mContentResolver;
int ringerModeFromSettings =
- System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
+ Settings.Global.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
int ringerMode = ringerModeFromSettings;
// sanity check in case the settings are restored from a device with incompatible
// ringer modes
@@ -619,7 +620,7 @@
ringerMode = AudioManager.RINGER_MODE_SILENT;
}
if (ringerMode != ringerModeFromSettings) {
- System.putInt(cr, System.MODE_RINGER, ringerMode);
+ Settings.Global.putInt(cr, System.MODE_RINGER, ringerMode);
}
synchronized(mSettingsLock) {
mRingerMode = ringerMode;
@@ -638,23 +639,30 @@
// make sure settings for ringer mode are consistent with device type: non voice capable
// devices (tablets) include media stream in silent mode whereas phones don't.
- mRingerModeAffectedStreams = Settings.System.getInt(cr,
+ mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
- (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
+ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
+ UserHandle.USER_CURRENT);
if (mVoiceCapable) {
mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
} else {
mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
}
- Settings.System.putInt(cr,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
+ Settings.System.putIntForUser(cr,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ mRingerModeAffectedStreams,
+ UserHandle.USER_CURRENT);
- mMuteAffectedStreams = System.getInt(cr,
+ mMuteAffectedStreams = System.getIntForUser(cr,
System.MUTE_STREAMS_AFFECTED,
- ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
+ ((1 << AudioSystem.STREAM_MUSIC)|
+ (1 << AudioSystem.STREAM_RING)|
+ (1 << AudioSystem.STREAM_SYSTEM)),
+ UserHandle.USER_CURRENT);
- boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
+ boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
+ 0, UserHandle.USER_CURRENT) == 1;
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
@@ -1237,8 +1245,8 @@
private void restoreMasterVolume() {
if (mUseMasterVolume) {
- float volume = Settings.System.getFloat(mContentResolver,
- Settings.System.VOLUME_MASTER, -1.0f);
+ float volume = Settings.System.getFloatForUser(mContentResolver,
+ Settings.System.VOLUME_MASTER, -1.0f, UserHandle.USER_CURRENT);
if (volume >= 0.0f) {
AudioSystem.setMasterVolume(volume);
}
@@ -1674,6 +1682,10 @@
/** @see AudioManager#reloadAudioSettings() */
public void reloadAudioSettings() {
+ readAudioSettings(false /*userSwitch*/);
+ }
+
+ private void readAudioSettings(boolean userSwitch) {
// restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
readPersistedSettings();
@@ -1682,6 +1694,10 @@
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
VolumeStreamState streamState = mStreamStates[streamType];
+ if (userSwitch && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) {
+ continue;
+ }
+
synchronized (streamState) {
streamState.readSettings();
@@ -2536,7 +2552,8 @@
// device, continue otherwise
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
- int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
+ int index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
if (index == -1) {
continue;
}
@@ -2547,7 +2564,8 @@
// or default index
defaultIndex = (index > 0) ?
index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
- int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
+ int lastAudibleIndex = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
// a last audible index of 0 should never be stored for ring and notification
// streams on phones (voice capable devices).
@@ -2959,19 +2977,21 @@
int persistType,
int device) {
if ((persistType & PERSIST_CURRENT) != 0) {
- System.putInt(mContentResolver,
+ System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(false /* lastAudible */, device),
- (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
+ (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10,
+ UserHandle.USER_CURRENT);
}
if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
- System.putInt(mContentResolver,
+ System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(true /* lastAudible */, device),
- (streamState.getIndex(device, true /* lastAudible */) + 5) / 10);
+ (streamState.getIndex(device, true /* lastAudible */) + 5) / 10,
+ UserHandle.USER_CURRENT);
}
}
private void persistRingerMode(int ringerMode) {
- System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
+ Settings.Global.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
}
private void playSoundEffect(int effectType, int volume) {
@@ -3021,8 +3041,10 @@
}
private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
- Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
- receiver == null ? "" : receiver.flattenToString());
+ Settings.System.putStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER,
+ receiver == null ? "" : receiver.flattenToString(),
+ UserHandle.USER_CURRENT);
}
private void cleanupPlayer(MediaPlayer mp) {
@@ -3058,13 +3080,17 @@
break;
case MSG_PERSIST_MASTER_VOLUME:
- Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
- (float)msg.arg1 / (float)1000.0);
+ Settings.System.putFloatForUser(mContentResolver,
+ Settings.System.VOLUME_MASTER,
+ (float)msg.arg1 / (float)1000.0,
+ UserHandle.USER_CURRENT);
break;
case MSG_PERSIST_MASTER_VOLUME_MUTE:
- Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
- msg.arg1);
+ Settings.System.putIntForUser(mContentResolver,
+ Settings.System.VOLUME_MASTER_MUTE,
+ msg.arg1,
+ UserHandle.USER_CURRENT);
break;
case MSG_PERSIST_RINGER_MODE:
@@ -3248,10 +3274,11 @@
// and mRingerModeAffectedStreams, so will leave this synchronized for now.
// mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
synchronized (mSettingsLock) {
- int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
+ int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
- (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
+ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
+ UserHandle.USER_CURRENT);
if (mVoiceCapable) {
ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
} else {
@@ -3670,6 +3697,15 @@
AudioSystem.setParameters("screen_state=off");
} else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
handleConfigurationChanged(context);
+ } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+ readAudioSettings(true /*userSwitch*/);
+ // preserve STREAM_MUSIC volume from one user to the next.
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ mStreamStates[AudioSystem.STREAM_MUSIC], 0);
}
}
}
@@ -4544,8 +4580,8 @@
* Restore remote control receiver from the system settings.
*/
private void restoreMediaButtonReceiver() {
- String receiverName = Settings.System.getString(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER);
+ String receiverName = Settings.System.getStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
if ((null != receiverName) && !receiverName.isEmpty()) {
ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
// construct a PendingIntent targeted to the restored component name