Repository: beeware/briefcase

Issue:
`avdmanager` is not compatible with AVDs created by other instances of Android SDK or AVDs created by newer instances of Android SDK
### Describe the bug

The changes in https://github.com/beeware/briefcase/pull/1673 to respect `ANDROID_USER_HOME` and `ANDROID_AVD_HOME` modified how Briefcase finds existing AVDs from `emulator -list-avds` to `avdmanager list avd`. While this ostensibly works fine when all the AVDs are created by the Briefcase-managed Android SDK, there are issues when AVDs are created by other Android SDK instances.

`avdmanager` seems to be quite tied to the SDK it is running from; or, in the least, `avdmanager` doesn't consider the AVD valid if it can't find the AVD's system image in the SDK that `avdmanager` is running from.

Here, all the Pixel devices were created by the SDK that Android Studio uses and `avdmanager` is reporting that many of the AVDs have a missing system image. That's because their system image is located the SDK instance that Android Studio is using and not the Briefcase-managed SDK.
```console
❯ ~/.cache/briefcase/tools/android_sdk/cmdline-tools/9.0/bin/avdmanager list avd
Available Android Virtual Devices:
    Name: beePhone
  Device: pixel (Google)
    Path: /home/russell/.android/avd/beePhone.avd
  Target: Default Android System Image
          Based on: Android 12.0 (S) Tag/ABI: default/x86_64
    Skin: pixel_7_pro
  Sdcard: 512 MB

The following Android Virtual Devices could not be loaded:
    Name: Pixel_8_Pro_API_31
    Path: /home/russell/.android/avd/Pixel_8_Pro_API_31.avd
   Error: Google pixel_8_pro no longer exists as a device
---------
    Name: Pixel_3a_API_31
    Path: /home/russell/.android/avd/Pixel_3a_API_31.avd
   Error: Missing system image for Google Play x86_64 Pixel_3a_API_31.
---------
    Name: Pixel_6a_API_31
    Path: /home/russell/.android/avd/Pixel_6a_API_31.avd
   Error: Google pixel_6a no longer exists as a device
---------
    Name: Pixel_5_API_31
    Path: /home/russell/.android/avd/Pixel_5_API_31.avd
   Error: Missing system image for Google APIs x86_64 Pixel_5_API_31.
---------
    Name: Pixel_7a_API_34
    Path: /home/russell/.android/avd/Pixel_7a_API_34.avd
   Error: Google pixel_7a no longer exists as a device
```

Furthermore, though, `avdmanager` seems to have a real forward compatibility problem. This can be seen in running different versions of `avdmanager` within the same SDK.

Here is what 9.0 version of `avdmanager` says when I run it from the same SDK that Android Studio is using:
```console
❯ /opt/sdks/Android/sdk/cmdline-tools/9.0/bin/avdmanager list avd
Available Android Virtual Devices:
    Name: beePhone
  Device: pixel (Google)
    Path: /home/russell/.android/avd/beePhone.avd
  Target: Default Android System Image
          Based on: Android 12.0 (S) Tag/ABI: default/x86_64
    Skin: pixel_7_pro
  Sdcard: 512 MB
---------
    Name: Pixel_3a_API_31
  Device: pixel_3a (Google)
    Path: /home/russell/.android/avd/Pixel_3a_API_31.avd
  Target: Google Play (Google Inc.)
          Based on: Android 12.0 (S) Tag/ABI: google_apis_playstore/x86_64
    Skin: pixel_3a
  Sdcard: 512M
---------
    Name: Pixel_5_API_31
  Device: pixel_5 (Google)
    Path: /home/russell/.android/avd/Pixel_5_API_31.avd
  Target: Google APIs (Google Inc.)
          Based on: Android 12.0 (S) Tag/ABI: google_apis/x86_64
    Skin: pixel_5
  Sdcard: 512M

The following Android Virtual Devices could not be loaded:
    Name: Pixel_8_Pro_API_31
    Path: /home/russell/.android/avd/Pixel_8_Pro_API_31.avd
   Error: Google pixel_8_pro no longer exists as a device
---------
    Name: Pixel_6a_API_31
    Path: /home/russell/.android/avd/Pixel_6a_API_31.avd
   Error: Google pixel_6a no longer exists as a device
---------
    Name: Pixel_7a_API_34
    Path: /home/russell/.android/avd/Pixel_7a_API_34.avd
   Error: Google pixel_7a no longer exists as a device
```
The 9.0 version of `avdmanager` seemingly can't interpret the AVDs for newer Pixel phones because when I run the latest version of `avdmanager` for that SDK, it can interpret them:
```console
❯ /opt/sdks/Android/sdk/cmdline-tools/latest/bin/avdmanager list avd
Available Android Virtual Devices:
    Name: beePhone
  Device: pixel (Google)
    Path: /home/russell/.android/avd/beePhone.avd
  Target: Default Android System Image
          Based on: Android 12.0 ("S") Tag/ABI: default/x86_64
    Skin: pixel_7_pro
  Sdcard: 512 MB
---------
    Name: Pixel_3a_API_31
  Device: pixel_3a (Google)
    Path: /home/russell/.android/avd/Pixel_3a_API_31.avd
  Target: Google Play (Google Inc.)
          Based on: Android 12.0 ("S") Tag/ABI: google_apis_playstore/x86_64
    Skin: pixel_3a
  Sdcard: 512M
---------
    Name: Pixel_5_API_31
  Device: pixel_5 (Google)
    Path: /home/russell/.android/avd/Pixel_5_API_31.avd
  Target: Google APIs (Google Inc.)
          Based on: Android 12.0 ("S") Tag/ABI: google_apis/x86_64
    Skin: pixel_5
  Sdcard: 512M
---------
    Name: Pixel_6a_API_31
  Device: pixel_6a (Google)
    Path: /home/russell/.android/avd/Pixel_6a_API_31.avd
  Target: Google Play (Google Inc.)
          Based on: Android 12.0 ("S") Tag/ABI: google_apis_playstore/x86_64
    Skin: pixel_6a
  Sdcard: 512M
---------
    Name: Pixel_7a_API_34
  Device: pixel_7a (Google)
    Path: /home/russell/.android/avd/Pixel_7a_API_34.avd
  Target: Google Play (Google Inc.)
          Based on: Android 14.0 ("UpsideDownCake") Tag/ABI: google_apis_playstore/x86_64
    Skin: pixel_7a
  Sdcard: 512M
---------
    Name: Pixel_8_Pro_API_31
  Device: pixel_8_pro (Google)
    Path: /home/russell/.android/avd/Pixel_8_Pro_API_31.avd
  Target: Google Play (Google Inc.)
          Based on: Android 12.0 ("S") Tag/ABI: google_apis_playstore/x86_64
    Skin: pixel_8_pro
  Sdcard: 512M
```

