Android音量设置流程及调试方法

流程框图

在这里插入图片描述

初始化

开机的时候,系统会从数据库中更新当前的音量值给各个音频流

// 根据数据库的配置创建流的状态
private void createStreamStates() {
    int numStreamTypes = AudioSystem.getNumStreamTypes();
    VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
 
    for (int i = 0; i < numStreamTypes; i++) {
        streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);  // VolumeStreamState 构造
    }
 
    checkAllAliasStreamVolumes();   // 更新音量到设备中
}
 
// 在数据库中读取每个设备的流音量
private VolumeStreamState(String settingName, int streamType) {
 
    mVolumeIndexSettingName = settingName;
 
    mStreamType = streamType;
    mIndexMax = MAX_STREAM_VOLUME[streamType];
    AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
    mIndexMax *= 10;
 
    // mDeathHandlers must be created before calling readSettings()
    mDeathHandlers = new ArrayList<VolumeDeathHandler>();
 
    readSettings();         // 从数据库中读取数据,每个设备有自己独立的音量
}
 
// 数据库中键值的拼接方法,exp: volume_music_speaker
public String getSettingNameForDevice(int device) {
    String name = mVolumeIndexSettingName;
    String suffix = AudioSystem.getDeviceName(device);
    if (suffix.isEmpty()) {
        return name;
    }
    return name + "_" + suffix;
}
 
// 更新音量到设备中
private void checkAllAliasStreamVolumes() {
    int numStreamTypes = AudioSystem.getNumStreamTypes();
    for (int streamType = 0; streamType < numStreamTypes; streamType++) {
        if (streamType != mStreamVolumeAlias[streamType]) {
            mStreamStates[streamType].
                                setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
        }
        // apply stream volume
        if (!mStreamStates[streamType].isMuted()) {
            mStreamStates[streamType].applyAllVolumes();  // 更新设备音量
        }
    }
}

音量调整

上层通过java层接口设置音量,该流程主要分析 AudioService.java 提供的 setStreamVolume 接口

// AudioService.java 设置音量入口
public void setStreamVolume(int streamType, int index, int flags) {
    IAudioService service = getService();
    try {
        if (mUseMasterVolume) {
            service.setMasterVolume(index, flags, mContext.getOpPackageName());
        } else {
            service.setStreamVolume(streamType, index, flags, mContext.getOpPackageName());
        }
    } catch (RemoteException e) {
        Log.e(TAG, "Dead object in setStreamVolume", e);
    }
}
 
// AudioService.java 设置音量
public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
....
    ensureValidStreamType(streamType);
    int streamTypeAlias = mStreamVolumeAlias[streamType];
    VolumeStreamState streamState = mStreamStates[streamTypeAlias];
 
    final int device = getDeviceForStream(streamType);
    int oldIndex;
...
            onSetStreamVolume(streamType, index, flags, device);    // 发送 MSG_SET_DEVICE_VOLUME
            index = mStreamStates[streamType].getIndex(device);
    sendVolumeUpdate(streamType, oldIndex, index, flags);           // 发送广播
}
 
// onSetStreamVolume -> setStreamVolumeInt
private void setStreamVolumeInt(int streamType,
                                int index,
                                int device,
                                boolean force) {
    VolumeStreamState streamState = mStreamStates[streamType];
 
    if (streamState.setIndex(index, device) || force) {  // 更新到 streamState 的映射表中
        // Post message to set system volume (it in turn will post a message
        // to persist).
        sendMsg(mAudioHandler,
                MSG_SET_DEVICE_VOLUME,    // 更新音量到设备
                SENDMSG_QUEUE,
                device,
                0,
                streamState,
                0);
    }
}
 
// 处理消息:MSG_SET_DEVICE_VOLUME
private void setDeviceVolume(VolumeStreamState streamState, int device) {
 
    // Apply volume
    streamState.applyDeviceVolume(device);
....
    // Post a persist volume msg
    sendMsg(mAudioHandler,
            MSG_PERSIST_VOLUME,   // 保存数据库
            SENDMSG_QUEUE,
            device,
            0,
            streamState,
            PERSIST_DELAY);
 
}
 
// 通过message:MSG_PERSIST_VOLUME 把音量同步到数据库
private void persistVolume(VolumeStreamState streamState, int device) {
    if (mUseFixedVolume) {
        return;
    }
    System.putIntForUser(mContentResolver,
              streamState.getSettingNameForDevice(device),
              (streamState.getIndex(device) + 5)/ 10,
              UserHandle.USER_CURRENT);
}
 
// 通过binder通信调用到ap中的设置音量接口
AudioPolicyManagerBase::setStreamVolumeIndex
 
