Skip to content

Commit

Permalink
Request (poll) initial NetworkState at startup
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusPettersson98 committed Feb 7, 2025
1 parent 3ac1a12 commit 2192872
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 4 deletions.
1 change: 0 additions & 1 deletion talpid-core/src/connectivity_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions talpid-core/src/tunnel_state_machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ pub async fn spawn(
volume_update_rx,
#[cfg(target_os = "android")]
connectivity_listener,
#[cfg(target_os = "android")]
android_context,
#[cfg(target_os = "linux")]
linux_ids,
};
Expand Down Expand Up @@ -258,6 +260,8 @@ struct TunnelStateMachineInitArgs<G: TunnelParametersGenerator> {
volume_update_rx: mpsc::UnboundedReceiver<()>,
#[cfg(target_os = "android")]
connectivity_listener: ConnectivityListener,
#[cfg(target_os = "android")]
android_context: AndroidContext,
#[cfg(target_os = "linux")]
linux_ids: LinuxNetworkingIdentifiers,
}
Expand All @@ -281,6 +285,8 @@ impl TunnelStateMachine {
args.linux_ids.fwmark,
#[cfg(target_os = "linux")]
args.linux_ids.table_id,
#[cfg(target_os = "android")]
args.android_context,
)
.await
.map_err(Error::InitRouteManagerError)?;
Expand Down
79 changes: 76 additions & 3 deletions talpid-routing/src/unix/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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);
Expand All @@ -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(),
};

Expand Down Expand Up @@ -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)
}
5 changes: 5 additions & 0 deletions talpid-routing/src/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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));
Expand Down

0 comments on commit 2192872

Please sign in to comment.