Merge "Extract logic for media safe hearing."
This commit is contained in:
@@ -27,6 +27,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
|
||||
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
|
||||
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
|
||||
|
||||
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
|
||||
import static com.android.server.utils.EventLogger.Event.ALOGE;
|
||||
import static com.android.server.utils.EventLogger.Event.ALOGI;
|
||||
import static com.android.server.utils.EventLogger.Event.ALOGW;
|
||||
@@ -42,12 +43,10 @@ import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.IUidObserver;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.role.OnRoleHoldersChangedListener;
|
||||
import android.app.role.RoleManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
@@ -166,7 +165,6 @@ import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IntArray;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.PrintWriterPrinter;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
@@ -332,7 +330,7 @@ public class AudioService extends IAudioService.Stub
|
||||
private static final int SENDMSG_QUEUE = 2;
|
||||
|
||||
// AudioHandler messages
|
||||
private static final int MSG_SET_DEVICE_VOLUME = 0;
|
||||
/*package*/ static final int MSG_SET_DEVICE_VOLUME = 0;
|
||||
private static final int MSG_PERSIST_VOLUME = 1;
|
||||
private static final int MSG_PERSIST_VOLUME_GROUP = 2;
|
||||
private static final int MSG_PERSIST_RINGER_MODE = 3;
|
||||
@@ -342,13 +340,8 @@ public class AudioService extends IAudioService.Stub
|
||||
private static final int MSG_SET_FORCE_USE = 8;
|
||||
private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
|
||||
private static final int MSG_SET_ALL_VOLUMES = 10;
|
||||
private static final int MSG_CHECK_MUSIC_ACTIVE = 11;
|
||||
private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 12;
|
||||
private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 13;
|
||||
private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 14;
|
||||
private static final int MSG_UNLOAD_SOUND_EFFECTS = 15;
|
||||
private static final int MSG_SYSTEM_READY = 16;
|
||||
private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = 17;
|
||||
private static final int MSG_UNMUTE_STREAM = 18;
|
||||
private static final int MSG_DYN_POLICY_MIX_STATE_UPDATE = 19;
|
||||
private static final int MSG_INDICATE_SYSTEM_READY = 20;
|
||||
@@ -384,6 +377,9 @@ public class AudioService extends IAudioService.Stub
|
||||
private static final int MSG_RESET_SPATIALIZER = 50;
|
||||
private static final int MSG_NO_LOG_FOR_PLAYER_I = 51;
|
||||
|
||||
/** Messages handled by the {@link SoundDoseHelper}. */
|
||||
/*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000;
|
||||
|
||||
// start of messages handled under wakelock
|
||||
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
|
||||
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
|
||||
@@ -399,6 +395,9 @@ public class AudioService extends IAudioService.Stub
|
||||
// List of empty UIDs used to reset the active assistant list
|
||||
private static final int[] NO_ACTIVE_ASSISTANT_SERVICE_UIDS = new int[0];
|
||||
|
||||
// check playback or record activity every 6 seconds for UIDs owning mode IN_COMMUNICATION
|
||||
private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 6000;
|
||||
|
||||
/** @see AudioSystemThread */
|
||||
private AudioSystemThread mAudioSystemThread;
|
||||
/** @see AudioHandler */
|
||||
@@ -410,6 +409,10 @@ public class AudioService extends IAudioService.Stub
|
||||
return mStreamStates[stream].getIndex(device);
|
||||
}
|
||||
|
||||
/*package*/ VolumeStreamState getVssVolumeForStream(int stream) {
|
||||
return mStreamStates[stream];
|
||||
}
|
||||
|
||||
/*package*/ int getMaxVssVolumeForStream(int stream) {
|
||||
return mStreamStates[stream].getMaxIndex();
|
||||
}
|
||||
@@ -838,11 +841,6 @@ public class AudioService extends IAudioService.Stub
|
||||
|
||||
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
|
||||
|
||||
// Used when safe volume warning message display is requested by setStreamVolume(). In this
|
||||
// case, the new requested volume, stream type and device are stored in mPendingVolumeCommand
|
||||
// and used later when/if disableSafeMediaVolume() is called.
|
||||
private StreamVolumeCommand mPendingVolumeCommand;
|
||||
|
||||
private PowerManager.WakeLock mAudioEventWakeLock;
|
||||
|
||||
private final MediaFocusControl mMediaFocusControl;
|
||||
@@ -894,6 +892,8 @@ public class AudioService extends IAudioService.Stub
|
||||
private boolean mNavigationRepeatSoundEffectsEnabled;
|
||||
private boolean mHomeSoundEffectEnabled;
|
||||
|
||||
private final SoundDoseHelper mSoundDoseHelper;
|
||||
|
||||
@GuardedBy("mSettingsLock")
|
||||
private int mCurrentImeUid;
|
||||
|
||||
@@ -1184,6 +1184,9 @@ public class AudioService extends IAudioService.Stub
|
||||
mAudioHandler = new AudioHandler(looper);
|
||||
}
|
||||
|
||||
mSoundDoseHelper = new SoundDoseHelper(this, mContext, mAudioHandler, mSettings,
|
||||
mVolumeController);
|
||||
|
||||
AudioSystem.setErrorCallback(mAudioSystemCallback);
|
||||
|
||||
updateAudioHalPids();
|
||||
@@ -1199,16 +1202,6 @@ public class AudioService extends IAudioService.Stub
|
||||
new String("AudioService ctor"),
|
||||
0);
|
||||
|
||||
mSafeMediaVolumeState = mSettings.getGlobalInt(mContentResolver,
|
||||
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 onConfigureSafeVolume()
|
||||
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
|
||||
|
||||
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
mUseFixedVolume = mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_useFixedVolume);
|
||||
|
||||
@@ -1285,9 +1278,7 @@ public class AudioService extends IAudioService.Stub
|
||||
// persistent data
|
||||
initVolumeGroupStates();
|
||||
|
||||
// mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
|
||||
// relies on audio policy having correct ranges for volume indexes.
|
||||
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
|
||||
mSoundDoseHelper.initSafeUsbMediaVolumeIndex();
|
||||
|
||||
// Call setRingerModeInt() to apply correct mute
|
||||
// state on streams affected by ringer mode.
|
||||
@@ -1442,14 +1433,7 @@ public class AudioService extends IAudioService.Stub
|
||||
|
||||
mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
sendMsg(mAudioHandler,
|
||||
MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
|
||||
SENDMSG_REPLACE,
|
||||
0,
|
||||
0,
|
||||
TAG,
|
||||
SystemProperties.getBoolean("audio.safemedia.bypass", false) ?
|
||||
0 : SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
|
||||
mSoundDoseHelper.configureSafeMediaVolume(/*forced=*/true, TAG);
|
||||
|
||||
initA11yMonitoring();
|
||||
|
||||
@@ -1987,8 +1971,8 @@ public class AudioService extends IAudioService.Stub
|
||||
@AudioService.ConnectionState int state, String caller) {
|
||||
if (state == AudioService.CONNECTION_STATE_CONNECTED) {
|
||||
// DEVICE_OUT_HDMI is now connected
|
||||
if (mSafeMediaVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI)) {
|
||||
scheduleMusicActiveCheck();
|
||||
if (mSoundDoseHelper.safeDevicesContains(AudioSystem.DEVICE_OUT_HDMI)) {
|
||||
mSoundDoseHelper.scheduleMusicActiveCheck();
|
||||
}
|
||||
|
||||
if (isPlatformTelevision()) {
|
||||
@@ -3283,10 +3267,7 @@ public class AudioService extends IAudioService.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
// reset any pending volume command
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
mPendingVolumeCommand = null;
|
||||
}
|
||||
mSoundDoseHelper.invalidatPendingVolumeCommand();
|
||||
|
||||
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
|
||||
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
|
||||
@@ -3295,10 +3276,8 @@ public class AudioService extends IAudioService.Stub
|
||||
// Always toggle between max safe volume and 0 for fixed volume devices where safe
|
||||
// volume is enforced, and max and 0 for the others.
|
||||
// This is simulated by stepping by the full allowed volume range
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
|
||||
mSafeMediaVolumeDevices.contains(device)) {
|
||||
step = safeMediaVolumeIndex(device);
|
||||
} else {
|
||||
step = mSoundDoseHelper.getSafeMediaVolumeIndex(device);
|
||||
if (step < 0) {
|
||||
step = streamState.getMaxIndex();
|
||||
}
|
||||
if (aliasIndex != 0) {
|
||||
@@ -3371,10 +3350,10 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((direction == AudioManager.ADJUST_RAISE) &&
|
||||
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
|
||||
} else if ((direction == AudioManager.ADJUST_RAISE)
|
||||
&& mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias,
|
||||
aliasIndex + step, device, flags)) {
|
||||
Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
|
||||
mVolumeController.postDisplaySafeVolumeWarning(flags);
|
||||
} else if (!isFullVolumeDevice(device)
|
||||
&& (streamState.adjustIndex(direction * step, device, caller,
|
||||
hasModifyAudioSettings)
|
||||
@@ -3545,29 +3524,6 @@ public class AudioService extends IAudioService.Stub
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
|
||||
// StreamVolumeCommand contains the information needed to defer the process of
|
||||
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
|
||||
static class StreamVolumeCommand {
|
||||
public final int mStreamType;
|
||||
public final int mIndex;
|
||||
public final int mFlags;
|
||||
public final int mDevice;
|
||||
|
||||
StreamVolumeCommand(int streamType, int index, int flags, int device) {
|
||||
mStreamType = streamType;
|
||||
mIndex = index;
|
||||
mFlags = flags;
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder().append("{streamType=").append(mStreamType).append(",index=")
|
||||
.append(mIndex).append(",flags=").append(mFlags).append(",device=")
|
||||
.append(mDevice).append('}').toString();
|
||||
}
|
||||
}
|
||||
|
||||
private int getNewRingerMode(int stream, int index, int flags) {
|
||||
// setRingerMode does nothing if the device is single volume,so the value would be unchanged
|
||||
if (mIsSingleVolume) {
|
||||
@@ -3615,7 +3571,7 @@ public class AudioService extends IAudioService.Stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onSetStreamVolume(int streamType, int index, int flags, int device,
|
||||
/*package*/ void onSetStreamVolume(int streamType, int index, int flags, int device,
|
||||
String caller, boolean hasModifyAudioSettings) {
|
||||
final int stream = mStreamVolumeAlias[streamType];
|
||||
setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
|
||||
@@ -3959,7 +3915,7 @@ public class AudioService extends IAudioService.Stub
|
||||
updateHearingAidVolumeOnVoiceActivityUpdate();
|
||||
}
|
||||
if (mMediaPlaybackActive.getAndSet(mediaActive) != mediaActive && mediaActive) {
|
||||
scheduleMusicActiveCheck();
|
||||
mSoundDoseHelper.scheduleMusicActiveCheck();
|
||||
}
|
||||
// Update playback active state for all apps in audio mode stack.
|
||||
// When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
|
||||
@@ -4205,71 +4161,64 @@ public class AudioService extends IAudioService.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
// reset any pending volume command
|
||||
mPendingVolumeCommand = null;
|
||||
mSoundDoseHelper.invalidatPendingVolumeCommand();
|
||||
|
||||
oldIndex = streamState.getIndex(device);
|
||||
oldIndex = streamState.getIndex(device);
|
||||
|
||||
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
|
||||
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
|
||||
|
||||
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
|
||||
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
|
||||
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
|
||||
if (DEBUG_VOL) {
|
||||
Log.d(TAG, "setStreamVolume postSetAvrcpAbsoluteVolumeIndex index=" + index
|
||||
+ "stream=" + streamType);
|
||||
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
|
||||
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
|
||||
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
|
||||
if (DEBUG_VOL) {
|
||||
Log.d(TAG, "setStreamVolume postSetAvrcpAbsoluteVolumeIndex index=" + index
|
||||
+ "stream=" + streamType);
|
||||
}
|
||||
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
|
||||
} else if (isAbsoluteVolumeDevice(device)
|
||||
&& ((flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0)) {
|
||||
AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
|
||||
|
||||
dispatchAbsoluteVolumeChanged(streamType, info, index);
|
||||
}
|
||||
|
||||
if (AudioSystem.isLeAudioDeviceType(device)
|
||||
&& streamType == getBluetoothContextualVolumeStream()
|
||||
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
|
||||
if (DEBUG_VOL) {
|
||||
Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
|
||||
+ index + " stream=" + streamType);
|
||||
}
|
||||
mDeviceBroker.postSetLeAudioVolumeIndex(index, mStreamStates[streamType].getMaxIndex(),
|
||||
streamType);
|
||||
}
|
||||
|
||||
if (device == AudioSystem.DEVICE_OUT_HEARING_AID
|
||||
&& streamType == getBluetoothContextualVolumeStream()) {
|
||||
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
|
||||
+ " stream=" + streamType);
|
||||
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
|
||||
}
|
||||
|
||||
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
|
||||
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
|
||||
flags |= AudioManager.FLAG_FIXED_VOLUME;
|
||||
|
||||
// volume is either 0 or max allowed for fixed volume devices
|
||||
if (index != 0) {
|
||||
index = mSoundDoseHelper.getSafeMediaVolumeIndex(device);
|
||||
if (index < 0) {
|
||||
index = streamState.getMaxIndex();
|
||||
}
|
||||
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
|
||||
} else if (isAbsoluteVolumeDevice(device)
|
||||
&& ((flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0)) {
|
||||
AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
|
||||
|
||||
dispatchAbsoluteVolumeChanged(streamType, info, index);
|
||||
}
|
||||
|
||||
if (AudioSystem.isLeAudioDeviceType(device)
|
||||
&& streamType == getBluetoothContextualVolumeStream()
|
||||
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
|
||||
if (DEBUG_VOL) {
|
||||
Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
|
||||
+ index + " stream=" + streamType);
|
||||
}
|
||||
mDeviceBroker.postSetLeAudioVolumeIndex(index,
|
||||
mStreamStates[streamType].getMaxIndex(), streamType);
|
||||
}
|
||||
|
||||
if (device == AudioSystem.DEVICE_OUT_HEARING_AID
|
||||
&& streamType == getBluetoothContextualVolumeStream()) {
|
||||
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
|
||||
+ " stream=" + streamType);
|
||||
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
|
||||
}
|
||||
|
||||
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
|
||||
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
|
||||
flags |= AudioManager.FLAG_FIXED_VOLUME;
|
||||
|
||||
// volume is either 0 or max allowed for fixed volume devices
|
||||
if (index != 0) {
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
|
||||
mSafeMediaVolumeDevices.contains(device)) {
|
||||
index = safeMediaVolumeIndex(device);
|
||||
} else {
|
||||
index = streamState.getMaxIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
|
||||
mVolumeController.postDisplaySafeVolumeWarning(flags);
|
||||
mPendingVolumeCommand = new StreamVolumeCommand(
|
||||
streamType, index, flags, device);
|
||||
} else {
|
||||
onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
|
||||
index = mStreamStates[streamType].getIndex(device);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mSoundDoseHelper.willDisplayWarningAfterCheckVolume(streamType, index, device,
|
||||
flags)) {
|
||||
onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
|
||||
index = mStreamStates[streamType].getIndex(device);
|
||||
}
|
||||
|
||||
synchronized (mHdmiClientLock) {
|
||||
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
|
||||
&& (oldIndex != index)) {
|
||||
@@ -5846,14 +5795,8 @@ public class AudioService extends IAudioService.Stub
|
||||
checkAllAliasStreamVolumes();
|
||||
checkMuteAffectedStreams();
|
||||
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
mMusicActiveMs = MathUtils.constrain(mSettings.getSecureIntForUser(mContentResolver,
|
||||
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, 0, UserHandle.USER_CURRENT),
|
||||
0, UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX);
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
|
||||
enforceSafeMediaVolume(TAG);
|
||||
}
|
||||
}
|
||||
mSoundDoseHelper.restoreMusicActiveMs();
|
||||
mSoundDoseHelper.enforceSafeMediaVolumeIfActive(TAG);
|
||||
|
||||
readVolumeGroupsSettings();
|
||||
|
||||
@@ -6189,139 +6132,6 @@ public class AudioService extends IAudioService.Stub
|
||||
return mContentResolver;
|
||||
}
|
||||
|
||||
private void scheduleMusicActiveCheck() {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
cancelMusicActiveCheck();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelMusicActiveCheck() {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if (mMusicActiveIntent != null) {
|
||||
mAlarmManager.cancel(mMusicActiveIntent);
|
||||
mMusicActiveIntent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
private void onCheckMusicActive(String caller) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
|
||||
int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
|
||||
if (mSafeMediaVolumeDevices.contains(device)
|
||||
&& mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
|
||||
scheduleMusicActiveCheck();
|
||||
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
|
||||
if (index > safeMediaVolumeIndex(device)) {
|
||||
// Approximate cumulative active music time
|
||||
long curTimeMs = SystemClock.elapsedRealtime();
|
||||
if (mLastMusicActiveTimeMs != 0) {
|
||||
mMusicActiveMs += (int) (curTimeMs - mLastMusicActiveTimeMs);
|
||||
}
|
||||
mLastMusicActiveTimeMs = curTimeMs;
|
||||
Log.i(TAG, "onCheckMusicActive() mMusicActiveMs: " + mMusicActiveMs);
|
||||
if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
|
||||
setSafeMediaVolumeEnabled(true, caller);
|
||||
mMusicActiveMs = 0;
|
||||
}
|
||||
saveMusicActiveMs();
|
||||
}
|
||||
} else {
|
||||
cancelMusicActiveCheck();
|
||||
mLastMusicActiveTimeMs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMusicActiveMs() {
|
||||
mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget();
|
||||
}
|
||||
|
||||
private int getSafeUsbMediaVolumeIndex() {
|
||||
// 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(
|
||||
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);
|
||||
if (Float.isNaN(gainDB)) {
|
||||
//keep last min in case of read error
|
||||
break;
|
||||
} else if (gainDB == mSafeUsbMediaVolumeDbfs) {
|
||||
min = index;
|
||||
break;
|
||||
} else if (gainDB < mSafeUsbMediaVolumeDbfs) {
|
||||
min = index;
|
||||
} else {
|
||||
max = index;
|
||||
}
|
||||
}
|
||||
return min * 10;
|
||||
}
|
||||
|
||||
private void onConfigureSafeVolume(boolean force, String caller) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
int mcc = mContext.getResources().getConfiguration().mcc;
|
||||
if ((mMcc != mcc) || ((mMcc == 0) && force)) {
|
||||
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
|
||||
|
||||
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
|
||||
|
||||
boolean safeMediaVolumeEnabled =
|
||||
SystemProperties.getBoolean("audio.safemedia.force", false)
|
||||
|| mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_safe_media_volume_enabled);
|
||||
|
||||
boolean safeMediaVolumeBypass =
|
||||
SystemProperties.getBoolean("audio.safemedia.bypass", false);
|
||||
|
||||
// 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) {
|
||||
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
|
||||
// it to "active".
|
||||
if (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_INACTIVE) {
|
||||
if (mMusicActiveMs == 0) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
|
||||
enforceSafeMediaVolume(caller);
|
||||
} else {
|
||||
// We have existing playback time recorded, already confirmed.
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
|
||||
mLastMusicActiveTimeMs = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
persistedState = SAFE_MEDIA_VOLUME_DISABLED;
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
|
||||
}
|
||||
mMcc = mcc;
|
||||
sendMsg(mAudioHandler,
|
||||
MSG_PERSIST_SAFE_VOLUME_STATE,
|
||||
SENDMSG_QUEUE,
|
||||
persistedState,
|
||||
0,
|
||||
null,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Internal methods
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -7711,7 +7521,7 @@ public class AudioService extends IAudioService.Stub
|
||||
// 2 mSetModeLock
|
||||
// 3 mSettingsLock
|
||||
// 4 VolumeStreamState.class
|
||||
private class VolumeStreamState {
|
||||
/*package*/ class VolumeStreamState {
|
||||
private final int mStreamType;
|
||||
private int mIndexMin;
|
||||
// min index when user doesn't have permission to change audio settings
|
||||
@@ -8364,8 +8174,8 @@ public class AudioService extends IAudioService.Stub
|
||||
final VolumeStreamState streamState = mStreamStates[update.mStreamType];
|
||||
if (update.hasVolumeIndex()) {
|
||||
int index = update.getVolumeIndex();
|
||||
if (!checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
|
||||
index = safeMediaVolumeIndex(update.mDevice);
|
||||
if (!mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
|
||||
index = mSoundDoseHelper.safeMediaVolumeIndex(update.mDevice);
|
||||
}
|
||||
streamState.setIndex(index, update.mDevice, update.mCaller,
|
||||
// trusted as index is always validated before message is posted
|
||||
@@ -8415,7 +8225,7 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
|
||||
/** Handles internal volume messages in separate volume thread. */
|
||||
private class AudioHandler extends Handler {
|
||||
/*package*/ class AudioHandler extends Handler {
|
||||
|
||||
AudioHandler() {
|
||||
super();
|
||||
@@ -8462,12 +8272,6 @@ public class AudioService extends IAudioService.Stub
|
||||
mSettings.putGlobalInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
|
||||
}
|
||||
|
||||
private void onPersistSafeVolumeState(int state) {
|
||||
mSettings.putGlobalInt(mContentResolver,
|
||||
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
|
||||
state);
|
||||
}
|
||||
|
||||
private void onNotifyVolumeEvent(@NonNull IAudioPolicyCallback apc,
|
||||
@AudioManager.VolumeAdjustment int direction) {
|
||||
try {
|
||||
@@ -8585,19 +8389,6 @@ public class AudioService extends IAudioService.Stub
|
||||
mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
|
||||
break;
|
||||
|
||||
case MSG_CHECK_MUSIC_ACTIVE:
|
||||
onCheckMusicActive((String) msg.obj);
|
||||
break;
|
||||
|
||||
case MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED:
|
||||
case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
|
||||
onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED),
|
||||
(String) msg.obj);
|
||||
break;
|
||||
case MSG_PERSIST_SAFE_VOLUME_STATE:
|
||||
onPersistSafeVolumeState(msg.arg1);
|
||||
break;
|
||||
|
||||
case MSG_SYSTEM_READY:
|
||||
onSystemReady();
|
||||
break;
|
||||
@@ -8610,13 +8401,6 @@ public class AudioService extends IAudioService.Stub
|
||||
onAccessoryPlugMediaUnmute(msg.arg1);
|
||||
break;
|
||||
|
||||
case MSG_PERSIST_MUSIC_ACTIVE_MS:
|
||||
final int musicActiveMs = msg.arg1;
|
||||
mSettings.putSecureIntForUser(mContentResolver,
|
||||
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, musicActiveMs,
|
||||
UserHandle.USER_CURRENT);
|
||||
break;
|
||||
|
||||
case MSG_UNMUTE_STREAM:
|
||||
onUnmuteStream(msg.arg1, msg.arg2);
|
||||
break;
|
||||
@@ -8746,6 +8530,12 @@ public class AudioService extends IAudioService.Stub
|
||||
case MSG_NO_LOG_FOR_PLAYER_I:
|
||||
mPlaybackMonitor.ignorePlayerIId(msg.arg1);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (msg.what >= SAFE_MEDIA_VOLUME_MSG_START) {
|
||||
// msg could be for the SoundDoseHelper
|
||||
mSoundDoseHelper.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8862,8 +8652,8 @@ public class AudioService extends IAudioService.Stub
|
||||
/** only public for mocking/spying, do not call outside of AudioService */
|
||||
@VisibleForTesting
|
||||
public void checkMusicActive(int deviceType, String caller) {
|
||||
if (mSafeMediaVolumeDevices.contains(deviceType)) {
|
||||
scheduleMusicActiveCheck();
|
||||
if (mSoundDoseHelper.safeDevicesContains(deviceType)) {
|
||||
mSoundDoseHelper.scheduleMusicActiveCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8997,7 +8787,8 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
} else if (action.equals(ACTION_CHECK_MUSIC_ACTIVE)) {
|
||||
onCheckMusicActive(ACTION_CHECK_MUSIC_ACTIVE);
|
||||
mSoundDoseHelper.onCheckMusicActive(ACTION_CHECK_MUSIC_ACTIVE,
|
||||
mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0));
|
||||
}
|
||||
}
|
||||
} // end class AudioServiceBroadcastReceiver
|
||||
@@ -9883,13 +9674,7 @@ public class AudioService extends IAudioService.Stub
|
||||
// reading new configuration "safely" (i.e. under try catch) in case anything
|
||||
// goes wrong.
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
sendMsg(mAudioHandler,
|
||||
MSG_CONFIGURE_SAFE_MEDIA_VOLUME,
|
||||
SENDMSG_REPLACE,
|
||||
0,
|
||||
0,
|
||||
TAG,
|
||||
0);
|
||||
mSoundDoseHelper.configureSafeMediaVolume(/*forced*/false, TAG);
|
||||
|
||||
boolean cameraSoundForced = readCameraSoundForced();
|
||||
synchronized (mSettingsLock) {
|
||||
@@ -9947,143 +9732,10 @@ public class AudioService extends IAudioService.Stub
|
||||
return mDeviceBroker.startWatchingRoutes(observer);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================================
|
||||
// Safe media volume management.
|
||||
// MUSIC stream volume level is limited when headphones are connected according to safety
|
||||
// regulation. When the user attempts to raise the volume above the limit, a warning is
|
||||
// displayed and the user has to acknowlegde before the volume is actually changed.
|
||||
// The volume index corresponding to the limit is stored in config_safe_media_volume_index
|
||||
// property. Platforms with a different limit must set this property accordingly in their
|
||||
// overlay.
|
||||
//==========================================================================================
|
||||
|
||||
// mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
|
||||
// It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
|
||||
// or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
|
||||
// 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).
|
||||
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
|
||||
private static final int SAFE_MEDIA_VOLUME_ACTIVE = 3; // unconfirmed
|
||||
private int mSafeMediaVolumeState;
|
||||
private final Object mSafeMediaVolumeStateLock = new Object();
|
||||
|
||||
private int mMcc = 0;
|
||||
// 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
|
||||
// property, divided by 100.0.
|
||||
private float mSafeUsbMediaVolumeDbfs;
|
||||
// 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,
|
||||
/*package*/ final Set<Integer> mSafeMediaVolumeDevices = new HashSet<>(
|
||||
Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
|
||||
AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET));
|
||||
// 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
|
||||
// automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
|
||||
private int mMusicActiveMs;
|
||||
private long mLastMusicActiveTimeMs = 0;
|
||||
private PendingIntent mMusicActiveIntent = null;
|
||||
private AlarmManager mAlarmManager;
|
||||
|
||||
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
|
||||
private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
|
||||
private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
|
||||
// check playback or record activity every 6 seconds for UIDs owning mode IN_COMMUNICATION
|
||||
private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 6000;
|
||||
|
||||
private static final String ACTION_CHECK_MUSIC_ACTIVE =
|
||||
AudioService.class.getSimpleName() + ".CHECK_MUSIC_ACTIVE";
|
||||
private static final int REQUEST_CODE_CHECK_MUSIC_ACTIVE = 1;
|
||||
|
||||
private int safeMediaVolumeIndex(int device) {
|
||||
if (!mSafeMediaVolumeDevices.contains(device)) {
|
||||
return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
|
||||
}
|
||||
if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
|
||||
return mSafeUsbMediaVolumeIndex;
|
||||
} else {
|
||||
return mSafeMediaVolumeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void setSafeMediaVolumeEnabled(boolean on, String caller) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
|
||||
(mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
|
||||
if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
|
||||
enforceSafeMediaVolume(caller);
|
||||
} else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
|
||||
mMusicActiveMs = 1; // nonzero = confirmed
|
||||
mLastMusicActiveTimeMs = 0;
|
||||
saveMusicActiveMs();
|
||||
scheduleMusicActiveCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceSafeMediaVolume(String caller) {
|
||||
VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
|
||||
Set<Integer> devices = mSafeMediaVolumeDevices;
|
||||
|
||||
for (int device : devices) {
|
||||
int index = streamState.getIndex(device);
|
||||
if (index > safeMediaVolumeIndex(device)) {
|
||||
streamState.setIndex(safeMediaVolumeIndex(device), device, caller,
|
||||
true /*hasModifyAudioSettings*/);
|
||||
sendMsg(mAudioHandler,
|
||||
MSG_SET_DEVICE_VOLUME,
|
||||
SENDMSG_QUEUE,
|
||||
device,
|
||||
0,
|
||||
streamState,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSafeMediaVolume(int streamType, int index, int device) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)
|
||||
&& (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC)
|
||||
&& (mSafeMediaVolumeDevices.contains(device))
|
||||
&& (index > safeMediaVolumeIndex(device))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableSafeMediaVolume(String callingPackage) {
|
||||
enforceVolumeController("disable the safe media volume");
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
setSafeMediaVolumeEnabled(false, callingPackage);
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
if (mPendingVolumeCommand != null) {
|
||||
onSetStreamVolume(mPendingVolumeCommand.mStreamType,
|
||||
mPendingVolumeCommand.mIndex,
|
||||
mPendingVolumeCommand.mFlags,
|
||||
mPendingVolumeCommand.mDevice,
|
||||
callingPackage, true /*hasModifyAudioSettings*/);
|
||||
mPendingVolumeCommand = null;
|
||||
}
|
||||
}
|
||||
mSoundDoseHelper.disableSafeMediaVolume(callingPackage);
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
@@ -10411,15 +10063,8 @@ public class AudioService extends IAudioService.Stub
|
||||
|
||||
pw.println("\nOther state:");
|
||||
pw.print(" mVolumeController="); pw.println(mVolumeController);
|
||||
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);
|
||||
mSoundDoseHelper.dump(pw);
|
||||
pw.print(" sIndependentA11yVolume="); pw.println(sIndependentA11yVolume);
|
||||
pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand);
|
||||
pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs);
|
||||
pw.print(" mMcc="); pw.println(mMcc);
|
||||
pw.print(" mCameraSoundForced="); pw.println(isCameraSoundForced());
|
||||
pw.print(" mHasVibrator="); pw.println(mHasVibrator);
|
||||
pw.print(" mVolumePolicy="); pw.println(mVolumePolicy);
|
||||
@@ -10520,16 +10165,6 @@ public class AudioService extends IAudioService.Stub
|
||||
private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE
|
||||
+ MediaMetrics.SEPARATOR;
|
||||
|
||||
private static String safeMediaVolumeStateToString(int state) {
|
||||
switch(state) {
|
||||
case SAFE_MEDIA_VOLUME_NOT_CONFIGURED: return "SAFE_MEDIA_VOLUME_NOT_CONFIGURED";
|
||||
case SAFE_MEDIA_VOLUME_DISABLED: return "SAFE_MEDIA_VOLUME_DISABLED";
|
||||
case SAFE_MEDIA_VOLUME_INACTIVE: return "SAFE_MEDIA_VOLUME_INACTIVE";
|
||||
case SAFE_MEDIA_VOLUME_ACTIVE: return "SAFE_MEDIA_VOLUME_ACTIVE";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Inform AudioFlinger of our device's low RAM attribute
|
||||
private static void readAndSetLowRamDevice()
|
||||
{
|
||||
@@ -10609,7 +10244,14 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
public class VolumeController {
|
||||
/** Interface used for enforcing the safe hearing standard. */
|
||||
public interface ISafeHearingVolumeController {
|
||||
/** Displays an instructional safeguard as required by the safe hearing standard. */
|
||||
void postDisplaySafeVolumeWarning(int flags);
|
||||
}
|
||||
|
||||
/** Wrapper which encapsulates the {@link IVolumeController} functionality. */
|
||||
public class VolumeController implements ISafeHearingVolumeController {
|
||||
private static final String TAG = "VolumeController";
|
||||
|
||||
private IVolumeController mController;
|
||||
@@ -10696,6 +10338,7 @@ public class AudioService extends IAudioService.Stub
|
||||
return "VolumeController(" + asBinder() + ",mVisible=" + mVisible + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDisplaySafeVolumeWarning(int flags) {
|
||||
if (mController == null)
|
||||
return;
|
||||
|
||||
519
services/core/java/com/android/server/audio/SoundDoseHelper.java
Normal file
519
services/core/java/com/android/server/audio/SoundDoseHelper.java
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.audio;
|
||||
|
||||
import static com.android.server.audio.AudioService.MAX_STREAM_VOLUME;
|
||||
import static com.android.server.audio.AudioService.MIN_STREAM_VOLUME;
|
||||
import static com.android.server.audio.AudioService.MSG_SET_DEVICE_VOLUME;
|
||||
import static com.android.server.audio.AudioService.SAFE_MEDIA_VOLUME_MSG_START;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioSystem;
|
||||
import android.os.Binder;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.audio.AudioService.AudioHandler;
|
||||
import com.android.server.audio.AudioService.ISafeHearingVolumeController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Safe media volume management.
|
||||
* MUSIC stream volume level is limited when headphones are connected according to safety
|
||||
* regulation. When the user attempts to raise the volume above the limit, a warning is
|
||||
* displayed and the user has to acknowledge before the volume is actually changed.
|
||||
* The volume index corresponding to the limit is stored in config_safe_media_volume_index
|
||||
* property. Platforms with a different limit must set this property accordingly in their
|
||||
* overlay.
|
||||
*/
|
||||
public class SoundDoseHelper {
|
||||
private static final String TAG = "AS.SoundDoseHelper";
|
||||
|
||||
/*package*/ static final String ACTION_CHECK_MUSIC_ACTIVE =
|
||||
AudioService.class.getSimpleName() + ".CHECK_MUSIC_ACTIVE";
|
||||
|
||||
/** Flag to enable/disable the sound dose computation. */
|
||||
private static final boolean USE_CSD_FOR_SAFE_HEARING = false;
|
||||
|
||||
// mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
|
||||
// It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
|
||||
// or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
|
||||
// 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).
|
||||
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
|
||||
private static final int SAFE_MEDIA_VOLUME_ACTIVE = 3; // unconfirmed
|
||||
|
||||
private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = SAFE_MEDIA_VOLUME_MSG_START + 1;
|
||||
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 UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
|
||||
|
||||
// 30s after boot completed
|
||||
private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000;
|
||||
|
||||
private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
|
||||
private static final int REQUEST_CODE_CHECK_MUSIC_ACTIVE = 1;
|
||||
|
||||
private int mMcc = 0;
|
||||
|
||||
final Object mSafeMediaVolumeStateLock = new Object();
|
||||
private int mSafeMediaVolumeState;
|
||||
|
||||
// Used when safe volume warning message display is requested by setStreamVolume(). In this
|
||||
// case, the new requested volume, stream type and device are stored in mPendingVolumeCommand
|
||||
// and used later when/if disableSafeMediaVolume() is called.
|
||||
private StreamVolumeCommand mPendingVolumeCommand;
|
||||
|
||||
// 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
|
||||
// property, divided by 100.0.
|
||||
private float mSafeUsbMediaVolumeDbfs;
|
||||
|
||||
// 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));
|
||||
|
||||
|
||||
// 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
|
||||
// automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
|
||||
private int mMusicActiveMs;
|
||||
private long mLastMusicActiveTimeMs = 0;
|
||||
private PendingIntent mMusicActiveIntent = null;
|
||||
private final AlarmManager mAlarmManager;
|
||||
|
||||
@NonNull private final AudioService mAudioService;
|
||||
@NonNull private final SettingsAdapter mSettings;
|
||||
@NonNull private final AudioHandler mAudioHandler;
|
||||
@NonNull private final ISafeHearingVolumeController mVolumeController;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
SoundDoseHelper(@NonNull AudioService audioService, Context context,
|
||||
@NonNull AudioHandler audioHandler,
|
||||
@NonNull SettingsAdapter settings,
|
||||
@NonNull ISafeHearingVolumeController volumeController) {
|
||||
mAudioService = audioService;
|
||||
mAudioHandler = audioHandler;
|
||||
mSettings = settings;
|
||||
mVolumeController = volumeController;
|
||||
|
||||
mContext = context;
|
||||
|
||||
mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
|
||||
Settings.Global.AUDIO_SAFE_VOLUME_STATE, 0);
|
||||
if (USE_CSD_FOR_SAFE_HEARING) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
|
||||
}
|
||||
|
||||
// The default safe volume index read here will be replaced by the actual value when
|
||||
// the mcc is read by onConfigureSafeVolume()
|
||||
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
|
||||
|
||||
mAlarmManager = (AlarmManager) mContext.getSystemService(
|
||||
Context.ALARM_SERVICE);
|
||||
}
|
||||
|
||||
/*package*/ int safeMediaVolumeIndex(int device) {
|
||||
if (!mSafeMediaVolumeDevices.contains(device)) {
|
||||
return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
|
||||
}
|
||||
if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
|
||||
return mSafeUsbMediaVolumeIndex;
|
||||
} else {
|
||||
return mSafeMediaVolumeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void restoreMusicActiveMs() {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
mMusicActiveMs = MathUtils.constrain(
|
||||
mSettings.getSecureIntForUser(mAudioService.getContentResolver(),
|
||||
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, 0,
|
||||
UserHandle.USER_CURRENT),
|
||||
0, UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void enforceSafeMediaVolumeIfActive(String caller) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
|
||||
enforceSafeMediaVolume(caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*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);
|
||||
if (index > safeIndex) {
|
||||
streamState.setIndex(safeIndex, device, caller, true /*hasModifyAudioSettings*/);
|
||||
mAudioHandler.sendMessageAtTime(
|
||||
mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, device, /*arg2=*/0,
|
||||
streamState), /*delay=*/0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ boolean checkSafeMediaVolume(int streamType, int index, int device) {
|
||||
boolean result;
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
result = checkSafeMediaVolume_l(streamType, index, device);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@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))
|
||||
|| USE_CSD_FOR_SAFE_HEARING;
|
||||
}
|
||||
|
||||
/*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device,
|
||||
int flags) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if (!checkSafeMediaVolume_l(streamType, index, device)) {
|
||||
mVolumeController.postDisplaySafeVolumeWarning(flags);
|
||||
mPendingVolumeCommand = new StreamVolumeCommand(
|
||||
streamType, index, flags, device);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*package*/ void disableSafeMediaVolume(String callingPackage) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
setSafeMediaVolumeEnabled(false, callingPackage);
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
|
||||
if (mPendingVolumeCommand != null) {
|
||||
mAudioService.onSetStreamVolume(mPendingVolumeCommand.mStreamType,
|
||||
mPendingVolumeCommand.mIndex,
|
||||
mPendingVolumeCommand.mFlags,
|
||||
mPendingVolumeCommand.mDevice,
|
||||
callingPackage, true /*hasModifyAudioSettings*/);
|
||||
mPendingVolumeCommand = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void scheduleMusicActiveCheck() {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
cancelMusicActiveCheck();
|
||||
if (!USE_CSD_FOR_SAFE_HEARING) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void onCheckMusicActive(String caller, boolean isStreamActive) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
|
||||
int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC);
|
||||
if (mSafeMediaVolumeDevices.contains(device) && isStreamActive) {
|
||||
scheduleMusicActiveCheck();
|
||||
int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
|
||||
device);
|
||||
if (index > safeMediaVolumeIndex(device)) {
|
||||
// Approximate cumulative active music time
|
||||
long curTimeMs = SystemClock.elapsedRealtime();
|
||||
if (mLastMusicActiveTimeMs != 0) {
|
||||
mMusicActiveMs += (int) (curTimeMs - mLastMusicActiveTimeMs);
|
||||
}
|
||||
mLastMusicActiveTimeMs = curTimeMs;
|
||||
Log.i(TAG, "onCheckMusicActive() mMusicActiveMs: " + mMusicActiveMs);
|
||||
if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
|
||||
setSafeMediaVolumeEnabled(true, caller);
|
||||
mMusicActiveMs = 0;
|
||||
}
|
||||
saveMusicActiveMs();
|
||||
}
|
||||
} else {
|
||||
cancelMusicActiveCheck();
|
||||
mLastMusicActiveTimeMs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void configureSafeMediaVolume(boolean forced, String caller) {
|
||||
int msg = MSG_CONFIGURE_SAFE_MEDIA_VOLUME;
|
||||
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*/ int getSafeMediaVolumeIndex(int device) {
|
||||
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && mSafeMediaVolumeDevices.contains(
|
||||
device)) {
|
||||
return safeMediaVolumeIndex(device);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ boolean raiseVolumeDisplaySafeMediaVolume(int streamType, int index, int device,
|
||||
int flags) {
|
||||
if (checkSafeMediaVolume(streamType, index, device)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mVolumeController.postDisplaySafeVolumeWarning(flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*package*/ boolean safeDevicesContains(int device) {
|
||||
return mSafeMediaVolumeDevices.contains(device);
|
||||
}
|
||||
|
||||
/*package*/ void invalidatPendingVolumeCommand() {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
mPendingVolumeCommand = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
|
||||
onConfigureSafeVolume((msg.arg1 == 1), (String) msg.obj);
|
||||
break;
|
||||
case MSG_PERSIST_SAFE_VOLUME_STATE:
|
||||
onPersistSafeVolumeState(msg.arg1);
|
||||
break;
|
||||
case MSG_PERSIST_MUSIC_ACTIVE_MS:
|
||||
final int musicActiveMs = msg.arg1;
|
||||
mSettings.putSecureIntForUser(mAudioService.getContentResolver(),
|
||||
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, musicActiveMs,
|
||||
UserHandle.USER_CURRENT);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*package*/ void dump(PrintWriter pw) {
|
||||
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);
|
||||
pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs);
|
||||
pw.print(" mMcc="); pw.println(mMcc);
|
||||
pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand);
|
||||
}
|
||||
|
||||
private void onConfigureSafeVolume(boolean force, String caller) {
|
||||
synchronized (mSafeMediaVolumeStateLock) {
|
||||
int mcc = mContext.getResources().getConfiguration().mcc;
|
||||
if ((mMcc != mcc) || ((mMcc == 0) && force)) {
|
||||
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
|
||||
|
||||
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
|
||||
|
||||
boolean safeMediaVolumeEnabled =
|
||||
SystemProperties.getBoolean("audio.safemedia.force", false)
|
||||
|| mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_safe_media_volume_enabled);
|
||||
|
||||
boolean safeMediaVolumeBypass =
|
||||
SystemProperties.getBoolean("audio.safemedia.bypass", false);
|
||||
|
||||
// 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 && !USE_CSD_FOR_SAFE_HEARING) {
|
||||
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
|
||||
// it to "active".
|
||||
if (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_INACTIVE) {
|
||||
if (mMusicActiveMs == 0) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
|
||||
enforceSafeMediaVolume(caller);
|
||||
} else {
|
||||
// We have existing playback time recorded, already confirmed.
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
|
||||
mLastMusicActiveTimeMs = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
persistedState = SAFE_MEDIA_VOLUME_DISABLED;
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
|
||||
}
|
||||
mMcc = mcc;
|
||||
mAudioHandler.sendMessageAtTime(
|
||||
mAudioHandler.obtainMessage(MSG_PERSIST_SAFE_VOLUME_STATE,
|
||||
persistedState, /*arg2=*/0,
|
||||
/*obj=*/null), /*delay=*/0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mSafeMediaVolumeStateLock")
|
||||
private void setSafeMediaVolumeEnabled(boolean on, String caller) {
|
||||
if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) && (mSafeMediaVolumeState
|
||||
!= SAFE_MEDIA_VOLUME_DISABLED)) {
|
||||
if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
|
||||
enforceSafeMediaVolume(caller);
|
||||
} else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
|
||||
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
|
||||
mMusicActiveMs = 1; // nonzero = confirmed
|
||||
mLastMusicActiveTimeMs = 0;
|
||||
saveMusicActiveMs();
|
||||
scheduleMusicActiveCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mSafeMediaVolumeStateLock")
|
||||
private void cancelMusicActiveCheck() {
|
||||
if (mMusicActiveIntent != null) {
|
||||
mAlarmManager.cancel(mMusicActiveIntent);
|
||||
mMusicActiveIntent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mSafeMediaVolumeStateLock")
|
||||
private void saveMusicActiveMs() {
|
||||
mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget();
|
||||
}
|
||||
|
||||
private int getSafeUsbMediaVolumeIndex() {
|
||||
// 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(
|
||||
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);
|
||||
if (Float.isNaN(gainDB)) {
|
||||
//keep last min in case of read error
|
||||
break;
|
||||
} else if (gainDB == mSafeUsbMediaVolumeDbfs) {
|
||||
min = index;
|
||||
break;
|
||||
} else if (gainDB < mSafeUsbMediaVolumeDbfs) {
|
||||
min = index;
|
||||
} else {
|
||||
max = index;
|
||||
}
|
||||
}
|
||||
return min * 10;
|
||||
}
|
||||
|
||||
private void onPersistSafeVolumeState(int state) {
|
||||
mSettings.putGlobalInt(mAudioService.getContentResolver(),
|
||||
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
|
||||
state);
|
||||
}
|
||||
|
||||
private static String safeMediaVolumeStateToString(int state) {
|
||||
switch(state) {
|
||||
case SAFE_MEDIA_VOLUME_NOT_CONFIGURED: return "SAFE_MEDIA_VOLUME_NOT_CONFIGURED";
|
||||
case SAFE_MEDIA_VOLUME_DISABLED: return "SAFE_MEDIA_VOLUME_DISABLED";
|
||||
case SAFE_MEDIA_VOLUME_INACTIVE: return "SAFE_MEDIA_VOLUME_INACTIVE";
|
||||
case SAFE_MEDIA_VOLUME_ACTIVE: return "SAFE_MEDIA_VOLUME_ACTIVE";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// StreamVolumeCommand contains the information needed to defer the process of
|
||||
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
|
||||
private static class StreamVolumeCommand {
|
||||
public final int mStreamType;
|
||||
public final int mIndex;
|
||||
public final int mFlags;
|
||||
public final int mDevice;
|
||||
|
||||
StreamVolumeCommand(int streamType, int index, int flags, int device) {
|
||||
mStreamType = streamType;
|
||||
mIndex = index;
|
||||
mFlags = flags;
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder().append("{streamType=").append(mStreamType).append(",index=")
|
||||
.append(mIndex).append(",flags=").append(mFlags).append(",device=")
|
||||
.append(mDevice).append('}').toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user