### Steps to reproduce

Using Briefcase from `main` branch, run `briefcase run android` after creating AVDs with Android Studio.

### Expected behavior

Briefcase should be able to load AVDs that were created by other instances of Android SDK.

Before #1673, the emulator could identify AVDs even if the SDK that Briefcase was using didn't contain the system image that AVD was using; after selecting the AVD, Briefcase will verify the system image is available in the SDK and download it if it's not found.

So, at least technically, Briefcase could try to download any missing system images before calling `avdmanager`....but this seems unnecessarily premature and wasteful. On top of this, though, even once you download the missing system image in to the SDK that Briefcase is using, version 9.0 of `avdmanager` still doesn't work with devices that are "too new".

**Ultimately, it seems quite necessary to revert #1673 and attempt to implement `ANDROID_USER_HOME` and `ANDROID_AVD_HOME` support using `emulator` somehow....just note all its issues documented in #1673...**

### Screenshots

_No response_

### Environment

- Operating System: Windows 11, pop os 22.04
- Python version: 3.12.2
- Software versions:
  - Briefcase: `0.3.18.dev104+g94a448ba` (doesn't affect released versions)

### Logs

_No response_

### Additional context

_No response_


Discussion:

Patch:
diff --git a/changes/1665.feature.rst b/changes/1665.feature.rst
deleted file mode 100644
index 1cf0153cd..000000000
--- a/changes/1665.feature.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-The environment variables ``ANDROID_USER_HOME`` and ``ANDROID_AVD_HOME`` are now respected and passed through to Android SDK commands. This allows for storing AVDs for the Android emulator outside of the home directory.
diff --git a/changes/1689.misc.rst b/changes/1689.misc.rst
new file mode 100644
index 000000000..7c5f7aee7
--- /dev/null
+++ b/changes/1689.misc.rst
@@ -0,0 +1,1 @@
+Support for ``ANDROID_USER_HOME`` and ``ANDROID_AVD_HOME`` was removed after issues with running AVDs created by non-Briefcase-managed Android SDKs were discovered.
diff --git a/docs/reference/platforms/android/gradle.rst b/docs/reference/platforms/android/gradle.rst
index d36c8988b..47b65770a 100644
--- a/docs/reference/platforms/android/gradle.rst
+++ b/docs/reference/platforms/android/gradle.rst
@@ -12,7 +12,6 @@ Gradle project
 | |f|    | |y|   |     | |f|    |       | |v| | |f|    | |v| | |v|   |
 +--------+-------+-----+--------+-------+-----+--------+-----+-------+
 
-
 When generating an Android project, Briefcase produces a Gradle project.
 
 Environment
@@ -41,14 +40,6 @@ present in the environment, Briefcase will honor the deprecated
 must have version 9.0 of Command-line Tools installed; this version can be
 installed in the SDK Manager in Android Studio.
 
-Android Emulator
-----------------
-
-By default, the Android emulator creates Android Virtual Devices (AVD) in the
-``.android`` directory in your user's home directory. As the AVDs can consume
-large amounts of disk space, they can be stored in an arbitrary directory via
-the ``ANDROID_USER_HOME`` and ``ANDROID_AVD_HOME`` environment variables.
-
 Packaging format
 ================
 
diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py
index e2f0906d9..11a9a7d91 100644
--- a/src/briefcase/integrations/android_sdk.py
+++ b/src/briefcase/integrations/android_sdk.py
@@ -55,6 +55,7 @@ class AndroidSDK(ManagedTool):
 
     def __init__(self, tools: ToolCache, root_path: Path):
         super().__init__(tools=tools)
+        self.dot_android_path = self.tools.home_path / ".android"
         self.root_path = root_path
 
         # A wrapper for testing purposes
@@ -92,13 +93,6 @@ def cmdline_tools_url(self) -> str:
             f"commandlinetools-{platform_name}-{self.SDK_MANAGER_DOWNLOAD_VER}_latest.zip"
         )
 
-    @property
-    def dot_android_path(self) -> Path:
-        if user_home := self.tools.os.environ.get("ANDROID_USER_HOME"):
-            return Path(user_home)
-        else:
-            return self.tools.home_path / ".android"
-
     @property
     def cmdline_tools_path(self) -> Path:
         """Version-specific Command-line tools install root directory."""
@@ -130,26 +124,18 @@ def emulator_path(self) -> Path:
         return self.root_path / "emulator" / emulator
 
     @property
-    def avd_home_path(self) -> Path:
-        if avd_home := self.tools.os.environ.get("ANDROID_AVD_HOME"):
-            return Path(avd_home)
-        else:
-            return self.dot_android_path / "avd"
-
-    def avd_path(self, avd: str) -> Path:
-        return self.avd_home_path / f"{avd}.avd"
+    def avd_path(self) -> Path:
+        return self.dot_android_path / "avd"
 
