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 all 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,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>
GiviMAD marked this conversation as resolved.
Show resolved Hide resolved
</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