summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2025-02-04 16:09:01 +0100
committerDavid Göransson <david.goransson@mullvad.net>2025-02-06 11:03:00 +0100
commitc10bffeb8992ed5f662dcb636bdbf3e34bd01ba0 (patch)
tree24824cb8487d2909392cbf47375bf3e26b6736bd
parent341c10ba38752bc36151b8998064e706f70d9ea6 (diff)
downloadmullvadvpn-c10bffeb8992ed5f662dcb636bdbf3e34bd01ba0.tar.xz
mullvadvpn-c10bffeb8992ed5f662dcb636bdbf3e34bd01ba0.zip
Request (poll) initial NetworkState at startup
-rw-r--r--mullvad-daemon/src/lib.rs2
-rw-r--r--talpid-core/src/connectivity_listener.rs1
-rw-r--r--talpid-routing/src/unix/android.rs79
-rw-r--r--talpid-routing/src/unix/mod.rs5
4 files changed, 83 insertions, 4 deletions
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index fbd60a8e79..6e91e6efa1 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -790,6 +790,8 @@ impl Daemon {
mullvad_types::TUNNEL_FWMARK,
#[cfg(target_os = "linux")]
mullvad_types::TUNNEL_TABLE_ID,
+ #[cfg(target_os = "android")]
+ config.android_context.clone(),
)
.await
.map_err(Error::RouteManager)?;
diff --git a/talpid-core/src/connectivity_listener.rs b/talpid-core/src/connectivity_listener.rs
index 033d918e16..9bdf4bf87a 100644
--- a/talpid-core/src/connectivity_listener.rs
+++ b/talpid-core/src/connectivity_listener.rs
@@ -47,7 +47,6 @@ pub struct ConnectivityListener {
android_listener: GlobalRef,
}
-// Clean this up
static CONNECTIVITY_TX: Mutex<Option<UnboundedSender<Connectivity>>> = Mutex::new(None);
impl ConnectivityListener {
diff --git a/talpid-routing/src/unix/android.rs b/talpid-routing/src/unix/android.rs
index bea2f0d33d..be9f8b7d6a 100644
--- a/talpid-routing/src/unix/android.rs
+++ b/talpid-routing/src/unix/android.rs
@@ -7,10 +7,11 @@ use futures::channel::oneshot;
use futures::future::FutureExt;
use futures::select_biased;
use futures::stream::StreamExt;
+use jnix::jni::objects::JValue;
use jnix::jni::{objects::JObject, JNIEnv};
use jnix::{FromJava, JnixEnv};
-use talpid_types::android::NetworkState;
+use talpid_types::android::{AndroidContext, NetworkState};
use crate::{imp::RouteManagerCommand, Route};
@@ -23,6 +24,19 @@ pub enum Error {
RoutesTimedOut,
}
+/// Internal errors that may only happen during the initial poll for [NetworkState].
+#[derive(Debug, thiserror::Error)]
+enum JvmError {
+ #[error("Failed to attach Java VM to tunnel thread")]
+ AttachJvmToThread(#[source] jnix::jni::errors::Error),
+ #[error("Failed to call Java method {0}")]
+ CallMethod(&'static str, #[source] jnix::jni::errors::Error),
+ #[error("Failed to create global reference to Java object")]
+ CreateGlobalRef(#[source] jnix::jni::errors::Error),
+ #[error("Received an invalid result from {0}.{1}: {2}")]
+ InvalidMethodResult(&'static str, &'static str, String),
+}
+
/// The sender used by [Java_net_mullvad_talpid_ConnectivityListener_notifyDefaultNetworkChange]
/// to notify the route manager of changes to the network.
static ROUTE_UPDATES_TX: Mutex<Option<UnboundedSender<Option<NetworkState>>>> = Mutex::new(None);
@@ -42,15 +56,28 @@ pub struct RouteManagerImpl {
impl RouteManagerImpl {
#[allow(clippy::unused_async)]
- pub async fn new() -> Result<Self, Error> {
+ pub async fn new(android_context: AndroidContext) -> Result<Self, Error> {
// Create a channel between the kotlin client and route manager
let (tx, rx) = futures::channel::mpsc::unbounded();
*ROUTE_UPDATES_TX.lock().unwrap() = Some(tx);
+ // Try to poll for the current network state at startup.
+ // This will most likely be null, but it covers the edge case where a NetworkState
+ // update has been emitted before we anyone starts to listen for route updates some
+ // time in the future (when connecting).
+ let last_state = match current_network_state(android_context) {
+ Ok(initial_state) => initial_state,
+ Err(err) => {
+ log::error!("Failed while polling for initial NetworkState");
+ log::error!("{err}");
+ None
+ }
+ };
+
let route_manager = RouteManagerImpl {
network_state_updates: rx,
- last_state: Default::default(),
+ last_state,
waiting_for_routes: Default::default(),
};
@@ -157,3 +184,49 @@ pub extern "system" fn Java_net_mullvad_talpid_ConnectivityListener_notifyDefaul
log::warn!("Failed to send offline change event");
}
}
+
+/// Return the current NetworkState according to Android
+fn current_network_state(
+ android_context: AndroidContext,
+) -> Result<Option<NetworkState>, JvmError> {
+ let env = JnixEnv::from(
+ android_context
+ .jvm
+ .attach_current_thread_as_daemon()
+ .map_err(JvmError::AttachJvmToThread)?,
+ );
+
+ let result = env
+ .call_method(
+ android_context.vpn_service.as_obj(),
+ "getConnectivityListener",
+ "()Lnet/mullvad/talpid/ConnectivityListener;",
+ &[],
+ )
+ .map_err(|cause| JvmError::CallMethod("getConnectivityListener", cause))?;
+
+ let connectivity_listener = match result {
+ JValue::Object(object) => env
+ .new_global_ref(object)
+ .map_err(JvmError::CreateGlobalRef)?,
+ value => {
+ return Err(JvmError::InvalidMethodResult(
+ "MullvadVpnService",
+ "getConnectivityListener",
+ format!("{:?}", value),
+ ))
+ }
+ };
+
+ let network_state = env
+ .call_method(
+ connectivity_listener.as_obj(),
+ "getCurrentDefaultNetworkState",
+ "()Lnet/mullvad/talpid/model/NetworkState;",
+ &[],
+ )
+ .map_err(|cause| JvmError::CallMethod("getCurrentDefaultNetworkState", cause))?;
+
+ let network_state: Option<NetworkState> = FromJava::from_java(&env, network_state);
+ Ok(network_state)
+}
diff --git a/talpid-routing/src/unix/mod.rs b/talpid-routing/src/unix/mod.rs
index 300ccff918..042360d520 100644
--- a/talpid-routing/src/unix/mod.rs
+++ b/talpid-routing/src/unix/mod.rs
@@ -11,6 +11,8 @@ use futures::channel::{
oneshot,
};
use std::sync::Arc;
+#[cfg(target_os = "android")]
+use talpid_types::android::AndroidContext;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use futures::stream::Stream;
@@ -165,6 +167,7 @@ impl RouteManagerHandle {
pub async fn spawn(
#[cfg(target_os = "linux")] fwmark: u32,
#[cfg(target_os = "linux")] table_id: u32,
+ #[cfg(target_os = "android")] android_context: AndroidContext,
) -> Result<Self, Error> {
let (manage_tx, manage_rx) = mpsc::unbounded();
let manage_tx = Arc::new(manage_tx);
@@ -175,6 +178,8 @@ impl RouteManagerHandle {
table_id,
#[cfg(target_os = "macos")]
Arc::downgrade(&manage_tx),
+ #[cfg(target_os = "android")]
+ android_context,
)
.await?;
tokio::spawn(manager.run(manage_rx));