As it turns out, it is not trivial to control the audio volume of an Android device using ADB. At the time of writing, the only way appears to be using the service
tool. Actually, the service
command allows to "connect" to a number of services (104 on Android 6.0.1) and invoke functions. Not knowing much about this tool, I managed to completely mute all sounds and speakers of my Nexus 5, and I was stuck without any sound for quite some time. I did not find a way to unmute the sound from within the system UI, so I got to dive a little deeper into this.
If you know which service you want to use, you then need to find its interface declaration. The command
service list
gives you a list of all services with the associated interfaces, if applicable:
...
26 backup: [android.app.backup.IBackupManager]
27 jobscheduler: [android.app.job.IJobScheduler]
28 serial: [android.hardware.ISerialManager]
29 usb: [android.hardware.usb.IUsbManager]
30 midi: [android.media.midi.IMidiManager]
31 DockObserver: []
32 audio: [android.media.IAudioService]
33 wallpaper: [android.app.IWallpaperManager]
34 dropbox: [com.android.internal.os.IDropBoxManagerService]
35 search: [android.app.ISearchManager]
36 country_detector: [android.location.ICountryDetector]
37 location: [android.location.ILocationManager]
38 devicestoragemonitor: []
...
For me, the audio
service was relevant, which appeared at index 32. The associated interface is android.media.IAudioService
, so the next thing you will want to do is find the source code of that file. And, even more importantly, find the correct version of that file (this was my mistake in the first place). For 6.0.1, you can find the interface declaration here. If you select a different branch on GitHub, you will see that the file differs from release to release.
In order to call a function of the interface, we need to know its index in the interface declaration. For example, to mute all sound, I will use IAudioService.setMasterMute
. This function is the 8th method in the interface declaration. We will need this index soon, and it appears to start with 1 (so the first function has the index 1, not 0!). Additionally, we need to supply the correct arguments to the function. The declaration looks like this:
void setMasterMute(boolean mute, int flags, String callingPackage, int userId);
We will only supply the first two arguments, which are a boolean and an integer, which are both treated as integers for now.
In the ADB shell, the command
service call audio 8 i32 1 i32 0
mutes all sounds on my Nexus 5 (6.0.1). service call audio
is self explanatory, 8
is the function index, i32 1
represents the boolean value true
and i32 0
represents the integer 0
for the flags parameter.
We can use the function IAudioService.isMasterMute
to verify the result (or you could just try to make a call or watch a video, you should hear nothing, no matter what you set your volume to). Its signature is simple:
boolean isMasterMute();
It is the 7th function in the interface, so we can call it using
service call audio 7
After issuing the command, it should output something like
shell@hammerhead:/ $ service call audio 7
Result: Parcel(00000000 00000001 '........')
Note that the last digit is set to 1, and as isMasterMute()
should return a boolean, we interpret this as true
.
Unmuting is as simple as muting the device:
service call audio 8 i32 0 i32 0
Again, use isMasterMute()
to verify the result:
shell@hammerhead:/ $ service call audio 7
Result: Parcel(00000000 00000000 '........')
All zeroes represent the value false
, so the result is correct!
Basically, whenever possible. The command and the interfaces are documented poorly and they are subject to change from release to release. You can use the command for manual debugging (and that might be the reason it was developed), but if you intend to use it in scripting, you will eventually run into problems. You need to change the command for each release, possibly even depending on the device, and I have no idea how much CyanogenMod and friends care about such compatibility. Many features are available without using service
, and if there is a better interface to it, use it.