diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/PodModel.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/PodModel.kt index 0877c079..af920971 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/PodModel.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/PodModel.kt @@ -99,7 +99,6 @@ enum class PodModel( hasAllowOffOption = true, hasStemConfig = true, hasSleepDetection = true, - needsInitExt = true, ), modelNumbers = setOf("A3055", "A3056", "A3057"), // earphones leftPodIconRes = R.drawable.device_airpods_gen4anc_left, @@ -158,7 +157,6 @@ enum class PodModel( hasAllowOffOption = true, hasStemConfig = true, hasSleepDetection = true, - needsInitExt = true, ), modelNumbers = setOf("A2698", "A2699", "A2931"), // earphones leftPodIconRes = R.drawable.device_airpods_pro2_left, @@ -192,7 +190,6 @@ enum class PodModel( hasAllowOffOption = true, hasStemConfig = true, hasSleepDetection = true, - needsInitExt = true, ), modelNumbers = setOf("A3047", "A3048", "A3049"), // earphones leftPodIconRes = R.drawable.device_airpods_pro2_left, @@ -226,7 +223,6 @@ enum class PodModel( hasAllowOffOption = true, hasStemConfig = true, hasSleepDetection = true, - needsInitExt = true, ), modelNumbers = setOf("A3063", "A3064", "A3065"), // earphones leftPodIconRes = R.drawable.device_airpods_pro2_left, @@ -281,7 +277,6 @@ enum class PodModel( hasEarDetectionToggle = true, hasListeningModeCycle = true, hasAllowOffOption = true, - needsInitExt = true, ), modelNumbers = setOf("A3454"), // headphones ), @@ -550,7 +545,5 @@ enum class PodModel( val hasAllowOffOption: Boolean = false, val hasStemConfig: Boolean = false, val hasSleepDetection: Boolean = false, - // Protocol - val needsInitExt: Boolean = false, ) } diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/AapConnection.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/AapConnection.kt index 334e0696..9caaa34b 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/AapConnection.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/AapConnection.kt @@ -110,12 +110,10 @@ internal class AapConnection( } log(TAG) { "Notification enable sent" } - // Send InitExt for models that need it (Pro 2/3/USB-C, AP4 ANC) - profile.encodeInitExt()?.let { initExt -> - sock.outputStream.write(initExt) - sock.outputStream.flush() - log(TAG) { "InitExt sent" } - } + // Send InitExt — enables advanced features on H2+ devices, ignored by older models + sock.outputStream.write(profile.encodeInitExt()) + sock.outputStream.flush() + log(TAG) { "InitExt sent" } // Request private keys (IRK + ENC) for BLE encrypted battery profile.encodePrivateKeyRequest()?.let { keyReq -> diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapDeviceProfile.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapDeviceProfile.kt index 54ebc8f5..efdb9ccd 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapDeviceProfile.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/AapDeviceProfile.kt @@ -50,10 +50,11 @@ interface AapDeviceProfile { fun encodeNotificationEnable(): List /** - * Encode the extended init packet (0x4D) for models that require it - * (e.g., Pro 2/3/USB-C, AirPods 4 ANC). Returns null if not needed. + * Encode the extended init packet (0x4D). + * Enables advanced features (Adaptive Transparency, Conversational Awareness during playback) + * on H2+ devices; silently ignored by older models. */ - fun encodeInitExt(): ByteArray? + fun encodeInitExt(): ByteArray /** * Encode a private key request (command 0x30). diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt index 1505052d..dd3adf98 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/DefaultAapDeviceProfile.kt @@ -83,13 +83,10 @@ class DefaultAapDeviceProfile( byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x0f, 0x00, 0xff.toByte(), 0xff.toByte(), 0xff.toByte(), 0xff.toByte()), ) - override fun encodeInitExt(): ByteArray? { - if (!model.features.needsInitExt) return null - return byteArrayOf( - 0x04, 0x00, 0x04, 0x00, 0x4d, 0x00, 0xd7.toByte(), 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ) - } + override fun encodeInitExt(): ByteArray = byteArrayOf( + 0x04, 0x00, 0x04, 0x00, 0x4d, 0x00, 0xd7.toByte(), 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ) override fun encodeCommand(command: AapCommand): ByteArray = when (command) { is AapCommand.SetAncMode -> buildSettingsMessage(SETTING_ANC_MODE, encodeAncMode(command.mode)) diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileNewSettingsTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileNewSettingsTest.kt index a6e4759d..d00e42fc 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileNewSettingsTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileNewSettingsTest.kt @@ -349,7 +349,6 @@ class DefaultAapDeviceProfileNewSettingsTest : BaseAapSessionTest() { f.hasListeningModeCycle shouldBe true f.hasAllowOffOption shouldBe true f.hasEarDetectionToggle shouldBe true - f.needsInitExt shouldBe true // Headphone — no stem/swipe/dual-pod/case features f.hasDualPods shouldBe false f.hasCase shouldBe false diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt index cc3091c4..b67b1d3c 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/devices/DefaultAapDeviceProfileTest.kt @@ -57,19 +57,9 @@ class DefaultAapDeviceProfileTest : BaseAapSessionTest() { @Nested inner class InitExtTests { - @Test - fun `returned for Pro 2`() { DefaultAapDeviceProfile(PodModel.AIRPODS_PRO2).encodeInitExt().shouldNotBeNull() } - @Test fun `returned for Pro 3`() { DefaultAapDeviceProfile(PodModel.AIRPODS_PRO3).encodeInitExt().shouldNotBeNull() } - @Test fun `returned for AP4 ANC`() { DefaultAapDeviceProfile(PodModel.AIRPODS_GEN4_ANC).encodeInitExt().shouldNotBeNull() } - @Test - fun `null for basic AirPods`() { DefaultAapDeviceProfile(PodModel.AIRPODS_GEN3).encodeInitExt().shouldBeNull() } - @Test fun `null for Pro 1`() { DefaultAapDeviceProfile(PodModel.AIRPODS_PRO).encodeInitExt().shouldBeNull() } - @Test fun `null for Max`() { DefaultAapDeviceProfile(PodModel.AIRPODS_MAX).encodeInitExt().shouldBeNull() } - @Test fun `returned for Max 2`() { DefaultAapDeviceProfile(PodModel.AIRPODS_MAX2).encodeInitExt().shouldNotBeNull() } - @Test fun `has correct command byte`() { - profile.encodeInitExt()!![4] shouldBe 0x4d.toByte() + profile.encodeInitExt()[4] shouldBe 0x4d.toByte() } }