Skip to content
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
7 changes: 0 additions & 7 deletions app/src/main/java/eu/darken/capod/pods/core/apple/PodModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -281,7 +277,6 @@ enum class PodModel(
hasEarDetectionToggle = true,
hasListeningModeCycle = true,
hasAllowOffOption = true,
needsInitExt = true,
),
modelNumbers = setOf("A3454"), // headphones
),
Expand Down Expand Up @@ -550,7 +545,5 @@ enum class PodModel(
val hasAllowOffOption: Boolean = false,
val hasStemConfig: Boolean = false,
val hasSleepDetection: Boolean = false,
// Protocol
val needsInitExt: Boolean = false,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down