使用adb设置/获取安卓设备音量的通用技巧

最近在调一个定制开发的安卓设备,其中有一项需求是从PC端发起指令去获取/控制设备的音量,考虑使用adb来实现。由于android版本比较低(6.0),网上搜到的攻略不起作用。费了一番周折,最终实现一组兼容各种android版本(≥6)的技巧。特此记录。

1. 获取音量

目前网上存在的获取音量指令,一般形如这样:

adb shell media volume --show --stream <stream_id> --get

其中<stream_id>表示需要控制的音频流类型,枚举如下:

<stream_id> 音频流类型 描述
0 STREAM_VOICE_CALL 通话音量
1 STREAM_SYSTEM 系统音量
2 STREAM_RING 铃声音量
3 STREAM_MUSIC 媒体音量
4 STREAM_ALARM 闹钟音量
5 STREAM_NOTIFICATION 通知音量
6 STREAM_BLUETOOTH_SCO 蓝牙 SCO 音量
7 STREAM_ENFORCED_AUDIBLE 强制可听音量
8 STREAM_DTMF DTMF 音量
9 STREAM_TTS 文本到语音(TTS)音量
10 STREAM_ACCESSIBILITY 辅助功能音量

例如,希望获取媒体音量,则<stream_id>取3,完整的adb指令为:

adb shell media volume --show --stream 3 --get

然而……

很显然,当前版本的media不支持volume命令。

有意思的是,当我对一台标称为HarmonyOS 3.0.0的老设备执行这个指令的时候,它是生效的。

然后我又顺手查了一下它的一些信息……

android 10 对应的API Level确实是29,只能说HarmonyOS和android版本的对应关系做的还是比较讲究的

这么看来,在android 6,media指令确实没有获取音量的功能。还有什么办法呢?

我观察了一下对Harmony设备获取音量的过程。其中Connecting to AudioService这一条输出很有意思。这说明通过adb指令获取设备音量的操作,实际上是与android系统服务通信获取了相应的数据。不论是android哪一个版本,都应该有AudioService或者类似的东西。然后我想到,dumpsys可以获取系统服务的状态信息,是不是也可以获取“音频服务”的状态信息?

adb shell dumpsys -l

我在枚举的服务名称中,发现了一个令我感兴趣的一个名字:

于是

adb shell dumpsys audio

输出中可以看到以下一段信息:

……

Remote Control Display list entries:

Stream volumes (device: index)
- STREAM_VOICE_CALL:
Muted: false
Min: 1
Max: 5
Current: 40000000 (default): 4
Devices: speaker
- STREAM_SYSTEM:
Muted: false
Min: 0
Max: 7
Current: 2 (speaker): 5, 40000000 (default): 5
Devices: speaker
- STREAM_RING:
Muted: false
Min: 0
Max: 7
Current: 2 (speaker): 5, 40000000 (default): 5
Devices: speaker
- STREAM_MUSIC:
Muted: false
Min: 0
Max: 15
Current: 2 (speaker): 5, 4 (headset): 10, 8 (headphone): 10, 40000000 (default): 11
Devices: speaker
- STREAM_ALARM:
Muted: false
Min: 0
Max: 7
Current: 2 (speaker): 2, 40000000 (default): 6
Devices: speaker
- STREAM_NOTIFICATION:
Muted: false
Min: 0
Max: 7
Current: 2 (speaker): 5, 40000000 (default): 5
Devices: speaker
- STREAM_BLUETOOTH_SCO:
Muted: false
Min: 1
Max: 15
Current: 40000000 (default): 7
Devices: speaker
- STREAM_SYSTEM_ENFORCED:
Muted: false
Min: 0
Max: 7
Current: 2 (speaker): 5, 40000000 (default): 5
Devices: speaker
- STREAM_DTMF:
Muted: false
Min: 0
Max: 15
Current: 2 (speaker): 5, 4 (headset): 10, 8 (headphone): 10, 40000000 (default): 11
Devices: speaker
- STREAM_TTS:
Muted: false
Min: 0
Max: 15
Current: 2 (speaker): 5, 4 (headset): 10, 8 (headphone): 10, 40000000 (default): 11
Devices: speaker

……

这样就拿到了各种音频流的当前值、最小值、最大值和默认值。观察到每一种音频流的信息占用五行,可以根据音频流的名称,使用grep -A指令来进行过滤,关键字就是音频流类型的描述。例如:

adb shell "dumpsys audio | grep -A 5 STREAM_MUSIC"

结果如下:

这组指令可以兼容各种android版本

2. 设置音量

目前网上存在的设置音量指令,一般形如这样:

