Skip to content

Commit 4ca9a50

Browse files
committed
Merge remote-tracking branch 'origin/atavism/issue-3723' into atavism/issue-3723-payment-screenshot
2 parents f649f17 + 57621f3 commit 4ca9a50

1 file changed

Lines changed: 28 additions & 5 deletions

File tree

android/app/src/main/kotlin/org/getlantern/lantern/service/LanternVpnService.kt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.net.VpnService
88
import android.os.Build
99
import android.os.ParcelFileDescriptor
1010
import java.util.concurrent.atomic.AtomicBoolean
11+
import kotlinx.coroutines.CoroutineExceptionHandler
1112
import kotlinx.coroutines.CoroutineScope
1213
import kotlinx.coroutines.Dispatchers
1314
import kotlinx.coroutines.SupervisorJob
@@ -135,8 +136,24 @@ class LanternVpnService :
135136
}
136137

137138
// Create a CoroutineScope tied to the service's lifecycle.
138-
// SupervisorJob ensures that failure in one child doesn't cancel the whole scope.
139-
private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
139+
//
140+
// SupervisorJob keeps one child's failure from cancelling its siblings, but it does
141+
// not handle the exception: without a CoroutineExceptionHandler an uncaught throw in
142+
// any launch{} below reaches the thread's default handler and takes the process down
143+
// with no Kotlin log and no Go crash file, which reads as a silent death. The handler
144+
// makes that case diagnosable and reports it to the UI like any other VPN error.
145+
private val serviceScope =
146+
CoroutineScope(
147+
Dispatchers.IO + SupervisorJob() +
148+
CoroutineExceptionHandler { _, e ->
149+
AppLogger.e(TAG, "Uncaught exception in service coroutine", e)
150+
VpnStatusManager.postVPNError(
151+
errorCode = "service_coroutine_uncaught",
152+
errorMessage = "Unexpected VPN service error",
153+
error = e,
154+
)
155+
},
156+
)
140157

141158
override fun onStartCommand(
142159
intent: Intent?,
@@ -386,10 +403,16 @@ class LanternVpnService :
386403
VpnStatusManager.postVPNStatus(VPNStatus.MissingPermission)
387404
return@withContext
388405
}
389-
// Show foreground notification immediately — required by the OS as soon as
390-
// VPN service starts, replaced by connected notification on success.
391-
notificationHelper.showStartingVPNConnectedNotification(this@LanternVpnService)
392406
runCatching {
407+
// Show foreground notification immediately — required by the OS as soon as
408+
// VPN service starts, replaced by the connected notification on success.
409+
// This is startForeground() underneath, which the OS can refuse over
410+
// foreground-service type, permission, or vendor policy. Inside the block
411+
// so a refusal takes this operation's own failure path — errorCode-tagged
412+
// reporting, network-monitor teardown, and serviceCleanUp when the caller
413+
// asked for it — rather than the service-wide handler, which only logs and
414+
// posts a generic error.
415+
notificationHelper.showStartingVPNConnectedNotification(this@LanternVpnService)
393416
// Radiance is pre-warmed via ACTION_START_RADIANCE, but as a background
394417
// service it may have been killed by the OS before setup completed.
395418
// Re-run setup here under the foreground notification so it is guaranteed

0 commit comments

Comments
 (0)