Agent Skill · Appium

environment-setup-android

Prepare and validate Android SDK, Java, and device tooling for Appium Android drivers

Provider: Appium Path in repo: skills/environment-setup-android/SKILL.md

Skill body

environment-setup-android

Goal

Prepares a working Android automation environment for Appium by validating Java, Android SDK command-line tools, required SDK packages, environment variables, and ADB device visibility, with a verify-and-fix loop until all mandatory checks pass.

Decision Logic

Instructions

  1. Detect OS and validate Java/base tooling macOS/Linux:
    uname -s
    java -version
    javac -version
    echo "$JAVA_HOME"
    command -v adb
    ls "$ANDROID_HOME/emulator/emulator"
    test -x "$ANDROID_HOME/emulator/emulator" && echo "emulator binary: OK"
    

    Windows PowerShell:

    [System.Environment]::OSVersion.VersionString
    java -version
    javac -version
    $env:JAVA_HOME
    Get-Command adb.exe -ErrorAction SilentlyContinue
    Test-Path "$env:ANDROID_HOME\emulator\emulator.exe"
    
  2. Install Android SDK tooling when ANDROID_HOME path is missing Trigger checks:
    • macOS/Linux:
      [ -n "$ANDROID_HOME" ] && [ -d "$ANDROID_HOME" ] || echo "run step 2"
      
    • Windows PowerShell:
      if (-not $env:ANDROID_HOME -or -not (Test-Path $env:ANDROID_HOME)) { "run step 2" }
      

      Option A (Android Studio app path; macOS priority when app exists):

    • Download Android Studio from https://developer.android.com/studio.
    • On macOS, check for the app in both /Applications/Android Studio.app and $HOME/Applications/Android Studio.app.
    • You may either complete first launch and install SDK components from SDK Manager, or use the bundled JBR plus the official command-line tools zip to bootstrap the SDK non-interactively with sdkmanager.
    • Use platform default SDK path after setup:
      • macOS: $HOME/Library/Android/sdk
      • Linux: $HOME/Android/Sdk
      • Windows: %LOCALAPPDATA%\Android\Sdk

    Option B (CLI tools only):

    • macOS official command-line tools example:
      curl -L -o /tmp/commandlinetools-mac-latest.zip https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip
      unzip -q /tmp/commandlinetools-mac-latest.zip -d /tmp/android-cmdline-tools
      export JAVA_HOME="$HOME/Applications/Android Studio.app/Contents/jbr/Contents/Home"
      if [ ! -d "$JAVA_HOME" ]; then
      export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
      fi
      export PATH="$JAVA_HOME/bin:$PATH"
      /tmp/android-cmdline-tools/cmdline-tools/bin/sdkmanager --sdk_root="$HOME/Library/Android/sdk" "cmdline-tools;latest"
      
    • macOS/Homebrew fallback example:
      [ -d "/Applications/Android Studio.app" ] || brew install --cask android-commandlinetools
      mkdir -p "$HOME/Library/Android/sdk/cmdline-tools/latest"
      cp -R /opt/homebrew/share/android-commandlinetools/* "$HOME/Library/Android/sdk/cmdline-tools/latest/"
      
    • Linux example (Debian/Ubuntu-style prerequisites + cmdline tools placement):
      sudo apt-get update
      sudo apt-get install -y unzip wget openjdk-21-jdk
      mkdir -p "$HOME/Android/Sdk/cmdline-tools/latest"
      
    • Windows example (PowerShell, after extracting Android command-line tools zip):
      New-Item -ItemType Directory -Force "$env:LOCALAPPDATA\Android\Sdk\cmdline-tools\latest"
      
  3. Configure Java for fresh environments (skip if Java already works) Trigger checks:
    • macOS/Linux:
      if command -v java >/dev/null 2>&1 && command -v javac >/dev/null 2>&1; then
      java -version >/dev/null 2>&1 && javac -version >/dev/null 2>&1 && echo "Java already available; skip step 3" || echo "run step 3"
      else
      echo "run step 3"
      fi
      
    • Windows PowerShell:
      if ((Get-Command java.exe -ErrorAction SilentlyContinue) -and (Get-Command javac.exe -ErrorAction SilentlyContinue)) {
      java -version *> $null
      if ($LASTEXITCODE -eq 0) {
        javac -version *> $null
        if ($LASTEXITCODE -eq 0) { "Java already available; skip step 3" } else { "run step 3" }
      } else { "run step 3" }
      } else { "run step 3" }
      

      macOS primary method for fresh setup (Android Studio bundled JBR):

      if [ -d "$HOME/Applications/Android Studio.app/Contents/jbr/Contents/Home" ]; then
      export JAVA_HOME="$HOME/Applications/Android Studio.app/Contents/jbr/Contents/Home"
      export PATH="$JAVA_HOME/bin:$PATH"
      java -version
      javac -version
      elif [ -d "/Applications/Android Studio.app/Contents/jbr/Contents/Home" ]; then
      export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
      export PATH="$JAVA_HOME/bin:$PATH"
      java -version
      javac -version
      else
      echo "Android Studio JBR not found; use fallback method below"
      fi
      

      macOS fallback method (only when Android Studio is not installed):

      brew install --cask temurin
      export JAVA_HOME="$(/usr/libexec/java_home -v 21)"
      export PATH="$JAVA_HOME/bin:$PATH"
      java -version
      javac -version
      

      Linux primary method for fresh setup (Android Studio bundled JBR):

      if [ -d "$HOME/android-studio/jbr" ]; then
      export JAVA_HOME="$HOME/android-studio/jbr"
      elif [ -d "/opt/android-studio/jbr" ]; then
      export JAVA_HOME="/opt/android-studio/jbr"
      elif [ -d "/usr/local/android-studio/jbr" ]; then
      export JAVA_HOME="/usr/local/android-studio/jbr"
      else
      echo "Android Studio JBR not found; use fallback method below"
      fi
      if [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ]; then
      export PATH="$JAVA_HOME/bin:$PATH"
      java -version
      javac -version
      fi
      

      Linux fallback method (OpenJDK 21 example):

      export JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"
      export PATH="$JAVA_HOME/bin:$PATH"
      java -version
      javac -version
      

      Windows primary method for fresh setup (Android Studio bundled JBR, persist for current user):

      $studioJbrCandidates = @(
      "$env:LOCALAPPDATA\Programs\Android Studio\jbr",
      "C:\Program Files\Android\Android Studio\jbr"
      )
      $studioJbr = $studioJbrCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
      if ($studioJbr) {
      [Environment]::SetEnvironmentVariable('JAVA_HOME', $studioJbr, 'User')
      $currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
      if ($currentPath -notlike "*$studioJbr\bin*") {
        [Environment]::SetEnvironmentVariable('Path', "$currentPath;$studioJbr\bin", 'User')
      }
      $env:JAVA_HOME = [Environment]::GetEnvironmentVariable('JAVA_HOME', 'User')
      $env:PATH = "$studioJbr\bin;$env:PATH"
      java -version
      javac -version
      } else {
      "Android Studio JBR not found; use fallback method below"
      }
      

      Windows fallback method (only when Android Studio is not installed):

      winget install -e --id Microsoft.OpenJDK.17 --accept-source-agreements --accept-package-agreements
      $jdkRoot = Get-ChildItem "C:\Program Files\Microsoft" -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -like 'jdk-*' } | Sort-Object Name -Descending | Select-Object -First 1
      if ($jdkRoot) {
      [Environment]::SetEnvironmentVariable('JAVA_HOME', $jdkRoot.FullName, 'User')
      $currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
      if ($currentPath -notlike "*$($jdkRoot.FullName)\\bin*") {
        [Environment]::SetEnvironmentVariable('Path', "$currentPath;$($jdkRoot.FullName)\\bin", 'User')
      }
      $env:JAVA_HOME = [Environment]::GetEnvironmentVariable('JAVA_HOME', 'User')
      $env:PATH = "$env:JAVA_HOME\\bin;$env:PATH"
      }
      java -version
      javac -version
      
  4. Configure Android environment variables and PATH macOS (priority: Android Studio SDK path):
    export ANDROID_HOME="$HOME/Library/Android/sdk"
    export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/emulator:$PATH"
    echo "$ANDROID_HOME"
    command -v adb
    ls "$ANDROID_HOME/emulator/emulator"
    

    Linux:

    export ANDROID_HOME="$HOME/Android/Sdk"
    export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
    echo "$ANDROID_HOME"
    command -v adb
    ls "$ANDROID_HOME/emulator/emulator"
    

    Windows PowerShell (persist for current user):

    [Environment]::SetEnvironmentVariable('ANDROID_HOME', "$env:LOCALAPPDATA\Android\Sdk", 'User')
    $androidPaths = "$env:LOCALAPPDATA\Android\Sdk\platform-tools;$env:LOCALAPPDATA\Android\Sdk\cmdline-tools\latest\bin"
    $currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
    if ($currentPath -notlike "*$androidPaths*") {
      [Environment]::SetEnvironmentVariable('Path', "$currentPath;$androidPaths", 'User')
    }
    $env:ANDROID_HOME = [Environment]::GetEnvironmentVariable('ANDROID_HOME', 'User')
    
  5. Accept SDK licenses and install required packages macOS/Linux:
    if command -v sdkmanager >/dev/null 2>&1; then yes | sdkmanager --sdk_root="$ANDROID_HOME" --licenses; fi
    if command -v sdkmanager >/dev/null 2>&1; then sdkmanager --sdk_root="$ANDROID_HOME" "platform-tools" "build-tools;34.0.0" "platforms;android-34" "emulator"; fi
    

    Windows PowerShell:

    if (Get-Command sdkmanager.bat -ErrorAction SilentlyContinue) { cmd /c "(for /l %i in (1,1,200) do @echo y)| sdkmanager.bat --licenses" }
    if (Get-Command sdkmanager.bat -ErrorAction SilentlyContinue) { sdkmanager.bat "platform-tools" "build-tools;34.0.0" "platforms;android-34" "emulator" }
    

    If license acceptance still fails in headless CI-like runs, pre-seed license hashes and retry package install:

     $licensesDir = "$env:ANDROID_HOME\licenses"
     New-Item -ItemType Directory -Force $licensesDir | Out-Null
     Set-Content -Path "$licensesDir\android-sdk-license" -Value "24333f8a63b6825ea9c5514f83c2829b004d1fee`n8933bad161af4178b1185d1a37fbf41ea5269c55`nd56f5187479451eabf01fb78af6dfcb131a6481e"
    
  6. Verify Android SDK and ADB state macOS/Linux:
    if command -v sdkmanager >/dev/null 2>&1; then sdkmanager --list | head -n 80; fi
    adb version
    command -v emulator
    ls "$ANDROID_HOME/emulator/emulator"
    test -x "$ANDROID_HOME/emulator/emulator" && echo "emulator binary: OK"
    

    Windows PowerShell:

    if (Get-Command sdkmanager.bat -ErrorAction SilentlyContinue) { sdkmanager.bat --list }
    adb.exe version
    Test-Path "$env:ANDROID_HOME\emulator\emulator.exe"
    
  7. Optional emulator instance preparation (if no physical device is connected and no emulator exists) Skip step 7 when either of the following is true:
    • At least one device is already connected (adb devices shows a device entry)
    • At least one emulator instance already exists (emulator -list-avds is non-empty)

    Prepare an emulator instance using the latest stable system-image version and host-optimized architecture only when both are false. macOS/Linux (prefer native architecture first, then fallback):

    ARCH=$(uname -m)
    if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
      PRIMARY_ARCH="arm64-v8a"
      FALLBACK_ARCH="x86_64"
    else
      PRIMARY_ARCH="x86_64"
      FALLBACK_ARCH="arm64-v8a"
    fi
    LATEST_API=$(sdkmanager --list | grep -o "system-images;android-[0-9]\+;google_apis;${PRIMARY_ARCH}" | sed 's/.*android-\([0-9]\+\).*/\1/' | sort -n | tail -1)
    IMAGE_ARCH="$PRIMARY_ARCH"
    if [ -z "$LATEST_API" ]; then
      LATEST_API=$(sdkmanager --list | grep -o "system-images;android-[0-9]\+;google_apis;${FALLBACK_ARCH}" | sed 's/.*android-\([0-9]\+\).*/\1/' | sort -n | tail -1)
      IMAGE_ARCH="$FALLBACK_ARCH"
    fi
    IMAGE="system-images;android-${LATEST_API};google_apis;${IMAGE_ARCH}"
    sdkmanager "$IMAGE"
    echo "no" | avdmanager create avd -n "api${LATEST_API}-google-${IMAGE_ARCH}" -k "$IMAGE"
    emulator -list-avds
    

    Windows PowerShell (prefer x86_64, fallback arm64-v8a):

    $primaryArch = "x86_64"
    $fallbackArch = "arm64-v8a"
    $matches = sdkmanager.bat --list | Select-String "system-images;android-[0-9]+;google_apis;$primaryArch"
    $imageArch = $primaryArch
    if (-not $matches) {
      $matches = sdkmanager.bat --list | Select-String "system-images;android-[0-9]+;google_apis;$fallbackArch"
      $imageArch = $fallbackArch
    }
    $latestApi = ($matches | ForEach-Object { [int]([regex]::Match($_.Line, 'android-(\d+)').Groups[1].Value) } | Sort-Object)[-1]
    $image = "system-images;android-$latestApi;google_apis;$imageArch"
    sdkmanager.bat $image
    cmd /c "echo no| avdmanager.bat create avd -n api$latestApi-google-$imageArch -k $image"
    emulator.exe -list-avds
    

    Report version details in the task result:

    • macOS/Linux:
      emulator -version
      emulator -list-avds
      if command -v sdkmanager >/dev/null 2>&1; then sdkmanager --list | grep "system-images;android-" | head -n 20; fi
      
    • Windows PowerShell:
      emulator.exe -version
      emulator.exe -list-avds
      if (Get-Command sdkmanager.bat -ErrorAction SilentlyContinue) { sdkmanager.bat --list | Select-String "system-images;android-" | Select-Object -First 20 }
      
  8. Completion criteria Mark complete only when all are true:
    • java -version and javac -version succeed
    • adb is executable from PATH
    • Emulator binary exists under ANDROID_HOME/emulator/emulator (or %ANDROID_HOME%\emulator\emulator.exe on Windows)
    • Required SDK packages are installed (platform-tools, one platform, one build-tools version)
    • Existing Java setup is preserved when Java already works (no forced reconfiguration)
    • On fresh setup, Android Studio bundled JBR is used as JAVA_HOME when Android Studio is present (macOS/Linux/Windows)
    • Android environment checks pass without requiring a connected device
    • Latest stable emulator/system-image version is prepared with host-optimized architecture only when no connected devices and no existing emulators are present; otherwise step 7 is skipped and current version details are reported in the task result

Constraints

Skill frontmatter

metadata: {"last_modified"=>"Mon, 27 Apr 2026 22:00:00 GMT"}