-    def avd_config_filepath(self, avd: str) -> Path:
-        return self.avd_path(avd) / "config.ini"
+    def avd_config_filename(self, avd: str) -> Path:
+        return self.avd_path / f"{avd}.avd/config.ini"
 
     @property
     def env(self) -> dict[str, str]:
         return {
             "ANDROID_HOME": os.fsdecode(self.root_path),
             "ANDROID_SDK_ROOT": os.fsdecode(self.root_path),
-            "ANDROID_USER_HOME": os.fsdecode(self.dot_android_path),
-            "ANDROID_AVD_HOME": os.fsdecode(self.avd_home_path),
-            "JAVA_HOME": os.fsdecode(self.tools.java.java_home),
+            "JAVA_HOME": str(self.tools.java.java_home),
         }
 
     @property
@@ -494,14 +480,19 @@ def install_cmdline_tools(self) -> bool:
             prefix=self.full_name,
         )
         self.tools.logger.info(f"Using Android SDK at {self.root_path}")
+        latest_sdkmanager_path = (
+            self.root_path
+            / "cmdline-tools"
+            / "latest"
+            / "bin"
+            / self.sdkmanager_filename
+        )
         try:
             self.tools.subprocess.run(
                 [
-                    self.root_path
-                    / f"cmdline-tools/latest/bin/{self.sdkmanager_filename}",
+                    latest_sdkmanager_path,
                     f"cmdline-tools;{self.SDK_MANAGER_VER}",
                 ],
-                env=self.env,
                 check=True,
                 stream_output=False,
             )
@@ -541,7 +532,7 @@ def delete_legacy_sdk_tools(self):
     Any emulators created with the older Android SDK Tools will not be
     compatible with the new tools. You will need to create new
     emulators. Old emulators can be removed by deleting the files
