Snap for 11762235 from 7cc540d9ec9c0e1f27f986a3e8dd87b42aca723b to sdk-release

Change-Id: Ifb0bc5e724c697c3a70b9b76816629257b511ee5
diff --git a/atest/Android.bp b/atest/Android.bp
index e07880f..91cb723 100644
--- a/atest/Android.bp
+++ b/atest/Android.bp
@@ -46,6 +46,11 @@
     data: [
         "bazel/resources/**/*",
     ],
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
 }
 
 python_binary_host {
@@ -54,18 +59,13 @@
     main: "atest_main.py",
     data: [
         ":atest_flag_list_for_completion",
+        ":atest_log_uploader",
     ],
     // Make atest's built name be atest-dev
     stem: "atest-dev",
     dist: {
         targets: ["droidcore"],
     },
-    version: {
-        py3: {
-            // TODO(b/271346015): Enable embedded_launcher
-            embedded_launcher: false,
-        },
-    },
 }
 
 python_binary_host {
@@ -80,11 +80,6 @@
     libs: [
         "atest_ca_certs_locater",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 python_binary_host {
@@ -110,11 +105,6 @@
     name: "atest_flag_list_generator",
     defaults: ["atest_defaults"],
     main: "atest_flag_list_generator.py",
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 genrule {
diff --git a/atest/integration_tests/atest_command_success_tests.py b/atest/integration_tests/atest_command_success_tests.py
index f721b6d..99e6031 100644
--- a/atest/integration_tests/atest_command_success_tests.py
+++ b/atest/integration_tests/atest_command_success_tests.py
@@ -25,24 +25,34 @@
   def test_example_instrumentation_tests(self):
     """Test if atest can run for the example instrumentation test path."""
     test_path = 'platform_testing/tests/example/instrumentation'
-    self._verify_atest_command_success(test_path, [test_path])
+    self._verify_atest_command_success(
+        test_path, is_device_required=True, snapshot_include_paths=[test_path]
+    )
 
   def test_csuite_harness_tests(self):
     """Test if csuite-harness-tests command runs successfully."""
-    self._verify_atest_command_success('csuite-harness-tests --no-bazel-mode --host')
+    self._verify_atest_command_success(
+        'csuite-harness-tests --no-bazel-mode --host', is_device_required=False
+    )
 
   def test_csuite_cli_test(self):
     """Test if csuite_cli_test command runs successfully."""
-    self._verify_atest_command_success('csuite_cli_test --no-bazel-mode --host')
+    self._verify_atest_command_success(
+        'csuite_cli_test --no-bazel-mode --host', is_device_required=False
+    )
 
   def _verify_atest_command_success(
-      self, cmd: str, snapshot_include_paths: list[str] = None
+      self,
+      cmd: str,
+      is_device_required: bool,
+      snapshot_include_paths: list[str] = None,
   ) -> None:
     """Verifies whether an Atest command run completed with exit code 0.
 
     Args:
         cmd: The atest command to run. Note to leave 'atest' or 'atest-dev' out
           from the command.
+        is_device_required: Whether the test requires a device.
         snapshot_include_paths: Any source paths needed to run the test in test
           environment.
     """
@@ -51,14 +61,18 @@
     def build_step(
         step_in: atest_integration_test.StepInput,
     ) -> atest_integration_test.StepOutput:
-      self.run_atest_command(cmd + ' -cb', step_in).check_returncode()
+      self.run_atest_command(
+          cmd + ' -cb', step_in, include_device_serial=False
+      ).check_returncode()
       step_out = self.create_step_output()
       if snapshot_include_paths:
         step_out.add_snapshot_include_paths(snapshot_include_paths)
       return step_out
 
     def test_step(step_in: atest_integration_test.StepInput) -> None:
-      self.run_atest_command(cmd + ' -it', step_in).check_returncode()
+      self.run_atest_command(
+          cmd + ' -it', step_in, include_device_serial=is_device_required
+      ).check_returncode()
 
     script.add_build_step(build_step)
     script.add_test_step(test_step)
diff --git a/atest/integration_tests/atest_command_verification_tests.py b/atest/integration_tests/atest_command_verification_tests.py
index ec7b3a6..134e823 100644
--- a/atest/integration_tests/atest_command_verification_tests.py
+++ b/atest/integration_tests/atest_command_verification_tests.py
@@ -16,7 +16,6 @@
 
 """A collection of integration test cases for atest."""
 
-import logging
 import os
 from typing import Any, Callable
 import atest_integration_test
@@ -630,6 +629,7 @@
         atest_integration_test.StepInput(
             os.environ, os.environ['ANDROID_BUILD_TOP'], cls.get_config(), {}
         ),
+        include_device_serial=False,
         print_output=False,
     ).check_returncode()
 
@@ -711,7 +711,9 @@
     def build_step(
         step_in: atest_integration_test.StepInput,
     ) -> atest_integration_test.StepOutput:
-      result = self.run_atest_command(atest_cmd + ' --dry-run -cit', step_in)
+      result = self.run_atest_command(
+          atest_cmd + ' --dry-run -cit', step_in, include_device_serial=False
+      )
       result.check_returncode()
       runner_cmd = result.get_atest_log_values_from_prefix(
           _DRY_RUN_COMMAND_LOG_PREFIX
diff --git a/atest/integration_tests/atest_integration_test.py b/atest/integration_tests/atest_integration_test.py
index fd60477..502c324 100644
--- a/atest/integration_tests/atest_integration_test.py
+++ b/atest/integration_tests/atest_integration_test.py
@@ -347,6 +347,7 @@
       cls,
       cmd: str,
       step_in: split_build_test_script.StepInput,
+      include_device_serial: bool,
       print_output: bool = True,
       use_prebuilt_atest_binary=None,
   ) -> AtestRunResult:
@@ -356,6 +357,10 @@
         cmd: command string for Atest. Do not add 'atest-dev' or 'atest' in the
           beginning of the command.
         step_in: The step input object from build or test step.
+        include_device_serial: Whether a device is required for the atest
+          command. This argument is only used to determine whether to include
+          device serial in the command. It does not add device/deviceless
+          arguments such as '--host'.
         print_output: Whether to print the stdout and stderr while the command
           is running.
         use_prebuilt_atest_binary: Whether to run the command using the prebuilt
@@ -366,15 +371,21 @@
     """
     if use_prebuilt_atest_binary is None:
       use_prebuilt_atest_binary = step_in.get_config().use_prebuilt_atest_binary
-    complete_cmd = (
-        f'{"atest" if use_prebuilt_atest_binary else "atest-dev"}'
-        f' {cmd}{step_in.get_device_serial_args_or_empty()}'
+    atest_binary = 'atest' if use_prebuilt_atest_binary else 'atest-dev'
+
+    # TODO: b/336839543 - Throw error here when serial is required but not set
+    # instead of from step_in.get_device_serial_args_or_empty()
+    serial_arg = (
+        step_in.get_device_serial_args_or_empty()
+        if include_device_serial
+        else ''
     )
+    complete_cmd = f'{atest_binary}{serial_arg} {cmd}'
 
     indentation = '  '
     logging.debug('Executing atest command: %s', complete_cmd)
     logging.debug(
-        indentation + 'Command environment variables: %s', step_in.get_env()
+        '%sCommand environment variables: %s', indentation, step_in.get_env()
     )
     result = AtestRunResult(
         cls._run_shell_command(
@@ -389,14 +400,16 @@
     )
 
     wrap_output_lines = lambda output_str: ''.join((
-        indentation * 2 + '> %s' % line for line in output_str.splitlines(True)
+        f'{indentation * 2}> %s' % line for line in output_str.splitlines(True)
     ))
     logging.debug(
-        indentation + 'Command stdout:\n%s',
+        '%sCommand stdout:\n%s',
+        indentation,
         wrap_output_lines(result.get_stdout()),
     )
     logging.debug(
-        indentation + 'Atest log:\n%s',
+        '%sAtest log:\n%s',
+        indentation,
         wrap_output_lines(result.get_atest_log()),
     )
 
diff --git a/atest/integration_tests/atest_test_archetype_integration_tests.py b/atest/integration_tests/atest_test_archetype_integration_tests.py
index f9129af..ffa94d2 100644
--- a/atest/integration_tests/atest_test_archetype_integration_tests.py
+++ b/atest/integration_tests/atest_test_archetype_integration_tests.py
@@ -26,6 +26,7 @@
     _verify_test_passed_failed_ignored_counts(
         self,
         atest_command=self._TARGET_NAME + ' --no-bazel-mode --host',
+        is_device_required=False,
         expected_passed_count=2,
         expected_failed_count=1,
         expected_ignored_count=0,
@@ -39,6 +40,7 @@
     _verify_test_passed_failed_ignored_counts(
         self,
         atest_command=self._TARGET_NAME + ' --no-bazel-mode --host',
+        is_device_required=False,
         expected_passed_count=2,
         expected_failed_count=1,
         expected_ignored_count=0,
@@ -52,6 +54,7 @@
     _verify_test_passed_failed_ignored_counts(
         self,
         atest_command=self._TARGET_NAME,
+        is_device_required=True,
         expected_passed_count=2,
         expected_failed_count=1,
         expected_ignored_count=0,
@@ -61,6 +64,7 @@
 def _verify_test_passed_failed_ignored_counts(
     test_case: atest_integration_test.AtestTestCase,
     atest_command: str,
+    is_device_required: bool,
     expected_passed_count: int,
     expected_failed_count: int,
     expected_ignored_count: int,
@@ -71,6 +75,7 @@
       test_case: The reference to the calling test case.
       atest_command: The atest command to execute. Note: exclude 'atest',
         'atest-dev', '-b', '-i', and '-t' from it.
+      is_device_required: Whether the test requires a device.
       expected_passed_count: Number of expected passed count.
       expected_failed_count: Number of expected failed count.
       expected_ignored_count: Number of expected ignored count.
@@ -83,14 +88,17 @@
   ) -> atest_integration_test.StepOutput:
 
     test_case.run_atest_command(
-        atest_command + ' -cb', step_in
+        atest_command + ' -cb', step_in, include_device_serial=False
     ).check_returncode()
 
     return test_case.create_step_output()
 
   def test_step(step_in: atest_integration_test.StepInput) -> None:
     result = test_case.run_atest_command(
-        atest_command + ' -it', step_in, print_output=False
+        atest_command + ' -it',
+        step_in,
+        include_device_serial=is_device_required,
+        print_output=False,
     )
 
     test_case.assertEqual(result.get_passed_count(), expected_passed_count)
diff --git a/atest/integration_tests/split_build_test_script.py b/atest/integration_tests/split_build_test_script.py
index 7f2e975..bae93d2 100644
--- a/atest/integration_tests/split_build_test_script.py
+++ b/atest/integration_tests/split_build_test_script.py
@@ -58,7 +58,6 @@
   device_serial: str = None
   is_build_env: bool = False
   is_test_env: bool = False
-  is_device_serial_required = True
   snapshot_storage_path: pathlib.Path = None
   snapshot_storage_tar_path: pathlib.Path = None
   workspace_path: pathlib.Path = None
@@ -76,10 +75,17 @@
 
   def get_device_serial_args_or_empty(self) -> str:
     """Gets command arguments for device serial. May return empty string."""
+    # TODO: b/336839543 - Remove this method when we deprecate the support to
+    # run the integration test directly through 'python **.py' command.
     if self._config.device_serial:
       return ' -s ' + self._config.device_serial
-    if self._config.is_device_serial_required:
+    if ANDROID_BUILD_TOP_KEY not in os.environ and self._config.is_test_env:
+      # Likely in test lab environment, where connected devices can are
+      # allocated to other tests. In this case we must explicitly set device
+      # serials in any atest calls .
       raise RuntimeError('Device serial is required but not set')
+    # Empty is allowed because it allows tradefed to decide which device to
+    # select in local run.
     return ''
 
   def get_device_serial(self) -> str:
@@ -914,9 +920,6 @@
   )
   config.snapshot_storage_tar_path = snapshot_storage_tar_path
   config.workspace_path = integration_test_out_path.joinpath('workspace')
-  # Device serial is not required during local run, and
-  # ANDROID_BUILD_TOP_KEY env being available implies it's local run.
-  config.is_device_serial_required = ANDROID_BUILD_TOP_KEY not in os.environ
   config.is_tar_snapshot = args.tar_snapshot
 
   if config_update_function:
diff --git a/atest/logstorage/log_uploader.py b/atest/logstorage/log_uploader.py
index ce37901..a8fe86f 100644
--- a/atest/logstorage/log_uploader.py
+++ b/atest/logstorage/log_uploader.py
@@ -215,7 +215,7 @@
 
 def upload_logs_detached(logs_dir: pathlib.Path):
   """Upload logs to AnTS in a detached process."""
-  if not os.environ.get(_ENABLE_ATEST_LOG_UPLOADING_ENV_KEY, '').lower() in [
+  if os.environ.get(_ENABLE_ATEST_LOG_UPLOADING_ENV_KEY, '1').lower() not in [
       'true',
       '1',
   ]:
diff --git a/atest/logstorage/log_uploader_unittest.py b/atest/logstorage/log_uploader_unittest.py
index 3084919..09ca398 100644
--- a/atest/logstorage/log_uploader_unittest.py
+++ b/atest/logstorage/log_uploader_unittest.py
@@ -88,12 +88,21 @@
       self, mock_process
   ):
     self._set_upload_constants_available(True)
-    self._set_upload_env_var(None)
+    self._set_upload_env_var('false')
 
     log_uploader.upload_logs_detached(pathlib.Path('any'))
 
     mock_process.assert_not_called()
 
+  @patch('multiprocessing.Process.__new__')
+  def test_upload_logs_detached_process_started_by_default(self, mock_process):
+    self._set_upload_constants_available(True)
+    self._set_upload_env_var(None)
+
+    log_uploader.upload_logs_detached(pathlib.Path('any'))
+
+    mock_process.assert_called_once()
+
   def _set_upload_env_var(self, value) -> None:
     """Set upload environment variable."""
     if value is None: