Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/1895.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Android system image verification now uses ``sdkmanager --list_installed`` to confirm the image is fully installed, rather than only checking if the directory exists. If the image is not installed, it will be downloaded automatically.
31 changes: 24 additions & 7 deletions src/briefcase/integrations/android_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,27 @@ def verify_avd(self, avd: str):
except KeyError:
self.tools.console.debug(f"Device {avd!r} doesn't define a skin.")

def list_installed_system_images(self) -> set[str]:
"""Returns a set of installed system image package identifiers.

e.g., ``{"system-images;android-31;default;x86_64"}``
"""
try:
output = self.tools.subprocess.check_output(
[self.sdkmanager_path, "--list_installed"],
env=self.env,
)
except subprocess.CalledProcessError as e:
raise BriefcaseCommandError(
"Unable to invoke the Android SDK manager"
) from e

return {
line.split("|")[0].strip()
for line in output.splitlines()
if line.strip().startswith("system-images")
}

def verify_system_image(self, system_image: str):
"""Verify that the required system image is installed.

Expand Down Expand Up @@ -766,13 +787,9 @@ def verify_system_image(self, system_image: str):
"""
)

# Convert the system image into a path where that system image
# would be expected, and see if the location exists.
system_image_path = self.root_path
for part in system_image_parts:
system_image_path = system_image_path / part

if system_image_path.exists():
# Use sdkmanager to verify the system image is fully installed,
# not just that the directory exists.
if system_image in self.list_installed_system_images():
# Found the system image.
return

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import subprocess

import pytest

from briefcase.exceptions import BriefcaseCommandError


def test_list_installed_system_images(mock_tools, android_sdk):
"""Returns a set of installed system image package identifiers."""

mock_tools.subprocess.check_output.return_value = (
"Installed packages:\n"
" Path | Version | Description | Location\n"
" ------- | ------- | ------- | -------\n"
" system-images;android-31;default;x86_64 | 5 | Intel x86_64 Atom System Image | system-images/android-31/default/x86_64\n"
" emulator | 35.4.9 | Android Emulator | emulator\n"
)

result = android_sdk.list_installed_system_images()

assert result == {"system-images;android-31;default;x86_64"}
mock_tools.subprocess.check_output.assert_called_once_with(
[android_sdk.sdkmanager_path, "--list_installed"],
env=android_sdk.env,
)


def test_no_installed_system_images(mock_tools, android_sdk):
"""If no system images are installed, an empty set is returned."""
mock_tools.subprocess.check_output.return_value = (
"Installed packages:\n"
" Path | Version | Description | Location\n"
" ------- | ------- | ------- | -------\n"
" emulator | 35.4.9 | Android Emulator | emulator\n"
)

result = android_sdk.list_installed_system_images()

assert result == set()


def test_list_installed_system_images_failure(mock_tools, android_sdk):
"""If sdkmanager fails, an error is raised."""
mock_tools.subprocess.check_output.side_effect = subprocess.CalledProcessError(
1, ""
)

with pytest.raises(BriefcaseCommandError):
android_sdk.list_installed_system_images()
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,12 @@ def test_existing_system_image(mock_tools, android_sdk):
# Mock the host arch
mock_tools.host_arch = "AMD64" if platform.system() == "Windows" else "x86_64"

# Mock the existence of a system image
(android_sdk.root_path / "system-images/android-31/default/x86_64").mkdir(
parents=True
# Mock sdkmanager reporting the system image as installed
mock_tools.subprocess.check_output.return_value = (
"Installed packages:\n"
" Path | Version | Description | Location\n"
" ------- | ------- | ------- | -------\n"
" system-images;android-31;default;x86_64 | 5 | Intel x86_64 Atom System Image | system-images/android-31/default/x86_64\n"
)

# Verify the system image that we already have
Expand Down