@@ -6,6 +6,7 @@ use super::{detect_target_ok, ensure_init, env, get_app, get_config, read_option
66use crate :: {
77 helpers:: config:: get as get_tauri_config,
88 interface:: { AppInterface , Interface } ,
9+ mobile:: CliOptions ,
910 Result ,
1011} ;
1112use clap:: { ArgAction , Parser } ;
@@ -87,36 +88,17 @@ pub fn command(options: Options) -> Result<()> {
8788 . dev_url
8889 . clone ( ) ;
8990
90- if let Some ( port) = dev_url. and_then ( |url| url. port_or_known_default ( ) ) {
91- let forward = format ! ( "tcp:{port}" ) ;
92- log:: info!( "Forwarding port {port} with adb" ) ;
91+ if let Some ( url) = dev_url {
92+ let localhost = match url. host ( ) {
93+ Some ( url:: Host :: Domain ( d) ) => d == "localhost" ,
94+ Some ( url:: Host :: Ipv4 ( i) ) => i == std:: net:: Ipv4Addr :: LOCALHOST ,
95+ _ => false ,
96+ } ;
9397
94- let devices = adb:: device_list ( & env) . unwrap_or_default ( ) ;
95-
96- // clear port forwarding for all devices
97- for device in & devices {
98- remove_adb_reverse ( & env, device. serial_no ( ) , & forward) ;
99- }
100-
101- // if there's a known target, we should force use it
102- if let Some ( target_device) = & cli_options. target_device {
103- run_adb_reverse ( & env, & target_device. id , & forward, & forward) . with_context ( || {
104- format ! (
105- "failed to forward port with adb, is the {} device connected?" ,
106- target_device. name,
107- )
108- } ) ?;
109- } else if devices. len ( ) == 1 {
110- let device = devices. first ( ) . unwrap ( ) ;
111- run_adb_reverse ( & env, device. serial_no ( ) , & forward, & forward) . with_context ( || {
112- format ! (
113- "failed to forward port with adb, is the {} device connected?" ,
114- device. name( ) ,
115- )
116- } ) ?;
117- } else if devices. len ( ) > 1 {
118- anyhow:: bail!( "Multiple Android devices are connected ({}), please disconnect devices you do not intend to use so Tauri can determine which to use" ,
119- devices. iter( ) . map( |d| d. name( ) ) . collect:: <Vec <_>>( ) . join( ", " ) ) ;
98+ if localhost {
99+ if let Some ( port) = url. port_or_known_default ( ) {
100+ adb_forward_port ( port, & env, & cli_options) ?;
101+ }
120102 }
121103 }
122104 }
@@ -180,6 +162,102 @@ fn validate_lib(path: &Path) -> Result<()> {
180162 Ok ( ( ) )
181163}
182164
165+ fn adb_forward_port (
166+ port : u16 ,
167+ env : & cargo_mobile2:: android:: env:: Env ,
168+ cli_options : & CliOptions ,
169+ ) -> Result < ( ) > {
170+ let forward = format ! ( "tcp:{port}" ) ;
171+ log:: info!( "Forwarding port {port} with adb" ) ;
172+
173+ let mut devices = adb:: device_list ( env) . unwrap_or_default ( ) ;
174+ // if we could not detect any running device, let's wait a few seconds, it might be booting up
175+ if devices. is_empty ( ) {
176+ log:: warn!(
177+ "ADB device list is empty, waiting a few seconds to see if there's any booting device..."
178+ ) ;
179+
180+ let max = 5 ;
181+ let mut count = 0 ;
182+ loop {
183+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) ;
184+
185+ devices = adb:: device_list ( env) . unwrap_or_default ( ) ;
186+ if !devices. is_empty ( ) {
187+ break ;
188+ }
189+
190+ count += 1 ;
191+ if count == max {
192+ break ;
193+ }
194+ }
195+ }
196+
197+ let target_device = if let Some ( target_device) = & cli_options. target_device {
198+ Some ( ( target_device. id . clone ( ) , target_device. name . clone ( ) ) )
199+ } else if devices. len ( ) == 1 {
200+ let device = devices. first ( ) . unwrap ( ) ;
201+ Some ( ( device. serial_no ( ) . to_string ( ) , device. name ( ) . to_string ( ) ) )
202+ } else if devices. len ( ) > 1 {
203+ anyhow:: bail!( "Multiple Android devices are connected ({}), please disconnect devices you do not intend to use so Tauri can determine which to use" ,
204+ devices. iter( ) . map( |d| d. name( ) ) . collect:: <Vec <_>>( ) . join( ", " ) ) ;
205+ } else {
206+ // when building the app without running to a device, we might have an empty devices list
207+ None
208+ } ;
209+
210+ if let Some ( ( target_device_serial_no, target_device_name) ) = target_device {
211+ let mut already_forwarded = false ;
212+
213+ // clear port forwarding for all devices
214+ for device in & devices {
215+ let reverse_list_output = adb_reverse_list ( env, device. serial_no ( ) ) ?;
216+
217+ // check if the device has the port forwarded
218+ if String :: from_utf8_lossy ( & reverse_list_output. stdout ) . contains ( & forward) {
219+ // device matches our target, we can skip forwarding
220+ if device. serial_no ( ) == target_device_serial_no {
221+ log:: debug!(
222+ "device {} already has the forward for {}" ,
223+ device. name( ) ,
224+ forward
225+ ) ;
226+ already_forwarded = true ;
227+ }
228+ break ;
229+ }
230+ }
231+
232+ // if there's a known target, we should forward the port to it
233+ if already_forwarded {
234+ log:: info!( "{forward} already forwarded to {target_device_name}" ) ;
235+ } else {
236+ loop {
237+ run_adb_reverse ( env, & target_device_serial_no, & forward, & forward) . with_context ( || {
238+ format ! ( "failed to forward port with adb, is the {target_device_name} device connected?" , )
239+ } ) ?;
240+
241+ let reverse_list_output = adb_reverse_list ( env, & target_device_serial_no) ?;
242+ // wait and retry until the port has actually been forwarded
243+ if String :: from_utf8_lossy ( & reverse_list_output. stdout ) . contains ( & forward) {
244+ break ;
245+ } else {
246+ log:: warn!(
247+ "waiting for the port to be forwarded to {}..." ,
248+ target_device_name
249+ ) ;
250+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) ;
251+ }
252+ }
253+ }
254+ } else {
255+ log:: warn!( "no running devices detected with ADB; skipping port forwarding" ) ;
256+ }
257+
258+ Ok ( ( ) )
259+ }
260+
183261fn run_adb_reverse (
184262 env : & cargo_mobile2:: android:: env:: Env ,
185263 device_serial_no : & str ,
@@ -193,15 +271,13 @@ fn run_adb_reverse(
193271 . run ( )
194272}
195273
196- fn remove_adb_reverse (
274+ fn adb_reverse_list (
197275 env : & cargo_mobile2:: android:: env:: Env ,
198276 device_serial_no : & str ,
199- remote : & str ,
200- ) {
201- // ignore errors in case the port is not forwarded
202- let _ = adb:: adb ( env, [ "-s" , device_serial_no, "reverse" , "--remove" , remote] )
277+ ) -> std:: io:: Result < std:: process:: Output > {
278+ adb:: adb ( env, [ "-s" , device_serial_no, "reverse" , "--list" ] )
203279 . stdin_file ( os_pipe:: dup_stdin ( ) . unwrap ( ) )
204- . stdout_file ( os_pipe :: dup_stdout ( ) . unwrap ( ) )
205- . stderr_file ( os_pipe :: dup_stdout ( ) . unwrap ( ) )
206- . run ( ) ;
280+ . stdout_capture ( )
281+ . stderr_capture ( )
282+ . run ( )
207283}
0 commit comments