From 91d7fc31ff30c20a247dcf7b4d2a6306120d68b3 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 15:55:53 -0800 Subject: [PATCH 01/22] Refactor emulator start and stop functions for clarity and efficiency. --- tools/python/run_android_emulator.py | 3 +- tools/python/util/android/android.py | 80 ++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/tools/python/run_android_emulator.py b/tools/python/run_android_emulator.py index 2826921726556..89022e2d388d3 100755 --- a/tools/python/run_android_emulator.py +++ b/tools/python/run_android_emulator.py @@ -77,12 +77,13 @@ def main(): sys.stdin.readline() elif args.start: + print(f"Starting emulator: {args.avd_name}") emulator_proc = android.start_emulator(**start_emulator_args) - with open(args.emulator_pid_file, mode="w") as emulator_pid_file: print(f"{emulator_proc.pid}", file=emulator_pid_file) elif args.stop: + print (f"Stopping emulator: {args.avd_name}") with open(args.emulator_pid_file) as emulator_pid_file: emulator_pid = int(emulator_pid_file.readline().strip()) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index dd2dcce01bf4a..04a0616c5ac82 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -16,8 +16,8 @@ _log = get_logger("util.android") - -SdkToolPaths = collections.namedtuple("SdkToolPaths", ["emulator", "adb", "sdkmanager", "avdmanager"]) +SdkToolPaths = collections.namedtuple("SdkToolPaths", + ["emulator", "adb", "sdkmanager", "avdmanager"]) def get_sdk_tool_paths(sdk_root: str): @@ -34,15 +34,18 @@ def filename(name, windows_extension): emulator=str((sdk_root / "emulator" / filename("emulator", "exe")).resolve(strict=True)), adb=str((sdk_root / "platform-tools" / filename("adb", "exe")).resolve(strict=True)), sdkmanager=str( - (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("sdkmanager", "bat")).resolve(strict=True) + (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("sdkmanager", "bat")).resolve( + strict=True) ), avdmanager=str( - (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("avdmanager", "bat")).resolve(strict=True) + (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("avdmanager", "bat")).resolve( + strict=True) ), ) -def create_virtual_device(sdk_tool_paths: SdkToolPaths, system_image_package_name: str, avd_name: str): +def create_virtual_device(sdk_tool_paths: SdkToolPaths, system_image_package_name: str, + avd_name: str): run(sdk_tool_paths.sdkmanager, "--install", system_image_package_name, input=b"y") run( @@ -105,8 +108,26 @@ def _stop_process_with_pid(pid: int): def start_emulator( - sdk_tool_paths: SdkToolPaths, avd_name: str, extra_args: typing.Optional[typing.Sequence[str]] = None + sdk_tool_paths: SdkToolPaths, avd_name: str, + extra_args: typing.Optional[typing.Sequence[str]] = None ) -> subprocess.Popen: + def check_emulator_running() -> bool: + """ + Check if an emulator is already running by parsing adb devices output. + """ + try: + output = subprocess.check_output([sdk_tool_paths.adb, "devices"], timeout=10, text=True) + # Filter lines containing "emulator" to detect running emulators + running_devices = [line for line in output.splitlines() if "emulator" in line] + return len(running_devices) > 0 + except subprocess.SubprocessError as e: + _log.error(f"Error checking running emulators: {e}") + return False + + if check_emulator_running(): + raise RuntimeError( + "An emulator is already running. Please close it before starting a new one.") + with contextlib.ExitStack() as emulator_stack, contextlib.ExitStack() as waiter_stack: emulator_args = [ sdk_tool_paths.emulator, @@ -202,16 +223,57 @@ def start_emulator( elif datetime.datetime.now() > end_time: raise RuntimeError("Emulator startup timeout. sys.boot_completed was not set.") - _log.debug(f"sys.boot_completed='{getprop_value}'. Sleeping for {sleep_interval_seconds} before retrying.") + _log.debug( + f"sys.boot_completed='{getprop_value}'. Sleeping for {sleep_interval_seconds} before retrying.") time.sleep(sleep_interval_seconds) - + # Verify if the emulator is now running + if not check_emulator_running(): + raise RuntimeError("Emulator failed to start.") return emulator_process -def stop_emulator(emulator_proc_or_pid: typing.Union[subprocess.Popen, int]): +def stop_emulator( + emulator_proc_or_pid: typing.Union[subprocess.Popen, int], timeout: int = 120 +): + """ + Stops the emulator process, checking its running status before and after stopping. + + :param emulator_proc_or_pid: The emulator process (subprocess.Popen) or PID (int). + :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + """ + + def is_emulator_running() -> bool: + """Check if any emulator instance is running using adb.""" + try: + output = subprocess.check_output(["adb", "devices"], text=True, timeout=10) + running_devices = [line for line in output.splitlines() if "emulator" in line] + return len(running_devices) > 0 + except subprocess.SubprocessError as e: + _log.error(f"Error checking running emulators: {e}") + return False + + if not is_emulator_running(): + _log.warning("No emulator instances are currently running.") + return + if isinstance(emulator_proc_or_pid, subprocess.Popen): + _log.info("Stopping emulator using subprocess.Popen instance.") _stop_process(emulator_proc_or_pid) elif isinstance(emulator_proc_or_pid, int): + _log.info(f"Stopping emulator with PID: {emulator_proc_or_pid}") _stop_process_with_pid(emulator_proc_or_pid) else: raise ValueError("Expected either a PID or subprocess.Popen instance.") + + # Loop to check if the emulator stops within the timeout + interval = 5 + end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) + + while is_emulator_running(): + if datetime.datetime.now() > end_time: + raise RuntimeError( + f"Failed to stop the emulator within the specified timeout = {timeout} seconds.") + _log.debug("Emulator still running. Checking again in 5 seconds...") + time.sleep(interval) + + _log.info("Emulator stopped successfully.") From d9af6f02f8745e5225dffd598663da13cba0e186 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 16:34:47 -0800 Subject: [PATCH 02/22] Refactor emulator start and stop functions for clarity and efficiency. --- tools/python/run_android_emulator.py | 4 +- tools/python/util/android/android.py | 147 +++++++++++++++++++-------- 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/tools/python/run_android_emulator.py b/tools/python/run_android_emulator.py index 89022e2d388d3..04d80d5151ed5 100755 --- a/tools/python/run_android_emulator.py +++ b/tools/python/run_android_emulator.py @@ -77,13 +77,13 @@ def main(): sys.stdin.readline() elif args.start: - print(f"Starting emulator: {args.avd_name}") + log.info(f"Starting emulator: {args.avd_name}") emulator_proc = android.start_emulator(**start_emulator_args) with open(args.emulator_pid_file, mode="w") as emulator_pid_file: print(f"{emulator_proc.pid}", file=emulator_pid_file) elif args.stop: - print (f"Stopping emulator: {args.avd_name}") + log.info (f"Stopping emulator: {args.avd_name}") with open(args.emulator_pid_file) as emulator_pid_file: emulator_pid = int(emulator_pid_file.readline().strip()) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 04a0616c5ac82..50a99e040667f 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -111,22 +111,9 @@ def start_emulator( sdk_tool_paths: SdkToolPaths, avd_name: str, extra_args: typing.Optional[typing.Sequence[str]] = None ) -> subprocess.Popen: - def check_emulator_running() -> bool: - """ - Check if an emulator is already running by parsing adb devices output. - """ - try: - output = subprocess.check_output([sdk_tool_paths.adb, "devices"], timeout=10, text=True) - # Filter lines containing "emulator" to detect running emulators - running_devices = [line for line in output.splitlines() if "emulator" in line] - return len(running_devices) > 0 - except subprocess.SubprocessError as e: - _log.error(f"Error checking running emulators: {e}") - return False - - if check_emulator_running(): + if is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError( - "An emulator is already running. Please close it before starting a new one.") + f"An emulator with avd_name{avd_name} is already running. Please close it before starting a new one.") with contextlib.ExitStack() as emulator_stack, contextlib.ExitStack() as waiter_stack: emulator_args = [ @@ -227,49 +214,78 @@ def check_emulator_running() -> bool: f"sys.boot_completed='{getprop_value}'. Sleeping for {sleep_interval_seconds} before retrying.") time.sleep(sleep_interval_seconds) # Verify if the emulator is now running - if not check_emulator_running(): + if not is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError("Emulator failed to start.") return emulator_process -def stop_emulator( - emulator_proc_or_pid: typing.Union[subprocess.Popen, int], timeout: int = 120 -): +def is_emulator_running_by_avd(avd_name: str) -> bool: """ - Stops the emulator process, checking its running status before and after stopping. + Check if an emulator is running based on the provided AVD name. - :param emulator_proc_or_pid: The emulator process (subprocess.Popen) or PID (int). - :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + :param avd_name: Name of the Android Virtual Device (AVD) to check. + :return: True if an emulator with the given AVD name is running, False otherwise. """ + try: + # Step 1: List running devices + result = subprocess.check_output(["adb", "devices"], text=True).strip() + running_emulators = [ + line.split("\t")[0] for line in result.splitlines()[1:] if "emulator" in line + ] - def is_emulator_running() -> bool: - """Check if any emulator instance is running using adb.""" - try: - output = subprocess.check_output(["adb", "devices"], text=True, timeout=10) - running_devices = [line for line in output.splitlines() if "emulator" in line] - return len(running_devices) > 0 - except subprocess.SubprocessError as e: - _log.error(f"Error checking running emulators: {e}") - return False - - if not is_emulator_running(): - _log.warning("No emulator instances are currently running.") + if not running_emulators: + return False # No emulators running + + # Step 2: Check each running emulator's AVD name + for emulator in running_emulators: + try: + avd_info = subprocess.check_output( + ["adb", "-s", emulator, "emu", "avd", "name"], text=True + ).strip() + if avd_info == avd_name: + return True + except subprocess.SubprocessError: + continue # Skip if there's an issue querying a specific emulator + + return False # No matching AVD name found + except subprocess.SubprocessError as e: + print(f"Error checking emulator status: {e}") + return False + + +def is_emulator_running_by_proc(emulator_proc: subprocess.Popen) -> bool: + """Check if the emulator process is running based on a Popen instance.""" + return emulator_proc.poll() is None + + +def is_emulator_running_by_pid(emulator_pid: int) -> bool: + """Check if the emulator process is running based on PID.""" + try: + os.kill(emulator_pid, 0) # Signal 0 checks process existence + return True + except OSError: + return False + + +def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout: int = 120): + """ + Stops the emulator process using a subprocess.Popen instance. + + :param emulator_proc: The emulator process as a subprocess.Popen instance. + :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + """ + if not is_emulator_running_by_proc(emulator_proc): + _log.warning("The specified emulator process is not running.") return - if isinstance(emulator_proc_or_pid, subprocess.Popen): - _log.info("Stopping emulator using subprocess.Popen instance.") - _stop_process(emulator_proc_or_pid) - elif isinstance(emulator_proc_or_pid, int): - _log.info(f"Stopping emulator with PID: {emulator_proc_or_pid}") - _stop_process_with_pid(emulator_proc_or_pid) - else: - raise ValueError("Expected either a PID or subprocess.Popen instance.") + _log.info("Stopping emulator using subprocess.Popen instance.") + _stop_process(emulator_proc) - # Loop to check if the emulator stops within the timeout + # Wait for the process to stop interval = 5 end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - while is_emulator_running(): + while is_emulator_running_by_proc(emulator_proc): if datetime.datetime.now() > end_time: raise RuntimeError( f"Failed to stop the emulator within the specified timeout = {timeout} seconds.") @@ -277,3 +293,46 @@ def is_emulator_running() -> bool: time.sleep(interval) _log.info("Emulator stopped successfully.") + + +def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): + """ + Stops the emulator process using a PID. + + :param emulator_pid: The emulator process PID. + :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + """ + if not is_emulator_running_by_pid(emulator_pid): + _log.warning(f"No emulator process with PID {emulator_pid} is currently running.") + return + + _log.info(f"Stopping emulator with PID: {emulator_pid}") + _stop_process_with_pid(emulator_pid) + + # Wait for the process to stop + interval = 5 + end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) + + while is_emulator_running_by_pid(emulator_pid): + if datetime.datetime.now() > end_time: + raise RuntimeError( + f"Failed to stop the emulator with PID {emulator_pid} within the specified timeout = {timeout} seconds.") + _log.debug("Emulator still running. Checking again in 5 seconds...") + time.sleep(interval) + + _log.info("Emulator stopped successfully.") + + +def stop_emulator(emulator_proc_or_pid: typing.Union[subprocess.Popen, int], timeout: int = 120): + """ + Stops the emulator process, checking its running status before and after stopping. + + :param emulator_proc_or_pid: The emulator process (subprocess.Popen) or PID (int). + :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + """ + if isinstance(emulator_proc_or_pid, subprocess.Popen): + stop_emulator_by_proc(emulator_proc_or_pid, timeout) + elif isinstance(emulator_proc_or_pid, int): + stop_emulator_by_pid(emulator_proc_or_pid, timeout) + else: + raise ValueError("Expected either a PID or subprocess.Popen instance.") From 85780c36abf2a65c2d2932a3e44ff5f822f60a26 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 16:36:19 -0800 Subject: [PATCH 03/22] Refactor emulator start and stop functions for clarity and efficiency. --- tools/python/run_android_emulator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/python/run_android_emulator.py b/tools/python/run_android_emulator.py index 04d80d5151ed5..2826921726556 100755 --- a/tools/python/run_android_emulator.py +++ b/tools/python/run_android_emulator.py @@ -77,13 +77,12 @@ def main(): sys.stdin.readline() elif args.start: - log.info(f"Starting emulator: {args.avd_name}") emulator_proc = android.start_emulator(**start_emulator_args) + with open(args.emulator_pid_file, mode="w") as emulator_pid_file: print(f"{emulator_proc.pid}", file=emulator_pid_file) elif args.stop: - log.info (f"Stopping emulator: {args.avd_name}") with open(args.emulator_pid_file) as emulator_pid_file: emulator_pid = int(emulator_pid_file.readline().strip()) From afa5e9a8830afeaa91f354b95f39b652d99da85c Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 16:40:25 -0800 Subject: [PATCH 04/22] Refactor emulator start and stop functions for clarity and efficiency. --- tools/python/util/android/android.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 50a99e040667f..db98ff8e96953 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -16,8 +16,8 @@ _log = get_logger("util.android") -SdkToolPaths = collections.namedtuple("SdkToolPaths", - ["emulator", "adb", "sdkmanager", "avdmanager"]) + +SdkToolPaths = collections.namedtuple("SdkToolPaths", ["emulator", "adb", "sdkmanager", "avdmanager"]) def get_sdk_tool_paths(sdk_root: str): @@ -34,18 +34,15 @@ def filename(name, windows_extension): emulator=str((sdk_root / "emulator" / filename("emulator", "exe")).resolve(strict=True)), adb=str((sdk_root / "platform-tools" / filename("adb", "exe")).resolve(strict=True)), sdkmanager=str( - (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("sdkmanager", "bat")).resolve( - strict=True) + (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("sdkmanager", "bat")).resolve(strict=True) ), avdmanager=str( - (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("avdmanager", "bat")).resolve( - strict=True) + (sdk_root / "cmdline-tools" / "latest" / "bin" / filename("avdmanager", "bat")).resolve(strict=True) ), ) -def create_virtual_device(sdk_tool_paths: SdkToolPaths, system_image_package_name: str, - avd_name: str): +def create_virtual_device(sdk_tool_paths: SdkToolPaths, system_image_package_name: str, avd_name: str): run(sdk_tool_paths.sdkmanager, "--install", system_image_package_name, input=b"y") run( @@ -108,13 +105,11 @@ def _stop_process_with_pid(pid: int): def start_emulator( - sdk_tool_paths: SdkToolPaths, avd_name: str, - extra_args: typing.Optional[typing.Sequence[str]] = None + sdk_tool_paths: SdkToolPaths, avd_name: str, extra_args: typing.Optional[typing.Sequence[str]] = None ) -> subprocess.Popen: if is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError( f"An emulator with avd_name{avd_name} is already running. Please close it before starting a new one.") - with contextlib.ExitStack() as emulator_stack, contextlib.ExitStack() as waiter_stack: emulator_args = [ sdk_tool_paths.emulator, @@ -210,19 +205,16 @@ def start_emulator( elif datetime.datetime.now() > end_time: raise RuntimeError("Emulator startup timeout. sys.boot_completed was not set.") - _log.debug( - f"sys.boot_completed='{getprop_value}'. Sleeping for {sleep_interval_seconds} before retrying.") + _log.debug(f"sys.boot_completed='{getprop_value}'. Sleeping for {sleep_interval_seconds} before retrying.") time.sleep(sleep_interval_seconds) # Verify if the emulator is now running if not is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError("Emulator failed to start.") return emulator_process - def is_emulator_running_by_avd(avd_name: str) -> bool: """ Check if an emulator is running based on the provided AVD name. - :param avd_name: Name of the Android Virtual Device (AVD) to check. :return: True if an emulator with the given AVD name is running, False otherwise. """ @@ -270,7 +262,6 @@ def is_emulator_running_by_pid(emulator_pid: int) -> bool: def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout: int = 120): """ Stops the emulator process using a subprocess.Popen instance. - :param emulator_proc: The emulator process as a subprocess.Popen instance. :param timeout: Maximum time (in seconds) to wait for the emulator to stop. """ @@ -298,7 +289,6 @@ def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout: int = 120): def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): """ Stops the emulator process using a PID. - :param emulator_pid: The emulator process PID. :param timeout: Maximum time (in seconds) to wait for the emulator to stop. """ @@ -326,7 +316,6 @@ def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): def stop_emulator(emulator_proc_or_pid: typing.Union[subprocess.Popen, int], timeout: int = 120): """ Stops the emulator process, checking its running status before and after stopping. - :param emulator_proc_or_pid: The emulator process (subprocess.Popen) or PID (int). :param timeout: Maximum time (in seconds) to wait for the emulator to stop. """ From 1ceeb249437d75959d66449a7f9cee05f46a5382 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 16:58:33 -0800 Subject: [PATCH 05/22] avd_info --- tools/python/util/android/android.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index db98ff8e96953..8489267c5a178 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -105,7 +105,8 @@ def _stop_process_with_pid(pid: int): def start_emulator( - sdk_tool_paths: SdkToolPaths, avd_name: str, extra_args: typing.Optional[typing.Sequence[str]] = None + sdk_tool_paths: SdkToolPaths, avd_name: str, extra_args: typing.Optional[typing.Sequence[str]] = None, + timeout_minutes: int = 20 ) -> subprocess.Popen: if is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError( @@ -121,6 +122,7 @@ def start_emulator( "America/Los_Angeles", "-no-snapstorage", "-no-audio", + "-no-bt", # No bluetooth "-no-boot-anim", "-gpu", "guest", @@ -158,9 +160,9 @@ def start_emulator( waiter_stack.callback(_stop_process, waiter_process) # poll subprocesses. - # allow 20 minutes for startup as some CIs are slow. TODO: Make timeout configurable if needed. + # allow 20 minutes for startup as some CIs are slow. sleep_interval_seconds = 10 - end_time = datetime.datetime.now() + datetime.timedelta(minutes=20) + end_time = datetime.datetime.now() + datetime.timedelta(minutes=timeout_minutes) while True: waiter_ret, emulator_ret = waiter_process.poll(), emulator_process.poll() @@ -226,6 +228,7 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: ] if not running_emulators: + _log.warning("No emulators running.") return False # No emulators running # Step 2: Check each running emulator's AVD name @@ -237,11 +240,12 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: if avd_info == avd_name: return True except subprocess.SubprocessError: + _log.warning(f"Error checking AVD name for emulator: {emulator}") continue # Skip if there's an issue querying a specific emulator - + _log.warning(f"No emulator running with AVD name: {avd_name}") return False # No matching AVD name found except subprocess.SubprocessError as e: - print(f"Error checking emulator status: {e}") + _log.warning(f"Error checking emulator status: {e}") return False From bf977a92fa836558726b3a1a1cbb2599201aef5c Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 16:59:32 -0800 Subject: [PATCH 06/22] result --- tools/python/util/android/android.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 8489267c5a178..23897a36241c6 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -223,6 +223,7 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: try: # Step 1: List running devices result = subprocess.check_output(["adb", "devices"], text=True).strip() + _log.info(f"adb devices output:\n{result}") running_emulators = [ line.split("\t")[0] for line in result.splitlines()[1:] if "emulator" in line ] From a5a45ca588c04632c3c40e64130b292b2da6a20b Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:00:38 -0800 Subject: [PATCH 07/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 23897a36241c6..a50f894b3525c 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -110,7 +110,8 @@ def start_emulator( ) -> subprocess.Popen: if is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError( - f"An emulator with avd_name{avd_name} is already running. Please close it before starting a new one.") + f"An emulator with avd_name{avd_name} is already running. Please close it before starting a new one." + ) with contextlib.ExitStack() as emulator_stack, contextlib.ExitStack() as waiter_stack: emulator_args = [ sdk_tool_paths.emulator, From db94c41d4fccd5a6337f19fa96a479577b848e2a Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:00:49 -0800 Subject: [PATCH 08/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index a50f894b3525c..528a24b5074af 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -215,6 +215,7 @@ def start_emulator( raise RuntimeError("Emulator failed to start.") return emulator_process + def is_emulator_running_by_avd(avd_name: str) -> bool: """ Check if an emulator is running based on the provided AVD name. From 5c5b014fe16aefd7aee91f00bb7d024c758e5e5c Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:01:00 -0800 Subject: [PATCH 09/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 528a24b5074af..e767e69e0d284 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -237,9 +237,7 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: # Step 2: Check each running emulator's AVD name for emulator in running_emulators: try: - avd_info = subprocess.check_output( - ["adb", "-s", emulator, "emu", "avd", "name"], text=True - ).strip() + avd_info = subprocess.check_output(["adb", "-s", emulator, "emu", "avd", "name"], text=True).strip() if avd_info == avd_name: return True except subprocess.SubprocessError: From a405d62d25a434684a6456877effa2d6b73cf250 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:01:08 -0800 Subject: [PATCH 10/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index e767e69e0d284..a6f2235c3d56b 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -283,8 +283,7 @@ def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout: int = 120): while is_emulator_running_by_proc(emulator_proc): if datetime.datetime.now() > end_time: - raise RuntimeError( - f"Failed to stop the emulator within the specified timeout = {timeout} seconds.") + raise RuntimeError(f"Failed to stop the emulator within the specified timeout = {timeout} seconds.") _log.debug("Emulator still running. Checking again in 5 seconds...") time.sleep(interval) From b9ea99c47ad082862f021007c69425e7d144c977 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:01:15 -0800 Subject: [PATCH 11/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index a6f2235c3d56b..e43e13eaf7d2c 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -310,7 +310,8 @@ def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): while is_emulator_running_by_pid(emulator_pid): if datetime.datetime.now() > end_time: raise RuntimeError( - f"Failed to stop the emulator with PID {emulator_pid} within the specified timeout = {timeout} seconds.") + f"Failed to stop the emulator with PID {emulator_pid} within the specified timeout = {timeout} seconds." + ) _log.debug("Emulator still running. Checking again in 5 seconds...") time.sleep(interval) From 5f0f8a881f62f20b41a82838814999f231ab1c90 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:01:37 -0800 Subject: [PATCH 12/22] os --- tools/python/util/android/android.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 23897a36241c6..7bb4c424bd284 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -254,7 +254,7 @@ def is_emulator_running_by_proc(emulator_proc: subprocess.Popen) -> bool: """Check if the emulator process is running based on a Popen instance.""" return emulator_proc.poll() is None - +import os def is_emulator_running_by_pid(emulator_pid: int) -> bool: """Check if the emulator process is running based on PID.""" try: From 4ff05f731ef9e169144e5daa73204665c84ac5a7 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Fri, 15 Nov 2024 17:05:30 -0800 Subject: [PATCH 13/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 3c9b19acab9de..9e8402f6935e1 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -226,9 +226,7 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: # Step 1: List running devices result = subprocess.check_output(["adb", "devices"], text=True).strip() _log.info(f"adb devices output:\n{result}") - running_emulators = [ - line.split("\t")[0] for line in result.splitlines()[1:] if "emulator" in line - ] + running_emulators = [line.split("\t")[0] for line in result.splitlines()[1:] if "emulator" in line] if not running_emulators: _log.warning("No emulators running.") From 02d619f0de2aa5c24a398ba1517462ec72dcbb3d Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Mon, 18 Nov 2024 09:50:49 -0800 Subject: [PATCH 14/22] Disable bluetooth --- tools/python/util/android/android.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 9e8402f6935e1..a564777673312 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -4,6 +4,7 @@ import collections import contextlib import datetime +import os import signal import subprocess import time @@ -123,11 +124,13 @@ def start_emulator( "America/Los_Angeles", "-no-snapstorage", "-no-audio", - "-no-bt", # No bluetooth + "-no-snapshot", # No bluetooth "-no-boot-anim", "-gpu", "guest", "-delay-adb", + "-prop persist.sys.disable_bluetooth=true", + "-verbose", ] # For Linux CIs we must use "-no-window" otherwise you'll get @@ -229,7 +232,7 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: running_emulators = [line.split("\t")[0] for line in result.splitlines()[1:] if "emulator" in line] if not running_emulators: - _log.warning("No emulators running.") + _log.debug("No emulators running.") return False # No emulators running # Step 2: Check each running emulator's AVD name @@ -252,7 +255,7 @@ def is_emulator_running_by_proc(emulator_proc: subprocess.Popen) -> bool: """Check if the emulator process is running based on a Popen instance.""" return emulator_proc.poll() is None -import os + def is_emulator_running_by_pid(emulator_pid: int) -> bool: """Check if the emulator process is running based on PID.""" try: From f6a9526441fc571352ebd80448a9cd2bb39dc2b3 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Mon, 18 Nov 2024 10:30:15 -0800 Subject: [PATCH 15/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index a564777673312..f87ead3fb21a8 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -124,7 +124,7 @@ def start_emulator( "America/Los_Angeles", "-no-snapstorage", "-no-audio", - "-no-snapshot", # No bluetooth + "-no-snapshot", # No bluetooth "-no-boot-anim", "-gpu", "guest", From 2f3ddf1fc1a098719eef64e6761d503a6f3e1fc2 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Mon, 18 Nov 2024 11:43:57 -0800 Subject: [PATCH 16/22] remove -prop persist.sys.disable_bluetooth=true --- tools/python/util/android/android.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index f87ead3fb21a8..f1ee276302b17 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -129,7 +129,6 @@ def start_emulator( "-gpu", "guest", "-delay-adb", - "-prop persist.sys.disable_bluetooth=true", "-verbose", ] From b3c7cd4ff4b695e2bd36a5da9db664959e4cc9bf Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Mon, 18 Nov 2024 12:58:01 -0800 Subject: [PATCH 17/22] Check the avd name --- tools/python/util/android/android.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index f1ee276302b17..0207f084c549e 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -237,7 +237,8 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: # Step 2: Check each running emulator's AVD name for emulator in running_emulators: try: - avd_info = subprocess.check_output(["adb", "-s", emulator, "emu", "avd", "name"], text=True).strip() + avd_info = subprocess.check_output(["adb", "-s", emulator, "emu", "avd", "name"], text=True).strip().split('\n')[0] + _log.debug(f"AVD name for emulator {emulator}: {avd_info}") if avd_info == avd_name: return True except subprocess.SubprocessError: From fbf5bcca675f58edee21fa456a1bbc2e3d374a16 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Mon, 18 Nov 2024 13:56:25 -0800 Subject: [PATCH 18/22] Update tools/python/util/android/android.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tools/python/util/android/android.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 0207f084c549e..e4a7322101e92 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -237,7 +237,11 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: # Step 2: Check each running emulator's AVD name for emulator in running_emulators: try: - avd_info = subprocess.check_output(["adb", "-s", emulator, "emu", "avd", "name"], text=True).strip().split('\n')[0] + avd_info = ( + subprocess.check_output(["adb", "-s", emulator, "emu", "avd", "name"], text=True) + .strip() + .split("\n")[0] + ) _log.debug(f"AVD name for emulator {emulator}: {avd_info}") if avd_info == avd_name: return True From b98dbd3572457219b5566746fef97dcae80ed982 Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Mon, 18 Nov 2024 14:43:41 -0800 Subject: [PATCH 19/22] litrunner -a --- tools/python/util/android/android.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index e4a7322101e92..4ac09437de016 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -106,8 +106,10 @@ def _stop_process_with_pid(pid: int): def start_emulator( - sdk_tool_paths: SdkToolPaths, avd_name: str, extra_args: typing.Optional[typing.Sequence[str]] = None, - timeout_minutes: int = 20 + sdk_tool_paths: SdkToolPaths, + avd_name: str, + extra_args: typing.Optional[typing.Sequence[str]] = None, + timeout_minutes: int = 20, ) -> subprocess.Popen: if is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError( From 342e42c1d35c92e2a4d55c692d0da0db5ae12b3d Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Thu, 21 Nov 2024 12:53:46 -0800 Subject: [PATCH 20/22] Update tools/python/util/android/android.py Co-authored-by: Scott McKay --- tools/python/util/android/android.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 4ac09437de016..5eecc6e9eaf0b 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -250,6 +250,7 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: except subprocess.SubprocessError: _log.warning(f"Error checking AVD name for emulator: {emulator}") continue # Skip if there's an issue querying a specific emulator + _log.warning(f"No emulator running with AVD name: {avd_name}") return False # No matching AVD name found except subprocess.SubprocessError as e: From a36f12f3f5a18c42aa01d37f06eceda2802c347d Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Thu, 21 Nov 2024 12:54:21 -0800 Subject: [PATCH 21/22] Update tools/python/util/android/android.py Co-authored-by: Scott McKay --- tools/python/util/android/android.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 5eecc6e9eaf0b..5378f953123d2 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -214,6 +214,7 @@ def start_emulator( _log.debug(f"sys.boot_completed='{getprop_value}'. Sleeping for {sleep_interval_seconds} before retrying.") time.sleep(sleep_interval_seconds) + # Verify if the emulator is now running if not is_emulator_running_by_avd(avd_name=avd_name): raise RuntimeError("Emulator failed to start.") From 3c73a6e5fe4425b1acc4edec6c219b53fa322deb Mon Sep 17 00:00:00 2001 From: Jian Chen Date: Thu, 21 Nov 2024 13:13:09 -0800 Subject: [PATCH 22/22] Refactor the code --- tools/python/util/android/android.py | 43 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/tools/python/util/android/android.py b/tools/python/util/android/android.py index 5378f953123d2..24004d6be761d 100644 --- a/tools/python/util/android/android.py +++ b/tools/python/util/android/android.py @@ -111,7 +111,7 @@ def start_emulator( extra_args: typing.Optional[typing.Sequence[str]] = None, timeout_minutes: int = 20, ) -> subprocess.Popen: - if is_emulator_running_by_avd(avd_name=avd_name): + if check_emulator_running_using_avd_name(avd_name=avd_name): raise RuntimeError( f"An emulator with avd_name{avd_name} is already running. Please close it before starting a new one." ) @@ -126,7 +126,6 @@ def start_emulator( "America/Los_Angeles", "-no-snapstorage", "-no-audio", - "-no-snapshot", # No bluetooth "-no-boot-anim", "-gpu", "guest", @@ -216,12 +215,12 @@ def start_emulator( time.sleep(sleep_interval_seconds) # Verify if the emulator is now running - if not is_emulator_running_by_avd(avd_name=avd_name): + if not check_emulator_running_using_avd_name(avd_name=avd_name): raise RuntimeError("Emulator failed to start.") return emulator_process -def is_emulator_running_by_avd(avd_name: str) -> bool: +def check_emulator_running_using_avd_name(avd_name: str) -> bool: """ Check if an emulator is running based on the provided AVD name. :param avd_name: Name of the Android Virtual Device (AVD) to check. @@ -259,12 +258,12 @@ def is_emulator_running_by_avd(avd_name: str) -> bool: return False -def is_emulator_running_by_proc(emulator_proc: subprocess.Popen) -> bool: +def check_emulator_running_using_process(emulator_proc: subprocess.Popen) -> bool: """Check if the emulator process is running based on a Popen instance.""" return emulator_proc.poll() is None -def is_emulator_running_by_pid(emulator_pid: int) -> bool: +def check_emulator_running_using_pid(emulator_pid: int) -> bool: """Check if the emulator process is running based on PID.""" try: os.kill(emulator_pid, 0) # Signal 0 checks process existence @@ -273,13 +272,13 @@ def is_emulator_running_by_pid(emulator_pid: int) -> bool: return False -def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout: int = 120): +def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout_seconds: int = 120): """ Stops the emulator process using a subprocess.Popen instance. :param emulator_proc: The emulator process as a subprocess.Popen instance. - :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + :param timeout_seconds: Maximum time (in seconds) to wait for the emulator to stop. """ - if not is_emulator_running_by_proc(emulator_proc): + if not check_emulator_running_using_process(emulator_proc): _log.warning("The specified emulator process is not running.") return @@ -288,24 +287,24 @@ def stop_emulator_by_proc(emulator_proc: subprocess.Popen, timeout: int = 120): # Wait for the process to stop interval = 5 - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) + end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout_seconds) - while is_emulator_running_by_proc(emulator_proc): + while check_emulator_running_using_process(emulator_proc): if datetime.datetime.now() > end_time: - raise RuntimeError(f"Failed to stop the emulator within the specified timeout = {timeout} seconds.") + raise RuntimeError(f"Failed to stop the emulator within the specified timeout = {timeout_seconds} seconds.") _log.debug("Emulator still running. Checking again in 5 seconds...") time.sleep(interval) _log.info("Emulator stopped successfully.") -def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): +def stop_emulator_by_pid(emulator_pid: int, timeout_seconds: int = 120): """ Stops the emulator process using a PID. :param emulator_pid: The emulator process PID. - :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + :param timeout_seconds: Maximum time (in seconds) to wait for the emulator to stop. """ - if not is_emulator_running_by_pid(emulator_pid): + if not check_emulator_running_using_pid(emulator_pid): _log.warning(f"No emulator process with PID {emulator_pid} is currently running.") return @@ -314,12 +313,12 @@ def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): # Wait for the process to stop interval = 5 - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) + end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout_seconds) - while is_emulator_running_by_pid(emulator_pid): + while check_emulator_running_using_pid(emulator_pid): if datetime.datetime.now() > end_time: raise RuntimeError( - f"Failed to stop the emulator with PID {emulator_pid} within the specified timeout = {timeout} seconds." + f"Failed to stop the emulator with PID {emulator_pid} within the specified timeout = {timeout_seconds} seconds." ) _log.debug("Emulator still running. Checking again in 5 seconds...") time.sleep(interval) @@ -327,15 +326,15 @@ def stop_emulator_by_pid(emulator_pid: int, timeout: int = 120): _log.info("Emulator stopped successfully.") -def stop_emulator(emulator_proc_or_pid: typing.Union[subprocess.Popen, int], timeout: int = 120): +def stop_emulator(emulator_proc_or_pid: typing.Union[subprocess.Popen, int], timeout_seconds: int = 120): """ Stops the emulator process, checking its running status before and after stopping. :param emulator_proc_or_pid: The emulator process (subprocess.Popen) or PID (int). - :param timeout: Maximum time (in seconds) to wait for the emulator to stop. + :param timeout_seconds: Maximum time (in seconds) to wait for the emulator to stop. """ if isinstance(emulator_proc_or_pid, subprocess.Popen): - stop_emulator_by_proc(emulator_proc_or_pid, timeout) + stop_emulator_by_proc(emulator_proc_or_pid, timeout_seconds) elif isinstance(emulator_proc_or_pid, int): - stop_emulator_by_pid(emulator_proc_or_pid, timeout) + stop_emulator_by_pid(emulator_proc_or_pid, timeout_seconds) else: raise ValueError("Expected either a PID or subprocess.Popen instance.")