@@ -6,7 +6,7 @@ use cargo_mobile2::{
66 android:: {
77 adb,
88 config:: { Config as AndroidConfig , Metadata as AndroidMetadata , Raw as RawAndroidConfig } ,
9- device:: Device ,
9+ device:: { ConnectionStatus , Device } ,
1010 emulator,
1111 env:: Env ,
1212 target:: Target ,
@@ -678,7 +678,11 @@ fn delete_codegen_vars() {
678678}
679679
680680fn adb_device_prompt < ' a > ( env : & ' _ Env , target : Option < & str > ) -> Result < Device < ' a > > {
681- let device_list = adb:: device_list ( env) . context ( "failed to detect connected Android devices" ) ?;
681+ let device_list = adb:: device_list ( env)
682+ . context ( "failed to detect connected Android devices" ) ?
683+ . into_iter ( )
684+ . filter ( |d| d. status ( ) == ConnectionStatus :: Connected )
685+ . collect :: < Vec < _ > > ( ) ;
682686 if !device_list. is_empty ( ) {
683687 let device = if let Some ( t) = target {
684688 let ( device, score) = device_list
@@ -764,29 +768,172 @@ fn emulator_prompt(env: &'_ Env, target: Option<&str>) -> Result<emulator::Emula
764768 }
765769}
766770
771+ enum EmulatorStatus {
772+ Offline { serial_no : String } ,
773+ Connected ,
774+ }
775+
767776fn device_prompt < ' a > ( env : & ' _ Env , target : Option < & str > ) -> Result < Device < ' a > > {
768777 if let Ok ( device) = adb_device_prompt ( env, target) {
769778 Ok ( device)
770779 } else {
771780 let emulator = emulator_prompt ( env, target) ?;
772- log:: info!( "Starting emulator {}" , emulator. name( ) ) ;
773- emulator
774- . start_detached ( env)
775- . context ( "failed to start emulator" ) ?;
781+ let emulator_status = match adb:: device_list ( env) {
782+ Ok ( devices) => {
783+ // emulator might be running but disconnected from adb
784+ devices
785+ . iter ( )
786+ . find ( |d| d. name ( ) == emulator. name ( ) )
787+ . and_then ( |d| match d. status ( ) {
788+ ConnectionStatus :: Offline | ConnectionStatus :: Unauthorized => {
789+ Some ( EmulatorStatus :: Offline {
790+ serial_no : d. serial_no ( ) . to_string ( ) ,
791+ } )
792+ }
793+ ConnectionStatus :: Connected => Some ( EmulatorStatus :: Connected ) ,
794+ _ => None ,
795+ } )
796+ }
797+ // failed to get device information, check if the device name matches the emulator name
798+ Err (
799+ adb:: device_list:: Error :: ModelFailed {
800+ serial_no,
801+ error : adb:: get_prop:: Error :: CommandFailed { command : _, error } ,
802+ }
803+ | adb:: device_list:: Error :: AbiFailed {
804+ serial_no,
805+ error : adb:: get_prop:: Error :: CommandFailed { command : _, error } ,
806+ } ,
807+ ) => {
808+ if error. kind ( ) == std:: io:: ErrorKind :: TimedOut {
809+ // if the device name matches the emulator name, the emulator is already running and marked as connected
810+ // but we cannot connect to it
811+ adb:: device_name ( env, & serial_no) . map_or ( None , |device_name| {
812+ if device_name == emulator. name ( ) {
813+ Some ( EmulatorStatus :: Offline { serial_no } )
814+ } else {
815+ None
816+ }
817+ } )
818+ } else {
819+ None
820+ }
821+ }
822+ Err ( _) => None ,
823+ } ;
824+
825+ let emulator_already_running = emulator_status. is_some ( ) ;
826+ match emulator_status {
827+ Some ( EmulatorStatus :: Offline { serial_no } ) => {
828+ // emulator is available but not connected to adb, we must restart it
829+ log:: info!( "Emulator is not connected, we need to restart it" ) ;
830+ restart_emulator ( env, & serial_no, & emulator) ?;
831+ }
832+ Some ( EmulatorStatus :: Connected ) => {
833+ // emulator is already connected to adb
834+ // this is technically unreachable because we queried the device list with adb_device_prompt
835+ }
836+ None => {
837+ log:: info!( "Starting emulator {}" , emulator. name( ) ) ;
838+ emulator
839+ . start_detached ( env)
840+ . context ( "failed to start emulator" ) ?;
841+ }
842+ }
843+
776844 let mut tries = 0 ;
777845 loop {
778846 sleep ( Duration :: from_secs ( 2 ) ) ;
779- if let Ok ( device) = adb_device_prompt ( env, Some ( emulator. name ( ) ) ) {
780- return Ok ( device) ;
847+ // we do not filter for connected devices to detect emulators that are not connected to our adb anymore
848+ match adb:: device_list ( env) {
849+ Ok ( devices) => {
850+ if let Some ( device) = devices. into_iter ( ) . find ( |d| d. name ( ) == emulator. name ( ) ) {
851+ if device. status ( ) == ConnectionStatus :: Connected {
852+ return Ok ( device) ;
853+ }
854+ }
855+
856+ if tries >= 3 {
857+ log:: info!( "Waiting for emulator to start... (maybe the emulator is unauthorized or offline, run `adb devices` to check)" ) ;
858+ } else {
859+ log:: info!( "Waiting for emulator to start..." ) ;
860+ }
861+ tries += 1 ;
862+ }
863+ Err (
864+ adb:: device_list:: Error :: ModelFailed {
865+ serial_no,
866+ error : adb:: get_prop:: Error :: CommandFailed { command : _, error } ,
867+ }
868+ | adb:: device_list:: Error :: AbiFailed {
869+ serial_no,
870+ error : adb:: get_prop:: Error :: CommandFailed { command : _, error } ,
871+ } ,
872+ ) => {
873+ if emulator_already_running && error. kind ( ) == std:: io:: ErrorKind :: TimedOut {
874+ log:: info!( "Emulator is not responding, we need to restart it" ) ;
875+ restart_emulator ( env, & serial_no, & emulator) ?;
876+ tries = 0 ;
877+ } else {
878+ log:: error!( "failed to get properties for device {serial_no}: {error}" ) ;
879+ }
880+ }
881+ Err ( e) => {
882+ log:: error!( "failed to list devices with adb: {e}" ) ;
883+ tries += 1 ;
884+ }
781885 }
782- if tries >= 3 {
783- log:: info!( "Waiting for emulator to start... (maybe the emulator is unauthorized or offline, run `adb devices` to check)" ) ;
784- } else {
785- log:: info!( "Waiting for emulator to start..." ) ;
886+ }
887+ }
888+ }
889+
890+ fn restart_emulator ( env : & Env , serial_no : & str , emulator : & emulator:: Emulator ) -> Result < ( ) > {
891+ let granted_permission_to_restart =
892+ crate :: helpers:: prompts:: confirm ( "Do you want to restart the emulator?" , Some ( true ) )
893+ . unwrap_or_default ( ) ;
894+ if !granted_permission_to_restart {
895+ crate :: error:: bail!(
896+ "Cannot connect to the emulator, please restart it manually (a full boot might be required)"
897+ ) ;
898+ }
899+
900+ adb:: adb ( env, & [ "-s" , serial_no, "emu" , "kill" ] )
901+ . run ( )
902+ . context ( "failed to reboot emulator" ) ?;
903+
904+ log:: info!( "Waiting for emulator to exit..." ) ;
905+ loop {
906+ let devices = adb:: device_list ( env) . unwrap_or_default ( ) ;
907+ if devices
908+ . into_iter ( )
909+ . find ( |d| d. serial_no ( ) == serial_no)
910+ . is_none ( )
911+ {
912+ break ;
913+ }
914+ sleep ( Duration :: from_secs ( 1 ) ) ;
915+ }
916+
917+ log:: info!( "Restarting emulator with full boot" ) ;
918+ let mut tries = 0 ;
919+ loop {
920+ // wait a bit to make sure we can restart the emulator
921+ sleep ( Duration :: from_secs ( 2 ) ) ;
922+
923+ match emulator. start_detached_with_options ( env, emulator:: StartOptions :: new ( ) . full_boot ( ) ) {
924+ Ok ( _) => break ,
925+ Err ( e) => {
926+ tries += 1 ;
927+ if tries >= 3 {
928+ return Err ( e) . context ( "failed to start emulator" ) ;
929+ } else {
930+ log:: error!( "failed to start emulator, retrying..." ) ;
931+ }
786932 }
787- tries += 1 ;
788933 }
789934 }
935+
936+ Ok ( ( ) )
790937}
791938
792939fn detect_target_ok < ' a > ( env : & Env ) -> Option < & ' a Target < ' a > > {
0 commit comments