Android Safe Media分析

一.使用场景
Safe Media,字面意思是安全音量。前提是有耳机插入,并且有音乐或者FM播放,在我们调节音量的时候,如果音量达到安全音量值,会提出系统提示,提醒用户高音量下使用耳机会损坏听力。在用户选择OK后,安全音量失效,声音可以继续调高。但是此时系统开始计时,如果累计达到20h,音量自动强制降到安全音量,并弹出系统提示,提醒用户长时间在高音量下使用会损坏听力。
二.处理逻辑
在音量的调节的实现在AudioService中,AudioService位于frameworks/base/media/java/android/media/下。
在我们点击上下键按钮调节音量时,会调用

private void adjustStreamVolume(int streamType, int direction, int flags,String callingPackage, int uid) {
     ……
            if ((direction == AudioManager.ADJUST_RAISE) &&
                    !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
                Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
                mVolumeController.postDisplaySafeVolumeWarning(flags);
            } else if (streamState.adjustIndex(direction * step, device)) {
                // Post message to set system volume (it in turn will post a message
                // to persist). Do not change volume if stream is muted.
                sendMsg(mAudioHandler,
                        MSG_SET_DEVICE_VOLUME,
                        SENDMSG_QUEUE,
                        device,
                        0,
                        streamState,
                        0);
            }
       ……
}

首先有俩个判断,第一个direction == AudioManager.ADJUST_RAISE判断是否按音量上键,如果按下键就不用检查了。第二个判断是checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device),是检查aliasIndex + step是否超过安全音量。

private boolean checkSafeMediaVolume(int streamType, int index, int device) {
        synchronized (mSafeMediaVolumeState) {
            if(!prefs.getBoolean(firstShowSafeDialog,false) && (index > mSafeMediaVolumeIndex) &&
                         ((device & mSafeMediaVolumeDevices) != 0)){
                  SharedPreferences.Editor edit = prefs.edit();
                  edit.putBoolean(firstShowSafeDialog, true);
                  edit.commit();
                  mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
                  return false;
            }
            if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
                    ((device & mSafeMediaVolumeDevices) != 0) &&
                    (index > mSafeMediaVolumeIndex)) {
                return false;
            }
            return true;
        }
    }

在这个方法中,我们会判断mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE,即Safe Media的状态。(device & mSafeMediaVolumeDevices) != 0检查耳机是否插入,index > mSafeMediaVolumeIndex检查我们要设置的音量是否超过安全音量。如果这些条件都满足,调用mVolumeController.postDisplaySafeVolumeWarning(flags)弹出系统dialog。
点击OK后,调用setSafeMediaVolumeEnabled(false);

private void setSafeMediaVolumeEnabled(boolean on) {
        synchronized (mSafeMediaVolumeState) {
            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();
                } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
                    mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
                    mMusicActiveMs = 1;  // nonzero = confirmed
                    saveMusicActiveMs();
                    sendMsg(mAudioHandler,
                            MSG_CHECK_MUSIC_ACTIVE,
                            SENDMSG_REPLACE,
                            0,
                            0,
                            null,
                            MUSIC_ACTIVE_POLL_PERIOD_MS);
                }
            }
        }
    }

将mSafeMediaVolumeState的状态设置成SAFE_MEDIA_VOLUME_INACTIVE,然后发送消息

sendMsg(mAudioHandler,
         MSG_CHECK_MUSIC_ACTIVE,
         SENDMSG_REPLACE,
         0,
         0,
         null,
         MUSIC_ACTIVE_POLL_PERIOD_MS);

进入handleMessage处理消息

public void handleMessage(Message msg) {
            switch (msg.what) {
                 ……
                 case MSG_CHECK_MUSIC_ACTIVE:
                    onCheckMusicActive();
                    break;
                 ……
             }
      }

调用了onCheckMusicActive();

private void onCheckMusicActive() {
        synchronized (mSafeMediaVolumeState) {
            if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
                int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
                if ((device & mSafeMediaVolumeDevices) != 0) {
                    sendMsg(mAudioHandler,
                            MSG_CHECK_MUSIC_ACTIVE,
                            SENDMSG_REPLACE,
                            0,
                            0,
                            null,
                            MUSIC_ACTIVE_POLL_PERIOD_MS);
                    int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
                    if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
                            (index > mSafeMediaVolumeIndex)) {
                        // Approximate cumulative active music time
                        mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
                        if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
                            setSafeMediaVolumeEnabled(true);                            mVolumeController.postDisplaySafeVolumeWarningWithType(AudioManager.FLAG_SHOW_UI_WARNINGS,1);
                            mMusicActiveMs = 0;
                        }
                        saveMusicActiveMs();
                    }
                }
            }
        }
    }

在这个方法里,我们发现同样多很多判断,耳机设备是否插入,Safe Media的状态,STREAM_MUSIC的状态,以及当前音量值是否超过安全音量。如果耳机插入和Safe Media是SAFE_MEDIA_VOLUME_INACTIVE的状态,我们看到同样发了一个消息。

sendMsg(mAudioHandler,
           MSG_CHECK_MUSIC_ACTIVE,
           SENDMSG_REPLACE,
           0,
           0,
           null,
           MUSIC_ACTIVE_POLL_PERIOD_MS);

消息是与之前是相同的,因此进入一个消息循环,MUSIC_ACTIVE_POLL_PERIOD_MS是个表示1分钟的常量。也就是每隔一分钟发一个消息的循环执行。
在前面的条件满足的前提下,如果STREAM_MUSIC的状态是Active的,并且音量大于安全音量,会执行

mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
saveMusicActiveMs();

累加并保存时间。
如果累加时间到达一定值

mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX

UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX是设定的20小时的一个常量。执行

setSafeMediaVolumeEnabled(true);
private void setSafeMediaVolumeEnabled(boolean on) {
        synchronized (mSafeMediaVolumeState) {
            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();
                } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
                   ……
                }
            }
        }
    }

将mSafeMediaVolumeState的状态设定SAFE_MEDIA_VOLUME_ACTIVE,并且enforceSafeMediaVolume()

private void enforceSafeMediaVolume() {
        VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
        int devices = mSafeMediaVolumeDevices;
        int i = 0;

        while (devices != 0) {
            int device = 1 << i++;
            if ((device & devices) == 0) {
                continue;
            }
            int index = streamState.getIndex(device);
            if (index > mSafeMediaVolumeIndex) {
                streamState.setIndex(mSafeMediaVolumeIndex, device);
                sendMsg(mAudioHandler,
                        MSG_SET_DEVICE_VOLUME,
                        SENDMSG_QUEUE,
                        device,
                        0,
                        streamState,
                        0);
            }
            devices &= ~device;
        }
    }

获取耳机设备devices = mSafeMediaVolumeDevices,获取设备音量index = streamState.getIndex(device),如果设备音量大于安全音量,强制将设备设置成安全音量streamState.setIndex(mSafeMediaVolumeIndex, device)。
并且会执行

mVolumeController.postDisplaySafeVolumeWarningWithType(AudioManager.FLAG_SHOW_UI_WARNINGS,1);

弹出系统Dialog,提示用户长时间高音量使用耳机会损坏听力。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值