From 1af85c77b7c4471fd78849f76af7d142a5c21d01 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 23 Oct 2025 13:07:40 +1100 Subject: [PATCH] fix(android): properly free event after use (#10669) In order to properly free all memory allocated by the `Event` returned from connlib, we need to `.destroy()` it. For this to happen automatically, we can call the `.use` helper. Unfortunately, there are no compile-time warnings about this so we have to manually audit the generated code to check which objects needs closing after use. From what I can gather, the `Event` only needs to be closed because we hold a reference to the `DisconnectError` inside `Disconnected`. Because we exit after that anyway, I believe all memory is free'd regardless already. --- .../firezone/android/tunnel/TunnelService.kt | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt index 15fa644ee..95ff0cf50 100644 --- a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt +++ b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt @@ -45,6 +45,7 @@ import uniffi.connlib.Event import uniffi.connlib.ProtectSocket import uniffi.connlib.Session import uniffi.connlib.SessionInterface +import uniffi.connlib.use import java.nio.file.Files import java.nio.file.Paths import javax.inject.Inject @@ -508,42 +509,50 @@ class TunnelService : VpnService() { } } eventChannel.onReceive { event -> - when (event) { - is Event.ResourcesUpdated -> { - tunnelResources = event.resources.map { convertResource(it) } - resourcesUpdated() - } + event.use { event -> + when (event) { + is Event.ResourcesUpdated -> { + tunnelResources = event.resources.map { convertResource(it) } + resourcesUpdated() + } - is Event.TunInterfaceUpdated -> { - tunnelDnsAddresses = event.dns.toMutableList() - tunnelSearchDomain = event.searchDomain - tunnelIpv4Address = event.ipv4 - tunnelIpv6Address = event.ipv6 - tunnelRoutes.clear() - tunnelRoutes.addAll( - event.ipv4Routes.map { cidr -> - Cidr(address = cidr.address, prefix = cidr.prefix.toInt()) - }, - ) - tunnelRoutes.addAll( - event.ipv6Routes.map { cidr -> - Cidr(address = cidr.address, prefix = cidr.prefix.toInt()) - }, - ) - buildVpnService() - } + is Event.TunInterfaceUpdated -> { + tunnelDnsAddresses = event.dns.toMutableList() + tunnelSearchDomain = event.searchDomain + tunnelIpv4Address = event.ipv4 + tunnelIpv6Address = event.ipv6 + tunnelRoutes.clear() + tunnelRoutes.addAll( + event.ipv4Routes.map { cidr -> + Cidr( + address = cidr.address, + prefix = cidr.prefix.toInt(), + ) + }, + ) + tunnelRoutes.addAll( + event.ipv6Routes.map { cidr -> + Cidr( + address = cidr.address, + prefix = cidr.prefix.toInt(), + ) + }, + ) + buildVpnService() + } - is Event.Disconnected -> { - // Clear any user tokens and actorNames - repo.clearToken() - repo.clearActorName() + is Event.Disconnected -> { + // Clear any user tokens and actorNames + repo.clearToken() + repo.clearActorName() - running = false - } + running = false + } - null -> { - Log.i(TAG, "Event channel closed") - running = false + null -> { + Log.i(TAG, "Event channel closed") + running = false + } } } }