From 20a20c074424249e9a10f6b009f81847beea4d20 Mon Sep 17 00:00:00 2001
From: Malcolm Smith <smith@chaquo.com>
Date: Fri, 1 Mar 2024 16:46:44 +0000
Subject: [PATCH 1/3] Change Android's `sys.platform` from "linux" to "android"

---
 Lib/ctypes/__init__.py                        |  2 +-
 Lib/multiprocessing/util.py                   |  6 +----
 Lib/shutil.py                                 |  3 ++-
 Lib/test/support/__init__.py                  |  2 +-
 Lib/test/support/os_helper.py                 |  2 +-
 Lib/test/test_asyncio/test_subprocess.py      |  3 ++-
 Lib/test/test_c_locale_coercion.py            | 21 +++++++++---------
 Lib/test/test_fcntl.py                        |  4 ++--
 Lib/test/test_logging.py                      |  4 ++--
 Lib/test/test_mmap.py                         |  2 +-
 Lib/test/test_os.py                           |  7 +++---
 Lib/test/test_resource.py                     |  2 +-
 Lib/test/test_socket.py                       | 17 +++++++-------
 Lib/test/test_ssl.py                          |  2 +-
 Lib/test/test_sys.py                          |  5 ++---
 Lib/test/test_sysconfig.py                    | 22 ++++++++++++++-----
 Lib/test/test_tarfile.py                      |  2 +-
 Lib/test/test_time.py                         |  2 +-
 Lib/uuid.py                                   |  2 +-
 ...4-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst |  1 +
 Misc/platform_triplet.c                       | 14 +++++++++++-
 configure                                     |  1 +
 configure.ac                                  |  1 +
 23 files changed, 74 insertions(+), 53 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst

diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index d54ee05b15f5bd..f63e31a3fb0107 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -468,7 +468,7 @@ def LoadLibrary(self, name):
 
 if _os.name == "nt":
     pythonapi = PyDLL("python dll", None, _sys.dllhandle)
-elif hasattr(_sys, "getandroidapilevel"):
+elif _sys.platform == "android":
     pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2])
 elif _sys.platform == "cygwin":
     pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index 32871850ddec8b..75dde02d88c533 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -102,11 +102,7 @@ def log_to_stderr(level=None):
 # Abstract socket support
 
 def _platform_supports_abstract_sockets():
-    if sys.platform == "linux":
-        return True
-    if hasattr(sys, 'getandroidapilevel'):
-        return True
-    return False
+    return sys.platform in ("linux", "android")
 
 
 def is_abstract_socket_namespace(address):
diff --git a/Lib/shutil.py b/Lib/shutil.py
index c19ea0607208af..f8be82d97616d3 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -47,7 +47,8 @@
 COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
 # This should never be removed, see rationale in:
 # https://bugs.python.org/issue43743#msg393429
-_USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux")
+_USE_CP_SENDFILE = (hasattr(os, "sendfile")
+                    and sys.platform.startswith(("linux", "android")))
 _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile")  # macOS
 
 # CMD defaults in Windows 10
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 14e3766a34377b..9d168f11fc17b3 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -520,7 +520,7 @@ def requires_debug_ranges(reason='requires co_positions / debug_ranges'):
 # Is not actually used in tests, but is kept for compatibility.
 is_jython = sys.platform.startswith('java')
 
-is_android = hasattr(sys, 'getandroidapilevel')
+is_android = sys.platform == "android"
 
 if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos"}:
     unix_shell = '/system/bin/sh' if is_android else '/bin/sh'
diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py
index ffa5fc52e9b2bd..8071c248b9b67e 100644
--- a/Lib/test/support/os_helper.py
+++ b/Lib/test/support/os_helper.py
@@ -612,7 +612,7 @@ def __fspath__(self):
 def fd_count():
     """Count the number of open file descriptors.
     """
-    if sys.platform.startswith(('linux', 'freebsd', 'emscripten')):
+    if sys.platform.startswith(('linux', 'android', 'freebsd', 'emscripten')):
         fd_path = "/proc/self/fd"
     elif sys.platform == "darwin":
         fd_path = "/dev/fd"
diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py
index 890c0acc1720dc..cf1a1985338e40 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -480,7 +480,8 @@ async def empty_error():
         self.assertEqual(output, None)
         self.assertEqual(exitcode, 0)
 
