Device: Fix AirPods rename not actually changing the name#490
Merged
Conversation
Switch the AAP rename packet to the opcode 0x1A format (04 00 04 00 1A 00 01 [size] 00 [name]) matching the LibrePods documentation and Linux implementation. The previous 0x1E variant (from the LibrePods Android code) was silently ignored by AirPods Pro 2 USB-C firmware — no 0x001D echo, no persistence across reconnect. Verified on AirPods Pro 2 USB-C (firmware 81.2675...): the device now echoes the new name back via the next 0x001D INFORMATION message, and the name persists after disconnect/reconnect. Also hardens the rename UX: gate the edit icon on isAapReady (was isAapConnected, which allowed sending during HANDSHAKING), apply an optimistic deviceInfo update with a scoped rollback on send failure, surface send errors via a new Event.SendFailed + snackbar, and restrict dialog input to ASCII with inline error feedback. Unifies send() / sendProGated() through a single sendInternal() helper so error plumbing benefits every command, not just rename. Note: this only updates the AirPods firmware's self-reported name. Android's system Bluetooth settings read from the bond database and are not affected — renaming there still requires the Android system Bluetooth UI.
After the AAP rename succeeds, try to update Android's per-device bond alias via the hidden BluetoothDevice.setAlias(String) method, so the new name also shows up in the system Bluetooth settings on this phone. Known failure mode on Android 12+: setAlias is gated behind a Companion Device Manager (CDM) association at the service layer, and raises 'does not have a CDM association with the Bluetooth Device' for third-party apps that don't hold one. In that case, a dedicated snackbar explains that the system rename didn't go through and suggests renaming manually in system settings or re-pairing. The AAP-level rename is always attempted first and is the load-bearing part; the system alias is a best-effort extra.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Renaming AirPods from the device settings screen now actually changes the name. Before, tapping Rename sent a command that AirPods silently ignored — the new name only appeared in CAPod briefly and then reverted on the next reconnect.
After this fix, the rename is accepted by the AirPods firmware and the new name persists across disconnect/reconnect. On reconnect, AirPods report the new name back and CAPod shows it.
We also try to update Android's own Bluetooth device name (the one shown in system Bluetooth settings) at the same time. On Android 12+ this is blocked by a Companion Device Manager permission check, so if the system rename isn't allowed CAPod shows a snackbar telling the user to rename it in Android's Bluetooth settings or re-pair the AirPods once to let Android pick up the new name.
A few smaller rename/settings improvements came along with it:
Technical Context
buildRenameMessagewas using opcode0x1Ewith a trailing NUL (04 00 04 00 1E 00 [size] 00 [name] 00), which matches the LibrePods AndroidcreateRenamePacketimplementation. On-device testing on Pro 2 USB-C (firmware 81.2675...) showed AirPods silently discard this variant — no0x001Decho, no persistence.04 00 04 00 1A 00 01 [size] 00 [name](opcode0x1A, no trailing NUL). This matches the LibrePodsAAP Definitions.mddocs and the LibrePods Linuxairpods_packets.h. Verified end-to-end: device echoes the new name in its next0x001Ddevice info and the name survives a force-stop / reconnect cycle.BluetoothDevice.setAlias(String)because the public API 30+ variant requiresBLUETOOTH_PRIVILEGED. On Android 12+ the hidden method throwsSecurityException: does not have a CDM association with the Bluetooth Deviceunless the app has explicitly set up a Companion Device Manager association — which CAPod doesn't currently pursue (that'd be a separate pairing flow). So setAlias is effectively always best-effort, and the snackbar is the designed fallback.AapConnection.send()now rolls backdeviceInfo.nameifsendRawthrows — scoped only toSetDeviceNamebecause the rename has no device echo to correct a bad optimistic write, unlike the ANC / toggle paths.DeviceSettingsViewModelused to swallow all send exceptions with a log line. Unified bothsend()andsendProGated()paths throughsendInternal(), which emits a newEvent.SendFailedon exception. Host collects this and shows a snackbar. Benefits every setting, not just rename.SetDeviceName("MyPods"). New ViewModel tests cover rename forwarding, the no-target-address no-op, and theSendFailedevent on manager exceptions.AapConnection-level rename rollback tests are intentionally skipped — reachingConnectionState.READYin a unit test needs a blocking-InputStream harness that doesn't exist yet. The working protocol format is locked in by the full-frame encoder assertion, and the rollback logic is small enough to review without a test.parseNullTerminatedStringsdecodes device info withCharsets.US_ASCIIand filters to 0x20..0x7E, so a UTF-8 name would round-trip as garbage even if the device accepted it. Expanding the decoder to UTF-8 touches all device-info parsing and is tracked as a follow-up if non-ASCII rename becomes a real requirement.