Merge "CSD: Implement support for absolute volume" into udc-dev
This commit is contained in:
@@ -8358,6 +8358,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
synchronized (VolumeStreamState.class) {
|
synchronized (VolumeStreamState.class) {
|
||||||
// apply device specific volumes first
|
// apply device specific volumes first
|
||||||
int index;
|
int index;
|
||||||
|
boolean isAbsoluteVolume = false;
|
||||||
for (int i = 0; i < mIndexMap.size(); i++) {
|
for (int i = 0; i < mIndexMap.size(); i++) {
|
||||||
final int device = mIndexMap.keyAt(i);
|
final int device = mIndexMap.keyAt(i);
|
||||||
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
|
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
|
||||||
@@ -8366,6 +8367,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
} else if (isAbsoluteVolumeDevice(device)
|
} else if (isAbsoluteVolumeDevice(device)
|
||||||
|| isA2dpAbsoluteVolumeDevice(device)
|
|| isA2dpAbsoluteVolumeDevice(device)
|
||||||
|| AudioSystem.isLeAudioDeviceType(device)) {
|
|| AudioSystem.isLeAudioDeviceType(device)) {
|
||||||
|
isAbsoluteVolume = true;
|
||||||
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
|
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
|
||||||
} else if (isFullVolumeDevice(device)) {
|
} else if (isFullVolumeDevice(device)) {
|
||||||
index = (mIndexMax + 5)/10;
|
index = (mIndexMax + 5)/10;
|
||||||
@@ -8374,6 +8376,11 @@ public class AudioService extends IAudioService.Stub
|
|||||||
} else {
|
} else {
|
||||||
index = (mIndexMap.valueAt(i) + 5)/10;
|
index = (mIndexMap.valueAt(i) + 5)/10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION,
|
||||||
|
SENDMSG_REPLACE, device, isAbsoluteVolume ? 1 : 0, this,
|
||||||
|
/*delay=*/0);
|
||||||
|
|
||||||
setStreamVolumeIndex(index, device);
|
setStreamVolumeIndex(index, device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8848,6 +8855,10 @@ public class AudioService extends IAudioService.Stub
|
|||||||
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
|
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
|
||||||
|
|
||||||
synchronized (VolumeStreamState.class) {
|
synchronized (VolumeStreamState.class) {
|
||||||
|
sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_REPLACE,
|
||||||
|
device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)
|
||||||
|
|| AudioSystem.isLeAudioDeviceType(device) ? 1 : 0),
|
||||||
|
streamState, /*delay=*/0);
|
||||||
// Apply volume
|
// Apply volume
|
||||||
streamState.applyDeviceVolume_syncVSS(device);
|
streamState.applyDeviceVolume_syncVSS(device);
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,6 +90,7 @@ public class SoundDoseHelper {
|
|||||||
private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 2;
|
private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 2;
|
||||||
private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 3;
|
private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 3;
|
||||||
private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 4;
|
private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 4;
|
||||||
|
/*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 5;
|
||||||
|
|
||||||
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
|
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
|
||||||
|
|
||||||
@@ -142,6 +144,13 @@ public class SoundDoseHelper {
|
|||||||
Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
|
Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
|
||||||
AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET));
|
AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET));
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
// mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
|
// 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
|
// When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
|
||||||
@@ -158,10 +167,10 @@ public class SoundDoseHelper {
|
|||||||
|
|
||||||
private final boolean mEnableCsd;
|
private final boolean mEnableCsd;
|
||||||
|
|
||||||
private ISoundDose mSoundDose;
|
|
||||||
|
|
||||||
private final Object mCsdStateLock = new Object();
|
private final Object mCsdStateLock = new Object();
|
||||||
|
|
||||||
|
private final AtomicReference<ISoundDose> mSoundDose = new AtomicReference<>();
|
||||||
|
|
||||||
@GuardedBy("mCsdStateLock")
|
@GuardedBy("mCsdStateLock")
|
||||||
private float mCurrentCsd = 0.f;
|
private float mCurrentCsd = 0.f;
|
||||||
// dose at which the next dose reached warning occurs
|
// dose at which the next dose reached warning occurs
|
||||||
@@ -245,9 +254,11 @@ public class SoundDoseHelper {
|
|||||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
|
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
|
||||||
} else {
|
} else {
|
||||||
mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
|
mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
|
||||||
Settings.Global.AUDIO_SAFE_VOLUME_STATE, 0);
|
Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initCsd();
|
||||||
|
|
||||||
// The default safe volume index read here will be replaced by the actual value when
|
// The default safe volume index read here will be replaced by the actual value when
|
||||||
// the mcc is read by onConfigureSafeMedia()
|
// the mcc is read by onConfigureSafeMedia()
|
||||||
// For now we use the same index for RS2 initial warning with CSD
|
// For now we use the same index for RS2 initial warning with CSD
|
||||||
@@ -263,9 +274,14 @@ public class SoundDoseHelper {
|
|||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose == null) {
|
||||||
|
Log.w(TAG, "Sound dose interface not initialized");
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return mSoundDose.getOutputRs2();
|
return soundDose.getOutputRs2();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Exception while getting the RS2 exposure value", e);
|
Log.e(TAG, "Exception while getting the RS2 exposure value", e);
|
||||||
return 0.f;
|
return 0.f;
|
||||||
@@ -277,9 +293,14 @@ public class SoundDoseHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose == null) {
|
||||||
|
Log.w(TAG, "Sound dose interface not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mSoundDose.setOutputRs2(rs2Value);
|
soundDose.setOutputRs2(rs2Value);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Exception while setting the RS2 exposure value", e);
|
Log.e(TAG, "Exception while setting the RS2 exposure value", e);
|
||||||
}
|
}
|
||||||
@@ -290,9 +311,14 @@ public class SoundDoseHelper {
|
|||||||
return -1.f;
|
return -1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose == null) {
|
||||||
|
Log.w(TAG, "Sound dose interface not initialized");
|
||||||
|
return -1.f;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return mSoundDose.getCsd();
|
return soundDose.getCsd();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Exception while getting the CSD value", e);
|
Log.e(TAG, "Exception while getting the CSD value", e);
|
||||||
return -1.f;
|
return -1.f;
|
||||||
@@ -304,13 +330,18 @@ public class SoundDoseHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose == null) {
|
||||||
|
Log.w(TAG, "Sound dose interface not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final SoundDoseRecord record = new SoundDoseRecord();
|
final SoundDoseRecord record = new SoundDoseRecord();
|
||||||
record.timestamp = System.currentTimeMillis();
|
record.timestamp = System.currentTimeMillis();
|
||||||
record.value = csd;
|
record.value = csd;
|
||||||
final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record };
|
final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record };
|
||||||
mSoundDose.resetCsd(csd, recordArray);
|
soundDose.resetCsd(csd, recordArray);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Exception while setting the CSD value", e);
|
Log.e(TAG, "Exception while setting the CSD value", e);
|
||||||
}
|
}
|
||||||
@@ -321,9 +352,14 @@ public class SoundDoseHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose == null) {
|
||||||
|
Log.w(TAG, "Sound dose interface not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mSoundDose.forceUseFrameworkMel(useFrameworkMel);
|
soundDose.forceUseFrameworkMel(useFrameworkMel);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Exception while forcing the internal MEL computation", e);
|
Log.e(TAG, "Exception while forcing the internal MEL computation", e);
|
||||||
}
|
}
|
||||||
@@ -334,9 +370,14 @@ public class SoundDoseHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose == null) {
|
||||||
|
Log.w(TAG, "Sound dose interface not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mSoundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
|
soundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Exception while forcing CSD computation on all devices", e);
|
Log.e(TAG, "Exception while forcing CSD computation on all devices", e);
|
||||||
}
|
}
|
||||||
@@ -551,6 +592,15 @@ public class SoundDoseHelper {
|
|||||||
case MSG_PERSIST_CSD_VALUES:
|
case MSG_PERSIST_CSD_VALUES:
|
||||||
onPersistSoundDoseRecords();
|
onPersistSoundDoseRecords();
|
||||||
break;
|
break;
|
||||||
|
case MSG_CSD_UPDATE_ATTENUATION:
|
||||||
|
final int device = msg.arg1;
|
||||||
|
final boolean isAbsoluteVolume = (msg.arg2 == 1);
|
||||||
|
final AudioService.VolumeStreamState streamState =
|
||||||
|
(AudioService.VolumeStreamState) msg.obj;
|
||||||
|
|
||||||
|
updateDoseAttenuation(streamState.getIndex(device), device,
|
||||||
|
streamState.getStreamType(), isAbsoluteVolume);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Unexpected msg to handle: " + msg.what);
|
Log.e(TAG, "Unexpected msg to handle: " + msg.what);
|
||||||
break;
|
break;
|
||||||
@@ -574,16 +624,18 @@ public class SoundDoseHelper {
|
|||||||
|
|
||||||
/*package*/void reset() {
|
/*package*/void reset() {
|
||||||
Log.d(TAG, "Reset the sound dose helper");
|
Log.d(TAG, "Reset the sound dose helper");
|
||||||
mSoundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback);
|
mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
|
||||||
|
|
||||||
synchronized (mCsdStateLock) {
|
synchronized (mCsdStateLock) {
|
||||||
try {
|
try {
|
||||||
if (mSoundDose != null && mSoundDose.asBinder().isBinderAlive()) {
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
|
if (soundDose != null && soundDose.asBinder().isBinderAlive()) {
|
||||||
if (mCurrentCsd != 0.f) {
|
if (mCurrentCsd != 0.f) {
|
||||||
Log.d(TAG,
|
Log.d(TAG,
|
||||||
"Resetting the saved sound dose value " + mCurrentCsd);
|
"Resetting the saved sound dose value " + mCurrentCsd);
|
||||||
SoundDoseRecord[] records = mDoseRecords.toArray(
|
SoundDoseRecord[] records = mDoseRecords.toArray(
|
||||||
new SoundDoseRecord[0]);
|
new SoundDoseRecord[0]);
|
||||||
mSoundDose.resetCsd(mCurrentCsd, records);
|
soundDose.resetCsd(mCurrentCsd, records);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -592,36 +644,70 @@ public class SoundDoseHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initCsd() {
|
private void updateDoseAttenuation(int newIndex, int device, int streamType,
|
||||||
if (mEnableCsd) {
|
boolean isAbsoluteVolume) {
|
||||||
Log.v(TAG, "Initializing sound dose");
|
if (!mEnableCsd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mCsdStateLock) {
|
final ISoundDose soundDose = mSoundDose.get();
|
||||||
if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
|
if (soundDose == null) {
|
||||||
mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
|
Log.w(TAG, "Can not apply attenuation. ISoundDose itf is null.");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
float prevCsd = mCurrentCsd;
|
try {
|
||||||
// Restore persisted values
|
if (!isAbsoluteVolume) {
|
||||||
mCurrentCsd = parseGlobalSettingFloat(
|
// remove any possible previous attenuation
|
||||||
Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f);
|
soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
|
||||||
if (mCurrentCsd != prevCsd) {
|
return;
|
||||||
mNextCsdWarning = parseGlobalSettingFloat(
|
|
||||||
Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
|
|
||||||
final List<SoundDoseRecord> records = persistedStringToRecordList(
|
|
||||||
mSettings.getGlobalString(mAudioService.getContentResolver(),
|
|
||||||
Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS),
|
|
||||||
mGlobalTimeOffsetInSecs);
|
|
||||||
if (records != null) {
|
|
||||||
mDoseRecords.addAll(records);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
|
||||||
|
&& mSafeMediaCsdDevices.contains(device)) {
|
||||||
|
soundDose.updateAttenuation(
|
||||||
|
AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
|
||||||
|
(newIndex + 5) / 10,
|
||||||
|
device), device);
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Could not apply the attenuation for MEL calculation with volume index "
|
||||||
|
+ newIndex, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initCsd() {
|
||||||
|
if (!mEnableCsd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v(TAG, "Initializing sound dose");
|
||||||
|
|
||||||
|
synchronized (mCsdStateLock) {
|
||||||
|
if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
|
||||||
|
mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
float prevCsd = mCurrentCsd;
|
||||||
|
// Restore persisted values
|
||||||
|
mCurrentCsd = parseGlobalSettingFloat(
|
||||||
|
Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f);
|
||||||
|
if (mCurrentCsd != prevCsd) {
|
||||||
|
mNextCsdWarning = parseGlobalSettingFloat(
|
||||||
|
Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
|
||||||
|
final List<SoundDoseRecord> records = persistedStringToRecordList(
|
||||||
|
mSettings.getGlobalString(mAudioService.getContentResolver(),
|
||||||
|
Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS),
|
||||||
|
mGlobalTimeOffsetInSecs);
|
||||||
|
if (records != null) {
|
||||||
|
mDoseRecords.addAll(records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
private void onConfigureSafeMedia(boolean force, String caller) {
|
private void onConfigureSafeMedia(boolean force, String caller) {
|
||||||
synchronized (mSafeMediaVolumeStateLock) {
|
synchronized (mSafeMediaVolumeStateLock) {
|
||||||
int mcc = mContext.getResources().getConfiguration().mcc;
|
int mcc = mContext.getResources().getConfiguration().mcc;
|
||||||
@@ -668,10 +754,6 @@ public class SoundDoseHelper {
|
|||||||
/*obj=*/null), /*delay=*/0);
|
/*obj=*/null), /*delay=*/0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEnableCsd) {
|
|
||||||
initCsd();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTimeoutMsForWarning(@AudioManager.CsdWarning int csdWarning) {
|
private int getTimeoutMsForWarning(@AudioManager.CsdWarning int csdWarning) {
|
||||||
|
|||||||
Reference in New Issue
Block a user