-    @unittest.skipIf(sys.platform != 'linux', "Don't have /dev/stdin")
+    @unittest.skipIf(sys.platform not in ('linux', 'android'),
+                     "Don't have /dev/stdin")
     def test_devstdin_input(self):
 
         async def devstdin_input(message):
diff --git a/Lib/test/test_c_locale_coercion.py b/Lib/test/test_c_locale_coercion.py
index 7334a325ba22f0..e4b0b8c451fd45 100644
--- a/Lib/test/test_c_locale_coercion.py
+++ b/Lib/test/test_c_locale_coercion.py
@@ -26,17 +26,16 @@
 TARGET_LOCALES = ["C.UTF-8", "C.utf8", "UTF-8"]
 
 # Apply some platform dependent overrides
-if sys.platform.startswith("linux"):
-    if support.is_android:
-        # Android defaults to using UTF-8 for all system interfaces
-        EXPECTED_C_LOCALE_STREAM_ENCODING = "utf-8"
-        EXPECTED_C_LOCALE_FS_ENCODING = "utf-8"
-    else:
-        # Linux distros typically alias the POSIX locale directly to the C
-        # locale.
-        # TODO: Once https://bugs.python.org/issue30672 is addressed, we'll be
-        #       able to check this case unconditionally
-        EXPECTED_C_LOCALE_EQUIVALENTS.append("POSIX")
+if sys.platform == "android":
+    # Android defaults to using UTF-8 for all system interfaces
+    EXPECTED_C_LOCALE_STREAM_ENCODING = "utf-8"
+    EXPECTED_C_LOCALE_FS_ENCODING = "utf-8"
+elif sys.platform.startswith("linux"):
+    # Linux distros typically alias the POSIX locale directly to the C
+    # locale.
+    # TODO: Once https://bugs.python.org/issue30672 is addressed, we'll be
+    #       able to check this case unconditionally
+    EXPECTED_C_LOCALE_EQUIVALENTS.append("POSIX")
 elif sys.platform.startswith("aix"):
     # AIX uses iso8859-1 in the C locale, other *nix platforms use ASCII
     EXPECTED_C_LOCALE_STREAM_ENCODING = "iso8859-1"
diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py
index 8b4ed4a9e3a4fe..5fae0de7423c87 100644
--- a/Lib/test/test_fcntl.py
+++ b/Lib/test/test_fcntl.py
@@ -129,8 +129,8 @@ def test_fcntl_bad_file_overflow(self):
             fcntl.fcntl(BadFile(INT_MIN - 1), fcntl.F_SETFL, os.O_NONBLOCK)
 
     @unittest.skipIf(
-        any(platform.machine().startswith(name) for name in {"arm", "aarch"})
-        and platform.system() in {"Linux", "Android"},
+        platform.machine().startswith(("arm", "aarch"))
+        and platform.system() in ("Linux", "Android"),
         "ARM Linux returns EINVAL for F_NOTIFY DN_MULTISHOT")
     def test_fcntl_64_bit(self):
         # Issue #1309352: fcntl shouldn't fail when the third arg fits in a
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index d87142698efa8b..c36a8c9e0b84c6 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -603,7 +603,7 @@ def test_name(self):
     def test_builtin_handlers(self):
         # We can't actually *use* too many handlers in the tests,
         # but we can try instantiating them with various options
-        if sys.platform in ('linux', 'darwin'):
+        if sys.platform in ('linux', 'android', 'darwin'):
             for existing in (True, False):
                 fn = make_temp_file()
                 if not existing:
@@ -667,7 +667,7 @@ def test_path_objects(self):
                     (logging.handlers.RotatingFileHandler, (pfn, 'a')),
                     (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
                 )
-        if sys.platform in ('linux', 'darwin'):
+        if sys.platform in ('linux', 'android', 'darwin'):
             cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
         for cls, args in cases:
             h = cls(*args, encoding="utf-8")
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index ac759757d24659..ee86227e026b67 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -837,7 +837,7 @@ def test_flush_return_value(self):
         mm.write(b'python')
         result = mm.flush()
         self.assertIsNone(result)
