@@ -153,6 +153,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
153153 // end of messages handled under wakelock
154154 private static final int MSG_SET_RSX_CONNECTION_STATE = 23 ; // change remote submix connection
155155 private static final int MSG_SET_FORCE_RSX_USE = 24 ; // force remote submix audio routing
156+ private static final int MSG_CHECK_MUSIC_ACTIVE = 25 ;
156157
157158 // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
158159 // persisted
@@ -430,6 +431,8 @@ public AudioService(Context context) {
430431 mContentResolver = context .getContentResolver ();
431432 mVoiceCapable = mContext .getResources ().getBoolean (
432433 com .android .internal .R .bool .config_voice_capable );
434+ mSafeMediaVolumeIndex = mContext .getResources ().getInteger (
435+ com .android .internal .R .integer .config_safe_media_volume_index ) * 10 ;
433436
434437 PowerManager pm = (PowerManager )context .getSystemService (Context .POWER_SERVICE );
435438 mMediaEventWakeLock = pm .newWakeLock (PowerManager .PARTIAL_WAKE_LOCK , "handleMediaEvent" );
@@ -454,6 +457,10 @@ public AudioService(Context context) {
454457 updateStreamVolumeAlias (false /*updateVolumes*/ );
455458 createStreamStates ();
456459
460+ synchronized (mSafeMediaVolumeEnabled ) {
461+ enforceSafeMediaVolume ();
462+ }
463+
457464 mMediaServerOk = true ;
458465
459466 // Call setRingerModeInt() to apply correct mute
@@ -738,6 +745,11 @@ public void adjustStreamVolume(int streamType, int direction, int flags) {
738745 // convert one UI step (+/-1) into a number of internal units on the stream alias
739746 int step = rescaleIndex (10 , streamType , streamTypeAlias );
740747
748+ if ((direction == AudioManager .ADJUST_RAISE ) &&
749+ !checkSafeMediaVolume (streamTypeAlias , aliasIndex + step , device )) {
750+ return ;
751+ }
752+
741753 // If either the client forces allowing ringer modes for this adjustment,
742754 // or the stream type is one that is affected by ringer modes
743755 if (((flags & AudioManager .FLAG_ALLOW_RINGER_MODES ) != 0 ) ||
@@ -815,12 +827,17 @@ public void setStreamVolume(int streamType, int index, int flags) {
815827 VolumeStreamState streamState = mStreamStates [mStreamVolumeAlias [streamType ]];
816828
817829 final int device = getDeviceForStream (streamType );
830+
818831 // get last audible index if stream is muted, current index otherwise
819832 final int oldIndex = streamState .getIndex (device ,
820833 (streamState .muteCount () != 0 ) /* lastAudible */ );
821834
822835 index = rescaleIndex (index * 10 , streamType , mStreamVolumeAlias [streamType ]);
823836
837+ if (!checkSafeMediaVolume (mStreamVolumeAlias [streamType ], index , device )) {
838+ return ;
839+ }
840+
824841 // setting volume on master stream type also controls silent mode
825842 if (((flags & AudioManager .FLAG_ALLOW_RINGER_MODES ) != 0 ) ||
826843 (mStreamVolumeAlias [streamType ] == getMasterStreamType ())) {
@@ -1681,6 +1698,10 @@ public void reloadAudioSettings() {
16811698
16821699 checkAllAliasStreamVolumes ();
16831700
1701+ synchronized (mSafeMediaVolumeEnabled ) {
1702+ enforceSafeMediaVolume ();
1703+ }
1704+
16841705 // apply new ringer mode
16851706 setRingerModeInt (getRingerMode (), false );
16861707 }
@@ -2138,6 +2159,33 @@ private void onSetRsxConnectionState(int available, int address) {
21382159 String .valueOf (address ) /*device_address*/ );
21392160 }
21402161
2162+ private void onCheckMusicActive () {
2163+ synchronized (mSafeMediaVolumeEnabled ) {
2164+ if (!mSafeMediaVolumeEnabled ) {
2165+ int device = getDeviceForStream (AudioSystem .STREAM_MUSIC );
2166+
2167+ if ((device & mSafeMediaVolumeDevices ) != 0 ) {
2168+ sendMsg (mAudioHandler ,
2169+ MSG_CHECK_MUSIC_ACTIVE ,
2170+ SENDMSG_REPLACE ,
2171+ device ,
2172+ 0 ,
2173+ null ,
2174+ MUSIC_ACTIVE_POLL_PERIOD_MS );
2175+ if (AudioSystem .isStreamActive (AudioSystem .STREAM_MUSIC , 0 )) {
2176+ // Approximate cumulative active music time
2177+ mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS ;
2178+ if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX ) {
2179+ setSafeMediaVolumeEnabled (true );
2180+ mMusicActiveMs = 0 ;
2181+ mVolumePanel .postDisplaySafeVolumeWarning ();
2182+ }
2183+ }
2184+ }
2185+ }
2186+ }
2187+ }
2188+
21412189 ///////////////////////////////////////////////////////////////////////////
21422190 // Internal methods
21432191 ///////////////////////////////////////////////////////////////////////////
@@ -2397,6 +2445,14 @@ private int getDeviceForStream(int stream) {
23972445 public void setWiredDeviceConnectionState (int device , int state , String name ) {
23982446 synchronized (mConnectedDevices ) {
23992447 int delay = checkSendBecomingNoisyIntent (device , state );
2448+ if ((device & mSafeMediaVolumeDevices ) != 0 ) {
2449+ setSafeMediaVolumeEnabled (state != 0 );
2450+ // insert delay to allow new volume to apply before switching to headphones
2451+ if ((delay < SAFE_VOLUME_DELAY_MS ) && (state != 0 )) {
2452+ delay = SAFE_VOLUME_DELAY_MS ;
2453+ }
2454+ }
2455+
24002456 queueMsgUnderWakeLock (mAudioHandler ,
24012457 MSG_SET_WIRED_DEVICE_CONNECTION_STATE ,
24022458 device ,
@@ -3168,6 +3224,10 @@ public void handleMessage(Message msg) {
31683224 case MSG_SET_RSX_CONNECTION_STATE :
31693225 onSetRsxConnectionState (msg .arg1 /*available*/ , msg .arg2 /*address*/ );
31703226 break ;
3227+
3228+ case MSG_CHECK_MUSIC_ACTIVE :
3229+ onCheckMusicActive ();
3230+ break ;
31713231 }
31723232 }
31733233 }
@@ -4426,7 +4486,7 @@ private void dumpRCCStack(PrintWriter pw) {
44264486 " -- vol: " + rcse .mPlaybackVolume +
44274487 " -- volMax: " + rcse .mPlaybackVolumeMax +
44284488 " -- volObs: " + rcse .mRemoteVolumeObs );
4429-
4489+
44304490 }
44314491 }
44324492 synchronized (mMainRemote ) {
@@ -5415,6 +5475,109 @@ public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
54155475 }
54165476 }
54175477
5478+
5479+ //==========================================================================================
5480+ // Safe media volume management.
5481+ // MUSIC stream volume level is limited when headphones are connected according to safety
5482+ // regulation. When the user attempts to raise the volume above the limit, a warning is
5483+ // displayed and the user has to acknowlegde before the volume is actually changed.
5484+ // The volume index corresponding to the limit is stored in config_safe_media_volume_index
5485+ // property. Platforms with a different limit must set this property accordingly in their
5486+ // overlay.
5487+ //==========================================================================================
5488+
5489+ // mSafeMediaVolumeEnabled indicates whether the media volume is limited over headphones.
5490+ // It is true by default when headphones or a headset are inserted and can be overriden by
5491+ // calling AudioService.disableSafeMediaVolume() (when user opts out).
5492+ private Boolean mSafeMediaVolumeEnabled = new Boolean (false );
5493+ // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
5494+ private final int mSafeMediaVolumeIndex ;
5495+ // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
5496+ private final int mSafeMediaVolumeDevices = AudioSystem .DEVICE_OUT_WIRED_HEADSET |
5497+ AudioSystem .DEVICE_OUT_WIRED_HEADPHONE ;
5498+ // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
5499+ // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
5500+ // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
5501+ private int mMusicActiveMs ;
5502+ private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000 ); // 20 hours
5503+ private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000 ; // 1 minute polling interval
5504+ private static final int SAFE_VOLUME_DELAY_MS = 500 ; // 500ms before switching to headphones
5505+
5506+ private void setSafeMediaVolumeEnabled (boolean on ) {
5507+ synchronized (mSafeMediaVolumeEnabled ) {
5508+ if (on && !mSafeMediaVolumeEnabled ) {
5509+ enforceSafeMediaVolume ();
5510+ } else if (!on && mSafeMediaVolumeEnabled ) {
5511+ mMusicActiveMs = 0 ;
5512+ sendMsg (mAudioHandler ,
5513+ MSG_CHECK_MUSIC_ACTIVE ,
5514+ SENDMSG_REPLACE ,
5515+ 0 ,
5516+ 0 ,
5517+ null ,
5518+ MUSIC_ACTIVE_POLL_PERIOD_MS );
5519+ }
5520+ mSafeMediaVolumeEnabled = on ;
5521+ }
5522+ }
5523+
5524+ private void enforceSafeMediaVolume () {
5525+ VolumeStreamState streamState = mStreamStates [AudioSystem .STREAM_MUSIC ];
5526+ boolean lastAudible = (streamState .muteCount () != 0 );
5527+ int devices = mSafeMediaVolumeDevices ;
5528+ int i = 0 ;
5529+
5530+ while (devices != 0 ) {
5531+ int device = 1 << i ++;
5532+ if ((device & devices ) == 0 ) {
5533+ continue ;
5534+ }
5535+ int index = streamState .getIndex (device , lastAudible );
5536+ if (index > mSafeMediaVolumeIndex ) {
5537+ if (lastAudible ) {
5538+ streamState .setLastAudibleIndex (mSafeMediaVolumeIndex , device );
5539+ sendMsg (mAudioHandler ,
5540+ MSG_PERSIST_VOLUME ,
5541+ SENDMSG_QUEUE ,
5542+ PERSIST_LAST_AUDIBLE ,
5543+ device ,
5544+ streamState ,
5545+ PERSIST_DELAY );
5546+ } else {
5547+ streamState .setIndex (mSafeMediaVolumeIndex , device , true );
5548+ sendMsg (mAudioHandler ,
5549+ MSG_SET_DEVICE_VOLUME ,
5550+ SENDMSG_QUEUE ,
5551+ device ,
5552+ 0 ,
5553+ streamState ,
5554+ 0 );
5555+ }
5556+ }
5557+ devices &= ~device ;
5558+ }
5559+ }
5560+
5561+ private boolean checkSafeMediaVolume (int streamType , int index , int device ) {
5562+ synchronized (mSafeMediaVolumeEnabled ) {
5563+ if (mSafeMediaVolumeEnabled &&
5564+ (mStreamVolumeAlias [streamType ] == AudioSystem .STREAM_MUSIC ) &&
5565+ ((device & mSafeMediaVolumeDevices ) != 0 ) &&
5566+ (index > mSafeMediaVolumeIndex )) {
5567+ mVolumePanel .postDisplaySafeVolumeWarning ();
5568+ return false ;
5569+ }
5570+ return true ;
5571+ }
5572+ }
5573+
5574+ public void disableSafeMediaVolume () {
5575+ synchronized (mSafeMediaVolumeEnabled ) {
5576+ setSafeMediaVolumeEnabled (false );
5577+ }
5578+ }
5579+
5580+
54185581 @ Override
54195582 protected void dump (FileDescriptor fd , PrintWriter pw , String [] args ) {
54205583 mContext .enforceCallingOrSelfPermission (android .Manifest .permission .DUMP , TAG );
0 commit comments