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:
darken
2026-04-15 08:07:12 +02:00
committed by Matthias Urhahn
parent f17b6ca99f
commit 6f90687b72
6 changed files with 13 additions and 35 deletions
@@ -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).
@@ -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))