-    in {self.avd_home_path} matching the emulator name.
+    in {self.avd_path} matching the emulator name.
 
 *************************************************************************
 """
@@ -566,7 +557,7 @@ def list_packages(self):
         try:
             # check_output always writes its output to debug
             self.tools.subprocess.check_output(
-                [self.sdkmanager_path, "--list_installed"],
+                [os.fsdecode(self.sdkmanager_path), "--list_installed"],
                 env=self.env,
             )
         except subprocess.CalledProcessError as e:
@@ -602,7 +593,7 @@ def verify_license(self):
             # Using subprocess.run() with no I/O redirection so the user sees
             # the full output and can send input.
             self.tools.subprocess.run(
-                [self.sdkmanager_path, "--licenses"],
+                [os.fsdecode(self.sdkmanager_path), "--licenses"],
                 env=self.env,
                 check=True,
                 stream_output=False,
@@ -650,7 +641,11 @@ def verify_emulator(self):
         self.tools.logger.info("Downloading the Android emulator...")
         try:
             self.tools.subprocess.run(
-                [self.sdkmanager_path, "platform-tools", "emulator"],
+                [
+                    os.fsdecode(self.sdkmanager_path),
+                    "platform-tools",
+                    "emulator",
+                ],
                 env=self.env,
                 check=True,
                 stream_output=False,
@@ -776,7 +771,10 @@ def verify_system_image(self, system_image: str):
         )
         try:
             self.tools.subprocess.run(
-                [self.sdkmanager_path, system_image],
+                [
+                    os.fsdecode(self.sdkmanager_path),
+                    system_image,
+                ],
                 env=self.env,
                 check=True,
                 stream_output=False,
@@ -833,17 +831,11 @@ def verify_emulator_skin(self, skin: str):
 
     def emulators(self) -> list[str]:
         """Find the list of emulators that are available."""
-        # Only check for existing AVDs if the AVD home path exists; otherwise
-        # the call to avdmanager to list them will create the directory.
-        if not self.avd_home_path.is_dir():
-            return []
-
         try:
             # Capture `stderr` so that if the process exits with failure, the
             # stderr data is in `e.output`.
             output = self.tools.subprocess.check_output(
-                [self.avdmanager_path, "list", "avd", "--compact"],
-                env=self.env,
+                [os.fsdecode(self.emulator_path), "-list-avds"]
             ).strip()
             # AVD names are returned one per line.
             if len(output) == 0:
@@ -856,8 +848,7 @@ def devices(self) -> dict[str, dict[str, str | bool]]:
         """Find the devices that are attached and available to ADB."""
         try:
             output = self.tools.subprocess.check_output(
-                [self.adb_path, "devices", "-l"],
-                env=self.env,
+                [os.fsdecode(self.adb_path), "devices", "-l"]
             ).strip()
             # Process the output of `adb devices -l`.
             # The first line is header information.
@@ -1196,20 +1187,17 @@ def _create_emulator(
         if system_image is None:
             system_image = self.DEFAULT_SYSTEM_IMAGE
 
-        # Ensure the required skin is available
+        # Ensure the required skin is available.
         self.verify_emulator_skin(skin)
 
-        # Ensure the required system image is available
+        # Ensure the required system image is available.
         self.verify_system_image(system_image)
 
-        # Ensure AVD home exists so avdmanager doesn't use default directory
-        self.avd_home_path.mkdir(parents=True, exist_ok=True)
-
         with self.tools.input.wait_bar(f"Creating Android emulator {avd}..."):
             try:
                 self.tools.subprocess.check_output(
                     [
-                        self.avdmanager_path,
+                        os.fsdecode(self.avdmanager_path),
                         "--verbose",
                         "create",
                         "avd",
@@ -1250,7 +1238,7 @@ def avd_config(self, avd: str) -> dict[str, str]:
         # Parse the existing config into key-value pairs
         avd_config = {}
         try:
-            with self.avd_config_filepath(avd).open("r", encoding="utf-8") as f:
+            with self.avd_config_filename(avd).open("r", encoding="utf-8") as f:
                 for line in f:
                     try:
                         key, value = line.rstrip().split("=", 1)
@@ -1277,7 +1265,7 @@ def update_emulator_config(self, avd: str, updates: dict[str, str]):
         avd_config.update(updates)
 
         # Write the update configuration.
-        with self.avd_config_filepath(avd).open("w", encoding="utf-8") as f:
+        with self.avd_config_filename(avd).open("w", encoding="utf-8") as f:
             for key, value in avd_config.items():
                 f.write(f"{key}={value}\n")
 
@@ -1301,7 +1289,13 @@ def start_emulator(
             extra_args = []
 
         emulator_popen = self.tools.subprocess.Popen(
-            [self.emulator_path, f"@{avd}", "-dns-server", "8.8.8.8"] + extra_args,
+            [
+                os.fsdecode(self.emulator_path),
+                f"@{avd}",
+                "-dns-server",
+                "8.8.8.8",
+            ]
+            + extra_args,
             env=self.env,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
@@ -1486,7 +1480,7 @@ def run(self, *arguments: SubprocessArgT, quiet: bool = False) -> str:
         try:
             output = self.tools.subprocess.check_output(
                 [
-                    self.tools.android_sdk.adb_path,
+                    os.fsdecode(self.tools.android_sdk.adb_path),
                     "-s",
                     self.device,
                 ]
@@ -1494,11 +1488,10 @@ def run(self, *arguments: SubprocessArgT, quiet: bool = False) -> str:
                     (os.fsdecode(arg) if isinstance(arg, Path) else arg)
                     for arg in arguments
                 ],
-                env=self.tools.android_sdk.env,
                 quiet=quiet,
             )
             # add returns status code 0 in the case of failure. The only tangible evidence
-            # of failure is the message "Failure [INSTALL_FAILED_OLDER_SDK]" in the
+            # of failure is the message "Failure [INSTALL_FAILED_OLDER_SDK]" in the,
             # console output; so if that message exists in the output, raise an exception.
             if "Failure [INSTALL_FAILED_OLDER_SDK]" in output:
                 raise BriefcaseCommandError(
@@ -1601,7 +1594,7 @@ def logcat(self, pid: str) -> subprocess.Popen:
         # See #1425 for details.
         return self.tools.subprocess.Popen(
             [
-                self.tools.android_sdk.adb_path,
+                os.fsdecode(self.tools.android_sdk.adb_path),
                 "-s",
                 self.device,
                 "logcat",
@@ -1630,7 +1623,7 @@ def logcat_tail(self, since: datetime):
             # See #1425 for details.
             self.tools.subprocess.run(
                 [
-                    self.tools.android_sdk.adb_path,
+                    os.fsdecode(self.tools.android_sdk.adb_path),
                     "-s",
                     self.device,
                     "logcat",


Test patch:
diff --git a/tests/integrations/android_sdk/ADB/test_kill.py b/tests/integrations/android_sdk/ADB/test_kill.py
index 05360aa37..81b15b83f 100644
--- a/tests/integrations/android_sdk/ADB/test_kill.py
+++ b/tests/integrations/android_sdk/ADB/test_kill.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 from unittest.mock import MagicMock
 
@@ -14,13 +15,12 @@ def test_kill(mock_tools, adb):
     # Validate call parameters.
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            mock_tools.android_sdk.adb_path,
+            os.fsdecode(mock_tools.android_sdk.adb_path),
             "-s",
             "exampleDevice",
             "emu",
             "kill",
         ],
-        env=mock_tools.android_sdk.env,
         quiet=False,
     )
 
diff --git a/tests/integrations/android_sdk/ADB/test_logcat.py b/tests/integrations/android_sdk/ADB/test_logcat.py
index 7870dbaf3..9992e24e7 100644
--- a/tests/integrations/android_sdk/ADB/test_logcat.py
+++ b/tests/integrations/android_sdk/ADB/test_logcat.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 from unittest import mock
 
@@ -24,7 +25,7 @@ def test_logcat(mock_tools, adb, is_color_enabled, monkeypatch):
     # Validate call parameters.
     mock_tools.subprocess.Popen.assert_called_once_with(
         [
-            mock_tools.android_sdk.adb_path,
+            os.fsdecode(mock_tools.android_sdk.adb_path),
             "-s",
             "exampleDevice",
             "logcat",
diff --git a/tests/integrations/android_sdk/ADB/test_logcat_tail.py b/tests/integrations/android_sdk/ADB/test_logcat_tail.py
index 51ad2e12a..07eacd6d3 100644
--- a/tests/integrations/android_sdk/ADB/test_logcat_tail.py
+++ b/tests/integrations/android_sdk/ADB/test_logcat_tail.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 from datetime import datetime
 from unittest.mock import MagicMock, PropertyMock
@@ -23,7 +24,7 @@ def test_logcat_tail(mock_tools, adb, is_color_enabled, monkeypatch):
     # Validate call parameters.
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            mock_tools.android_sdk.adb_path,
+            os.fsdecode(mock_tools.android_sdk.adb_path),
             "-s",
             "exampleDevice",
             "logcat",
diff --git a/tests/integrations/android_sdk/ADB/test_run.py b/tests/integrations/android_sdk/ADB/test_run.py
index 0d9e5ee87..c3cf7106f 100644
--- a/tests/integrations/android_sdk/ADB/test_run.py
+++ b/tests/integrations/android_sdk/ADB/test_run.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 import sys
 from pathlib import Path
@@ -14,14 +15,17 @@ def test_simple_command(mock_tools, adb, tmp_path):
     # Check that adb was invoked with the expected commands
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            tmp_path
-            / f"sdk/platform-tools/adb{'.exe' if sys.platform == 'win32' else ''}",
+            os.fsdecode(
+                tmp_path
+                / "sdk"
+                / "platform-tools"
+                / f"adb{'.exe' if sys.platform == 'win32' else ''}"
+            ),
             "-s",
             "exampleDevice",
             "example",
             "command",
         ],
-        env=mock_tools.android_sdk.env,
         quiet=False,
     )
 
@@ -33,14 +37,17 @@ def test_quiet_command(mock_tools, adb, tmp_path):
     # Check that adb was invoked with the expected commands
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            tmp_path
-            / f"sdk/platform-tools/adb{'.exe' if sys.platform == 'win32' else ''}",
+            os.fsdecode(
+                tmp_path
+                / "sdk"
+                / "platform-tools"
+                / f"adb{'.exe' if sys.platform == 'win32' else ''}"
+            ),
             "-s",
             "exampleDevice",
             "example",
             "command",
         ],
-        env=mock_tools.android_sdk.env,
         quiet=True,
     )
 
@@ -79,14 +86,17 @@ def test_error_handling(mock_tools, adb, name, exception, tmp_path):
     # Check that adb was invoked as expected
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            tmp_path
-            / f"sdk/platform-tools/adb{'.exe' if sys.platform == 'win32' else ''}",
+            os.fsdecode(
+                tmp_path
+                / "sdk"
+                / "platform-tools"
+                / f"adb{'.exe' if sys.platform == 'win32' else ''}"
+            ),
             "-s",
             "exampleDevice",
             "example",
             "command",
         ],
-        env=mock_tools.android_sdk.env,
         quiet=False,
     )
 
diff --git a/tests/integrations/android_sdk/AndroidSDK/test__create_emulator.py b/tests/integrations/android_sdk/AndroidSDK/test__create_emulator.py
index 1270dbc87..4c864be52 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test__create_emulator.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test__create_emulator.py
@@ -1,5 +1,6 @@
+import os
 import subprocess
-from unittest.mock import ANY, MagicMock, PropertyMock
+from unittest.mock import ANY, MagicMock
 
 import pytest
 
@@ -87,7 +88,7 @@ def test_create_emulator(
     # avdmanager was invoked
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            android_sdk.avdmanager_path,
+            os.fsdecode(android_sdk.avdmanager_path),
             "--verbose",
             "create",
             "avd",
@@ -160,7 +161,7 @@ def test_create_emulator_with_defaults(
     # avdmanager was invoked
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            android_sdk.avdmanager_path,
+            os.fsdecode(android_sdk.avdmanager_path),
             "--verbose",
             "create",
             "avd",
@@ -183,69 +184,6 @@ def test_create_emulator_with_defaults(
     assert "skin.name=pixel_7_pro" in config
 
 
-def test_create_emulator_with_nonexistent_avd_home(
-    mock_tools,
-    android_sdk,
-    tmp_path,
-    monkeypatch,
-):
-    """Ensure a AVD home directory is created for a new emulator."""
-    # Mock the hardware and operating system to specific values
-    mock_tools.host_os = "Darwin"
-    mock_tools.host_arch = "x86_64"
-
-    # Mock system image and skin verification
-    android_sdk.verify_system_image = MagicMock()
-    android_sdk.verify_emulator_skin = MagicMock()
-
-    # Mock a custom location for the AVDs
-    android_sdk.update_emulator_config = MagicMock()
-    avd_home_path = tmp_path / "my-avds"
-    monkeypatch.setattr(
-        type(android_sdk),
-        "avd_home_path",
-        PropertyMock(return_value=avd_home_path),
-    )
-    assert avd_home_path.is_dir() is False
-
-    # Create the emulator
-    android_sdk._create_emulator(
-        avd="new-emulator",
-        device_type="slab",
-        skin="slab_skin",
-        system_image="system-images;android-42;default;gothic",
-    )
-
-    # The system image was verified
-    android_sdk.verify_system_image.assert_called_once_with(
-        "system-images;android-42;default;gothic"
-    )
-
-    # The emulator skin was verified
-    android_sdk.verify_emulator_skin.assert_called_once_with("slab_skin")
-
-    # avdmanager was invoked
-    mock_tools.subprocess.check_output.assert_called_once_with(
-        [
-            android_sdk.avdmanager_path,
-            "--verbose",
-            "create",
-            "avd",
-            "--name",
-            "new-emulator",
-            "--abi",
-            "x86_64",
-            "--package",
-            "system-images;android-42;default;gothic",
-            "--device",
-            "slab",
-        ],
-        env=android_sdk.env,
-    )
-
-    assert avd_home_path.is_dir()
-
-
 def test_create_failure(mock_tools, android_sdk):
     """If avdmanager fails, an error is raised."""
     # Mock system image and skin verification
@@ -270,7 +208,7 @@ def test_create_failure(mock_tools, android_sdk):
     # avdmanager was invoked
     mock_tools.subprocess.check_output.assert_called_once_with(
         [
-            android_sdk.avdmanager_path,
+            os.fsdecode(android_sdk.avdmanager_path),
             "--verbose",
             "create",
             "avd",
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_emulators.py b/tests/integrations/android_sdk/AndroidSDK/test_emulators.py
index e7ddb4f9e..1619345c7 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_emulators.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_emulators.py
@@ -5,15 +5,8 @@
 from briefcase.exceptions import BriefcaseCommandError
 
 
-def test_no_avd_home(mock_tools, android_sdk):
-    """If the AVD home directory doesn't exist, an empty list is returned."""
-    assert not android_sdk.avd_home_path.is_dir()
-    assert android_sdk.emulators() == []
-
-
 def test_no_emulators(mock_tools, android_sdk):
     """If there are no emulators, an empty list is returned."""
