Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[androiddebugbridge] Fix volume channel for android 11/12 #13828

Merged
merged 6 commits into from
Dec 3, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,17 @@ 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_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,30 @@
<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" required="true">
GiviMAD marked this conversation as resolved.
Show resolved Hide resolved
<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_headset">volume_music_headset</option>
<option value="volume_music_usb_headset">volume_music_usb_headset</option>
<option value="volume_system">volume_system</option>
GiviMAD marked this conversation as resolved.
Show resolved Hide resolved
</options>
<advanced>true</advanced>
</parameter>
<parameter name="deviceMaxVolume" type="integer" required="true" min="1" max="100">
GiviMAD marked this conversation as resolved.
Show resolved Hide resolved
<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