From 9badec8e3a0e9cc19906549e60dac0e0601f7478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Sun, 19 Apr 2026 16:07:13 +0100 Subject: [PATCH] Sony: Read LDAC and NC/AMBIENT button value on connection --- .../prefs/ButtonFunctionNcAmbient.java | 10 +++ .../sony/headphones/protocol/Message.java | 2 +- .../impl/AbstractSonyProtocolImpl.java | 4 + .../protocol/impl/v1/SonyProtocolImplV1.java | 81 +++++++++++++++---- .../protocol/impl/v2/SonyProtocolImplV2.java | 16 +++- 5 files changed, 93 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java index 556b9fc8d6..be6489323c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java @@ -45,6 +45,16 @@ public class ButtonFunctionNcAmbient { return code; } + public static ButtonFunctionNcAmbient.Mode fromCode(final byte b) { + for (ButtonFunctionNcAmbient.Mode value : ButtonFunctionNcAmbient.Mode.values()) { + if (value.getCode() == b) { + return value; + } + } + + return null; + } + public static Mode fromPrefValue(final String prefValue) { for (final Mode mode : values()) { if (mode.prefValue.equals(prefValue)) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/Message.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/Message.java index 4c5437c639..57c7a50864 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/Message.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/Message.java @@ -86,7 +86,7 @@ public class Message { public String toString() { if (payload.length > 0) { - return String.format(Locale.getDefault(), "Message{Cmd=%s, Seq=%d, PayloadType=%d, Payload=%s}", type, sequenceNumber, payload[0], GB.hexdump(payload)); + return String.format(Locale.getDefault(), "Message{Cmd=%s, Seq=%d, PayloadType=%s, Payload=%s}", type, sequenceNumber, String.format("0x%02x", payload[0]), GB.hexdump(payload)); } else { return String.format(Locale.getDefault(), "Message{Cmd=%s, Seq=%d}", type, sequenceNumber); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java index 38706af440..d40e1c5cd9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java @@ -88,8 +88,12 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request getAudioUpsampling(); + public abstract Request getAudioLDAC(); + public abstract Request setAudioLDAC(final AudioLDAC config); + public abstract Request getButtonFunctionNcAmbient(); + public abstract Request setButtonFunctionNcAmbient(final ButtonFunctionNcAmbient config); public abstract Request setAudioUpsampling(final AudioUpsampling config); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java index 9e40280346..7d18e5455b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java @@ -261,6 +261,17 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { ); } + @Override + public Request getAudioLDAC() { + return new Request( + PayloadTypeV1.AUDIO_UPSAMPLING_GET.getMessageType(), + new byte[]{ + PayloadTypeV1.AUDIO_UPSAMPLING_GET.getCode(), + (byte) 0x01 + } + ); + } + @Override public Request setAudioLDAC(final AudioLDAC config) { return new Request( @@ -274,6 +285,17 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { ); } + @Override + public Request getButtonFunctionNcAmbient() { + return new Request( + PayloadTypeV1.TOUCH_SENSOR_GET.getMessageType(), + new byte[]{ + PayloadTypeV1.TOUCH_SENSOR_GET.getCode(), + (byte) 0xd1 + } + ); + } + @Override public Request setButtonFunctionNcAmbient(final ButtonFunctionNcAmbient config) { return new Request( @@ -675,7 +697,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { capabilityRequests.add(getFirmwareVersion()); capabilityRequests.add(getAudioCodec()); - final Map capabilityRequestMap = new LinkedHashMap() {{ + final Map capabilityRequestMap = new LinkedHashMap<>() {{ put(SonyHeadphonesCapabilities.BatterySingle, getBattery(BatteryType.SINGLE)); put(SonyHeadphonesCapabilities.BatteryDual, getBattery(BatteryType.DUAL)); put(SonyHeadphonesCapabilities.BatteryDual2, getBattery(BatteryType.DUAL2)); @@ -684,6 +706,8 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { put(SonyHeadphonesCapabilities.AmbientSoundControl2, getAmbientSoundControl()); put(SonyHeadphonesCapabilities.AncOptimizer, getNoiseCancellingOptimizerState()); put(SonyHeadphonesCapabilities.AudioUpsampling, getAudioUpsampling()); + put(SonyHeadphonesCapabilities.AudioLDAC, getAudioLDAC()); + put(SonyHeadphonesCapabilities.ButtonFunctionNcAmbient, getButtonFunctionNcAmbient()); put(SonyHeadphonesCapabilities.ButtonModesLeftRight, getButtonModes()); put(SonyHeadphonesCapabilities.VoiceNotifications, getVoiceNotifications()); put(SonyHeadphonesCapabilities.AutomaticPowerOffWhenTakenOff, getAutomaticPowerOff()); @@ -856,14 +880,27 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { final Boolean enabled = booleanFromByte(payload[3]); if (enabled == null) { - LOG.warn("Unknown audio upsampling code {}", String.format("%02x", payload[3])); + LOG.warn("Unknown audio upsampling value {}", String.format("%02x", payload[3])); return Collections.emptyList(); } - LOG.debug("Audio Upsampling: {}", enabled); + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences(); - final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() - .withPreferences(new AudioUpsampling(enabled).toPreferences()); + switch (payload[1]) { + case 0x01: + // LDAC + LOG.debug("Audio Upsampling LDAC: {}", !enabled); + event.withPreferences(new AudioLDAC(!enabled).toPreferences()); + break; + case 0x02: + // DSEE / Audio upsampling + LOG.debug("Audio Upsampling DSEE: {}", enabled); + event.withPreferences(new AudioUpsampling(enabled).toPreferences()); + break; + default: + LOG.warn("Unknown audio upsampling code {}", String.format("%02x", payload[1])); + return Collections.emptyList(); + } return Collections.singletonList(event); } @@ -1215,25 +1252,35 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { return Collections.emptyList(); } - boolean enabled; + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences(); - switch (payload[3]) { - case 0x00: - enabled = false; + switch (payload[1]) { + case (byte) 0xd1: + final ButtonFunctionNcAmbient.Mode mode = ButtonFunctionNcAmbient.Mode.fromCode(payload[3]); + event.withPreferences(new ButtonFunctionNcAmbient(mode).toPreferences()); + LOG.debug("Nc Ambient button mode: {}", mode); break; - case 0x01: - enabled = true; + case (byte) 0xd2: + boolean enabled; + switch (payload[3]) { + case 0x00: + enabled = false; + break; + case 0x01: + enabled = true; + break; + default: + LOG.warn("Unknown touch sensor value {}", String.format("%02x", payload[3])); + return Collections.emptyList(); + } + LOG.debug("Touch Sensor: {}", enabled); + event.withPreferences(new TouchSensor(enabled).toPreferences()); break; default: - LOG.warn("Unknown touch sensor code {}", String.format("%02x", payload[3])); + LOG.warn("Unknown touch sensor code {}", String.format("%02x", payload[1])); return Collections.emptyList(); } - LOG.debug("Touch Sensor: {}", enabled); - - final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() - .withPreferences(new TouchSensor(enabled).toPreferences()); - return Collections.singletonList(event); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java index 552eb8bf2e..90ed85c2c0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java @@ -225,15 +225,27 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 { ); } + @Override + public Request getAudioLDAC() { + LOG.warn("Audio LDAC get not implemented for V2"); + return null; + } + @Override public Request setAudioLDAC(final AudioLDAC config) { - LOG.warn("Audio LDAC not implemented for V2"); + LOG.warn("Audio LDAC set not implemented for V2"); + return null; + } + + @Override + public Request getButtonFunctionNcAmbient() { + LOG.warn("Button function NC ambient get not implemented for V2"); return null; } @Override public Request setButtonFunctionNcAmbient(final ButtonFunctionNcAmbient config) { - LOG.warn("Button function NC ambient not implemented for V2"); + LOG.warn("Button function NC ambient set not implemented for V2"); return null; }