Skip to content

Commit 6d7eb9b

Browse files
committed
fix(android): use connected device foreground service
1 parent 7d357a7 commit 6d7eb9b

2 files changed

Lines changed: 1 addition & 17 deletions

File tree

apps/android/app/src/main/java/ai/openclaw/app/NodeForegroundService.kt

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import kotlinx.coroutines.launch
2323
class NodeForegroundService : Service() {
2424
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
2525
private var notificationJob: Job? = null
26-
private var didStartForeground = false
2726
private var voiceCaptureMode = VoiceCaptureMode.Off
2827

2928
override fun onCreate() {
@@ -183,13 +182,7 @@ class NodeForegroundService : Service() {
183182

184183
private fun startForegroundWithTypes(notification: Notification) {
185184
val serviceTypes = foregroundServiceTypesForVoiceMode(voiceCaptureMode)
186-
if (didStartForeground) {
187-
// Re-issue startForeground when Talk mode toggles so Android sees the microphone service type.
188-
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, serviceTypes)
189-
return
190-
}
191185
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, serviceTypes)
192-
didStartForeground = true
193186
}
194187

195188
companion object {
@@ -200,19 +193,16 @@ class NodeForegroundService : Service() {
200193
private const val ACTION_SET_VOICE_CAPTURE_MODE = "ai.openclaw.app.action.SET_VOICE_CAPTURE_MODE"
201194
private const val EXTRA_VOICE_CAPTURE_MODE = "ai.openclaw.app.extra.VOICE_CAPTURE_MODE"
202195

203-
/** Starts the persistent node foreground service from UI lifecycle code. */
204196
fun start(context: Context) {
205197
val intent = Intent(context, NodeForegroundService::class.java)
206198
context.startForegroundService(intent)
207199
}
208200

209-
/** Requests disconnect through the service action path so notification actions and UI share behavior. */
210201
fun stop(context: Context) {
211202
val intent = Intent(context, NodeForegroundService::class.java).setAction(ACTION_STOP)
212203
context.startService(intent)
213204
}
214205

215-
/** Updates Android's foreground-service type before voice capture mode changes require microphone access. */
216206
fun setVoiceCaptureMode(
217207
context: Context,
218208
mode: VoiceCaptureMode,
@@ -231,9 +221,6 @@ class NodeForegroundService : Service() {
231221
}
232222
}
233223

234-
/**
235-
* Foreground-service type mask required by Android for the current voice capture mode.
236-
*/
237224
internal fun foregroundServiceTypesForVoiceMode(mode: VoiceCaptureMode): Int {
238225
val base = ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
239226
return if (mode == VoiceCaptureMode.TalkMode) {
@@ -243,9 +230,6 @@ internal fun foregroundServiceTypesForVoiceMode(mode: VoiceCaptureMode): Int {
243230
}
244231
}
245232

246-
/**
247-
* Compact notification suffix for voice state; kept pure for service-notification tests.
248-
*/
249233
internal fun voiceNotificationSuffix(
250234
mode: VoiceCaptureMode,
251235
manualMicEnabled: Boolean,

docs/platforms/android.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ See [Camera node](/nodes/camera) for parameters and CLI helpers.
215215
### 8) Voice + expanded Android command surface
216216

217217
- Voice tab: Android has two explicit capture modes. **Mic** is a manual Voice-tab session that sends each pause as a chat turn and stops when the app leaves the foreground or the user leaves the Voice tab. **Talk** is continuous Talk Mode and keeps listening until toggled off or the node disconnects.
218-
- Talk Mode promotes the existing foreground service from `dataSync` to `dataSync|microphone` before capture starts, then demotes it when Talk Mode stops. Android 14+ requires the `FOREGROUND_SERVICE_MICROPHONE` declaration, the `RECORD_AUDIO` runtime grant, and the microphone service type at runtime.
218+
- Talk Mode promotes the existing foreground service from `connectedDevice` to `connectedDevice|microphone` before capture starts, then demotes it when Talk Mode stops. The node service declares `FOREGROUND_SERVICE_CONNECTED_DEVICE` with `CHANGE_NETWORK_STATE`; Android 14+ also requires the `FOREGROUND_SERVICE_MICROPHONE` declaration, the `RECORD_AUDIO` runtime grant, and the microphone service type at runtime.
219219
- By default, Android Talk uses native speech recognition, Gateway chat, and `talk.speak` through the configured gateway Talk provider. Local system TTS is used only when `talk.speak` is unavailable.
220220
- Android Talk uses realtime Gateway relay only when `talk.realtime.mode` is `realtime` and `talk.realtime.transport` is `gateway-relay`.
221221
- Voice wake remains disabled in the Android UX/runtime.

0 commit comments

Comments
 (0)