The ovmobilebench.android.installer module provides a comprehensive Python API for automating the installation and configuration of Android SDK, NDK, and related tools with a modular, type-safe, and well-tested implementation.
- Cross-platform support: Windows, macOS, Linux (x86_64, arm64)
- Automated SDK/NDK installation: Downloads and configures Android development tools
- AVD management: Create and manage Android Virtual Devices
- Environment configuration: Export environment variables for various shells
- Type safety: Full type hints and runtime validation
- Structured logging: Human-readable and JSON Lines output
- Idempotent operations: Safe to run multiple times
- CI/CD integration: Optimized for automated environments
The module is part of the OVMobileBench package:
pip install -e .from ovmobilebench.android.installer import ensure_android_tools
# Install Android SDK and NDK
result = ensure_android_tools(
sdk_root="/path/to/android-sdk",
api=30,
target="google_atd",
arch="arm64-v8a",
ndk="r26d"
)
print(f"SDK installed at: {result['sdk_root']}")
print(f"NDK installed at: {result['ndk_path']}")# Install Android tools
ovmobilebench-android-installer setup \
--sdk-root /path/to/sdk \
--api 30 \
--target google_atd \
--arch arm64-v8a \
--ndk r26d
# Verify installation
ovmobilebench-android-installer verify --sdk-root /path/to/sdk
# List available targets
ovmobilebench-android-installer list-targetsovmobilebench/android/installer/
├── __init__.py # Package exports
├── api.py # Public API functions
├── cli.py # Command-line interface
├── core.py # Main orchestration logic
├── types.py # Data models and types
├── errors.py # Custom exceptions
├── logging.py # Structured logging
├── detect.py # Platform detection
├── env.py # Environment variables
├── plan.py # Installation planning
├── sdkmanager.py # SDK management
├── ndk.py # NDK resolution
└── avd.py # AVD management
Defines data models for the installer:
from ovmobilebench.android.installer.types import (
NdkSpec, # NDK specification (alias or path)
AndroidVersion, # Android API version info
SystemImageSpec, # System image specification
InstallerPlan, # Installation plan
InstallerResult, # Installation result
HostInfo, # Host system information
)
# Example: Specify NDK by alias
ndk = NdkSpec(alias="r26d")
# Or by path
ndk = NdkSpec(path="/opt/android-ndk-r26d")Main orchestration class:
from ovmobilebench.android.installer.core import AndroidInstaller
installer = AndroidInstaller(sdk_root="/path/to/sdk")
# Perform installation
result = installer.ensure(
api=30,
target="google_atd",
arch="arm64-v8a",
ndk=NdkSpec(alias="r26d"),
create_avd_name="my_avd",
install_build_tools="34.0.0",
accept_licenses=True,
dry_run=False
)
# Verify installation
status = installer.verify()
# Clean up temporary files
installer.cleanup(remove_downloads=True, remove_temp=True)Wraps Android SDK Manager:
from ovmobilebench.android.installer.sdkmanager import SdkManager
sdk = SdkManager(sdk_root="/path/to/sdk")
# Install components
sdk.ensure_cmdline_tools()
sdk.ensure_platform_tools()
sdk.ensure_platform(api=30)
sdk.ensure_system_image(api=30, target="google_atd", arch="arm64-v8a")
sdk.ensure_emulator()
sdk.ensure_build_tools("34.0.0")
# Accept licenses
sdk.accept_licenses()
# List installed components
components = sdk.list_installed()Manages NDK installations:
from ovmobilebench.android.installer.ndk import NdkResolver
ndk = NdkResolver(sdk_root="/path/to/sdk")
# Install NDK
ndk_path = ndk.ensure(NdkSpec(alias="r26d"))
# List installed NDKs
installed = ndk.list_installed()
for version, path in installed:
print(f"NDK {version}: {path}")Creates and manages Android Virtual Devices:
from ovmobilebench.android.installer.avd import AvdManager
avd = AvdManager(sdk_root="/path/to/sdk")
# Create AVD
avd.create(
name="test_avd",
api=30,
target="google_atd",
arch="arm64-v8a",
device="pixel_5"
)
# List AVDs
avds = avd.list_avds()
# Get AVD info
info = avd.get_info("test_avd")
# Delete AVD
avd.delete("test_avd")Manages environment variables:
from ovmobilebench.android.installer.env import EnvExporter
env = EnvExporter(sdk_root="/path/to/sdk")
# Export to dictionary
env_dict = env.export_dict(ndk_path="/path/to/ndk")
# Export to shell script
env.export_to_stdout(ndk_path="/path/to/ndk", format="bash")
# Export to GitHub Actions
env.export_to_github_env(ndk_path="/path/to/ndk")
# Set in current process
env.set_in_process(ndk_path="/path/to/ndk")Detects host system capabilities:
from ovmobilebench.android.installer.detect import (
detect_host,
detect_java_version,
check_disk_space,
is_ci_environment,
get_recommended_settings
)
# Detect host system
host = detect_host()
print(f"OS: {host.os}, Arch: {host.arch}, KVM: {host.has_kvm}")
# Check Java
java_version = detect_java_version()
# Check disk space
has_space = check_disk_space("/path/to/sdk", required_gb=15)
# Get recommendations
settings = get_recommended_settings()Plans and validates installations:
from ovmobilebench.android.installer.plan import Planner
planner = Planner(sdk_root="/path/to/sdk")
# Build installation plan
plan = planner.build_plan(
api=30,
target="google_atd",
arch="arm64-v8a",
ndk=NdkSpec(alias="r26d"),
create_avd_name="test_avd"
)
# Validate plan
is_valid = planner.is_valid_combination(api=30, target="google_atd", arch="arm64-v8a")
# Estimate size
size_gb = planner.estimate_size(plan)Provides structured logging with timing:
from ovmobilebench.android.installer.logging import StructuredLogger
logger = StructuredLogger(
name="installer",
verbose=True,
jsonl_path="/tmp/install.jsonl"
)
# Log with context
logger.info("Starting installation", api=30, arch="arm64-v8a")
# Track steps with timing
with logger.step("install_ndk", version="r26d"):
# Installation code here
pass # Step duration is automatically tracked
logger.success("Installation complete", duration=45.2)Main function for installing Android tools.
def ensure_android_tools(
sdk_root: str | Path,
api: int,
target: str = "google_atd",
arch: str = "arm64-v8a",
ndk: str | Path | NdkSpec | None = None,
create_avd_name: str | None = None,
install_platform_tools: bool = True,
install_emulator: bool = True,
install_build_tools: str | None = None,
accept_licenses: bool = True,
dry_run: bool = False,
verbose: bool = False,
jsonl_path: Path | None = None
) -> InstallerResultParameters:
sdk_root: Android SDK installation directoryapi: Android API level (e.g., 30, 31, 33)target: System image target ("google_atd", "google_apis", "default")arch: Architecture ("arm64-v8a", "x86_64", "x86", "armeabi-v7a")ndk: NDK specification (alias like "r26d" or absolute path)create_avd_name: Name for AVD creation (None to skip)install_platform_tools: Install ADB and platform toolsinstall_emulator: Install Android Emulatorinstall_build_tools: Build tools version (e.g., "34.0.0")accept_licenses: Automatically accept SDK licensesdry_run: Preview without making changesverbose: Enable detailed loggingjsonl_path: Path for JSON Lines log output
Returns:
InstallerResult dictionary with:
sdk_root: SDK installation pathndk_path: NDK installation path (if installed)avd_created: Whether AVD was createdperformed: Dictionary of performed actions
Export Android environment variables.
def export_android_env(
sdk_root: str | Path,
ndk_path: str | Path | None = None,
format: str = "dict"
) -> dict[str, str] | strParameters:
sdk_root: Android SDK root directoryndk_path: NDK installation pathformat: Output format ("dict", "bash", "fish", "windows", "github")
Returns: Environment variables as dictionary or formatted string
Verify Android tools installation.
def verify_installation(
sdk_root: str | Path,
verbose: bool = True
) -> dict[str, Any]Parameters:
sdk_root: Android SDK root directoryverbose: Print verification results
Returns: Dictionary with installation status
import os
from ovmobilebench.android.installer import ensure_android_tools
# Minimal installation for CI
result = ensure_android_tools(
sdk_root=os.environ.get("ANDROID_HOME", "/opt/android-sdk"),
api=30,
target="google_atd",
arch="arm64-v8a",
ndk="r26d",
create_avd_name=None, # No AVD in CI
install_emulator=False, # No emulator needed
dry_run=False
)
# Export environment for build
from ovmobilebench.android.installer import export_android_env
env_vars = export_android_env(
sdk_root=result["sdk_root"],
ndk_path=result["ndk_path"],
format="github" # For GitHub Actions
)from pathlib import Path
from ovmobilebench.android.installer import (
ensure_android_tools,
verify_installation
)
# Full installation for development
home = Path.home()
sdk_root = home / "Android" / "sdk"
result = ensure_android_tools(
sdk_root=sdk_root,
api=33,
target="google_apis",
arch="arm64-v8a",
ndk="r26d",
create_avd_name="dev_phone",
install_build_tools="34.0.0",
verbose=True,
jsonl_path=home / "android_install.jsonl"
)
# Verify everything is installed
status = verify_installation(sdk_root, verbose=True)from ovmobilebench.android.installer import ensure_android_tools
# Install only NDK for cross-compilation
result = ensure_android_tools(
sdk_root="/opt/android-sdk",
api=30, # Required for planning
target="default",
arch="arm64-v8a",
ndk="r26d",
install_platform_tools=False,
install_emulator=False,
create_avd_name=None
)
print(f"NDK installed at: {result['ndk_path']}")from ovmobilebench.android.installer import ensure_android_tools
# Preview what would be installed
result = ensure_android_tools(
sdk_root="/opt/android-sdk",
api=30,
target="google_atd",
arch="x86_64",
ndk="r26d",
create_avd_name="test_avd",
dry_run=True,
verbose=True
)
print("Would install:", result["performed"])- Linux: x86_64, arm64 (Ubuntu 20.04+, RHEL 8+)
- macOS: x86_64, arm64 (macOS 11+)
- Windows: x86_64 (Windows 10+)
- API 21-34 (Android 5.0 - 14)
default: Basic Android system imagegoogle_apis: Includes Google Play Servicesgoogle_atd: Automated Test Device (faster, for testing)google_apis_playstore: Includes Play Store
arm64-v8a: 64-bit ARM (recommended for M1/M2 Macs)armeabi-v7a: 32-bit ARMx86_64: 64-bit x86 (recommended for Intel with KVM)x86: 32-bit x86
- Aliases: r21e, r22b, r23c, r24, r25c, r26d
- Direct versions: 21.4.7075529, 22.1.7171670, etc.
The module sets the following environment variables:
ANDROID_HOME: Android SDK root directoryANDROID_SDK_ROOT: Same as ANDROID_HOMEANDROID_NDK_HOME: NDK installation directoryANDROID_NDK_ROOT: Same as ANDROID_NDK_HOMEPATH: Updated with platform-tools, emulator, and NDK paths
The module provides a hierarchy of custom exceptions:
from ovmobilebench.android.installer.errors import (
InstallerError, # Base exception
InvalidArgumentError, # Invalid parameters
ComponentNotFoundError, # Missing component
DownloadError, # Download failure
UnpackError, # Extraction failure
SdkManagerError, # SDK Manager error
AvdManagerError, # AVD Manager error
PermissionError, # Permission denied
)
try:
result = ensure_android_tools(...)
except InvalidArgumentError as e:
print(f"Invalid configuration: {e}")
except DownloadError as e:
print(f"Download failed: {e}")
except InstallerError as e:
print(f"Installation failed: {e}")The module supports multiple logging modes:
# Verbose console output
ensure_android_tools(..., verbose=True)# Structured logging to file
ensure_android_tools(..., jsonl_path="/tmp/install.jsonl")
# Parse logs
import json
with open("/tmp/install.jsonl") as f:
for line in f:
log = json.loads(line)
print(f"{log['timestamp']}: {log['message']}")from ovmobilebench.android.installer.logging import StructuredLogger
from ovmobilebench.android.installer import set_logger
# Use custom logger
logger = StructuredLogger("custom", verbose=True)
set_logger(logger)
ensure_android_tools(...)Always test with dry_run=True before actual installation:
# Test configuration
result = ensure_android_tools(..., dry_run=True)
if result["performed"]:
# Proceed with actual installation
result = ensure_android_tools(..., dry_run=False)Always verify the installation succeeded:
result = ensure_android_tools(...)
status = verify_installation(result["sdk_root"])
assert status["cmdline_tools"], "Missing cmdline-tools"
assert status["ndk"], "Missing NDK"Wrap installations in try-except blocks:
try:
result = ensure_android_tools(...)
except PermissionError:
print("Run with elevated permissions")
except DownloadError:
print("Check network connection")- Use
google_atdfor CI/testing (faster) - Use
google_apisfor development - Use
google_apis_playstorefor Play Store testing
Remove downloads after installation:
from ovmobilebench.android.installer.core import AndroidInstaller
installer = AndroidInstaller(sdk_root)
result = installer.ensure(...)
installer.cleanup(remove_downloads=True)DownloadError: certificate verify failed
Solution: Update certificates or use corporate proxy settings
PermissionError: Permission denied: /opt/android-sdk
Solution: Ensure write permissions or use user directory
Warning: Low disk space detected (< 15GB free)
Solution: Free up space or use different location
Warning: Java not detected
Solution: Install JDK 11+ and ensure it's in PATH
Info: KVM not available, using software acceleration
Solution: Enable virtualization in BIOS or use ARM images on ARM hosts
Enable detailed logging for troubleshooting:
import logging
logging.basicConfig(level=logging.DEBUG)
result = ensure_android_tools(
...,
verbose=True,
jsonl_path="/tmp/debug.jsonl"
)The module integrates with OVMobileBench pipeline:
# experiments/android_example.yaml
build:
android_ndk: r26d
android_api: 30
device:
type: android
platform: arm64-v8aThe pipeline automatically uses this module to ensure NDK is installed before building.
See CONTRIBUTING.md for development guidelines.
Apache License 2.0. See LICENSE for details.