// 最后在Mixer里准备播放时,音量生效
case VOLUME:
    switch (param) {
    case VOLUME0:
    case VOLUME1:
        if (track.volume[param-VOLUME0] != valueInt) {
            ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
            track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16;
            track.volume[param-VOLUME0] = valueInt;
            if (target == VOLUME) {
                track.prevVolume[param-VOLUME0] = valueInt << 16;
                track.volumeInc[param-VOLUME0] = 0;
            } else {
                int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0];
                int32_t volInc = d / int32_t(mState.frameCount);
                track.volumeInc[param-VOLUME0] = volInc;
                if (volInc == 0) {
                    track.prevVolume[param-VOLUME0] = valueInt << 16;
                }
            }
            invalidateState(1 << name);
        }
        break;

参考链接:https://blog.csdn.net/xlnaan/article/details/80527902

常用调试手段

系统音量查询

DumpSys

我们可以利用android的dumpsys机制,来查询系统的流音量

root@rk3188:/data # dumpsys media.audio_policy
 
Outputs dump:
- Output 2 dump:                            // outputs index
Sampling rate: 44100
Format: 00000001
Channels: 00000003
Latency: 92
Flags 00000002
Devices 00000002    // 设备号
 Stream volume refCount muteCount           // 这边声音流的相关信息
 00     0.444     00       00
 01     0.501     00       00
 02     0.135     00       00
 03     1.000     00       00
 04     0.135     00       00
 05     0.135     00       00
 06     -1.000     00       00
 07     0.501     00       00
 08     0.501     00       00
 09     1.000     00       00
 
// Android系统现有流的定义
typedef enum {
    AUDIO_STREAM_DEFAULT          = -1,
    AUDIO_STREAM_VOICE_CALL       = 0,
    AUDIO_STREAM_SYSTEM           = 1,
    AUDIO_STREAM_RING             = 2,
    AUDIO_STREAM_MUSIC            = 3,
    AUDIO_STREAM_ALARM            = 4,
    AUDIO_STREAM_NOTIFICATION     = 5,
    AUDIO_STREAM_BLUETOOTH_SCO    = 6,
    AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */
    AUDIO_STREAM_DTMF             = 8,
    AUDIO_STREAM_TTS              = 9,
 
    AUDIO_STREAM_CNT,
    AUDIO_STREAM_MAX              = AUDIO_STREAM_CNT - 1,
} audio_stream_type_t;

数据库(sqlite3)

// 首先进入数据库的存储目录
root@rk3188:/ # cd /data/data/com.android.providers.settings/databases
root@rk3188:/data/data/com.android.providers.settings/databases # ls
settings.db
settings.db-journal
ngs.db
// 音量数据保存在 settings.db 中的 system 表里,可以使用sqlite命令进行查询
root@rk3188:/data/data/com.android.providers.settings/databases # sqlite3 settings.db
SQLite version 3.7.11 2012-03-20 11:35:50
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .table                                      // 查看数据库里包含的表
android_metadata   bookmarks          secure
bluetooth_devices  global             system
sqlite> select * from system;                       // 选中system表dump出数据,我们可以看到volume相关的一些设置
2|volume_ring|5
3|volume_system|7
4|volume_voice|4
5|volume_alarm|6
6|volume_notification|5
7|volume_bluetooth_sco|7
34|volume_music_headset|10
35|volume_music_headphone|10
298|volume_master|1.0
1035|volume_ring_speaker|5
1504|volume_ring_analog_dock|5
1505|volume_alarm_speaker|7
1649|volume_music|20
1885|volume_music_analog_dock|99
1893|volume_voice_analog_dock|100
1895|volume_music_speaker|70
1896|volume_voice_speaker|70

Audio模块Log开关

默认的发行版本中可见的log非常少,我们需要修改代码的log定义,然后替换对应so文件把用于调试的log全部打开。
具体的修改方法如下:

// 以 AudioPolicyManagerBase.cpp 文件举例,我们只需要把文件前面的log注释打开,代码中的调试Log就可以打印出来
 
diff --git a/hardware/rk29/audio/AudioPolicyManagerBase.cpp b/hardware/rk29/audio/AudioPolicyManagerBase.cpp
old mode 100644
new mode 100755
index cba5eb4..d18b348
--- a/hardware/rk29/audio/AudioPolicyManagerBase.cpp
+++ b/hardware/rk29/audio/AudioPolicyManagerBase.cpp
@@ -15,9 +15,9 @@
  */
 
#define LOG_TAG "AudioPolicyManagerBase"
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
 
-//#define VERY_VERBOSE_LOGGING
+#define VERY_VERBOSE_LOGGING
#ifdef VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else

Mixer设备查询

