Android L 音量调节问题分析

       Android升到5.0或5.1之后,细心的同学可能会发现,在我们调节音量的时候,无法调到静音模式。音量调到1的时候,再往下调,就变成震动模式了。如果你说再按下键不就是静音模式了吗?其实这不是静音模式,而是禁止打扰的情景模式ZenMode。ZenMode分为禁止打扰,仅容许优先打扰内容,一律容许打扰。即在音量seekBar下面的3个按钮。而静音模式,震动模式,正常模式是属于RingerMode的。你可以用你的手机测试一下。你按音量键往下调到震动,再往上调一格,此时音量显示为1,图标是正常模式,然后重启手机。手机重启之后,进入Settings->Sound & notification,你会发现Ring volume的seekBar为0,但是图标显示的是正常模式。明明我在重启之前手机音量1了,而且手机的Ringtone是有声音的,怎么重启之后就变成0了呢?网上有人说用这个方法把手机就调成静音模式了,其实这不是静音模式,而是音量为0的正常模式。

      在我们按音量键调节音量时,首先在PhoneWindow.java中,被onKeyDown和onKeyUp捕获。最终的处理是在AudioService中实现,AudioService位于frameworks/base/media/java/android/media/下。
最终实现的方法是

private void adjustStreamVolume(int streamType, int direction, int flags,String callingPackage, int uid) {
          ………………
       if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                (streamTypeAlias == getMasterStreamType())) {
            ………………
            final int result = checkForRingerModeChange(aliasIndex, direction, step);
            adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
           …………………
        }
     …………………
}


这部分是我们关心的,其中

final int result = checkForRingerModeChange(aliasIndex, direction, step);
adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;


调用了

checkForRingerModeChange(aliasIndex, direction, step);


我们看看这个方法做了什么。

private int checkForRingerModeChange(int oldIndex, int direction,  int step) {
        int result = FLAG_ADJUST_VOLUME;
        int ringerMode = getRingerModeInternal();

        switch (ringerMode) {
        …………………
        case RINGER_MODE_VIBRATE:
            ……………………
            if ((direction == AudioManager.ADJUST_LOWER)) {
                …………………
            } else if (direction == AudioManager.ADJUST_RAISE) {
                ringerMode = RINGER_MODE_NORMAL;
            }
            result &= ~FLAG_ADJUST_VOLUME;
            break;
        ………………………
        setRingerMode(ringerMode, TAG + ".checkForRingerModeChange", false /*external*/);
        return result;
    }


可以看到

result &= ~FLAG_ADJUST_VOLUME;


而回到adjustStreamVolume

adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;


显然adjustVolume为FALSE,因此不会进入这个分支,

if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
    ……………
    sendMsg(mAudioHandler,
                        MSG_SET_DEVICE_VOLUME,
                        SENDMSG_QUEUE,
                        device,
                        0,
                        streamState,
                        0);
     …………………
}


这个消息是设置硬件音量,通过handleMessage调用

private void setDeviceVolume(VolumeStreamState streamState, int device) {
           …………………
            sendMsg(mAudioHandler,
                    MSG_PERSIST_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    PERSIST_DELAY);
  }


省略的是具体的设置相关硬件的音量,而我们关心的是这个Message,通过handleMessage调用,

private void persistVolume(VolumeStreamState streamState, int device) {
            …………………………
            System.putIntForUser(mContentResolver,
                      streamState.getSettingNameForDevice(device),
                      (streamState.getIndex(device) + 5)/ 10,
                      UserHandle.USER_CURRENT);
 }


这个方法很简单,就是将音量值保存到数据库。


    在前面我们可以看到,在从0跳到1,与从1到2相比多了一个RingerMode的设置,也就是从震动模式到正常模式。因此不会发出设置音量的这个消息。但是此时ringtone确实是有声音的。其实是在checkForRingerModeChange方法中,调用了

setRingerMode(ringerMode, TAG + ".checkForRingerModeChange", false /*external*/);

    因此它将设置音量的工作交给setRingerMode。既然RingerMode跟音量有关,这样做也是为了避免重复和冲突。而setRingerMode最终会调到setRingerModeInt方法。
private void setRingerModeInt(int ringerMode, boolean persist) {
        …………………
        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
            …………………………
            if (!shouldMute) {
                if ((isPlatformVoice() || mHasVibrator) &&
                        mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
                        Set set = mStreamStates[streamType].mIndex.entrySet();
                        Iterator i = set.iterator();
                        while (i.hasNext()) {
                            Map.Entry entry = (Map.Entry)i.next();
                            if ((Integer)entry.getValue() == 0) {
                                entry.setValue(10);
                            }
                        }
                    }
                }
        ……………………
        }
   ………………………
}

      通过循环将相关的Stream音量设为1,因此ringtone是有声音的,但是并没有send message来将音量值写到数据库中,此时数据库的值任然为0,重启手机需要这个值来初始化,因此出现我们在开头描述的现象。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值