1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-22 06:41:06 +02:00

Sony Headphones: Refactor V1 protocol to simplify V2 implementation

This commit is contained in:
José Rebelo 2022-10-19 20:50:39 +01:00
parent 1dca054853
commit d4ba532b11
10 changed files with 185 additions and 156 deletions

View File

@ -24,7 +24,6 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig; import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;

View File

@ -36,7 +36,7 @@ public class AmbientSoundControl {
private final boolean focusOnVoice; private final boolean focusOnVoice;
private final int ambientSound; private final int ambientSound;
public AmbientSoundControl(Mode mode, boolean focusOnVoice, int ambientSound) { public AmbientSoundControl(final Mode mode, final boolean focusOnVoice, final int ambientSound) {
if (ambientSound < 0 || ambientSound > 20) { if (ambientSound < 0 || ambientSound > 20) {
throw new IllegalArgumentException(String.format("Level must be between 0 and 20 (was %d)", ambientSound)); throw new IllegalArgumentException(String.format("Level must be between 0 and 20 (was %d)", ambientSound));
} }

View File

@ -48,6 +48,16 @@ public enum AutomaticPowerOff {
}}; }};
} }
public static AutomaticPowerOff fromCode(final byte b1, final byte b2) {
for (AutomaticPowerOff value : AutomaticPowerOff.values()) {
if (value.getCode()[0] == b1 && value.getCode()[1] == b2) {
return value;
}
}
return null;
}
public static AutomaticPowerOff fromPreferences(final SharedPreferences prefs) { public static AutomaticPowerOff fromPreferences(final SharedPreferences prefs) {
return AutomaticPowerOff.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_AUTOMATIC_POWER_OFF, "off").toUpperCase()); return AutomaticPowerOff.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_AUTOMATIC_POWER_OFF, "off").toUpperCase());
} }

View File

@ -40,6 +40,16 @@ public class ButtonModes {
public byte getCode() { public byte getCode() {
return this.code; return this.code;
} }
public static Mode fromCode(final byte code) {
for (ButtonModes.Mode value : ButtonModes.Mode.values()) {
if (value.getCode() == code) {
return value;
}
}
return null;
}
} }
final Mode left; final Mode left;

View File

@ -54,6 +54,16 @@ public enum EqualizerPreset {
}}; }};
} }
public static EqualizerPreset fromCode(final byte code) {
for (EqualizerPreset value : EqualizerPreset.values()) {
if (value.getCode() == code) {
return value;
}
}
return null;
}
public static EqualizerPreset fromPreferences(final SharedPreferences prefs) { public static EqualizerPreset fromPreferences(final SharedPreferences prefs) {
return EqualizerPreset.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE, "off").toUpperCase()); return EqualizerPreset.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE, "off").toUpperCase());
} }

View File

@ -208,6 +208,11 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
return super.encodeSendConfiguration(config); return super.encodeSendConfiguration(config);
} }
if (configRequest == null) {
LOG.warn("Failed to encode config request for {}", config);
return super.encodeSendConfiguration(config);
}
pendingAcks++; pendingAcks++;
return configRequest.encode(sequenceNumber); return configRequest.encode(sequenceNumber);

View File