adb shell media volume --show --stream <stream_id> --set <volume_level>

其中<stream_id>表示需要控制的音频流类型,<volume_level>表示要设置的音量值。

adb shell media volume --show --stream 3 --set 5

当然,在android 6执行还是会报错,我同样试了一下在Harmony下的反馈

继续想办法。既然获取音量是与android系统服务通信获取了相应的数据,那么设置音量有没有可能也通过与系统服务通信来实现呢?然后我想到,service call可以调用系统服务执行一些操作,是不是也可以对“音频服务”执行一些操作?先检索一下有什么能用的:

adb shell "service list | grep audio"

我在检索的结果中,也发现了一个令我感兴趣的一个名字:

于是

adb shell service call audio 3 i32 3 i32 6 i32 0

设备出现了预期的响应。这组指令也可以兼容各种android版本。

3. dumpsys指令详解

基本格式:

dumpsys [-l | SERVICE [ARGS]]

-l: 列出支持的服务列表
SERVICE [ARGS]: 列出以SERVICE指定名称的服务的状态参数;如果不带任何参数,则dumpsys列出所支持的所有服务的所有状态参数。

这两种用法对于android 6 及以后的版本应该都是可用的,更早的版本没有实际测试,等遇到了再补充。更新的版本可能增加了更丰富的参数,比如超时控制、略过的服务名等等。

4. service指令详解

基本格式:

adb shell service list

输出支持的服务列表,输出格式为name: [Service],形如audio: [android.media.IAudioService]

adb shell service check SERVICE

检测具体的一个服务是否支持,SERVICE表示服务名称

adb shell service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR ] ...

调用一个服务接口,其中:

  • SERVICE表示服务名称;
  • CODE表示接口函数序号;
  • [i32 N | i64 N | f N | d N | s16 STR ]表示函数参数形式及参数;i32表示32位整形数,i64表示64位整形数,f表示浮点数,d表示双精度浮点数,s16表示字符串。

唯一的难点在于确认函数序号,这个没有太好的办法, 只能去翻对应版本android的源码。仍以设置音量为例,如果设备的系统版本是android 6,那么根据service list的返回,确认音频服务名为audio,对应的源码文件为IAudioService.aidl

// IAudioService.aidl android 6
……
interface IAudioService {
    oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
            String callingPackage, String caller);
    void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
    void setStreamVolume(int streamType, int index, int flags, String callingPackage);
……
}

可见在android 6中,setStreamVolume函数是第3个函数,对应的三个参数依次表示音频流类型(32位整形数)、音量(32位整形数)、标记(32位整形数),因此设置android 6设备的媒体音量为6时,相应的指令应为:

adb shell service call audio 3 i32 3 i32 6 i32 0

如果设备的系统版本是android 10harmonyOS 3.0.0,那么根据service list的返回,确认音频服务名为audio,对应的源码文件为IAudioService.aidl

// IAudioService.aidl android 10
……
interface IAudioService {
    // C++ and Java methods below.

    // WARNING: When methods are inserted or deleted in this section, the transaction IDs in
    // frameworks/native/include/audiomanager/IAudioManager.h must be updated to match the order
    // in this file.
    //
    // When a method's argument list is changed, BpAudioManager's corresponding serialization code
    // (if any) in frameworks/native/services/audiomanager/IAudioManager.cpp must be updated.
    int trackPlayer(in PlayerBase.PlayerIdCard pic);
    oneway void playerAttributes(in int piid, in AudioAttributes attr);
    oneway void playerEvent(in int piid, in int event);
    oneway void releasePlayer(in int piid);
    int trackRecorder(in IBinder recorder);
    oneway void recorderEvent(in int riid, in int event);
    oneway void releaseRecorder(in int riid);
    // Java-only methods below.
    oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
            String callingPackage, String caller);
    void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
    @UnsupportedAppUsage
    void setStreamVolume(int streamType, int index, int flags, String callingPackage);
……
}

可见在android 10harmonyOS 3.0.0中,setStreamVolume函数是第10个函数,对应的三个参数依次表示音频流类型(32位整形数)、音量(32位整形数)、标记(32位整形数),因此设置android 10设备的媒体音量为10时,相应的指令应为:

adb shell service call audio 10 i32 3 i32 10 i32 0

关于参数flags的含义,暂时没有查到官方说明。实际测试,当flags == 1时,设置音量时,相应音频流类型的音量控制托条会主动显示;当flags == 0时,则不显示音量控制托条;flags取0或者1,实际音量设置均成功。其他取值暂未测试。

二零二五年四月十二日

顾毅 写于厦门