-    android_sdk.avd_home_path.mkdir(parents=True, exist_ok=True)
     mock_tools.subprocess.check_output.return_value = ""
 
     assert android_sdk.emulators() == []
@@ -21,7 +14,6 @@ def test_no_emulators(mock_tools, android_sdk):
 
 def test_one_emulator(mock_tools, android_sdk):
     """If there is a single emulator, it is returned."""
-    android_sdk.avd_home_path.mkdir(parents=True, exist_ok=True)
     mock_tools.subprocess.check_output.return_value = "first\n"
 
     assert android_sdk.emulators() == ["first"]
@@ -29,7 +21,6 @@ def test_one_emulator(mock_tools, android_sdk):
 
 def test_multiple_emulators(mock_tools, android_sdk):
     """If there are multiple emulators, they are all returned."""
-    android_sdk.avd_home_path.mkdir(parents=True, exist_ok=True)
     mock_tools.subprocess.check_output.return_value = "first\nsecond\nthird\n"
 
     assert android_sdk.emulators() == ["first", "second", "third"]
@@ -37,9 +28,8 @@ def test_multiple_emulators(mock_tools, android_sdk):
 
 def test_adb_error(mock_tools, android_sdk):
     """If there is a problem invoking adb, an error is returned."""
-    android_sdk.avd_home_path.mkdir(parents=True, exist_ok=True)
     mock_tools.subprocess.check_output.side_effect = subprocess.CalledProcessError(
-        returncode=69, cmd="avdmanager list avd --compact"
+        returncode=69, cmd="adb devices -l"
     )
 
     with pytest.raises(BriefcaseCommandError):
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_list_packages.py b/tests/integrations/android_sdk/AndroidSDK/test_list_packages.py
index 731a756be..dbeb5734b 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_list_packages.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_list_packages.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 
 import pytest
