Play received voice notes from Android Auto - #2033
Open
JorgeRdg wants to merge 2 commits into
Open
Conversation
Android Auto only ever receives the text body of a message. For a voice note that is the placeholder "Voice message", so the recording is unreachable without picking up the phone -- which is exactly what should not happen while driving. CarMessage has carried setMultimediaMimeType/setMultimediaUri since androidx.car.app 1.4.0, the version already pinned here, so the message can carry the audio itself with no dependency bump. buildCarMessage() now resolves the note's local file through FileLoader, exposes it as a content:// URI via the FileProvider the app already declares, and sets it on the builder with audio/ogg. When the file is not cached yet it is fetched and the template rebuilt, so the player appears on its own; voice notes are not auto-downloaded by default and in the car the message typically arrives mid-drive, so without this it would almost always fall back to text. Privacy gates the attachment. buildCarMessage() already asked getShortStringForMessage() for preview[0] and then ignored it, which was harmless while only redacted text was sent. Attaching audio changes that: with previews off the body reads as a placeholder while the recording would play aloud in the car, leaking exactly what the user chose to hide. The attachment therefore honours preview[0] and the passcode state, matching the guard NotificationsController already applies to the equivalent MessagingStyle attachment. Encrypted chats were already excluded in collectUnreadDuringDrive(). Two details that would otherwise fail silently. fileLoaded is posted on the per-account NotificationCenter, not the global one this screen uses for its other events, so it is observed on the current account and moved when activeAccountChanged arrives. And the host reads the URI from its own process while CarMessage crosses Binder rather than riding an Intent, so FLAG_GRANT_READ_URI_PERMISSION cannot be attached to one -- the grant is issued explicitly against getHostInfo().getPackageName(), falling back to text when it fails, since an unreadable URI would surface as a broken control in the car. The gating lives in CarVoiceAttachment rather than inline so it can be unit tested without a car host.
The read access handed to the car host was never given back, so it outlived the drive: one grant accumulated per voice note, and after switching accounts the host kept read access to the previous account's files. Grants are now tracked and dropped when the screen is destroyed and before the active account changes. Not tied to onPause or to a timer, unlike the notification code that grants an image to SystemUI and revokes 20s later. That works there because SystemUI renders the image immediately; here the host opens the file when the user presses play, which can come long after the message appeared, so a delayed or pause-triggered revoke would risk cutting playback. Binding the grants to the screen's lifetime and to the active account bounds them without that risk. Reported by sourcery-ai on #106.
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.
Android Auto only ever receives the text body of a message. For a voice note that is the placeholder
"Voice message", so the recording is unreachable without picking up the phone — which is exactly what should not happen while driving.CarMessagehas carriedsetMultimediaMimeType/setMultimediaUrisinceandroidx.car.app1.4.0, the version already pinned inTMessagesProj/build.gradle, so this needs no dependency bump and is independent of #2008.What it does
buildCarMessage()resolves the note throughFileLoader, exposes it via the.providerFileProviderthe app already declares, and sets it on the builder withaudio/ogg. When the file is not cached yet it is fetched and the template rebuilt, so the player appears on its own — voice notes are not auto-downloaded by default and in the car the message typically arrives mid-drive, so without this it would almost always fall back to text.This reuses the exact pattern
NotificationsControlleralready applies to the equivalentMessagingStyleattachment (setData("audio/ogg", uri)withpendingVoiceLoads), so the FileProvider paths involved are already exercised in production by the notification path.Notes for review
Privacy.
buildCarMessage()already askedgetShortStringForMessage()forpreview[0]and then ignored it. Harmless while only redacted text was sent, but attaching audio changes it: with previews off the body reads as a placeholder while the recording would play aloud in the car, leaking exactly what the user chose to hide. The attachment honourspreview[0]and the passcode state, matching the existing notification guard. Encrypted chats were already excluded incollectUnreadDuringDrive().fileLoadedis per-account.HomeScreenuses the globalNotificationCenterfor its other events, butfileLoadedis posted onNotificationCenter.getInstance(account). Observing it globally registers fine and never fires, so the observer lives on the current account and moves onactiveAccountChanged, detaching from the outgoing account first.URI permission. The host reads the URI from its own process and
CarMessagecrosses Binder rather than riding anIntent, soFLAG_GRANT_READ_URI_PERMISSIONcannot be attached to one. The grant is issued explicitly againstgetHostInfo().getPackageName(), falling back to text when it fails, since an unreadable URI would surface as a broken control in the car.Testing
Developed and verified in a Nagram-based fork, first on the Desktop Head Unit and then confirmed working in a real car: a voice note arriving mid-drive appears with a play control and plays through the car audio.
The gating is isolated in
CarVoiceAttachmentso it can be unit tested without a car host. I kept the tests out of this PR because the project has no JVM unit-test setup (src/test, Robolectric andtestOptionsare all absent) and adding one felt out of scope here — happy to include them, or the test scaffolding, if you would like it.