Mixer的一些设备状态对于定位Audio问题非常有帮助,这些设备里包括控制通路开关、音量、增益等;这些配置对于Audio是能够正常使用,起着至关重要的作用。
Android系统内置了一些查询和设置命令,使用方法如下:

cmd: amix
// 查询
root@rk3188:/ # amix -c 0
Card:0
  id iface dev sub idx num perms     type   name
   1 MIXER   0   0   0   2 rw        BOOL   HP Playback Switch: OFF OFF { OFF=0, ON=1 }
   2 MIXER   0   0   0   2 rw  R     INT32  HP Playback Volume: 31 31 { 0-31 }
   3 MIXER   0   0   0   2 rw        BOOL   OUT Playback Switch: OFF OFF { OFF=0, ON=1 }
   4 MIXER   0   0   0   2 rw        BOOL   OUT Channel Switch: OFF OFF { OFF=0, ON=1 }
   5 MIXER   0   0   0   2 rw  R     INT32  OUT Playback Volume: 31 31 { 0-39 }
   6 MIXER   0   0   0   2 rw  R     INT32  DAC1 Playback Volume: 175 175 { 0-175 }
   7 MIXER   0   0   0   1 rw        ENUM   IN1 Mode Control: (0 Single ended) { Single ended=0, Differential=1 }
   8 MIXER   0   0   0   1 rw  R     INT32  IN1 Boost: 0 { 0-8 }
   9 MIXER   0   0   0   1 rw        ENUM   IN2 Mode Control: (0 Single ended) { Single ended=0, Differential=1 }
  10 MIXER   0   0   0   1 rw  R     INT32  IN2 Boost: 1 { 0-8 }
  11 MIXER   0   0   0   2 rw  R     INT32  IN Capture Volume: 23 23 { 0-31 }
// 设置
root@rk3188:/ # amix -c 0 1 1
Card:0
HP Playback Switch: ON ON { OFF=0, ON=1 }
 
// 重新查询之后发现修改的配置已经生效
root@rk3188:/ # amix -c 0
Card:0
  id iface dev sub idx num perms     type   name
   1 MIXER   0   0   0   2 rw        BOOL   HP Playback Switch: ON ON { OFF=0, ON=1 }    // 从 OFF -> ON
   2 MIXER   0   0   0   2 rw  R     INT32  HP Playback Volume: 31 31 { 0-31 }
 
cmd: alsa_amixer
// 和amix的使用方法类似,只是展现的格式有些差异
root@rk3188:/ # alsa_amixer -c 0 contents
numid=14,iface=MIXER,name='ADC Boost Gain'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=3,step=0
  : values=0,0
  | dBscale-min=0.00dB,step=12.00dB,mute=0
numid=12,iface=MIXER,name='ADC Capture Switch'
  ; type=BOOLEAN,access=rw------,values=2
  : values=off,off
numid=13,iface=MIXER,name='ADC Capture Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=127,step=0
  : values=47,47
  | dBscale-min=-176.25dB,step=3.75dB,mute=0
numid=38,iface=MIXER,name='DAC MIXL INF1 Switch'
### Android 系统音量键工作流程 #### 接收事件 当用户按下音量键时,硬件会生成输入事件并通过 `/dev/input/event` 文件传递给 EventHub。EventHub 负责读取这些设备文件并将原始数据转换成 `KeyEvent` 对象[^1]。 #### 处理事件 `KeyEvent` 对象随后会被发送至 InputReader 和 InputDispatcher 进行进一步处理。InputDispatcher 将该事件分发给当前处于前台的应用程序或系统服务。如果应用程序未捕获此事件,则它最终由系统的 PhoneWindowManager 组件接管并处理[^3]。 #### AudioService 的介入 一旦音量键事件到达框架层,AudioService 开始发挥作用。AudioService 是负责管理和调整音频流的主要组件之一。具体来说: - 当检测到音量增加 (`KEYCODE_VOLUME_UP`) 或减少 (`KEYCODE_VOLUME_DOWN`) 键被按下的时候, - AudioService 会调用相应的方法来改变指定音频流(如媒体播放器、电话铃声等)的音量级别。 - 同时也会触发 UI 更新以显示新的音量水平[^2]。 ```java // 获取 AudioManager 实例 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: // 减少音量逻辑 audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI); return true; case KeyEvent.KEYCODE_VOLUME_UP: // 增加音量逻辑 audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); return true; default: break; } return super.onKeyDown(keyCode, event); } ``` #### 特殊情况:禁用音量键 在某些场景下可能需要临时关闭音量键的功能,比如教育平板电脑在课堂模式中不允许学生随意更改音量设置。这可以通过修改 `PhoneWindowManager.java` 中的相关方法实现,在特定条件下忽略来自用户的音量增减请求[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值