@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Locale; import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.PayloadType;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class Message { public class Message {
@ -38,7 +37,7 @@ public class Message {
* - Message Type ({@link MessageType}) * - Message Type ({@link MessageType})
* - Sequence Number - needs to be updated with the one sent in the ACK responses * - Sequence Number - needs to be updated with the one sent in the ACK responses
* - Payload Length - 4-byte big endian int with number of bytes that will follow * - Payload Length - 4-byte big endian int with number of bytes that will follow
* - N bytes of payload data (first being the {@link PayloadType}) * - N bytes of payload data (first being the PayloadType)
* - Checksum (1-byte sum, excluding header) * - Checksum (1-byte sum, excluding header)
* - MESSAGE_TRAILER * - MESSAGE_TRAILER
* <p> * <p>
@ -87,8 +86,7 @@ public class Message {
public String toString() { public String toString() {
if (payload.length > 0) { if (payload.length > 0) {
final PayloadType payloadType = PayloadType.fromCode(type, payload[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, payloadType, GB.hexdump(payload));
} else { } else {
return String.format(Locale.getDefault(), "Message{Cmd=%s, Seq=%d}", type, sequenceNumber); return String.format(Locale.getDefault(), "Message{Cmd=%s, Seq=%d}", type, sequenceNumber);
} }

View File

@ -18,7 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.pro
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType;
public enum PayloadType { public enum PayloadTypeV1 {
INIT_REQUEST(MessageType.COMMAND_1, 0x00), INIT_REQUEST(MessageType.COMMAND_1, 0x00),
INIT_REPLY(MessageType.COMMAND_1, 0x01), INIT_REPLY(MessageType.COMMAND_1, 0x01),
@ -93,7 +93,7 @@ public enum PayloadType {
private final MessageType messageType; private final MessageType messageType;
private final byte code; private final byte code;
PayloadType(final MessageType messageType, final int code) { PayloadTypeV1(final MessageType messageType, final int code) {
this.messageType = messageType; this.messageType = messageType;
this.code = (byte) code; this.code = (byte) code;
} }
@ -106,13 +106,13 @@ public enum PayloadType {
return this.code; return this.code;
} }
public static PayloadType fromCode(final MessageType messageType, final byte code) { public static PayloadTypeV1 fromCode(final MessageType messageType, final byte code) {
for (final PayloadType payloadType : values()) { for (final PayloadTypeV1 payloadType : values()) {
if (messageType.equals(payloadType.messageType) && payloadType.code == code) { if (messageType.equals(payloadType.messageType) && payloadType.code == code) {
return payloadType; return payloadType;
} }
} }
return PayloadType.UNKNOWN; return PayloadTypeV1.UNKNOWN;
} }
} }

View File

@ -61,7 +61,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.prot
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.VirtualSoundParam; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.VirtualSoundParam;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@ -74,9 +73,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getAmbientSoundControl() { public Request getAmbientSoundControl() {
return new Request( return new Request(
PayloadType.AMBIENT_SOUND_CONTROL_GET.getMessageType(), PayloadTypeV1.AMBIENT_SOUND_CONTROL_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AMBIENT_SOUND_CONTROL_GET.getCode(), PayloadTypeV1.AMBIENT_SOUND_CONTROL_GET.getCode(),
(byte) 0x02 (byte) 0x02
} }
); );
@ -86,7 +85,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
public Request setAmbientSoundControl(final AmbientSoundControl ambientSoundControl) { public Request setAmbientSoundControl(final AmbientSoundControl ambientSoundControl) {
final ByteBuffer buf = ByteBuffer.allocate(8); final ByteBuffer buf = ByteBuffer.allocate(8);
buf.put(PayloadType.AMBIENT_SOUND_CONTROL_SET.getCode()); buf.put(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getCode());
buf.put((byte) 0x02); buf.put((byte) 0x02);
if (AmbientSoundControl.Mode.OFF.equals(ambientSoundControl.getMode())) { if (AmbientSoundControl.Mode.OFF.equals(ambientSoundControl.getMode())) {
@ -135,15 +134,15 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
break; break;
} }
return new Request(PayloadType.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array()); return new Request(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array());
} }
@Override @Override
public Request getNoiseCancellingOptimizerState() { public Request getNoiseCancellingOptimizerState() {
return new Request( return new Request(
PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getMessageType(), PayloadTypeV1.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getCode(), PayloadTypeV1.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getCode(),
(byte) 0x01 (byte) 0x01
} }
); );
@ -152,21 +151,21 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getAudioCodec() { public Request getAudioCodec() {
return new Request( return new Request(
PayloadType.AUDIO_CODEC_REQUEST.getMessageType(), PayloadTypeV1.AUDIO_CODEC_REQUEST.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUDIO_CODEC_REQUEST.getCode(), PayloadTypeV1.AUDIO_CODEC_REQUEST.getCode(),
(byte) 0x00 (byte) 0x00
} }
); );
} }
@Override @Override
public Request getBattery(BatteryType batteryType) { public Request getBattery(final BatteryType batteryType) {
return new Request( return new Request(
PayloadType.BATTERY_LEVEL_REQUEST.getMessageType(), PayloadTypeV1.BATTERY_LEVEL_REQUEST.getMessageType(),
new byte[]{ new byte[]{
PayloadType.BATTERY_LEVEL_REQUEST.getCode(), PayloadTypeV1.BATTERY_LEVEL_REQUEST.getCode(),
batteryType.getCode() encodeBatteryType(batteryType)
} }
); );
} }
@ -174,9 +173,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getFirmwareVersion() { public Request getFirmwareVersion() {
return new Request( return new Request(
PayloadType.FW_VERSION_REQUEST.getMessageType(), PayloadTypeV1.FW_VERSION_REQUEST.getMessageType(),
new byte[]{ new byte[]{
PayloadType.FW_VERSION_REQUEST.getCode(), PayloadTypeV1.FW_VERSION_REQUEST.getCode(),
(byte) 0x02 (byte) 0x02
} }
); );
@ -185,20 +184,20 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getAudioUpsampling() { public Request getAudioUpsampling() {
return new Request( return new Request(
PayloadType.AUDIO_UPSAMPLING_GET.getMessageType(), PayloadTypeV1.AUDIO_UPSAMPLING_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUDIO_UPSAMPLING_GET.getCode(), PayloadTypeV1.AUDIO_UPSAMPLING_GET.getCode(),
(byte) 0x02 (byte) 0x02
} }
); );
} }
@Override @Override
public Request setAudioUpsampling(AudioUpsampling config) { public Request setAudioUpsampling(final AudioUpsampling config) {
return new Request( return new Request(
PayloadType.AUDIO_UPSAMPLING_SET.getMessageType(), PayloadTypeV1.AUDIO_UPSAMPLING_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUDIO_UPSAMPLING_SET.getCode(), PayloadTypeV1.AUDIO_UPSAMPLING_SET.getCode(),
(byte) 0x02, (byte) 0x02,
(byte) 0x00, (byte) 0x00,
(byte) (config.isEnabled() ? 0x01 : 0x00) (byte) (config.isEnabled() ? 0x01 : 0x00)
@ -209,9 +208,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getAutomaticPowerOff() { public Request getAutomaticPowerOff() {
return new Request( return new Request(
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(),
(byte) 0x04 (byte) 0x04
} }
); );
@ -220,9 +219,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setAutomaticPowerOff(final AutomaticPowerOff config) { public Request setAutomaticPowerOff(final AutomaticPowerOff config) {
return new Request( return new Request(
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
(byte) 0x04, (byte) 0x04,
(byte) 0x01, (byte) 0x01,
config.getCode()[0], config.getCode()[0],
@ -233,9 +232,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
public Request getButtonModes() { public Request getButtonModes() {
return new Request( return new Request(
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(),
(byte) 0x06 (byte) 0x06
} }
); );
@ -243,9 +242,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
public Request setButtonModes(final ButtonModes config) { public Request setButtonModes(final ButtonModes config) {
return new Request( return new Request(
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
(byte) 0x06, (byte) 0x06,
(byte) 0x02, (byte) 0x02,
config.getModeLeft().getCode(), config.getModeLeft().getCode(),
@ -257,9 +256,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getPauseWhenTakenOff() { public Request getPauseWhenTakenOff() {
return new Request( return new Request(
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(),
(byte) 0x03 (byte) 0x03
} }
); );
@ -268,9 +267,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setPauseWhenTakenOff(final PauseWhenTakenOff config) { public Request setPauseWhenTakenOff(final PauseWhenTakenOff config) {
return new Request( return new Request(
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(), PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
(byte) 0x03, (byte) 0x03,
(byte) 0x00, (byte) 0x00,
(byte) (config.isEnabled() ? 0x01 : 0x00) (byte) (config.isEnabled() ? 0x01 : 0x00)
@ -281,9 +280,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getEqualizer() { public Request getEqualizer() {
return new Request( return new Request(
PayloadType.EQUALIZER_GET.getMessageType(), PayloadTypeV1.EQUALIZER_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.EQUALIZER_GET.getCode(), PayloadTypeV1.EQUALIZER_GET.getCode(),
(byte) 0x01 (byte) 0x01
} }
); );
@ -292,9 +291,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setEqualizerPreset(final EqualizerPreset config) { public Request setEqualizerPreset(final EqualizerPreset config) {
return new Request( return new Request(
PayloadType.EQUALIZER_SET.getMessageType(), PayloadTypeV1.EQUALIZER_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.EQUALIZER_SET.getCode(), PayloadTypeV1.EQUALIZER_SET.getCode(),
(byte) 0x01, (byte) 0x01,
config.getCode(), config.getCode(),
(byte) 0x00 (byte) 0x00
@ -306,7 +305,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
public Request setEqualizerCustomBands(final EqualizerCustomBands config) { public Request setEqualizerCustomBands(final EqualizerCustomBands config) {
final ByteBuffer buf = ByteBuffer.allocate(10); final ByteBuffer buf = ByteBuffer.allocate(10);
buf.put(PayloadType.EQUALIZER_SET.getCode()); buf.put(PayloadTypeV1.EQUALIZER_SET.getCode());
buf.put((byte) 0x01); buf.put((byte) 0x01);
buf.put((byte) 0xff); buf.put((byte) 0xff);
buf.put((byte) 0x06); buf.put((byte) 0x06);
@ -317,7 +316,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
} }
return new Request( return new Request(
PayloadType.EQUALIZER_SET.getMessageType(), PayloadTypeV1.EQUALIZER_SET.getMessageType(),
buf.array() buf.array()
); );
} }
@ -325,9 +324,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getSoundPosition() { public Request getSoundPosition() {
return new Request( return new Request(
PayloadType.SOUND_POSITION_OR_MODE_GET.getMessageType(), PayloadTypeV1.SOUND_POSITION_OR_MODE_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.SOUND_POSITION_OR_MODE_GET.getCode(), PayloadTypeV1.SOUND_POSITION_OR_MODE_GET.getCode(),
(byte) 0x02 (byte) 0x02
} }
); );
@ -336,9 +335,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setSoundPosition(final SoundPosition config) { public Request setSoundPosition(final SoundPosition config) {
return new Request( return new Request(
PayloadType.SOUND_POSITION_OR_MODE_SET.getMessageType(), PayloadTypeV1.SOUND_POSITION_OR_MODE_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.SOUND_POSITION_OR_MODE_SET.getCode(), PayloadTypeV1.SOUND_POSITION_OR_MODE_SET.getCode(),
VirtualSoundParam.SOUND_POSITION.getCode(), VirtualSoundParam.SOUND_POSITION.getCode(),
config.getCode() config.getCode()
} }
@ -348,9 +347,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getSurroundMode() { public Request getSurroundMode() {
return new Request( return new Request(
PayloadType.SOUND_POSITION_OR_MODE_GET.getMessageType(), PayloadTypeV1.SOUND_POSITION_OR_MODE_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.SOUND_POSITION_OR_MODE_GET.getCode(), PayloadTypeV1.SOUND_POSITION_OR_MODE_GET.getCode(),
VirtualSoundParam.SURROUND_MODE.getCode() VirtualSoundParam.SURROUND_MODE.getCode()
} }
); );
@ -359,9 +358,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setSurroundMode(final SurroundMode config) { public Request setSurroundMode(final SurroundMode config) {
return new Request( return new Request(
PayloadType.SOUND_POSITION_OR_MODE_SET.getMessageType(), PayloadTypeV1.SOUND_POSITION_OR_MODE_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.SOUND_POSITION_OR_MODE_SET.getCode(), PayloadTypeV1.SOUND_POSITION_OR_MODE_SET.getCode(),
VirtualSoundParam.SURROUND_MODE.getCode(), VirtualSoundParam.SURROUND_MODE.getCode(),
config.getCode() config.getCode()
} }
@ -371,9 +370,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getTouchSensor() { public Request getTouchSensor() {
return new Request( return new Request(
PayloadType.TOUCH_SENSOR_GET.getMessageType(), PayloadTypeV1.TOUCH_SENSOR_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.TOUCH_SENSOR_GET.getCode(), PayloadTypeV1.TOUCH_SENSOR_GET.getCode(),
(byte) 0xd2 (byte) 0xd2
} }
); );
@ -382,9 +381,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setTouchSensor(final TouchSensor config) { public Request setTouchSensor(final TouchSensor config) {
return new Request( return new Request(
PayloadType.TOUCH_SENSOR_SET.getMessageType(), PayloadTypeV1.TOUCH_SENSOR_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.TOUCH_SENSOR_SET.getCode(), PayloadTypeV1.TOUCH_SENSOR_SET.getCode(),
(byte) 0xd2, (byte) 0xd2,
(byte) 0x01, (byte) 0x01,
(byte) (config.isEnabled() ? 0x01 : 0x00) (byte) (config.isEnabled() ? 0x01 : 0x00)
@ -395,9 +394,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request getVoiceNotifications() { public Request getVoiceNotifications() {
return new Request( return new Request(
PayloadType.VOICE_NOTIFICATIONS_GET.getMessageType(), PayloadTypeV1.VOICE_NOTIFICATIONS_GET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.VOICE_NOTIFICATIONS_GET.getCode(), PayloadTypeV1.VOICE_NOTIFICATIONS_GET.getCode(),
(byte) 0x01, (byte) 0x01,
(byte) 0x01 (byte) 0x01
} }
@ -407,9 +406,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request setVoiceNotifications(final VoiceNotifications config) { public Request setVoiceNotifications(final VoiceNotifications config) {
return new Request( return new Request(
PayloadType.VOICE_NOTIFICATIONS_SET.getMessageType(), PayloadTypeV1.VOICE_NOTIFICATIONS_SET.getMessageType(),
new byte[]{ new byte[]{
PayloadType.VOICE_NOTIFICATIONS_SET.getCode(), PayloadTypeV1.VOICE_NOTIFICATIONS_SET.getCode(),
(byte) 0x01, (byte) 0x01,
(byte) 0x01, (byte) 0x01,
(byte) (config.isEnabled() ? 0x01 : 0x00) (byte) (config.isEnabled() ? 0x01 : 0x00)
@ -420,9 +419,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request startNoiseCancellingOptimizer(final boolean start) { public Request startNoiseCancellingOptimizer(final boolean start) {
return new Request( return new Request(
PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getMessageType(), PayloadTypeV1.NOISE_CANCELLING_OPTIMIZER_START.getMessageType(),
new byte[]{ new byte[]{
PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getCode(), PayloadTypeV1.NOISE_CANCELLING_OPTIMIZER_START.getCode(),
(byte) 0x01, (byte) 0x01,
(byte) 0x00, (byte) 0x00,
(byte) (start ? 0x01 : 0x00) (byte) (start ? 0x01 : 0x00)
@ -433,9 +432,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public Request powerOff() { public Request powerOff() {
return new Request( return new Request(
PayloadType.POWER_OFF.getMessageType(), PayloadTypeV1.POWER_OFF.getMessageType(),
new byte[]{ new byte[]{
PayloadType.POWER_OFF.getCode(), PayloadTypeV1.POWER_OFF.getCode(),
(byte) 0x00, (byte) 0x00,
(byte) 0x01 (byte) 0x01
} }
@ -444,7 +443,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
@Override @Override
public List<? extends GBDeviceEvent> handlePayload(final MessageType messageType, final byte[] payload) { public List<? extends GBDeviceEvent> handlePayload(final MessageType messageType, final byte[] payload) {
final PayloadType payloadType = PayloadType.fromCode(messageType, payload[0]); final PayloadTypeV1 payloadType = PayloadTypeV1.fromCode(messageType, payload[0]);
switch (payloadType) { switch (payloadType) {
case INIT_REPLY: case INIT_REPLY:
@ -493,12 +492,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
} }
public List<? extends GBDeviceEvent> handleInitResponse(final byte[] payload) { public List<? extends GBDeviceEvent> handleInitResponse(final byte[] payload) {
if (payload.length != 4) { final SonyHeadphonesCoordinator coordinator = getCoordinator();
LOG.warn("Unexpected payload length {}", payload.length);
return Collections.emptyList();
}
final SonyHeadphonesCoordinator coordinator = (SonyHeadphonesCoordinator) DeviceHelper.getInstance().getCoordinator(getDevice());
// Populate the init requests // Populate the init requests
final List<Request> capabilityRequests = new ArrayList<>(); final List<Request> capabilityRequests = new ArrayList<>();
@ -530,6 +524,9 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
} }
} }
// Remove any requests that are not supported by other protocol version
capabilityRequests.removeAll(Collections.singleton(null));
return Collections.singletonList(new SonyHeadphonesEnqueueRequestEvent(capabilityRequests)); return Collections.singletonList(new SonyHeadphonesEnqueueRequestEvent(capabilityRequests));
} }
@ -570,17 +567,10 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
boolean focusOnVoice; final Boolean focusOnVoice = booleanFromByte(payload[6]);
switch (payload[6]) { if (focusOnVoice == null) {
case 0x00: LOG.warn("Unknown focus on voice mode {}", String.format("%02x", payload[6]));
focusOnVoice = false; return Collections.emptyList();
break;
case 0x01:
focusOnVoice = true;
break;
default:
LOG.warn("Unknown focus on voice mode {}", String.format("%02x", payload[6]));
return Collections.emptyList();
} }
int ambientSound = payload[7]; int ambientSound = payload[7];
@ -606,7 +596,6 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
} }
final NoiseCancellingOptimizerStatus status = NoiseCancellingOptimizerStatus.fromCode(payload[3]); final NoiseCancellingOptimizerStatus status = NoiseCancellingOptimizerStatus.fromCode(payload[3]);
if (status == null) { if (status == null) {
LOG.warn("Unable to determine noise cancelling opptimizer status from {}", GB.hexdump(payload)); LOG.warn("Unable to determine noise cancelling opptimizer status from {}", GB.hexdump(payload));
return Collections.emptyList(); return Collections.emptyList();
@ -649,18 +638,10 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
boolean enabled; final Boolean enabled = booleanFromByte(payload[3]);
if (enabled == null) {
switch (payload[3]) { LOG.warn("Unknown audio upsampling code {}", String.format("%02x", payload[3]));
case 0x00: return Collections.emptyList();
enabled = false;
break;
case 0x01:
enabled = true;
break;
default:
LOG.warn("Unknown audio upsampling code {}", String.format("%02x", payload[3]));
return Collections.emptyList();
} }
LOG.debug("Audio Upsampling: {}", enabled); LOG.debug("Audio Upsampling: {}", enabled);
@ -683,15 +664,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
AutomaticPowerOff mode = null; final AutomaticPowerOff mode = AutomaticPowerOff.fromCode(payload[3], payload[4]);
for (AutomaticPowerOff value : AutomaticPowerOff.values()) {
if (value.getCode()[0] == payload[3] && value.getCode()[1] == payload[4]) {
mode = value;
break;
}
}
if (mode == null) { if (mode == null) {
LOG.warn("Unknown automatic power off codes {}", String.format("%02x %02x", payload[3], payload[4])); LOG.warn("Unknown automatic power off codes {}", String.format("%02x %02x", payload[3], payload[4]));
return Collections.emptyList(); return Collections.emptyList();
@ -711,22 +684,8 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
ButtonModes.Mode modeLeft = null; final ButtonModes.Mode modeLeft = ButtonModes.Mode.fromCode(payload[3]);
for (ButtonModes.Mode value : ButtonModes.Mode.values()) { final ButtonModes.Mode modeRight = ButtonModes.Mode.fromCode(payload[4]);
if (value.getCode() == payload[3]) {
modeLeft = value;
break;
}
}
ButtonModes.Mode modeRight = null;
for (ButtonModes.Mode value : ButtonModes.Mode.values()) {
if (value.getCode() == payload[4]) {
modeRight = value;
break;
}
}
if (modeLeft == null || modeRight == null) { if (modeLeft == null || modeRight == null) {
LOG.warn("Unknown button mode codes {}", String.format("%02x %02x", payload[3], payload[4])); LOG.warn("Unknown button mode codes {}", String.format("%02x %02x", payload[3], payload[4]));
return Collections.emptyList(); return Collections.emptyList();
@ -746,21 +705,13 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
boolean enabled; final Boolean enabled = booleanFromByte(payload[3]);
if (enabled == null) {
switch (payload[3]) { LOG.warn("Unknown pause when taken off code {}", String.format("%02x", payload[3]));
case 0x00: return Collections.emptyList();
enabled = false;
break;
case 0x01:
enabled = true;
break;
default:
LOG.warn("Unknown pause when taken off code {}", String.format("%02x", payload[3]));
return Collections.emptyList();
} }
LOG.debug("Touch Sensor: {}", enabled); LOG.debug("Pause when taken off: {}", enabled);
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
.withPreferences(new PauseWhenTakenOff(enabled).toPreferences()); .withPreferences(new PauseWhenTakenOff(enabled).toPreferences());
@ -769,7 +720,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
} }
public List<? extends GBDeviceEvent> handleBattery(final byte[] payload) { public List<? extends GBDeviceEvent> handleBattery(final byte[] payload) {
final BatteryType batteryType = BatteryType.fromCode(payload[1]); final BatteryType batteryType = decodeBatteryType(payload[1]);
if (batteryType == null) { if (batteryType == null) {
LOG.warn("Unknown battery type code {}", String.format("%02x", payload[1])); LOG.warn("Unknown battery type code {}", String.format("%02x", payload[1]));
@ -822,8 +773,12 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
final AudioCodec audioCodec = AudioCodec.fromCode(payload[2]); if (payload[1] != 0x00) {
LOG.warn("Not audio codec, ignoring");
return Collections.emptyList();
}
final AudioCodec audioCodec = AudioCodec.fromCode(payload[2]);
if (audioCodec == null) { if (audioCodec == null) {
LOG.warn("Unable to determine audio codec from {}", GB.hexdump(payload)); LOG.warn("Unable to determine audio codec from {}", GB.hexdump(payload));
return Collections.emptyList(); return Collections.emptyList();
@ -843,14 +798,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.emptyList(); return Collections.emptyList();
} }
EqualizerPreset mode = null; final EqualizerPreset mode = EqualizerPreset.fromCode(payload[2]);
for (EqualizerPreset value : EqualizerPreset.values()) {
if (value.getCode() == payload[2]) {
mode = value;
break;
}
}
if (mode == null) { if (mode == null) {
LOG.warn("Unknown equalizer preset code {}", String.format("%02x", payload[2])); LOG.warn("Unknown equalizer preset code {}", String.format("%02x", payload[2]));
return Collections.emptyList(); return Collections.emptyList();
@ -879,6 +827,11 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
public List<? extends GBDeviceEvent> handleFirmwareVersion(final byte[] payload) { public List<? extends GBDeviceEvent> handleFirmwareVersion(final byte[] payload) {
final Pattern VERSION_REGEX = Pattern.compile("^[0-9.\\-a-zA-Z_]+$"); final Pattern VERSION_REGEX = Pattern.compile("^[0-9.\\-a-zA-Z_]+$");
if (payload[1] != 0x02) {
LOG.warn("Not firmware version, ignoring");
return Collections.emptyList();
}
if (payload.length < 4) { if (payload.length < 4) {
LOG.warn("Unexpected payload length {}", payload.length); LOG.warn("Unexpected payload length {}", payload.length);
return Collections.emptyList(); return Collections.emptyList();
@ -886,6 +839,12 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
final String firmwareVersion = new String(Arrays.copyOfRange(payload, 3, payload.length)); final String firmwareVersion = new String(Arrays.copyOfRange(payload, 3, payload.length));
final int expectedLength = payload[2] & 0xff;
if (firmwareVersion.length() != expectedLength) {
LOG.warn("Unexpected firmware version length {}, expected {}", firmwareVersion.length(), expectedLength);
return Collections.emptyList();
}
if (!VERSION_REGEX.matcher(firmwareVersion).find()) { if (!VERSION_REGEX.matcher(firmwareVersion).find()) {
LOG.warn("Unexpected characters in version '{}'", firmwareVersion); LOG.warn("Unexpected characters in version '{}'", firmwareVersion);
return Collections.emptyList(); return Collections.emptyList();
@ -1060,9 +1019,47 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.singletonList(event); return Collections.singletonList(event);
} }
private boolean supportsWindNoiseCancelling() { protected Boolean booleanFromByte(final byte b) {
final SonyHeadphonesCoordinator coordinator = (SonyHeadphonesCoordinator) DeviceHelper.getInstance().getCoordinator(getDevice()); switch (b) {
case 0x00:
return false;
case 0x01:
return true;
default:
}
return null;
}
protected boolean supportsWindNoiseCancelling() {
final SonyHeadphonesCoordinator coordinator = getCoordinator();
return coordinator.supports(SonyHeadphonesCapabilities.WindNoiseReduction); return coordinator.supports(SonyHeadphonesCapabilities.WindNoiseReduction);
} }
protected BatteryType decodeBatteryType(final byte b) {
switch (b) {
case 0x00:
return BatteryType.SINGLE;
case 0x01:
return BatteryType.DUAL;
case 0x02:
return BatteryType.CASE;
}
return null;
}
protected byte encodeBatteryType(final BatteryType batteryType) {
switch (batteryType) {
case SINGLE:
return 0x00;
case DUAL:
return 0x01;
case CASE:
return 0x02;
}
throw new IllegalArgumentException("Unknown battery type " + batteryType);
}
} }

View File

@ -38,6 +38,12 @@ public enum NoiseCancellingOptimizerStatus {
return this.code; return this.code;
} }
public String i18n(final Context context) {
final String stringName = String.format("sony_anc_optimizer_status_%s", name().toLowerCase(Locale.ROOT));
final int stringId = context.getResources().getIdentifier(stringName, "string", context.getPackageName());
return context.getString(stringId);
}
public static NoiseCancellingOptimizerStatus fromCode(final byte code) { public static NoiseCancellingOptimizerStatus fromCode(final byte code) {
for (final NoiseCancellingOptimizerStatus audioCodec : values()) { for (final NoiseCancellingOptimizerStatus audioCodec : values()) {
if (audioCodec.code == code) { if (audioCodec.code == code) {
@ -47,10 +53,4 @@ public enum NoiseCancellingOptimizerStatus {
return null; return null;
} }
public String i18n(final Context context) {
final String stringName = String.format("sony_anc_optimizer_status_%s", name().toLowerCase(Locale.ROOT));
final int stringId = context.getResources().getIdentifier(stringName, "string", context.getPackageName());
return context.getString(stringId);
}
} }