@@ -7,10 +7,11 @@ use futures::channel::oneshot;
7
7
use futures:: future:: FutureExt ;
8
8
use futures:: select_biased;
9
9
use futures:: stream:: StreamExt ;
10
+ use jnix:: jni:: objects:: JValue ;
10
11
use jnix:: jni:: { objects:: JObject , JNIEnv } ;
11
12
use jnix:: { FromJava , JnixEnv } ;
12
13
13
- use talpid_types:: android:: NetworkState ;
14
+ use talpid_types:: android:: { AndroidContext , NetworkState } ;
14
15
15
16
use crate :: { imp:: RouteManagerCommand , Route } ;
16
17
@@ -23,6 +24,19 @@ pub enum Error {
23
24
RoutesTimedOut ,
24
25
}
25
26
27
+ /// Internal errors that may only happen during the initial poll for [NetworkState].
28
+ #[ derive( Debug , thiserror:: Error ) ]
29
+ enum JvmError {
30
+ #[ error( "Failed to attach Java VM to tunnel thread" ) ]
31
+ AttachJvmToThread ( #[ source] jnix:: jni:: errors:: Error ) ,
32
+ #[ error( "Failed to call Java method {0}" ) ]
33
+ CallMethod ( & ' static str , #[ source] jnix:: jni:: errors:: Error ) ,
34
+ #[ error( "Failed to create global reference to Java object" ) ]
35
+ CreateGlobalRef ( #[ source] jnix:: jni:: errors:: Error ) ,
36
+ #[ error( "Received an invalid result from {0}.{1}: {2}" ) ]
37
+ InvalidMethodResult ( & ' static str , & ' static str , String ) ,
38
+ }
39
+
26
40
/// The sender used by [Java_net_mullvad_talpid_ConnectivityListener_notifyDefaultNetworkChange]
27
41
/// to notify the route manager of changes to the network.
28
42
static ROUTE_UPDATES_TX : Mutex < Option < UnboundedSender < Option < NetworkState > > > > = Mutex :: new ( None ) ;
@@ -42,15 +56,28 @@ pub struct RouteManagerImpl {
42
56
43
57
impl RouteManagerImpl {
44
58
#[ allow( clippy:: unused_async) ]
45
- pub async fn new ( ) -> Result < Self , Error > {
59
+ pub async fn new ( android_context : AndroidContext ) -> Result < Self , Error > {
46
60
// Create a channel between the kotlin client and route manager
47
61
let ( tx, rx) = futures:: channel:: mpsc:: unbounded ( ) ;
48
62
49
63
* ROUTE_UPDATES_TX . lock ( ) . unwrap ( ) = Some ( tx) ;
50
64
65
+ // Try to poll for the current network state at startup.
66
+ // This will most likely be null, but it covers the edge case where a NetworkState
67
+ // update has been emitted before we anyone starts to listen for route updates some
68
+ // time in the future (when connecting).
69
+ let last_state = match current_network_state ( android_context) {
70
+ Ok ( initial_state) => initial_state,
71
+ Err ( err) => {
72
+ log:: error!( "Failed while polling for initial NetworkState" ) ;
73
+ log:: error!( "{err}" ) ;
74
+ None
75
+ }
76
+ } ;
77
+
51
78
let route_manager = RouteManagerImpl {
52
79
network_state_updates : rx,
53
- last_state : Default :: default ( ) ,
80
+ last_state,
54
81
waiting_for_routes : Default :: default ( ) ,
55
82
} ;
56
83
@@ -157,3 +184,49 @@ pub extern "system" fn Java_net_mullvad_talpid_ConnectivityListener_notifyDefaul
157
184
log:: warn!( "Failed to send offline change event" ) ;
158
185
}
159
186
}
187
+
188
+ /// Return the current NetworkState according to Android
189
+ fn current_network_state (
190
+ android_context : AndroidContext ,
191
+ ) -> Result < Option < NetworkState > , JvmError > {
192
+ let env = JnixEnv :: from (
193
+ android_context
194
+ . jvm
195
+ . attach_current_thread_as_daemon ( )
196
+ . map_err ( JvmError :: AttachJvmToThread ) ?,
197
+ ) ;
198
+
199
+ let result = env
200
+ . call_method (
201
+ android_context. vpn_service . as_obj ( ) ,
202
+ "getConnectivityListener" ,
203
+ "()Lnet/mullvad/talpid/ConnectivityListener;" ,
204
+ & [ ] ,
205
+ )
206
+ . map_err ( |cause| JvmError :: CallMethod ( "getConnectivityListener" , cause) ) ?;
207
+
208
+ let connectivity_listener = match result {
209
+ JValue :: Object ( object) => env
210
+ . new_global_ref ( object)
211
+ . map_err ( JvmError :: CreateGlobalRef ) ?,
212
+ value => {
213
+ return Err ( JvmError :: InvalidMethodResult (
214
+ "MullvadVpnService" ,
215
+ "getConnectivityListener" ,
216
+ format ! ( "{:?}" , value) ,
217
+ ) )
218
+ }
219
+ } ;
220
+
221
+ let network_state = env
222
+ . call_method (
223
+ connectivity_listener. as_obj ( ) ,
224
+ "getCurrentDefaultNetworkState" ,
225
+ "()Lnet/mullvad/talpid/model/NetworkState;" ,
226
+ & [ ] ,
227
+ )
228
+ . map_err ( |cause| JvmError :: CallMethod ( "getCurrentDefaultNetworkState" , cause) ) ?;
229
+
230
+ let network_state: Option < NetworkState > = FromJava :: from_java ( & env, network_state) ;
231
+ Ok ( network_state)
232
+ }
0 commit comments