Use a stronger verification for the existence of an Android emulator system image
Describe the bug
As shown in https://github.com/beeware/briefcase/issues/1893, the current check in Briefcase for the existence of a system image is too weak. It only confirms an assumed filesystem path exists to verify a system image has been previously downloaded and is available.
In at least some failure modes, this filesystem path can exist while the system image is not wholly available. This failure mode will be quite difficult for users without Android SDK knowledge to recover from since Briefcase will attempt to use the system image with avdmanager to create an AVD...which will fail:
>>> Running Command: subprocess.py:827
>>> 'C:\Users\user\AppData\Local\BeeWare\briefcase\Cache\tools\android_sdk\cmdline-tools\12.0\bin\avdmanager.bat' --verbose create avd --name subprocess.py:827
beePhone --abi x86_64 --package 'system-images;android-31;default;x86_64' --device pixel
>>> Working Directory: subprocess.py:827
>>> d:\temp\beeware\myapp subprocess.py:827
>>> Environment Overrides: subprocess.py:827
>>> ANDROID_HOME=C:\Users\user\AppData\Local\BeeWare\briefcase\Cache\tools\android_sdk subprocess.py:827
>>> ANDROID_SDK_ROOT=C:\Users\user\AppData\Local\BeeWare\briefcase\Cache\tools\android_sdk subprocess.py:827
>>> JAVA_HOME=C:\Users\user\AppData\Local\BeeWare\briefcase\Cache\tools\java17 subprocess.py:827
>>> XDG_CONFIG_HOME=None subprocess.py:827
[22:11:24] >>> Command Output: subprocess.py:827
>>> Loading local repository... subprocess.py:827
>>> [========= ] 25% Loading local repository... subprocess.py:827
>>> [========= ] 25% Fetch remote repository... subprocess.py:827
>>> [=======================================] 100% Fetch remote repository... subprocess.py:827
>>> subprocess.py:827
>>> Error: Package path is not valid. Valid system image paths are: subprocess.py:827
>>> null subprocess.py:827
>>> Return code: 1 subprocess.py:827
Creating Android emulator beePhone... errored android_sdk.py:1188
Steps to reproduce
The error can be manufactured by simply deleting the contents of the system image directory within the Android SDK and then attempting to use Briefcase to create a new AVD to run with the emulator.
Expected behavior
Briefcase's check for a system image should align with avdmanager's conclusion about the availability of a system image. My guess is avdmanager is using a check similar to below to ensure a specific system image is available.
> ~/.cache/briefcase/tools/android_sdk/cmdline-tools/12.0/bin/sdkmanager --list_installed
[=======================================] 100% Fetch remote repository...
Installed packages:
Path | Version | Description | Location
------- | ------- | ------- | -------
emulator | 34.2.15 | Android Emulator | emulator
platform-tools | 35.0.1 | Android SDK Platform-Tools | platform-tools
system-images;android-31;default;x86_64 | 5 | Intel x86_64 Atom System Image | system-images/android-31/default/x86_64
Ideally, though, perhaps, we'd have a more machine-parseable version of this output...
Screenshots
No response
Environment
- Operating System: Win 11
- Python version: 3.11
- Software versions:
- Briefcase: 0.3.19
Logs
https://github.com/user-attachments/files/16073483/briefcase.2024_07_02-22_11_24.run.log
Additional context
No response
Agreed; more broadly, we should probably have checksum validation for any download where we're in a position to provide it (especially the stub binaries and support packages)
especially the stub binaries and support packages
This would definitely be a good first start; as we are leveraging GitHub for attesting PyPI package artifacts, we could probably do the same here to verify authenticity and integrity of Briefcase's artifacts. And Standalone Python may be amenable to a PR to publish attestations as well.
In at least some failure modes, this filesystem path can exist while the system image is not wholly available.
The download is very large and is likely to be interrupted, so this is quite a common failure mode. It happened to at least 2 people at PyCon US this year, and there's no way for them to recover without expert help.
My guess is
avdmanageris using a check similar to below to ensure a specific system image is available.> ~/.cache/briefcase/tools/android_sdk/cmdline-tools/12.0/bin/sdkmanager --list_installed
~~I think we actually used to do this in an older version of Briefcase, but we've since reduced the number of sdkmanager calls because they were relatively slow.~~ Actually no, it looks like we only ever passed the --list_installed output through for debug logging, we never actually parsed it.
Maybe we can replicate whatever sdkmanager does to check for the system image being fully installed, e.g. I think there might be a metadata file which doesn't get written until the installation succeeds.
To clarify the scope here - the goal is to:
- Add a utility method to the Android SDK integration tooling that can invoke
sdkmanager --list_installed, and get back a list of installed APIs (possibly filtering that list of APIs to only include system images) - Integrate a check that the required system image is available before trying to create an AVD with that system image.
There is some overlap with #737 - that ticket also requires parsing the list of installed system images so that the list of available options can be presented to the user. That ticket potentially also involves parsing the same list, but of available images, so that the user can choose a base image that isn't currently installed.