diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-02-04 16:09:01 +0100 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2025-02-06 11:03:00 +0100 |
| commit | c10bffeb8992ed5f662dcb636bdbf3e34bd01ba0 (patch) | |
| tree | 24824cb8487d2909392cbf47375bf3e26b6736bd | |
| parent | 341c10ba38752bc36151b8998064e706f70d9ea6 (diff) | |
| download | mullvadvpn-c10bffeb8992ed5f662dcb636bdbf3e34bd01ba0.tar.xz mullvadvpn-c10bffeb8992ed5f662dcb636bdbf3e34bd01ba0.zip | |
Request (poll) initial NetworkState at startup
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/connectivity_listener.rs | 1 | ||||
| -rw-r--r-- | talpid-routing/src/unix/android.rs | 79 | ||||
| -rw-r--r-- | talpid-routing/src/unix/mod.rs | 5 |
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)); |
