Skip to content

Commit

Permalink
[androiddebugbridge] Fix volume channel for android 11/12 (#13828)
Browse files Browse the repository at this point in the history
* [androiddebugbridge] Fix volume channel for android 11/12
* [androiddebugbridge] Try get max volume level from device properties
* [androiddebugbridge] Fixes reported code analysis
* [androiddebugbridge] Fix comparison

Signed-off-by: Miguel Álvarez <[email protected]>
  • Loading branch information
GiviMAD authored Dec 3, 2022
1 parent d777aa4 commit 3e068ed
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 19 deletions.
2 changes: 2 additions & 0 deletions bundles/org.openhab.binding.androiddebugbridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ You could customize the discovery process through the binding options.
| refreshTime | int | Seconds between device status refreshes (default: 30) |
| timeout | int | Command timeout in seconds (default: 5) |
| recordDuration | int | Record input duration in seconds |
| deviceMaxVolume | int | Assumed max volume for devices with android versions that do not expose this value. |
| volumeSettingKey | String | Settings key for android versions where volume is gather using settings command (>=android 11). |
| mediaStateJSONConfig | String | Expects a JSON array. Allow to configure the media state detection method per app. Described in the following section |

## Media State Detection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ public class AndroidDebugBridgeConfiguration {
* Record input duration in seconds.
*/
public int recordDuration = 5;
/**
* Assumed max volume for devices with android versions that do not expose this value (>=android 11).
*/
public int deviceMaxVolume = 25;
/**
* Settings key for android versions where volume is gather using settings command (>=android 11).
*/
public String volumeSettingKey = "volume_music_hdmi";
/**
* Configure media state detection behavior by package
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,34 @@ public class AndroidDebugBridgeDevice {
private int port = 5555;
private int timeoutSec = 5;
private int recordDuration;
private @Nullable Integer maxVolumeLevel = null;
private @Nullable Socket socket;
private @Nullable AdbConnection connection;
private @Nullable Future<String> commandFuture;
private int majorVersionNumber = 0;
private int minorVersionNumber = 0;
private int patchVersionNumber = 0;
/**
* Assumed max volume for android versions that do not expose this value.
*/
private int deviceMaxVolume = 25;
private String volumeSettingKey = "volume_music_hdmi";

public AndroidDebugBridgeDevice(ScheduledExecutorService scheduler) {
this.scheduler = scheduler;
}

public void configure(String ip, int port, int timeout, int recordDuration) {
public void configure(AndroidDebugBridgeConfiguration config) {
configureConnection(config.ip, config.port, config.timeout);
this.recordDuration = config.recordDuration;
this.volumeSettingKey = config.volumeSettingKey;
this.deviceMaxVolume = config.deviceMaxVolume;
}

public void configureConnection(String ip, int port, int timeout) {
this.ip = ip;
this.port = port;
this.timeoutSec = timeout;
this.recordDuration = recordDuration;
}

public void sendKeyEvent(String eventCode)
Expand Down Expand Up @@ -291,7 +303,16 @@ public int getPowerWakeLock() throws InterruptedException, AndroidDebugBridgeDev

private void setVolume(int stream, int volume)
throws AndroidDebugBridgeDeviceException, InterruptedException, TimeoutException, ExecutionException {
runAdbShell("media", "volume", "--show", "--stream", String.valueOf(stream), "--set", String.valueOf(volume));
if (isAtLeastVersion(12)) {
runAdbShell("service", "call", "audio", "11", "i32", String.valueOf(stream), "i32", String.valueOf(volume),
"i32", "1");
} else if (isAtLeastVersion(11)) {
runAdbShell("service", "call", "audio", "10", "i32", String.valueOf(stream), "i32", String.valueOf(volume),
"i32", "1");
} else {
runAdbShell("media", "volume", "--show", "--stream", String.valueOf(stream), "--set",
String.valueOf(volume));
}
}

public String getModel() throws AndroidDebugBridgeDeviceException, InterruptedException,
Expand Down Expand Up @@ -353,17 +374,32 @@ private String getDeviceProp(String name) throws AndroidDebugBridgeDeviceExcepti

private VolumeInfo getVolume(int stream) throws AndroidDebugBridgeDeviceException, InterruptedException,
AndroidDebugBridgeDeviceReadException, TimeoutException, ExecutionException {
String volumeResp = runAdbShell("media", "volume", "--show", "--stream", String.valueOf(stream), "--get", "|",
"grep", "volume");
Matcher matcher = VOLUME_PATTERN.matcher(volumeResp);
if (!matcher.find()) {
throw new AndroidDebugBridgeDeviceReadException("Unable to get volume info");
if (isAtLeastVersion(11)) {
String volumeResp = runAdbShell("settings", "get", "system", volumeSettingKey);
var maxVolumeLevel = this.maxVolumeLevel;
if (maxVolumeLevel == null) {
try {
maxVolumeLevel = Integer.parseInt(getDeviceProp("ro.config.media_vol_steps"));
this.maxVolumeLevel = maxVolumeLevel;
} catch (NumberFormatException ignored) {
logger.debug("Max volume level not available, using 'deviceMaxVolume' config");
maxVolumeLevel = deviceMaxVolume;
}
}
return new VolumeInfo(Integer.parseInt(volumeResp.replace("\n", "")), 0, maxVolumeLevel);
} else {
String volumeResp = runAdbShell("media", "volume", "--show", "--stream", String.valueOf(stream), "--get",
"|", "grep", "volume");
Matcher matcher = VOLUME_PATTERN.matcher(volumeResp);
if (!matcher.find()) {
throw new AndroidDebugBridgeDeviceReadException("Unable to get volume info");
}
var volumeInfo = new VolumeInfo(Integer.parseInt(matcher.group("current")),
Integer.parseInt(matcher.group("min")), Integer.parseInt(matcher.group("max")));
logger.debug("Device {}:{} VolumeInfo: current {}, min {}, max {}", this.ip, this.port, volumeInfo.current,
volumeInfo.min, volumeInfo.max);
return volumeInfo;
}
var volumeInfo = new VolumeInfo(Integer.parseInt(matcher.group("current")),
Integer.parseInt(matcher.group("min")), Integer.parseInt(matcher.group("max")));
logger.debug("Device {}:{} VolumeInfo: current {}, min {}, max {}", this.ip, this.port, volumeInfo.current,
volumeInfo.min, volumeInfo.max);
return volumeInfo;
}

public String recordInputEvents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,7 @@ public void initialize() {
if (mediaStateJSONConfig != null && !mediaStateJSONConfig.isEmpty()) {
loadMediaStateConfig(mediaStateJSONConfig);
}
adbConnection.configure(currentConfig.ip, currentConfig.port, currentConfig.timeout,
currentConfig.recordDuration);
adbConnection.configure(currentConfig);
var androidVersion = thing.getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION);
if (androidVersion != null) {
// configure android implementation to use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private void discoverWithADB(String ip, int port, String macAddress)
throws InterruptedException, AndroidDebugBridgeDeviceException, AndroidDebugBridgeDeviceReadException,
TimeoutException, ExecutionException {
var device = new AndroidDebugBridgeDevice(scheduler);
device.configure(ip, port, 10, 0);
device.configureConnection(ip, port, 10);
try {
device.connect();
logger.debug("connected adb at {}:{}", ip, port);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link AndroidTVMDNSDiscoveryParticipant} is responsible for discovering new and removed Android TV devices. It
Expand All @@ -50,7 +48,6 @@ public class AndroidTVMDNSDiscoveryParticipant implements MDNSDiscoveryParticipa

private static final String SERVICE_TYPE = "_androidtvremote2._tcp.local.";
private static final String MDNS_PROPERTY_MAC_ADDRESS = "bt";
private final Logger logger = LoggerFactory.getLogger(AndroidTVMDNSDiscoveryParticipant.class);

private boolean isAutoDiscoveryEnabled = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ thing-type.androiddebugbridge.android.description = Android Device Thing for And

# thing types config

thing-type.config.androiddebugbridge.android.deviceMaxVolume.label = Device Max Volume
thing-type.config.androiddebugbridge.android.deviceMaxVolume.description = Assumed max volume for devices with android versions that do not expose this value (>=android 11).
thing-type.config.androiddebugbridge.android.ip.label = IP Address
thing-type.config.androiddebugbridge.android.ip.description = Device ip address.
thing-type.config.androiddebugbridge.android.mediaStateJSONConfig.label = Media State Config
Expand All @@ -33,6 +35,16 @@ thing-type.config.androiddebugbridge.android.refreshTime.label = Refresh Time
thing-type.config.androiddebugbridge.android.refreshTime.description = Seconds between device status refreshes.
thing-type.config.androiddebugbridge.android.timeout.label = Command Timeout
thing-type.config.androiddebugbridge.android.timeout.description = Command timeout seconds.
thing-type.config.androiddebugbridge.android.volumeSettingKey.label = Volume Setting Key
thing-type.config.androiddebugbridge.android.volumeSettingKey.description = Settings key for android versions where the volume level is gathered using the 'settings' cli (>=android 11).
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_bluetooth_sco = volume bluetooth sco
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_music = volume music
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_music_bt_a2dp = volume music bt a2dp
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_music_hdmi = volume music hdmi
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_music_headphone = volume music headphone
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_music_headset = volume music headset
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_music_usb_headset = volume music usb headset
thing-type.config.androiddebugbridge.android.volumeSettingKey.option.volume_system = volume system

# channel types

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,29 @@
<description>JSON config that allows to modify the media state detection strategy for each app. Refer to the binding
documentation.</description>
</parameter>
<parameter name="volumeSettingKey" type="text">
<label>Volume Setting Key</label>
<description>Settings key for android versions where the volume level is gathered using the 'settings' cli
(>=android 11).</description>
<default>volume_music_hdmi</default>
<options>
<option value="volume_bluetooth_sco">volume bluetooth sco</option>
<option value="volume_music">volume music</option>
<option value="volume_music_bt_a2dp">volume music bt a2dp</option>
<option value="volume_music_hdmi">volume music hdmi</option>
<option value="volume_music_headphone">volume music headphone</option>
<option value="volume_music_headset">volume music headset</option>
<option value="volume_music_usb_headset">volume music usb headset</option>
<option value="volume_system">volume system</option>
</options>
<advanced>true</advanced>
</parameter>
<parameter name="deviceMaxVolume" type="integer" min="1" max="100">
<label>Device Max Volume</label>
<description>Assumed max volume for devices with android versions that do not expose this value (>=android 11).</description>
<default>25</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>

Expand Down

0 comments on commit 3e068ed

Please sign in to comment.