Merge "CSD: Implement logic for initial safe hearing warning"
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 45181e8..0d0e576 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1315,7 +1315,7 @@
// persistent data
initVolumeGroupStates();
- mSoundDoseHelper.initSafeUsbMediaVolumeIndex();
+ mSoundDoseHelper.initSafeMediaVolumeIndex();
// Link VGS on VSS
initVolumeStreamStates();
@@ -8837,7 +8837,7 @@
final VolumeStreamState streamState = mStreamStates[update.mStreamType];
if (update.hasVolumeIndex()) {
int index = update.getVolumeIndex();
- if (!mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
+ if (mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
index = mSoundDoseHelper.safeMediaVolumeIndex(update.mDevice);
}
streamState.setIndex(index, update.mDevice, update.mCaller,
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 4b30234..07fa868 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -52,10 +52,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -80,7 +79,6 @@
// SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
// can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
// (when user opts out).
- // Note: when CSD calculation is enabled the state is set to SAFE_MEDIA_VOLUME_DISABLED
private static final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
private static final int SAFE_MEDIA_VOLUME_DISABLED = 1;
private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2; // confirmed
@@ -127,30 +125,50 @@
// mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
private int mSafeMediaVolumeIndex;
- // mSafeUsbMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB
+ // mSafeMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB
// property, divided by 100.0.
- private float mSafeUsbMediaVolumeDbfs;
+ // For now using the same value for CSD supported devices
+ private float mSafeMediaVolumeDbfs;
- // mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index
- // corresponding to a gain of mSafeUsbMediaVolumeDbfs (defaulting to -37dB) in audio
- // flinger mixer.
- // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost
- // amplification when both effects are on with all band gains at maximum.
- // This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when
- // the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
- private int mSafeUsbMediaVolumeIndex;
- // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
- private final Set<Integer> mSafeMediaVolumeDevices = new HashSet<>(
- Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET));
+ private static class SafeDeviceVolumeInfo {
+ int mDeviceType;
+ int mSafeVolumeIndex = -1;
- private final Set<Integer> mSafeMediaCsdDevices = new HashSet<>(
- Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET,
- AudioSystem.DEVICE_OUT_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_BROADCAST,
- AudioSystem.DEVICE_OUT_HEARING_AID,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ SafeDeviceVolumeInfo(int deviceType) {
+ mDeviceType = deviceType;
+ }
+ }
+
+ /**
+ * mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced.
+ * Contains a safe volume index for a given device type.
+ * Indexes are used for headsets and is the music volume UI index
+ * corresponding to a gain of mSafeMediaVolumeDbfs (defaulting to -37dB) in audio
+ * flinger mixer.
+ * We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost
+ * amplification when both effects are on with all band gains at maximum.
+ * This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when
+ * the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
+ */
+ private final HashMap<Integer, SafeDeviceVolumeInfo> mSafeMediaVolumeDevices =
+ new HashMap<>() {{
+ put(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADSET));
+ put(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE));
+ put(AudioSystem.DEVICE_OUT_USB_HEADSET,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_USB_HEADSET));
+ put(AudioSystem.DEVICE_OUT_BLE_HEADSET,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_HEADSET));
+ put(AudioSystem.DEVICE_OUT_BLE_BROADCAST,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_BROADCAST));
+ put(AudioSystem.DEVICE_OUT_HEARING_AID,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_HEARING_AID));
+ put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES));
+ put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ }};
// mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
// When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
@@ -250,15 +268,11 @@
mContext = context;
mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default);
- if (mEnableCsd) {
- mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
- } else {
- mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
- Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
- }
-
initCsd();
+ mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
+ Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
+
// The default safe volume index read here will be replaced by the actual value when
// the mcc is read by onConfigureSafeMedia()
// For now we use the same index for RS2 initial warning with CSD
@@ -388,14 +402,12 @@
}
/*package*/ int safeMediaVolumeIndex(int device) {
- if (!mSafeMediaVolumeDevices.contains(device)) {
+ final SafeDeviceVolumeInfo vi = mSafeMediaVolumeDevices.get(device);
+ if (vi == null) {
return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
}
- if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
- return mSafeUsbMediaVolumeIndex;
- } else {
- return mSafeMediaVolumeIndex;
- }
+
+ return vi.mSafeVolumeIndex;
}
/*package*/ void restoreMusicActiveMs() {
@@ -419,20 +431,24 @@
/*package*/ void enforceSafeMediaVolume(String caller) {
AudioService.VolumeStreamState streamState = mAudioService.getVssVolumeForStream(
AudioSystem.STREAM_MUSIC);
- Set<Integer> devices = mSafeMediaVolumeDevices;
- for (int device : devices) {
- int index = streamState.getIndex(device);
- int safeIndex = safeMediaVolumeIndex(device);
+ for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) {
+ int index = streamState.getIndex(vi.mDeviceType);
+ int safeIndex = safeMediaVolumeIndex(vi.mDeviceType);
if (index > safeIndex) {
- streamState.setIndex(safeIndex, device, caller, true /*hasModifyAudioSettings*/);
+ streamState.setIndex(safeIndex, vi.mDeviceType, caller,
+ true /*hasModifyAudioSettings*/);
mAudioHandler.sendMessageAtTime(
- mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, device, /*arg2=*/0,
- streamState), /*delay=*/0);
+ mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, vi.mDeviceType,
+ /*arg2=*/0, streamState), /*delay=*/0);
}
}
}
+ /**
+ * Returns {@code true} if the safe media actions can be applied for the given stream type,
+ * volume index and device.
+ **/
/*package*/ boolean checkSafeMediaVolume(int streamType, int index, int device) {
boolean result;
synchronized (mSafeMediaVolumeStateLock) {
@@ -443,17 +459,16 @@
@GuardedBy("mSafeMediaVolumeStateLock")
private boolean checkSafeMediaVolume_l(int streamType, int index, int device) {
- return (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_ACTIVE)
- || (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC)
- || (!mSafeMediaVolumeDevices.contains(device))
- || (index <= safeMediaVolumeIndex(device))
- || mEnableCsd;
+ return (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)
+ && (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC)
+ && (mSafeMediaVolumeDevices.containsKey(device))
+ && (index > safeMediaVolumeIndex(device));
}
/*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device,
int flags) {
synchronized (mSafeMediaVolumeStateLock) {
- if (!checkSafeMediaVolume_l(streamType, index, device)) {
+ if (checkSafeMediaVolume_l(streamType, index, device)) {
mVolumeController.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
@@ -484,15 +499,13 @@
/*package*/ void scheduleMusicActiveCheck() {
synchronized (mSafeMediaVolumeStateLock) {
cancelMusicActiveCheck();
- if (!mEnableCsd) {
- mMusicActiveIntent = PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_CHECK_MUSIC_ACTIVE,
- new Intent(ACTION_CHECK_MUSIC_ACTIVE),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime()
- + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent);
- }
+ mMusicActiveIntent = PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_CHECK_MUSIC_ACTIVE,
+ new Intent(ACTION_CHECK_MUSIC_ACTIVE),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime()
+ + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent);
}
}
@@ -500,7 +513,7 @@
synchronized (mSafeMediaVolumeStateLock) {
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC);
- if (mSafeMediaVolumeDevices.contains(device) && isStreamActive) {
+ if (mSafeMediaVolumeDevices.containsKey(device) && isStreamActive) {
scheduleMusicActiveCheck();
int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
device);
@@ -528,27 +541,31 @@
/*package*/ void configureSafeMedia(boolean forced, String caller) {
int msg = MSG_CONFIGURE_SAFE_MEDIA;
- mAudioHandler.removeMessages(msg);
+ if (forced) {
+ // unforced should not cancel forced configure messages
+ mAudioHandler.removeMessages(msg);
+ }
long time = 0;
if (forced) {
time = (SystemClock.uptimeMillis() + (SystemProperties.getBoolean(
"audio.safemedia.bypass", false) ? 0 : SAFE_VOLUME_CONFIGURE_TIMEOUT_MS));
}
+
mAudioHandler.sendMessageAtTime(
mAudioHandler.obtainMessage(msg, /*arg1=*/forced ? 1 : 0, /*arg2=*/0, caller),
time);
}
- /*package*/ void initSafeUsbMediaVolumeIndex() {
- // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
- // relies on audio policy having correct ranges for volume indexes.
- mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+ /*package*/ void initSafeMediaVolumeIndex() {
+ for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) {
+ vi.mSafeVolumeIndex = getSafeDeviceMediaVolumeIndex(vi.mDeviceType);
+ }
}
/*package*/ int getSafeMediaVolumeIndex(int device) {
- if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && mSafeMediaVolumeDevices.contains(
- device)) {
+ if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE
+ && mSafeMediaVolumeDevices.containsKey(device)) {
return safeMediaVolumeIndex(device);
} else {
return -1;
@@ -557,7 +574,7 @@
/*package*/ boolean raiseVolumeDisplaySafeMediaVolume(int streamType, int index, int device,
int flags) {
- if (checkSafeMediaVolume(streamType, index, device)) {
+ if (!checkSafeMediaVolume(streamType, index, device)) {
return false;
}
@@ -566,7 +583,7 @@
}
/*package*/ boolean safeDevicesContains(int device) {
- return mSafeMediaVolumeDevices.contains(device);
+ return mSafeMediaVolumeDevices.containsKey(device);
}
/*package*/ void invalidatPendingVolumeCommand() {
@@ -612,8 +629,11 @@
pw.print(" mSafeMediaVolumeState=");
pw.println(safeMediaVolumeStateToString(mSafeMediaVolumeState));
pw.print(" mSafeMediaVolumeIndex="); pw.println(mSafeMediaVolumeIndex);
- pw.print(" mSafeUsbMediaVolumeIndex="); pw.println(mSafeUsbMediaVolumeIndex);
- pw.print(" mSafeUsbMediaVolumeDbfs="); pw.println(mSafeUsbMediaVolumeDbfs);
+ for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) {
+ pw.print(" mSafeMediaVolumeIndex["); pw.print(vi.mDeviceType);
+ pw.print("]="); pw.println(vi.mSafeVolumeIndex);
+ }
+ pw.print(" mSafeMediaVolumeDbfs="); pw.println(mSafeMediaVolumeDbfs);
pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs);
pw.print(" mMcc="); pw.println(mMcc);
pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand);
@@ -660,11 +680,12 @@
if (!isAbsoluteVolume) {
// remove any possible previous attenuation
soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
+
return;
}
if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
- && mSafeMediaCsdDevices.contains(device)) {
+ && mSafeMediaVolumeDevices.containsKey(device)) {
soundDose.updateAttenuation(
AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
(newIndex + 5) / 10,
@@ -715,7 +736,7 @@
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
- mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+ initSafeMediaVolumeIndex();
boolean safeMediaVolumeEnabled =
SystemProperties.getBoolean("audio.safemedia.force", false)
@@ -728,7 +749,7 @@
// The persisted state is either "disabled" or "active": this is the state applied
// next time we boot and cannot be "inactive"
int persistedState;
- if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) {
+ if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) {
persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
// The state can already be "inactive" here if the user has forced it before
// the 30 seconds timeout for forced configuration. In this case we don't reset
@@ -801,25 +822,32 @@
mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget();
}
- private int getSafeUsbMediaVolumeIndex() {
+ private int getSafeDeviceMediaVolumeIndex(int deviceType) {
+ // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
+ // instead of computing it from the volume curves
+ if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
+ || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) {
+ return mSafeMediaVolumeIndex;
+ }
+
// determine UI volume index corresponding to the wanted safe gain in dBFS
int min = MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
int max = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
- mSafeUsbMediaVolumeDbfs = mContext.getResources().getInteger(
+ mSafeMediaVolumeDbfs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_usb_mB) / 100.0f;
while (Math.abs(max - min) > 1) {
int index = (max + min) / 2;
- float gainDB = AudioSystem.getStreamVolumeDB(
- AudioSystem.STREAM_MUSIC, index, AudioSystem.DEVICE_OUT_USB_HEADSET);
+ float gainDB = AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index,
+ deviceType);
if (Float.isNaN(gainDB)) {
//keep last min in case of read error
break;
- } else if (gainDB == mSafeUsbMediaVolumeDbfs) {
+ } else if (gainDB == mSafeMediaVolumeDbfs) {
min = index;
break;
- } else if (gainDB < mSafeUsbMediaVolumeDbfs) {
+ } else if (gainDB < mSafeMediaVolumeDbfs) {
min = index;
} else {
max = index;