@@ -10,7 +11,7 @@ def test_list_packages(mock_tools, android_sdk):
     android_sdk.list_packages()
 
     mock_tools.subprocess.check_output.assert_called_once_with(
-        [android_sdk.sdkmanager_path, "--list_installed"],
+        [os.fsdecode(android_sdk.sdkmanager_path), "--list_installed"],
         env=android_sdk.env,
     )
 
@@ -24,6 +25,6 @@ def test_list_packages_failure(mock_tools, android_sdk):
         android_sdk.list_packages()
 
     mock_tools.subprocess.check_output.assert_called_once_with(
-        [android_sdk.sdkmanager_path, "--list_installed"],
+        [os.fsdecode(android_sdk.sdkmanager_path), "--list_installed"],
         env=android_sdk.env,
     )
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_properties.py b/tests/integrations/android_sdk/AndroidSDK/test_properties.py
index 8b38ab535..7c4fdb563 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_properties.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_properties.py
@@ -86,55 +86,8 @@ def test_emulator_path(mock_tools, android_sdk, host_os, emulator_name):
     )
 
 
-def test_dot_android_path(mock_tools, android_sdk, tmp_path):
-    """Default user android path is $HOME/.android/avd."""
-    assert android_sdk.dot_android_path == tmp_path / "home/.android"
-
-
-def test_dot_android_path_env_var(mock_tools, android_sdk, monkeypatch):
-    """ANDROID_AVD_HOME is respected as AVD path."""
-    mock_tools.os.environ = {"ANDROID_USER_HOME": "/my/android"}
-    assert android_sdk.dot_android_path == Path("/my/android")
-
-
-def test_avd_home_path(mock_tools, android_sdk, tmp_path):
-    """Default AVD path is $HOME/.android/avd."""
-    assert android_sdk.avd_home_path == tmp_path / "home/.android/avd"
-
-
-def test_avd_home_path_env_var(mock_tools, android_sdk, monkeypatch):
-    """ANDROID_AVD_HOME is respected as AVD path."""
-    mock_tools.os.environ = {"ANDROID_AVD_HOME": "/my/avds"}
-    assert android_sdk.avd_home_path == Path("/my/avds")
-
-
 def test_avd_path(mock_tools, android_sdk, tmp_path):
-    """Default path for AVD is correct."""
-    assert (
-        android_sdk.avd_path(avd="my-avd") == tmp_path / "home/.android/avd/my-avd.avd"
-    )
-
-
-def test_avd_path_env_var(mock_tools, android_sdk, tmp_path, monkeypatch):
-    """ANDROID_AVD_HOME is respected for AVD path."""
-    mock_tools.os.environ = {"ANDROID_AVD_HOME": "/my/avds"}
-    assert android_sdk.avd_path(avd="my-avd") == Path("/my/avds/my-avd.avd")
-
-
-def test_avd_config_filepath(mock_tools, android_sdk, tmp_path):
-    """Default AVD config file is correct."""
-    assert (
-        android_sdk.avd_config_filepath(avd="my-avd")
-        == tmp_path / "home/.android/avd/my-avd.avd/config.ini"
-    )
-
-
-def test_avd_config_filepath_env_var(mock_tools, android_sdk, tmp_path, monkeypatch):
-    """ANDROID_AVD_HOME is respected for AVD config filepath."""
-    mock_tools.os.environ = {"ANDROID_AVD_HOME": "/my/avds"}
-    assert android_sdk.avd_config_filepath(avd="my-avd") == Path(
-        "/my/avds/my-avd.avd/config.ini"
-    )
+    assert android_sdk.avd_path == tmp_path / "home/.android/avd"
 
 
 def test_simple_env(mock_tools, android_sdk, tmp_path):
