Your App as the Assistant
23/08/2026
Stuff I learnt about Android's Assist Service and how you can make your app replace Gemini
Since I worked on out of the app voice notes as mentioned in previous writing, that feature is powered by replacing your google assistant with the app so that android opens your app instead of Gemini.
Time to yap about how to make your app advertise itself as an Assist app so you can replace Gemini with something else just cus you can(smirking at iOS users stuck with Siri lmao).
Android actually lets you do this without any rooting or signature permission or whatever. You declare some services, user picks your app in settings, done. The docs on it are pretty thin though and when you get it wrong nothing tells you, your app just isn't in the list and you sit there wondering.
To make your app to show up under the "Digital Assistant menu"(idk what other phones call it but I have only Samsung in my household), you will need 3 things:
- a
VoiceInteractionService - a
VoiceInteractionSessionService - a
RecognitionService
Two of those barely do anything but you still need all three, I tried skipping.
Lets go over them one by one.
1. VoiceInteractionService
This is the actual service which marks your app as a digital assistant app. It's what android looks for when it builds that settings list. You don't need to override any method here, the class body is empty.
2. VoiceInteractionSessionService
This is the service the android will invoke when a new Voice Interaction Session is created, which basically means the user did the assistant gesture. You need to override its onNewSession method and it must return an instance of VoiceInteractionSession.
What is VoiceInteractionSession?
It represents an active voice interaction session and gets Context as param and has an onShow method. The onShow is invoked when the session UI is going to be shown. Like how Gemini shows its own listening thingy.
If you want to create your own ui, you override onCreateContentView method as this method is before onShow. And if you don't want any ui at all (my case, phone is in the pocket) you can call hide() and just do your thing without showing anything.
There's also onHandleAssist and onHandleScreenshot if you want the contents of the current screen or a screenshot of it, that's how the "what's on my screen" stuff works. You only get them if you ask for it in the xml config and the user can turn it off in settings anyway. I didn't need either so I skipped them.
3. RecognitionService
Its a speech recognizer if you are building an actual assistant app. It has onStartListening, onCancel and onStopListening. However if your app does not do speech recognition, you can just pass SpeechRecognizer.ERROR_CLIENT in listener callback to report it as "not available".
You can't skip it even if you never transcribe anything because the voice interaction xml makes you name a recognition service, it's not optional. Leave it out and your app just doesn't appear in the assistant list, no error, no log, nothing.
Setup
I will call my app NotGoogleAssistant with bundle id of fyi.procrastinator.not.google.assistant
- Extend
VoiceInteractionSession
Two things about this class that got me:
hide() kills the session, so anything you kicked off in onShow and expected to keep running is on borrowed time. My recording takes a while so I moved it into a foreground service instead of keeping it in here.
And onShow is on the main thread. Block it and the gesture feels broken and the user blames your app, fairly.
- Create
VoiceInteractionSessionService
- Extend
VoiceInteractionService
- Create a stub
RecognitionServiceSince this app does not really need voice recognition, just need to receive audio - I will add a stubRecognitionService
Time to do the spicy android manifest stuff now.
All three need android:exported="true" because it's the system process binding to them, not you. The android:permission lines mean only something holding BIND_VOICE_INTERACTION can bind, which is the OS, and android won't accept the service without them anyway.
Also the session service has no intent filter and that's correct, it gets reached through the xml config below and not through an action. I added one the first time because it looked wrong without one. It does nothing.
The resource xml:
voice_interaction_service.xml
Careful with those two class names, they're fully qualified strings so the compiler doesn't check them and neither does lint. If you typo one the build still succeeds and your app just doesn't show up in settings. I typed NotGoogleAssitant instead of NotGoogleAssistant and spent way too long on it before I noticed.
supportsAssist="true" is what makes you eligible to be the assistant instead of just some voice interaction thing, without it you won't show up in the picker.
supportsLaunchVoiceAssistFromKeyguard="true" lets the gesture work while the phone is locked, which I needed since the whole point was phone stays in pocket. Worth knowing it does not mean the phone is unlocked, so anything in there is running on the lock screen and file access can still be blocked until first unlock.
recognition_service.xml
Empty tag, it just needs to exist so the meta-data has something to point at.
Actually turning it on
Installing the app doesn't make you the assistant, the user has to pick you. On Samsung it's Settings > Apps > Default apps > Digital assistant app. On stock android it's Settings > Apps > Assistant & voice input. Other OEMs put it somewhere else obviously.
Instead of writing a 5 step guide in onboarding you can just send them there:
Keep the runCatching, there's always some device where that screen doesn't exist.
And to check if you're currently the one selected, useful for showing a "set me as your assistant" prompt without annoying people who already did:
That key isn't public API, it's just a settings string that's been stable forever. Use it for ui, don't gate real logic on it. If it comes back null just show the prompt.
Stuff that wasted my time
App not in the list at all. Almost always a typo'd class name in voice_interaction_service.xml. Otherwise a missing supportsAssist, missing exported, or no recognition service declared. adb logcat | grep -i voiceinteraction while you open that settings screen usually points at it.
App in the list but selecting it does nothing. Session service isn't resolvable, or it's missing BIND_VOICE_INTERACTION so the system refuses to bind to it.