mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
refactor(aap): Send InitExt unconditionally to all devices
Older devices silently ignore the 0x4D packet, so there is no need to gate it per model. Sending it unconditionally prevents silent feature degradation when new H2+ models are added without the flag. Remove needsInitExt from PodModel.Features.
This commit is contained in:
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -50,10 +50,11 @@ interface AapDeviceProfile {
|
||||
fun encodeNotificationEnable(): List<ByteArray>
|
||||
|
||||
/**
|
||||
* 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).
|
||||
|
||||
+4
-7
@@ -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))
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
+1
-11
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user