Loading core/java/android/view/VolumePanel.java +61 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import com.android.internal.R; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface.OnDismissListener; import android.content.BroadcastReceiver; Loading Loading @@ -92,6 +93,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private static final int MSG_REMOTE_VOLUME_CHANGED = 8; private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9; private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10; private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11; // Pseudo stream type for master volume private static final int STREAM_MASTER = -100; Loading Loading @@ -211,6 +213,31 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private ToneGenerator mToneGenerators[]; private Vibrator mVibrator; private static AlertDialog sConfirmSafeVolumeDialog; private static class WarningDialogReceiver extends BroadcastReceiver implements DialogInterface.OnDismissListener { private Context mContext; private Dialog mDialog; WarningDialogReceiver(Context context, Dialog dialog) { mContext = context; mDialog = dialog; IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { mDialog.cancel(); } public void onDismiss(DialogInterface unused) { mContext.unregisterReceiver(this); } } public VolumePanel(final Context context, AudioService volumeService) { mContext = context; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); Loading Loading @@ -528,6 +555,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie postMuteChanged(STREAM_MASTER, flags); } public void postDisplaySafeVolumeWarning() { obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, 0, 0).sendToTarget(); } /** * Override this if you have other work to do when the volume changes (for * example, vibrating, playing a sound, etc.). Make sure to call through to Loading Loading @@ -796,6 +827,32 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } } protected void onDisplaySafeVolumeWarning() { if (sConfirmSafeVolumeDialog != null) { sConfirmSafeVolumeDialog.dismiss(); } sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext) .setTitle(android.R.string.dialog_alert_title) .setMessage(com.android.internal.R.string.safe_media_volume_warning) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mAudioService.disableSafeMediaVolume(); } }) .setNegativeButton(com.android.internal.R.string.no, null) .setIconAttribute(android.R.attr.alertDialogIcon) .create(); final WarningDialogReceiver warning = new WarningDialogReceiver(mContext, sConfirmSafeVolumeDialog); sConfirmSafeVolumeDialog.setOnDismissListener(warning); sConfirmSafeVolumeDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmSafeVolumeDialog.show(); } /** * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. */ Loading Loading @@ -910,6 +967,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie case MSG_SLIDER_VISIBILITY_CHANGED: onSliderVisibilityChanged(msg.arg1, msg.arg2); break; case MSG_DISPLAY_SAFE_VOLUME_WARNING: onDisplaySafeVolumeWarning(); break; } } Loading core/res/res/values/config.xml +6 −0 Original line number Diff line number Diff line Loading @@ -937,4 +937,10 @@ larger than the minimum reported touchMajor/touchMinor values reported by the hardware. --> <dimen name="config_minScalingSpan">25mm</dimen> <!-- Safe headphone volume index. When music stream volume is below this index the SPL on headphone output is compliant to EN 60950 requirements for portable music players. --> <integer name="config_safe_media_volume_index">10</integer> </resources> core/res/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -3887,6 +3887,12 @@ Try again in <xliff:g id="number">%d</xliff:g> seconds. </string> <!-- Message shown in dialog when user is attempting to set the music volume above the recommended maximum level for headphones --> <string name="safe_media_volume_warning" product="default"> "Raise volume above the recommended level?" </string> <string name="kg_temp_back_string"> < </string> <!-- TODO: remove this --> </resources> core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -283,6 +283,7 @@ <java-symbol type="integer" name="config_soundEffectVolumeDb" /> <java-symbol type="integer" name="config_lockSoundVolumeDb" /> <java-symbol type="integer" name="config_multiuserMaximumUsers" /> <java-symbol type="integer" name="config_safe_media_volume_index" /> <java-symbol type="color" name="tab_indicator_text_v4" /> Loading Loading @@ -814,6 +815,7 @@ <java-symbol type="string" name="default_audio_route_name_dock_speakers" /> <java-symbol type="string" name="default_audio_route_name_hdmi" /> <java-symbol type="string" name="default_audio_route_category_name" /> <java-symbol type="string" name="safe_media_volume_warning" /> <java-symbol type="plurals" name="abbrev_in_num_days" /> <java-symbol type="plurals" name="abbrev_in_num_hours" /> Loading media/java/android/media/AudioService.java +164 −1 Original line number Diff line number Diff line Loading @@ -153,6 +153,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // end of messages handled under wakelock private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection private static final int MSG_SET_FORCE_RSX_USE = 24; // force remote submix audio routing private static final int MSG_CHECK_MUSIC_ACTIVE = 25; // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be // persisted Loading Loading @@ -430,6 +431,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mContentResolver = context.getContentResolver(); mVoiceCapable = mContext.getResources().getBoolean( com.android.internal.R.bool.config_voice_capable); mSafeMediaVolumeIndex = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_index) * 10; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); Loading @@ -454,6 +457,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { updateStreamVolumeAlias(false /*updateVolumes*/); createStreamStates(); synchronized (mSafeMediaVolumeEnabled) { enforceSafeMediaVolume(); } mMediaServerOk = true; // Call setRingerModeInt() to apply correct mute Loading Loading @@ -738,6 +745,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // convert one UI step (+/-1) into a number of internal units on the stream alias int step = rescaleIndex(10, streamType, streamTypeAlias); if ((direction == AudioManager.ADJUST_RAISE) && !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { return; } // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || Loading Loading @@ -815,12 +827,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished { VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]]; final int device = getDeviceForStream(streamType); // get last audible index if stream is muted, current index otherwise final int oldIndex = streamState.getIndex(device, (streamState.muteCount() != 0) /* lastAudible */); index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]); if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) { return; } // setting volume on master stream type also controls silent mode if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (mStreamVolumeAlias[streamType] == getMasterStreamType())) { Loading Loading @@ -1681,6 +1698,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { checkAllAliasStreamVolumes(); synchronized (mSafeMediaVolumeEnabled) { enforceSafeMediaVolume(); } // apply new ringer mode setRingerModeInt(getRingerMode(), false); } Loading Loading @@ -2138,6 +2159,33 @@ public class AudioService extends IAudioService.Stub implements OnFinished { String.valueOf(address) /*device_address*/); } private void onCheckMusicActive() { synchronized (mSafeMediaVolumeEnabled) { if (!mSafeMediaVolumeEnabled) { int device = getDeviceForStream(AudioSystem.STREAM_MUSIC); if ((device & mSafeMediaVolumeDevices) != 0) { sendMsg(mAudioHandler, MSG_CHECK_MUSIC_ACTIVE, SENDMSG_REPLACE, device, 0, null, MUSIC_ACTIVE_POLL_PERIOD_MS); if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) { // Approximate cumulative active music time mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS; if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) { setSafeMediaVolumeEnabled(true); mMusicActiveMs = 0; mVolumePanel.postDisplaySafeVolumeWarning(); } } } } } } /////////////////////////////////////////////////////////////////////////// // Internal methods /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -2397,6 +2445,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public void setWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { int delay = checkSendBecomingNoisyIntent(device, state); if ((device & mSafeMediaVolumeDevices) != 0) { setSafeMediaVolumeEnabled(state != 0); // insert delay to allow new volume to apply before switching to headphones if ((delay < SAFE_VOLUME_DELAY_MS) && (state != 0)) { delay = SAFE_VOLUME_DELAY_MS; } } queueMsgUnderWakeLock(mAudioHandler, MSG_SET_WIRED_DEVICE_CONNECTION_STATE, device, Loading Loading @@ -3168,6 +3224,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { case MSG_SET_RSX_CONNECTION_STATE: onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/); break; case MSG_CHECK_MUSIC_ACTIVE: onCheckMusicActive(); break; } } } Loading Loading @@ -5415,6 +5475,109 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } //========================================================================================== // 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. //========================================================================================== // mSafeMediaVolumeEnabled indicates whether the media volume is limited over headphones. // It is true by default when headphones or a headset are inserted and can be overriden by // calling AudioService.disableSafeMediaVolume() (when user opts out). private Boolean mSafeMediaVolumeEnabled = new Boolean(false); // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property private final int mSafeMediaVolumeIndex; // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced, private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE; // 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 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_DELAY_MS = 500; // 500ms before switching to headphones private void setSafeMediaVolumeEnabled(boolean on) { synchronized (mSafeMediaVolumeEnabled) { if (on && !mSafeMediaVolumeEnabled) { enforceSafeMediaVolume(); } else if (!on && mSafeMediaVolumeEnabled) { mMusicActiveMs = 0; sendMsg(mAudioHandler, MSG_CHECK_MUSIC_ACTIVE, SENDMSG_REPLACE, 0, 0, null, MUSIC_ACTIVE_POLL_PERIOD_MS); } mSafeMediaVolumeEnabled = on; } } private void enforceSafeMediaVolume() { VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC]; boolean lastAudible = (streamState.muteCount() != 0); int devices = mSafeMediaVolumeDevices; int i = 0; while (devices != 0) { int device = 1 << i++; if ((device & devices) == 0) { continue; } int index = streamState.getIndex(device, lastAudible); if (index > mSafeMediaVolumeIndex) { if (lastAudible) { streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device); sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, SENDMSG_QUEUE, PERSIST_LAST_AUDIBLE, device, streamState, PERSIST_DELAY); } else { streamState.setIndex(mSafeMediaVolumeIndex, device, true); sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, device, 0, streamState, 0); } } devices &= ~device; } } private boolean checkSafeMediaVolume(int streamType, int index, int device) { synchronized (mSafeMediaVolumeEnabled) { if (mSafeMediaVolumeEnabled && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && ((device & mSafeMediaVolumeDevices) != 0) && (index > mSafeMediaVolumeIndex)) { mVolumePanel.postDisplaySafeVolumeWarning(); return false; } return true; } } public void disableSafeMediaVolume() { synchronized (mSafeMediaVolumeEnabled) { setSafeMediaVolumeEnabled(false); } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); Loading Loading
core/java/android/view/VolumePanel.java +61 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import com.android.internal.R; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface.OnDismissListener; import android.content.BroadcastReceiver; Loading Loading @@ -92,6 +93,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private static final int MSG_REMOTE_VOLUME_CHANGED = 8; private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9; private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10; private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11; // Pseudo stream type for master volume private static final int STREAM_MASTER = -100; Loading Loading @@ -211,6 +213,31 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private ToneGenerator mToneGenerators[]; private Vibrator mVibrator; private static AlertDialog sConfirmSafeVolumeDialog; private static class WarningDialogReceiver extends BroadcastReceiver implements DialogInterface.OnDismissListener { private Context mContext; private Dialog mDialog; WarningDialogReceiver(Context context, Dialog dialog) { mContext = context; mDialog = dialog; IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { mDialog.cancel(); } public void onDismiss(DialogInterface unused) { mContext.unregisterReceiver(this); } } public VolumePanel(final Context context, AudioService volumeService) { mContext = context; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); Loading Loading @@ -528,6 +555,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie postMuteChanged(STREAM_MASTER, flags); } public void postDisplaySafeVolumeWarning() { obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, 0, 0).sendToTarget(); } /** * Override this if you have other work to do when the volume changes (for * example, vibrating, playing a sound, etc.). Make sure to call through to Loading Loading @@ -796,6 +827,32 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } } protected void onDisplaySafeVolumeWarning() { if (sConfirmSafeVolumeDialog != null) { sConfirmSafeVolumeDialog.dismiss(); } sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext) .setTitle(android.R.string.dialog_alert_title) .setMessage(com.android.internal.R.string.safe_media_volume_warning) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mAudioService.disableSafeMediaVolume(); } }) .setNegativeButton(com.android.internal.R.string.no, null) .setIconAttribute(android.R.attr.alertDialogIcon) .create(); final WarningDialogReceiver warning = new WarningDialogReceiver(mContext, sConfirmSafeVolumeDialog); sConfirmSafeVolumeDialog.setOnDismissListener(warning); sConfirmSafeVolumeDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmSafeVolumeDialog.show(); } /** * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. */ Loading Loading @@ -910,6 +967,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie case MSG_SLIDER_VISIBILITY_CHANGED: onSliderVisibilityChanged(msg.arg1, msg.arg2); break; case MSG_DISPLAY_SAFE_VOLUME_WARNING: onDisplaySafeVolumeWarning(); break; } } Loading
core/res/res/values/config.xml +6 −0 Original line number Diff line number Diff line Loading @@ -937,4 +937,10 @@ larger than the minimum reported touchMajor/touchMinor values reported by the hardware. --> <dimen name="config_minScalingSpan">25mm</dimen> <!-- Safe headphone volume index. When music stream volume is below this index the SPL on headphone output is compliant to EN 60950 requirements for portable music players. --> <integer name="config_safe_media_volume_index">10</integer> </resources>
core/res/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -3887,6 +3887,12 @@ Try again in <xliff:g id="number">%d</xliff:g> seconds. </string> <!-- Message shown in dialog when user is attempting to set the music volume above the recommended maximum level for headphones --> <string name="safe_media_volume_warning" product="default"> "Raise volume above the recommended level?" </string> <string name="kg_temp_back_string"> < </string> <!-- TODO: remove this --> </resources>
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -283,6 +283,7 @@ <java-symbol type="integer" name="config_soundEffectVolumeDb" /> <java-symbol type="integer" name="config_lockSoundVolumeDb" /> <java-symbol type="integer" name="config_multiuserMaximumUsers" /> <java-symbol type="integer" name="config_safe_media_volume_index" /> <java-symbol type="color" name="tab_indicator_text_v4" /> Loading Loading @@ -814,6 +815,7 @@ <java-symbol type="string" name="default_audio_route_name_dock_speakers" /> <java-symbol type="string" name="default_audio_route_name_hdmi" /> <java-symbol type="string" name="default_audio_route_category_name" /> <java-symbol type="string" name="safe_media_volume_warning" /> <java-symbol type="plurals" name="abbrev_in_num_days" /> <java-symbol type="plurals" name="abbrev_in_num_hours" /> Loading
media/java/android/media/AudioService.java +164 −1 Original line number Diff line number Diff line Loading @@ -153,6 +153,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // end of messages handled under wakelock private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection private static final int MSG_SET_FORCE_RSX_USE = 24; // force remote submix audio routing private static final int MSG_CHECK_MUSIC_ACTIVE = 25; // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be // persisted Loading Loading @@ -430,6 +431,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mContentResolver = context.getContentResolver(); mVoiceCapable = mContext.getResources().getBoolean( com.android.internal.R.bool.config_voice_capable); mSafeMediaVolumeIndex = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_index) * 10; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); Loading @@ -454,6 +457,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { updateStreamVolumeAlias(false /*updateVolumes*/); createStreamStates(); synchronized (mSafeMediaVolumeEnabled) { enforceSafeMediaVolume(); } mMediaServerOk = true; // Call setRingerModeInt() to apply correct mute Loading Loading @@ -738,6 +745,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // convert one UI step (+/-1) into a number of internal units on the stream alias int step = rescaleIndex(10, streamType, streamTypeAlias); if ((direction == AudioManager.ADJUST_RAISE) && !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { return; } // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || Loading Loading @@ -815,12 +827,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished { VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]]; final int device = getDeviceForStream(streamType); // get last audible index if stream is muted, current index otherwise final int oldIndex = streamState.getIndex(device, (streamState.muteCount() != 0) /* lastAudible */); index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]); if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) { return; } // setting volume on master stream type also controls silent mode if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (mStreamVolumeAlias[streamType] == getMasterStreamType())) { Loading Loading @@ -1681,6 +1698,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { checkAllAliasStreamVolumes(); synchronized (mSafeMediaVolumeEnabled) { enforceSafeMediaVolume(); } // apply new ringer mode setRingerModeInt(getRingerMode(), false); } Loading Loading @@ -2138,6 +2159,33 @@ public class AudioService extends IAudioService.Stub implements OnFinished { String.valueOf(address) /*device_address*/); } private void onCheckMusicActive() { synchronized (mSafeMediaVolumeEnabled) { if (!mSafeMediaVolumeEnabled) { int device = getDeviceForStream(AudioSystem.STREAM_MUSIC); if ((device & mSafeMediaVolumeDevices) != 0) { sendMsg(mAudioHandler, MSG_CHECK_MUSIC_ACTIVE, SENDMSG_REPLACE, device, 0, null, MUSIC_ACTIVE_POLL_PERIOD_MS); if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) { // Approximate cumulative active music time mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS; if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) { setSafeMediaVolumeEnabled(true); mMusicActiveMs = 0; mVolumePanel.postDisplaySafeVolumeWarning(); } } } } } } /////////////////////////////////////////////////////////////////////////// // Internal methods /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -2397,6 +2445,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public void setWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { int delay = checkSendBecomingNoisyIntent(device, state); if ((device & mSafeMediaVolumeDevices) != 0) { setSafeMediaVolumeEnabled(state != 0); // insert delay to allow new volume to apply before switching to headphones if ((delay < SAFE_VOLUME_DELAY_MS) && (state != 0)) { delay = SAFE_VOLUME_DELAY_MS; } } queueMsgUnderWakeLock(mAudioHandler, MSG_SET_WIRED_DEVICE_CONNECTION_STATE, device, Loading Loading @@ -3168,6 +3224,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { case MSG_SET_RSX_CONNECTION_STATE: onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/); break; case MSG_CHECK_MUSIC_ACTIVE: onCheckMusicActive(); break; } } } Loading Loading @@ -5415,6 +5475,109 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } //========================================================================================== // 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. //========================================================================================== // mSafeMediaVolumeEnabled indicates whether the media volume is limited over headphones. // It is true by default when headphones or a headset are inserted and can be overriden by // calling AudioService.disableSafeMediaVolume() (when user opts out). private Boolean mSafeMediaVolumeEnabled = new Boolean(false); // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property private final int mSafeMediaVolumeIndex; // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced, private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE; // 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 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_DELAY_MS = 500; // 500ms before switching to headphones private void setSafeMediaVolumeEnabled(boolean on) { synchronized (mSafeMediaVolumeEnabled) { if (on && !mSafeMediaVolumeEnabled) { enforceSafeMediaVolume(); } else if (!on && mSafeMediaVolumeEnabled) { mMusicActiveMs = 0; sendMsg(mAudioHandler, MSG_CHECK_MUSIC_ACTIVE, SENDMSG_REPLACE, 0, 0, null, MUSIC_ACTIVE_POLL_PERIOD_MS); } mSafeMediaVolumeEnabled = on; } } private void enforceSafeMediaVolume() { VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC]; boolean lastAudible = (streamState.muteCount() != 0); int devices = mSafeMediaVolumeDevices; int i = 0; while (devices != 0) { int device = 1 << i++; if ((device & devices) == 0) { continue; } int index = streamState.getIndex(device, lastAudible); if (index > mSafeMediaVolumeIndex) { if (lastAudible) { streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device); sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, SENDMSG_QUEUE, PERSIST_LAST_AUDIBLE, device, streamState, PERSIST_DELAY); } else { streamState.setIndex(mSafeMediaVolumeIndex, device, true); sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, device, 0, streamState, 0); } } devices &= ~device; } } private boolean checkSafeMediaVolume(int streamType, int index, int device) { synchronized (mSafeMediaVolumeEnabled) { if (mSafeMediaVolumeEnabled && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && ((device & mSafeMediaVolumeDevices) != 0) && (index > mSafeMediaVolumeIndex)) { mVolumePanel.postDisplaySafeVolumeWarning(); return false; } return true; } } public void disableSafeMediaVolume() { synchronized (mSafeMediaVolumeEnabled) { setSafeMediaVolumeEnabled(false); } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); Loading