-        if sys.platform.startswith('linux'):
+        if sys.platform.startswith(('linux', 'android')):
             # 'offset' must be a multiple of mmap.PAGESIZE on Linux.
             # See bpo-34754 for details.
             self.assertRaises(OSError, mm.flush, 1, len(b'python'))
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 3b2d5fccc30f3c..c1495ede0ccfc7 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -3533,9 +3533,8 @@ def test_set_get_priority(self):
 class TestSendfile(unittest.IsolatedAsyncioTestCase):
 
     DATA = b"12345abcde" * 16 * 1024  # 160 KiB
-    SUPPORT_HEADERS_TRAILERS = not sys.platform.startswith("linux") and \
-                               not sys.platform.startswith("solaris") and \
-                               not sys.platform.startswith("sunos")
+    SUPPORT_HEADERS_TRAILERS = \
+        not sys.platform.startswith(("linux", "android", "solaris", "sunos"))
     requires_headers_trailers = unittest.skipUnless(SUPPORT_HEADERS_TRAILERS,
             'requires headers and trailers support')
     requires_32b = unittest.skipUnless(sys.maxsize < 2**32,
@@ -5256,7 +5255,7 @@ def test_fork(self):
         else:
             assert_python_ok("-c", code, PYTHONMALLOC="malloc_debug")
 
-    @unittest.skipUnless(sys.platform in ("linux", "darwin"),
+    @unittest.skipUnless(sys.platform in ("linux", "android", "darwin"),
                          "Only Linux and macOS detect this today.")
     def test_fork_warns_when_non_python_thread_exists(self):
         code = """if 1:
diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py
index 317e7ca8f8c853..d23d3623235f38 100644
--- a/Lib/test/test_resource.py
+++ b/Lib/test/test_resource.py
@@ -138,7 +138,7 @@ def test_pagesize(self):
         self.assertIsInstance(pagesize, int)
         self.assertGreaterEqual(pagesize, 0)
 
-    @unittest.skipUnless(sys.platform == 'linux', 'test requires Linux')
+    @unittest.skipUnless(sys.platform in ('linux', 'android'), 'Linux only')
     def test_linux_constants(self):
         for attr in ['MSGQUEUE', 'NICE', 'RTPRIO', 'RTTIME', 'SIGPENDING']:
             with contextlib.suppress(AttributeError):
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index b936e9ae91daca..a7e657f5718524 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1199,8 +1199,8 @@ def testGetServBy(self):
         # I've ordered this by protocols that have both a tcp and udp
         # protocol, at least for modern Linuxes.
         if (
-            sys.platform.startswith(('freebsd', 'netbsd', 'gnukfreebsd'))
-            or sys.platform == 'linux'
+            sys.platform.startswith(
+                ('linux', 'android', 'freebsd', 'netbsd', 'gnukfreebsd'))
             or is_apple
         ):
             # avoid the 'echo' service on this platform, as there is an
@@ -1218,8 +1218,7 @@ def testGetServBy(self):
             raise OSError
         # Try same call with optional protocol omitted
         # Issue #26936: Android getservbyname() was broken before API 23.
-        if (not hasattr(sys, 'getandroidapilevel') or
-                sys.getandroidapilevel() >= 23):
+        if (not support.is_android) or sys.getandroidapilevel() >= 23:
             port2 = socket.getservbyname(service)
             eq(port, port2)
         # Try udp, but don't barf if it doesn't exist
@@ -1577,8 +1576,7 @@ def testGetaddrinfo(self):
         # port can be a string service name such as "http", a numeric
         # port number or None
         # Issue #26936: Android getaddrinfo() was broken before API level 23.
-        if (not hasattr(sys, 'getandroidapilevel') or
-                sys.getandroidapilevel() >= 23):
+        if (not support.is_android) or sys.getandroidapilevel() >= 23:
             socket.getaddrinfo(HOST, "http")
         socket.getaddrinfo(HOST, 80)
         socket.getaddrinfo(HOST, None)
@@ -3196,7 +3194,7 @@ def _testSendmsgTimeout(self):
     # Linux supports MSG_DONTWAIT when sending, but in general, it
     # only works when receiving.  Could add other platforms if they
     # support it too.
-    @skipWithClientIf(sys.platform not in {"linux"},
+    @skipWithClientIf(sys.platform not in {"linux", "android"},
                       "MSG_DONTWAIT not known to work on this platform when "
                       "sending")
     def testSendmsgDontWait(self):
@@ -5634,7 +5632,7 @@ def test_setblocking_invalidfd(self):
             sock.setblocking(False)
 
 
-@unittest.skipUnless(sys.platform == 'linux', 'Linux specific test')
+@unittest.skipUnless(sys.platform in ('linux', 'android'), 'Linux specific test')
 class TestLinuxAbstractNamespace(unittest.TestCase):
 
     UNIX_PATH_MAX = 108
@@ -5759,7 +5757,8 @@ def testUnencodableAddr(self):
         self.addCleanup(os_helper.unlink, path)
         self.assertEqual(self.sock.getsockname(), path)
 
-    @unittest.skipIf(sys.platform == 'linux', 'Linux specific test')
+    @unittest.skipIf(sys.platform in ('linux', 'android'),
+                     'Linux behavior is tested by TestLinuxAbstractNamespace')
     def testEmptyAddress(self):
         # Test that binding empty address fails.
         self.assertRaises(OSError, self.sock.bind, "")
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 1b18230d83577d..235206bafb937d 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -4871,7 +4871,7 @@ def run(self):
                         pass  # closed, protocol error, etc.
 
     def non_linux_skip_if_other_okay_error(self, err):
-        if sys.platform == "linux":
+        if sys.platform in ("linux", "android"):
             return  # Expect the full test setup to always work on Linux.
         if (isinstance(err, ConnectionResetError) or
             (isinstance(err, OSError) and err.errno == errno.EINVAL) or
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 38dcabd84d8170..37c16cd1047885 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -668,7 +668,7 @@ def test_thread_info(self):
         self.assertEqual(len(info), 3)
         self.assertIn(info.name, ('nt', 'pthread', 'pthread-stubs', 'solaris', None))
         self.assertIn(info.lock, ('semaphore', 'mutex+cond', None))
-        if sys.platform.startswith(("linux", "freebsd")):
+        if sys.platform.startswith(("linux", "android", "freebsd")):
             self.assertEqual(info.name, "pthread")
         elif sys.platform == "win32":
             self.assertEqual(info.name, "nt")
@@ -1101,8 +1101,7 @@ def __del__(self):
         self.assertEqual(stdout.rstrip(), b"")
         self.assertEqual(stderr.rstrip(), b"")
 
-    @unittest.skipUnless(hasattr(sys, 'getandroidapilevel'),
-                         'need sys.getandroidapilevel()')
+    @unittest.skipUnless(sys.platform == "android", "Android only")
     def test_getandroidapilevel(self):
         level = sys.getandroidapilevel()
         self.assertIsInstance(level, int)
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index bb87bf00dc2d1a..c8315bbc8b727d 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -1,3 +1,5 @@
+import platform
+import re
 import unittest
 import sys
 import os
@@ -516,12 +518,9 @@ def test_EXT_SUFFIX_in_vars(self):
         vars = sysconfig.get_config_vars()
         self.assertEqual(vars['EXT_SUFFIX'], _imp.extension_suffixes()[0])
 
-    @unittest.skipUnless(sys.platform == 'linux' and
-                         hasattr(sys.implementation, '_multiarch'),
-                         'multiarch-specific test')
-    def test_triplet_in_ext_suffix(self):
+    @unittest.skipUnless(sys.platform == 'linux', 'Linux-specific test')
+    def test_linux_ext_suffix(self):
         ctypes = import_module('ctypes')
-        import platform, re
         machine = platform.machine()
         suffix = sysconfig.get_config_var('EXT_SUFFIX')
         if re.match('(aarch64|arm|mips|ppc|powerpc|s390|sparc)', machine):
@@ -534,6 +533,19 @@ def test_triplet_in_ext_suffix(self):
             self.assertTrue(suffix.endswith(expected_suffixes),
                             f'unexpected suffix {suffix!r}')
 
+    @unittest.skipUnless(sys.platform == 'android', 'Android-specific test')
+    def test_android_ext_suffix(self):
+        machine = platform.machine()
+        suffix = sysconfig.get_config_var('EXT_SUFFIX')
+        expected_triplet = {
+            "x86_64": "x86_64-linux-android",
+            "i686": "i686-linux-android",
+            "aarch64": "aarch64-linux-android",
+            "armv7l": "arm-linux-androideabi",
+        }[machine]
+        self.assertTrue(suffix.endswith(f"-{expected_triplet}.so"),
+                        f"{machine=}, {suffix=}")
+
     @unittest.skipUnless(sys.platform == 'darwin', 'OS X-specific test')
     def test_osx_ext_suffix(self):
         suffix = sysconfig.get_config_var('EXT_SUFFIX')
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index a047780fdd7e17..39541faa237b24 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -1186,7 +1186,7 @@ def _fs_supports_holes():
         #
         # The function returns False if page size is larger than 4 KiB.
         # For example, ppc64 uses pages of 64 KiB.
-        if sys.platform.startswith("linux"):
+        if sys.platform.startswith(("linux", "android")):
             # Linux evidentially has 512 byte st_blocks units.
             name = os.path.join(TEMPDIR, "sparse-test")
             with open(name, "wb") as fobj:
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index a0aeea515afbd6..fb234b7bc5962a 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -511,7 +511,7 @@ def test_process_time(self):
 
     def test_thread_time(self):
         if not hasattr(time, 'thread_time'):
-            if sys.platform.startswith(('linux', 'win')):
+            if sys.platform.startswith(('linux', 'android', 'win')):
                 self.fail("time.thread_time() should be available on %r"
                           % (sys.platform,))
             else:
diff --git a/Lib/uuid.py b/Lib/uuid.py
index 470bc0d68597ab..ef0a612a1a6ba5 100644
--- a/Lib/uuid.py
+++ b/Lib/uuid.py
@@ -59,7 +59,7 @@
     import platform
     _platform_system = platform.system()
     _AIX     = _platform_system == 'AIX'
-    _LINUX   = _platform_system == 'Linux'
+    _LINUX   = _platform_system in ('Linux', 'Android')
 
 _MAC_DELIM = b':'
 _MAC_OMITS_LEADING_ZEROES = False
diff --git a/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst b/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst
new file mode 100644
index 00000000000000..c3d325d6c9a010
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst
@@ -0,0 +1 @@
+Change Android's ``sys.platform`` from "linux" to "android".
diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c
index 0b912e332510a6..06b03bfa9a266a 100644
--- a/Misc/platform_triplet.c
+++ b/Misc/platform_triplet.c
@@ -12,8 +12,20 @@
 #undef powerpc
 #undef sparc
 #undef unix
+
 #if defined(__ANDROID__)
-    # Android is not a multiarch system.
+#  if defined(__x86_64__)
+PLATFORM_TRIPLET=x86_64-linux-android
+#  elif defined(__i386__)
+PLATFORM_TRIPLET=i686-linux-android
+#  elif defined(__aarch64__)
+PLATFORM_TRIPLET=aarch64-linux-android
+#  elif defined(__arm__)
+PLATFORM_TRIPLET=arm-linux-androideabi
+#  else
+#    error unknown Android platform
+#  endif
+
 #elif defined(__linux__)
 /*
  * BEGIN of Linux block
diff --git a/configure b/configure
index 45f232164e7094..b7cf819b4cb2ef 100755
--- a/configure
+++ b/configure
@@ -4067,6 +4067,7 @@ then
 
     case $MACHDEP in
 	aix*) MACHDEP="aix";;
+	linux-android*) MACHDEP="android";;
 	linux*) MACHDEP="linux";;
 	cygwin*) MACHDEP="cygwin";;
 	darwin*) MACHDEP="darwin";;
diff --git a/configure.ac b/configure.ac
index b7671facc65a8a..6662ee615a0218 100644
--- a/configure.ac
+++ b/configure.ac
@@ -362,6 +362,7 @@ then
 
     case $MACHDEP in
 	aix*) MACHDEP="aix";;
+	linux-android*) MACHDEP="android";;
 	linux*) MACHDEP="linux";;
 	cygwin*) MACHDEP="cygwin";;
 	darwin*) MACHDEP="darwin";;

From 1a97f87381f654d423d095cccd5d9eebb85e2a08 Mon Sep 17 00:00:00 2001
From: Malcolm Smith <smith@chaquo.com>
Date: Sat, 2 Mar 2024 12:33:56 +0000
Subject: [PATCH 2/3] Apply suggestions from code review

Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
---
 Lib/test/test_os.py                                           | 4 ++--
 .../next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst  | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index c1495ede0ccfc7..df5e8370642cbe 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -3533,8 +3533,8 @@ def test_set_get_priority(self):
 class TestSendfile(unittest.IsolatedAsyncioTestCase):
 
     DATA = b"12345abcde" * 16 * 1024  # 160 KiB
-    SUPPORT_HEADERS_TRAILERS = \
-        not sys.platform.startswith(("linux", "android", "solaris", "sunos"))
+    SUPPORT_HEADERS_TRAILERS = (
+        not sys.platform.startswith(("linux", "android", "solaris", "sunos")))
     requires_headers_trailers = unittest.skipUnless(SUPPORT_HEADERS_TRAILERS,
             'requires headers and trailers support')
     requires_32b = unittest.skipUnless(sys.maxsize < 2**32,
diff --git a/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst b/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst
index c3d325d6c9a010..187475f56b4415 100644
--- a/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst
+++ b/Misc/NEWS.d/next/Build/2024-03-01-16-44-19.gh-issue-71052.Hs-9EP.rst
@@ -1 +1 @@
-Change Android's ``sys.platform`` from "linux" to "android".
+Change Android's :data:`sys.platform` from ``"linux"`` to ``"android"``.

From d49bd1a3bf1afd715ea1aa1870ee08387fa02a65 Mon Sep 17 00:00:00 2001
From: Malcolm Smith <smith@chaquo.com>
Date: Tue, 5 Mar 2024 13:35:33 +0000
Subject: [PATCH 3/3] Update `sys.platform` documentation

---
 Doc/library/sys.rst | 45 ++++++++++++++++++++-------------------------
 1 file changed, 20 insertions(+), 25 deletions(-)

diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 380ba1090b39b3..087a3454c33272 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -1367,47 +1367,42 @@ always available.
 
 .. data:: platform
 
-   This string contains a platform identifier that can be used to append
-   platform-specific components to :data:`sys.path`, for instance.
-
-   For Unix systems, except on Linux and AIX, this is the lowercased OS name as
-   returned by ``uname -s`` with the first part of the version as returned by
-   ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, *at the time
-   when Python was built*.  Unless you want to test for a specific system
-   version, it is therefore recommended to use the following idiom::
-
-      if sys.platform.startswith('freebsd'):
-          # FreeBSD-specific code here...
-      elif sys.platform.startswith('linux'):
-          # Linux-specific code here...
-      elif sys.platform.startswith('aix'):
-          # AIX-specific code here...
-
-   For other systems, the values are:
+   A string containing a platform identifier. Known values are:
 
    ================ ===========================
    System           ``platform`` value
    ================ ===========================
    AIX              ``'aix'``
+   Android          ``'android'``
    Emscripten       ``'emscripten'``
+   iOS              ``'ios'``
    Linux            ``'linux'``
-   WASI             ``'wasi'``
+   macOS            ``'darwin'``
    Windows          ``'win32'``
    Windows/Cygwin   ``'cygwin'``
-   macOS            ``'darwin'``
+   WASI             ``'wasi'``
    ================ ===========================
 
+   On Unix systems not listed in the table, the value is the lowercased OS name
+   as returned by ``uname -s``, with the first part of the version as returned by
+   ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, *at the time
+   when Python was built*.  Unless you want to test for a specific system
+   version, it is therefore recommended to use the following idiom::
+
+      if sys.platform.startswith('freebsd'):
+          # FreeBSD-specific code here...
+
    .. versionchanged:: 3.3
       On Linux, :data:`sys.platform` doesn't contain the major version anymore.
-      It is always ``'linux'``, instead of ``'linux2'`` or ``'linux3'``.  Since
-      older Python versions include the version number, it is recommended to
-      always use the ``startswith`` idiom presented above.
+      It is always ``'linux'``, instead of ``'linux2'`` or ``'linux3'``.
 
    .. versionchanged:: 3.8
       On AIX, :data:`sys.platform` doesn't contain the major version anymore.
-      It is always ``'aix'``, instead of ``'aix5'`` or ``'aix7'``.  Since
-      older Python versions include the version number, it is recommended to
-      always use the ``startswith`` idiom presented above.
+      It is always ``'aix'``, instead of ``'aix5'`` or ``'aix7'``.
+
+   .. versionchanged:: 3.13
+      On Android, :data:`sys.platform` now returns ``'android'`` rather than
+      ``'linux'``.
 
    .. seealso::