fix(android): Record crashlytics exception without crashing (#6475)

Instead of causing a NullPointerException, we should be more careful and
gracefully handle the case where the TunnelService's session pointer has
been cleared, but still record the exception in Crashlytics.

This patch also increases the robustness of the link properties DNS list
not having valid or useful DNS servers in them, and compacts the list
before sending to connlib, instead of potentially sending a JSON array
of nulls.

```
          Fatal Exception: java.lang.NullPointerException:
       at NetworkMonitor.onLinkPropertiesChanged(NetworkMonitor.kt:31)
       at android.net.ConnectivityManager$NetworkCallback.onAvailable(ConnectivityManager.java:4018)
       at android.net.ConnectivityManager$NetworkCallback.onAvailable(ConnectivityManager.java:3992)
       at android.net.ConnectivityManager$CallbackHandler.handleMessage(ConnectivityManager.java:4338)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.os.HandlerThread.run(HandlerThread.java:67)
```


This fixes an issue seen by customer where the MDM solution is yanking
the TunnelService without graceful shutdown.
This commit is contained in:
Jamil
2024-08-28 19:11:08 -07:00
committed by GitHub
parent 14b7652e0c
commit fa5da77484

View File

@@ -2,6 +2,8 @@
import android.net.ConnectivityManager
import android.net.LinkProperties
import android.net.Network
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.google.gson.Gson
import dev.firezone.android.tunnel.ConnlibSession
import dev.firezone.android.tunnel.TunnelService
@@ -27,13 +29,20 @@ class NetworkMonitor(private val tunnelService: TunnelService) : ConnectivityMan
lastDns = linkProperties.dnsServers
// Strip the scope id from IPv6 addresses. See https://github.com/firezone/firezone/issues/5781
val dnsList = linkProperties.dnsServers.map { it.hostAddress!!.split("%")[0] }
ConnlibSession.setDns(tunnelService.connlibSessionPtr!!, Gson().toJson(dnsList))
val dnsList =
linkProperties.dnsServers.mapNotNull {
it.hostAddress?.split("%")?.getOrNull(0)
}
tunnelService.connlibSessionPtr?.let {
ConnlibSession.setDns(it, Gson().toJson(dnsList))
} ?: Firebase.crashlytics.recordException(NullPointerException("connlibSessionPtr is null"))
}
if (lastNetwork != network) {
lastNetwork = network
ConnlibSession.reset(tunnelService.connlibSessionPtr!!)
tunnelService.connlibSessionPtr?.let {
ConnlibSession.reset(it)
} ?: Firebase.crashlytics.recordException(NullPointerException("connlibSessionPtr is null"))
}
// Release mutex lock