@@ -143,8 +96,6 @@ def test_simple_env(mock_tools, android_sdk, tmp_path):
         "JAVA_HOME": os.fsdecode(Path("/path/to/jdk")),
         "ANDROID_HOME": os.fsdecode(tmp_path / "sdk"),
         "ANDROID_SDK_ROOT": os.fsdecode(tmp_path / "sdk"),
-        "ANDROID_USER_HOME": os.fsdecode(tmp_path / "home/.android"),
-        "ANDROID_AVD_HOME": os.fsdecode(tmp_path / "home/.android/avd"),
     }
 
 
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py b/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py
index 3dacb2596..a0339daf2 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 from unittest.mock import MagicMock, call
 
@@ -108,7 +109,7 @@ def test_start_emulator(mock_tools, android_sdk):
     # The process was started.
     mock_tools.subprocess.Popen.assert_called_with(
         [
-            android_sdk.emulator_path,
+            os.fsdecode(android_sdk.emulator_path),
             "@idleEmulator",
             "-dns-server",
             "8.8.8.8",
@@ -189,7 +190,7 @@ def test_start_emulator_fast_start(mock_tools, android_sdk):
     # The process was started.
     mock_tools.subprocess.Popen.assert_called_with(
         [
-            android_sdk.emulator_path,
+            os.fsdecode(android_sdk.emulator_path),
             "@idleEmulator",
             "-dns-server",
             "8.8.8.8",
@@ -284,7 +285,7 @@ def test_emulator_fail_to_start(mock_tools, android_sdk):
     # The process was started.
     mock_tools.subprocess.Popen.assert_called_with(
         [
-            android_sdk.emulator_path,
+            os.fsdecode(android_sdk.emulator_path),
             "@idleEmulator",
             "-dns-server",
             "8.8.8.8",
@@ -388,7 +389,7 @@ def test_emulator_fail_to_boot(mock_tools, android_sdk):
     # The process was started.
     mock_tools.subprocess.Popen.assert_called_with(
         [
-            android_sdk.emulator_path,
+            os.fsdecode(android_sdk.emulator_path),
             "@idleEmulator",
             "-dns-server",
             "8.8.8.8",
@@ -457,7 +458,7 @@ def test_emulator_ctrl_c(mock_tools, android_sdk, emulator_comm, expected_log, c
     # The process was started.
     mock_tools.subprocess.Popen.assert_called_with(
         [
-            android_sdk.emulator_path,
+            os.fsdecode(android_sdk.emulator_path),
             "@idleEmulator",
             "-dns-server",
             "8.8.8.8",
@@ -523,7 +524,7 @@ def test_start_emulator_extra_args(mock_tools, android_sdk):
     # The process was started with the headless test options.
     mock_tools.subprocess.Popen.assert_called_with(
         [
-            android_sdk.emulator_path,
+            os.fsdecode(android_sdk.emulator_path),
             "@idleEmulator",
             "-dns-server",
             "8.8.8.8",
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify.py b/tests/integrations/android_sdk/AndroidSDK/test_verify.py
index 2619f85cf..4e393a05b 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_verify.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_verify.py
@@ -1,7 +1,6 @@
 import os
 import platform
 import shutil
-from pathlib import Path
 from unittest.mock import MagicMock
 
 import pytest
@@ -321,7 +320,7 @@ def test_user_provided_sdk_wrong_cmdline_tools_ver(
     # Create `sdkmanager` and the license file
     # for the *briefcase* managed version of the SDK.
     android_sdk_root_path = tmp_path / "tools/android_sdk"
-    tools_bin = android_sdk_root_path / f"cmdline-tools/{SDK_MGR_VER}/bin"
+    tools_bin = android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin"
     tools_bin.mkdir(parents=True, mode=0o755)
     (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755)
 
@@ -344,16 +343,14 @@ def test_user_provided_sdk_wrong_cmdline_tools_ver(
     # Required Command-line Tools installed
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            tmp_path / f"other_sdk/cmdline-tools/latest/bin/{SDKMANAGER_FILENAME}",
+            tmp_path
+            / "other_sdk"
+            / "cmdline-tools"
+            / "latest"
+            / "bin"
+            / SDKMANAGER_FILENAME,
             f"cmdline-tools;{SDK_MGR_VER}",
         ],
-        env={
-            "ANDROID_HOME": f"{tmp_path / 'other_sdk'}",
-            "ANDROID_SDK_ROOT": f"{tmp_path / 'other_sdk'}",
-            "ANDROID_USER_HOME": f"{tmp_path / 'home/.android'}",
-            "ANDROID_AVD_HOME": f"{tmp_path / 'home/.android/avd'}",
-            "JAVA_HOME": str(Path("/path/to/jdk")),
-        },
         check=True,
         stream_output=False,
     )
@@ -398,10 +395,14 @@ def test_user_provided_sdk_with_latest_cmdline_tools(
     # Required Command-line Tools installed
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            tmp_path / f"other_sdk/cmdline-tools/latest/bin/{SDKMANAGER_FILENAME}",
+            tmp_path
+            / "other_sdk"
+            / "cmdline-tools"
+            / "latest"
+            / "bin"
+            / SDKMANAGER_FILENAME,
             f"cmdline-tools;{SDK_MGR_VER}",
         ],
-        env=sdk.env,
         check=True,
         stream_output=False,
     )
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator.py b/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator.py
index c65cfa550..27c6f9f4e 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 import sys
 
@@ -61,7 +62,7 @@ def test_installs_android_emulator(mock_tools, android_sdk):
 
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            android_sdk.sdkmanager_path,
+            os.fsdecode(android_sdk.sdkmanager_path),
             "platform-tools",
             "emulator",
         ],
@@ -83,7 +84,7 @@ def test_partial_android_emulator_install(mock_tools, android_sdk):
 
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            android_sdk.sdkmanager_path,
+            os.fsdecode(android_sdk.sdkmanager_path),
             "platform-tools",
             "emulator",
         ],
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify_license.py b/tests/integrations/android_sdk/AndroidSDK/test_verify_license.py
index 0fd4eb738..60e97f3aa 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_verify_license.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_verify_license.py
@@ -1,3 +1,4 @@
+import os
 import subprocess
 
 import pytest
@@ -34,7 +35,7 @@ def accept_license(*args, **kwargs):
     mock_tools.subprocess.run.side_effect = accept_license
     android_sdk.verify_license()
     mock_tools.subprocess.run.assert_called_once_with(
-        [android_sdk.sdkmanager_path, "--licenses"],
+        [os.fsdecode(android_sdk.sdkmanager_path), "--licenses"],
         env=android_sdk.env,
         check=True,
         stream_output=False,
@@ -49,7 +50,7 @@ def test_verify_license_handles_sdkmanager_crash(mock_tools, android_sdk):
         android_sdk.verify_license()
 
     mock_tools.subprocess.run.assert_called_once_with(
-        [android_sdk.sdkmanager_path, "--licenses"],
+        [os.fsdecode(android_sdk.sdkmanager_path), "--licenses"],
         env=android_sdk.env,
         check=True,
         stream_output=False,
@@ -65,7 +66,7 @@ def test_verify_license_insists_on_agreement(mock_tools, android_sdk):
         android_sdk.verify_license()
 
     mock_tools.subprocess.run.assert_called_once_with(
-        [android_sdk.sdkmanager_path, "--licenses"],
+        [os.fsdecode(android_sdk.sdkmanager_path), "--licenses"],
         env=android_sdk.env,
         check=True,
         stream_output=False,
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify_system_image.py b/tests/integrations/android_sdk/AndroidSDK/test_verify_system_image.py
index 22b351d1d..b661e6e9d 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_verify_system_image.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_verify_system_image.py
@@ -1,3 +1,4 @@
+import os
 import platform
 from subprocess import CalledProcessError
 
@@ -67,7 +68,7 @@ def test_incompatible_abi(mock_tools, android_sdk, capsys):
     # The system image was installed.
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            android_sdk.sdkmanager_path,
+            os.fsdecode(android_sdk.sdkmanager_path),
             "system-images;android-31;default;anything",
         ],
         env=android_sdk.env,
@@ -104,7 +105,7 @@ def test_new_system_image(mock_tools, android_sdk):
     # The system image was installed.
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            android_sdk.sdkmanager_path,
+            os.fsdecode(android_sdk.sdkmanager_path),
             "system-images;android-31;default;x86_64",
         ],
         env=android_sdk.env,
@@ -134,7 +135,7 @@ def test_problem_downloading_system_image(mock_tools, android_sdk):
     # An attempt to install the system image was made
     mock_tools.subprocess.run.assert_called_once_with(
         [
-            android_sdk.sdkmanager_path,
+            os.fsdecode(android_sdk.sdkmanager_path),
             "system-images;android-31;default;x86_64",
         ],
         env=android_sdk.env,
diff --git a/tests/platforms/android/gradle/test_run.py b/tests/platforms/android/gradle/test_run.py
index 4f0246960..3db1db2ed 100644
--- a/tests/platforms/android/gradle/test_run.py
+++ b/tests/platforms/android/gradle/test_run.py
@@ -3,6 +3,7 @@
 import platform
 import sys
 import time
+from os.path import normpath
 from pathlib import Path
 from unittest import mock
 
@@ -52,7 +53,6 @@ def run_command(tmp_path, first_app_config, jdk):
     command.tools.requests = mock.MagicMock(spec_set=requests)
     command.tools.subprocess = mock.MagicMock(spec_set=Subprocess)
     command.tools.sys = mock.MagicMock(spec_set=sys)
-    command.tools.home_path = tmp_path / "home"
 
     command._stream_app_logs = mock.MagicMock()
 
@@ -524,7 +524,7 @@ def test_run_idle_device(run_command, first_app_config):
     )
 
 
-def test_log_file_extra(run_command, tmp_path, monkeypatch):
+def test_log_file_extra(run_command, monkeypatch):
     """Android commands register a log file extra to list SDK packages."""
     mock_android_sdk_verify = mock.MagicMock(return_value=run_command.tools.android_sdk)
     monkeypatch.setattr(AndroidSDK, "verify", mock_android_sdk_verify)
@@ -545,17 +545,14 @@ def test_log_file_extra(run_command, tmp_path, monkeypatch):
     run_command.tools.logger.save_log = True
     run_command.tools.logger.save_log_to_file(run_command)
 
-    sdk_manager = Path(
-        f"/path/to/android_sdk/cmdline-tools/{AndroidSDK.SDK_MANAGER_VER}"
-        f"/bin/sdkmanager{'.bat' if platform.system() == 'Windows' else ''}"
-    )
+    sdk_manager = f"/path/to/android_sdk/cmdline-tools/{AndroidSDK.SDK_MANAGER_VER}/bin/sdkmanager"
+    if platform.system() == "Windows":
+        sdk_manager += ".bat"
     run_command.tools.subprocess.check_output.assert_called_once_with(
-        [sdk_manager, "--list_installed"],
+        [normpath(sdk_manager), "--list_installed"],
         env={
             "ANDROID_HOME": str(run_command.tools.android_sdk.root_path),
             "ANDROID_SDK_ROOT": str(run_command.tools.android_sdk.root_path),
-            "ANDROID_USER_HOME": f"{tmp_path / 'home/.android'}",
-            "ANDROID_AVD_HOME": f"{tmp_path / 'home/.android/avd'}",
             "JAVA_HOME": str(run_command.tools.java.java_home),
         },
     )