使用adb设置/获取安卓设备音量的通用技巧
- 再创世纪·代码厨房
- 2025-04-12
- 85热度
- 0评论
最近在调一个定制开发的安卓设备,其中有一项需求是从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,实际音量设置均成功。其他取值暂未测试。
二零二五年四月十二日
顾毅 写于厦门