Merge "Mark 24Q4 as merged in aosp-main-future" into aosp-main-future
diff --git a/atest/atest_execution_info.py b/atest/atest_execution_info.py
index 0f35b98..064f096 100644
--- a/atest/atest_execution_info.py
+++ b/atest/atest_execution_info.py
@@ -389,10 +389,10 @@
print(f'Test logs: {log_path / "log"}')
log_link = html_path if html_path else log_path
if log_link:
- print(f'Log file list: {atest_utils.mark_magenta(f"file://{log_link}")}')
+ print(atest_utils.mark_magenta(f'Log file list: file://{log_link}'))
bug_report_url = AtestExecutionInfo._create_bug_report_url()
if bug_report_url:
- print(f'Issue report: {bug_report_url}')
+ print(atest_utils.mark_magenta(f"Bug report: {bug_report_url}"))
print()
# Do not send stacktrace with send_exit_event when exit code is not
diff --git a/atest/atest_main.py b/atest/atest_main.py
index fe12d41..590dcad 100755
--- a/atest/atest_main.py
+++ b/atest/atest_main.py
@@ -916,12 +916,6 @@
logging.debug('"--test" mode detected, will not rebuild module-info.')
return False
if self._args.rebuild_module_info:
- msg = (
- f'`{constants.REBUILD_MODULE_INFO_FLAG}` is no longer needed '
- f'since Atest can smartly rebuild {module_info._MODULE_INFO} '
- r'only when needed.'
- )
- atest_utils.colorful_print(msg, constants.YELLOW)
return True
logging.debug('Examinating the consistency of build files...')
if not atest_utils.build_files_integrity_is_ok():
diff --git a/atest/integration_tests/atest_dry_run_diff_tests.py b/atest/integration_tests/atest_dry_run_diff_tests.py
index b58b6ed..5031b5c 100644
--- a/atest/integration_tests/atest_dry_run_diff_tests.py
+++ b/atest/integration_tests/atest_dry_run_diff_tests.py
@@ -97,6 +97,7 @@
step_in,
include_device_serial=False,
use_prebuilt_atest_binary=use_prod,
+ pipe_to_stdin='n',
)
get_prod_result = functools.partial(run_command, True)
get_dev_result = functools.partial(run_command, False)
diff --git a/atest/integration_tests/atest_integration_test.py b/atest/integration_tests/atest_integration_test.py
index bf14881..c8416d6 100644
--- a/atest/integration_tests/atest_integration_test.py
+++ b/atest/integration_tests/atest_integration_test.py
@@ -344,6 +344,7 @@
include_device_serial: bool,
print_output: bool = True,
use_prebuilt_atest_binary=None,
+ pipe_to_stdin: str = None,
) -> AtestRunResult:
"""Run either `atest-dev` or `atest` command through subprocess.
@@ -359,6 +360,8 @@
is running.
use_prebuilt_atest_binary: Whether to run the command using the prebuilt
atest binary instead of the atest-dev binary.
+ pipe_to_stdin: A string value to pipe continuously to the stdin of the
+ command subprocess.
Returns:
An AtestRunResult object containing the run information.
@@ -387,6 +390,7 @@
env=step_in.get_env(),
cwd=step_in.get_repo_root(),
print_output=print_output,
+ pipe_to_stdin=pipe_to_stdin,
)
elapsed_time = time.time() - start_time
result = AtestRunResult(
@@ -419,39 +423,53 @@
env: dict[str, str],
cwd: str,
print_output: bool = True,
+ pipe_to_stdin: str = None,
) -> subprocess.CompletedProcess[str]:
"""Execute shell command with real time output printing and capture."""
- def read_output(read_src, print_dst, capture_dst):
+ def read_output(process, read_src, print_dst, capture_dst):
while (output := read_src.readline()) or process.poll() is None:
if output:
if print_output:
print(output, end='', file=print_dst)
capture_dst.append(output)
- with subprocess.Popen(
- cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- env=env,
- cwd=cwd,
- ) as process:
- stdout = []
- stderr = []
- with concurrent.futures.ThreadPoolExecutor() as executor:
- stdout_future = executor.submit(
- read_output, process.stdout, sys.stdout, stdout
- )
- stderr_future = executor.submit(
- read_output, process.stderr, sys.stderr, stderr
- )
- stdout_future.result()
- stderr_future.result()
+ # Disable log uploading when running locally.
+ env['ENABLE_ATEST_LOG_UPLOADING'] = 'false'
- return subprocess.CompletedProcess(
- cmd, process.poll(), ''.join(stdout), ''.join(stderr)
- )
+ def run_popen(stdin=None):
+ with subprocess.Popen(
+ cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ stdin=stdin,
+ text=True,
+ env=env,
+ cwd=cwd,
+ ) as process:
+ stdout = []
+ stderr = []
+ with concurrent.futures.ThreadPoolExecutor() as executor:
+ stdout_future = executor.submit(
+ read_output, process, process.stdout, sys.stdout, stdout
+ )
+ stderr_future = executor.submit(
+ read_output, process, process.stderr, sys.stderr, stderr
+ )
+ stdout_future.result()
+ stderr_future.result()
+
+ return subprocess.CompletedProcess(
+ cmd, process.poll(), ''.join(stdout), ''.join(stderr)
+ )
+
+ if pipe_to_stdin:
+ with subprocess.Popen(
+ ['yes', pipe_to_stdin], stdout=subprocess.PIPE
+ ) as yes_process:
+ return run_popen(yes_process.stdout)
+
+ return run_popen()
@staticmethod
def _get_jdk_path_list() -> str:
diff --git a/experiments/a/a.py b/experiments/a/a.py
index 4399ac2..da66e54 100644
--- a/experiments/a/a.py
+++ b/experiments/a/a.py
@@ -35,22 +35,20 @@
def run():
"""Entry point for tool."""
parser = argparse.ArgumentParser(
- description='Run workflows to build update and test modules',
+ description='A runs tools and workflows for local Android development',
formatter_class=argparse.RawDescriptionHelpFormatter,
)
- parser.add_argument(
- '-q',
- '--quiet',
- action='store_true',
- help='Do not display progress updates',
- )
- subparsers = parser.add_subparsers(dest='tool', required=True)
+ subparsers = parser.add_subparsers(dest='tool')
for _, tool_class in tools_map.items():
tool_class.add_parser(subparsers)
args = parser.parse_args()
# Tool
+ if not args.tool:
+ print('Error: Please specify a tool (eg. update)')
+ parser.print_help()
+ exit(1)
tool_name = args.tool.lower()
tool = tools_map[tool_name](args)
return tool.main()
diff --git a/experiments/a/tools/update.py b/experiments/a/tools/update.py
index a505af7..ea6663d 100644
--- a/experiments/a/tools/update.py
+++ b/experiments/a/tools/update.py
@@ -18,13 +18,13 @@
"""Update Tool."""
import argparse
-import inspect
-import os
-import sys
from core.errors import WorkflowError
from core.task_runner import Task
from core.task_runner import TaskRunner
+from tools.update_aliases import get_aliases
+from tools.update_utils import combine_build_commands
+from tools.update_utils import combine_update_commands
class Update:
@@ -37,9 +37,13 @@
def add_parser(cls, subparsers):
"""Parse command line update arguments."""
+ aliases = get_aliases()
epilog = 'Aliases:\n'
for alias in get_aliases().keys():
- epilog += f' {alias}\n'
+ name = alias
+ build_commands = (';').join(aliases[name].build())
+ update_commands = (';').join(aliases[name].update())
+ epilog += f' {name}:\n\t{build_commands}\n\t{update_commands}\n'
parser = subparsers.add_parser(
'update', epilog=epilog, formatter_class=argparse.RawTextHelpFormatter
@@ -49,12 +53,15 @@
parser.add_argument(
'--build-only',
action='store_true',
- help='Only build the specified targets, do not update the device.',
+ help='only build the specified targets, do not update the device.',
)
parser.add_argument(
'--update-only',
action='store_true',
- help='Only update the device with prebuilt targets, do not build.',
+ help=(
+ 'only update the device with prebuilt targets, do not build'
+ ' targets.'
+ ),
)
def main(self):
@@ -65,23 +72,23 @@
def gather_tasks(self):
"""Gathers tasks to run based on alias."""
tasks = []
-
- requested_aliases = self.args.alias
- aliases = get_aliases()
- if len(requested_aliases) >= 1:
- for a in requested_aliases:
- if a not in aliases:
- raise WorkflowError(f'Unknown Alias {a}')
-
build_tasks = []
update_tasks = []
- if len(requested_aliases) == 1:
- a = requested_aliases[0]
+ requested_aliases = self.args.alias
+ aliases = get_aliases()
+ for a in requested_aliases:
+ if a not in aliases:
+ raise WorkflowError(f'unknown alias: {a}')
config = aliases[a]
build_tasks += config.build()
update_tasks += config.update()
+ # combine build tasks
+ build_tasks = combine_build_commands(build_tasks)
+ # combine update tasks
+ update_tasks = combine_update_commands(update_tasks)
+
if self.args.build_only:
tasks = build_tasks
elif self.args.update_only:
@@ -115,424 +122,3 @@
else:
task_runner.add_task(task)
task_runner.start()
-
-
-class Alias:
- """Base class for defining an alias."""
-
- def build(self):
- return []
-
- def update(self):
- return []
-
-
-class Core(Alias):
- """Alias for Core."""
-
- def build(self):
- return ['m framework framework-minus-apex']
-
- def update(self):
- return [
- 'adevice update',
- ]
-
-
-class SystemServer(Alias):
- """Alias for SystemServer."""
-
- def update(self):
- return [
- 'adevice update --restart=none',
- 'adb kill systemserver',
- ]
-
-
-class SysUI(Alias):
- """Alias for SystemUI."""
-
- def build(self):
- if is_nexus():
- raise WorkflowError(
- "Target 'sysui' is not allowed on Nexus Experience devices.\n"
- 'Try sysuig (with g at the end) or sysuititan'
- )
- return ['m framework framework-minus-apex SystemUI']
-
- def update(self):
- target = 'com.android.systemui'
- return [
- 'adevice update --restart=none',
- f'adb shell "am force-stop {target}"',
- ]
-
-
-class SysUIG(Alias):
- """Alias for SystemUI for Google Devices."""
-
- def build(self):
- if not is_nexus():
- raise WorkflowError(
- "Target 'sysuig' is only allowed on Nexus Experience devices.\n"
- 'Try sysui (no g at the end)'
- )
- return ['m framework framework-minus-apex SystemUIGoogle']
-
- def update(self):
- target = 'com.android.systemui'
- return [
- 'adevice update --restart=none',
- f'adb shell am force-stop {target}',
- ]
-
-
-class SysUITitan(Alias):
- """Alias for SystemUI Titan devices."""
-
- def build(self):
- if not is_nexus():
- raise WorkflowError(
- "Target 'sysuititan' is only allowed on Nexus Experience devices.\n"
- 'Try sysui (no g at the end)'
- )
- return ['m framework framework-minus-apex SystemUITitan']
-
- def update(self):
- target = 'com.android.systemui'
- return [
- 'adevice update --restart=none',
- f'adb shell am force-stop {target}',
- ]
-
-
-class SysUIGo(Alias):
- """Alias for SystemUI."""
-
- def build(self):
- if not is_nexus():
- raise WorkflowError(
- "Target 'sysuigo' is only allowed on Nexus Experience devices.\n"
- 'Try sysui (no go at the end)'
- )
- return ['m framework framework-minus-apex SystemUIGo']
-
- def update(self):
- target = 'com.android.systemui'
- return [
- 'adevice update --restart=none',
- f'adb shell am force-stop {target}',
- ]
-
-
-class CarSysUI(Alias):
- """Alias for CarSystemUI."""
-
- def build(self):
- return ['m framework framework-minus-apex CarSystemUI']
-
- def update(self):
- target = 'com.android.systemui'
- return [
- 'adevice update --restart=none',
- f'adb shell am force-stop {target}',
- ]
-
-
-class CarSysUIG(Alias):
- """Alias for CarSystemUI."""
-
- def build(self):
- return ['m framework framework-minus-apex AAECarSystemUI']
-
- def update(self):
- target = 'com.android.systemui'
- return [
- 'adevice update --restart=none',
- f'adb shell am force-stop {target}',
- ]
-
-
-# These definitions are imported from makepush
-# https://team.git.corp.google.com/android-framework/toolbox/+/refs/heads/main/makepush/makepush.sh
-alias_definitions = {
- 'droid': {
- 'build': 'droid',
- 'update': 'flashall',
- },
- 'snod': {
- 'build': 'snod',
- 'update': 'flashall',
- },
- 'core_jni': {'build': 'libandroid_runtime'},
- 'res_jni': {'build': 'libandroidfw libidmap2'},
- 'idmap2': {'build': 'idmap2 idmap2d'},
- 'sf': {'build': 'surfaceflinger'},
- 'res': {'build': 'framework-res'},
- 'services': {'build': 'services protolog.conf.json.gz'},
- 'inputflinger': {'build': 'libinputflinger'},
- 'carsysui': {
- 'build': 'carSystemUI',
- 'update': 'adb shell am force-stop com.android.systemui',
- },
- 'carsysuig': {
- 'build': 'AAECarSystemUI',
- 'update': 'adb shell am force-stop com.android.systemui',
- },
- 'car-mainline': {
- 'build': 'AAECarSystemUI',
- 'update': (
- 'adb install -r --staged --enable-rollback'
- ' $OUT/system/apex/com.android.car.framework.apex'
- ),
- },
- 'carfwk': {'build': 'carfwk car-frameworks-service'},
- 'carfwk-module': {'build': 'car-frameworks-service-module'},
- 'carsettings': {
- 'build': 'carSettings',
- 'update': 'adb shell am force-stop com.android.car.settings',
- },
- 'carks': {
- 'build': 'EmbeddedKitchenSinkApp',
- 'update': 'adb shell am force-stop com.google.android.car.kitchensink',
- },
- 'carlauncher': {
- 'build': 'carLauncher',
- 'update': 'adb shell am force-stop com.android.car.carlauncher',
- },
- 'carlauncherg': {
- 'build': 'GASCarLauncher',
- 'update': 'adb shell am force-stop com.android.car.carlauncher',
- },
- 'car-md-launcher': {
- 'build': 'MultiDisplaySecondaryHomeTestLauncher',
- 'update': (
- 'adb install'
- ' $OUT/system/priv-app/MultiDisplaySecondaryHomeTestLauncher/MultiDisplaySecondaryHomeTestLauncher.apk'
- ),
- },
- 'carsuw': {
- 'build': 'carProvision',
- 'update': 'adb shell am force-stop com.android.car.provision',
- },
- 'car': {'build': 'android.car'},
- 'car-builtin': {'build': 'android.car.builtin'},
- 'vhal-legacy': {
- 'build': 'android.hardware.automotive.vehicle@2.0-service',
- 'update': (
- 'adb shell am force-stop'
- ' android.hardware.automotive.vehicle@2.0-service'
- ),
- },
- 'vhal': {
- 'build': 'android.hardware.automotive.vehicle@V1-default-service',
- 'update': (
- 'adb shell am force-stop'
- ' android.hardware.automotive.vehicle@V1-default-service'
- ),
- },
- 'vhal-pasa': {
- 'build': 'android.hardware.automotive.vehicle@V1-pasa-service',
- 'update': (
- 'adb shell am force-stop'
- ' android.hardware.automotive.vehicle@V1-pasa-service'
- ),
- },
- 'launcher': {'build': 'NexusLauncherRelease'},
- 'launcherd': {
- 'build': 'nexusLauncherDebug',
- 'update': (
- 'adb install'
- ' $OUT/anywhere/priv-app/NexusLauncherDebug/NexusLauncherDebug.apk'
- ),
- },
- 'launchergo': {
- 'build': 'launcherGoGoogle',
- 'update': 'adb shell am force-stop com.android.launcher3',
- },
- 'intentresolver': {
- 'build': 'intentResolver',
- 'update': 'adb shell am force-stop com.android.intentresolver',
- },
- 'sysuig': {
- 'build': 'systemUIGoogle',
- 'update': 'adb shell am force-stop com.android.systemui',
- },
- 'sysuititan': {
- 'build': 'systemUITitan',
- 'update': 'adb shell am force-stop com.android.systemui',
- },
- 'sysuigo': {
- 'build': 'systemUIGo',
- 'update': 'adb shell am force-stop com.android.systemui',
- },
- 'flagflipper': {
- 'build': 'theFlippinApp',
- 'update': 'adb shell am force-stop com.android.theflippinapp',
- },
- 'docsui': {
- 'build': 'documentsUI',
- 'update': 'adb shell am force-stop com.android.documentsui',
- },
- 'docsuig': {
- 'build': 'documentsUIGoogle',
- 'update': 'adb shell am force-stop com.google.android.documentsui',
- },
- 'settings': {
- 'build': 'settings',
- 'update': 'adb shell am force-stop com.android.settings',
- },
- 'settingsg': {
- 'build': 'SettingsGoogle',
- 'update': 'adb shell am force-stop com.google.android.settings',
- },
- 'settingsgf': {
- 'build': 'SettingsGoogleFutureFaceEnroll',
- 'update': (
- 'adb shell am force-stop'
- ' com.google.android.settings.future.biometrics.faceenroll'
- ),
- },
- 'settings_provider': {'build': 'SettingsProvider'},
- 'apidemos': {
- 'build': 'ApiDemos',
- 'update': (
- 'adb install'
- ' $OUT/testcases/ApiDemos/$var_cache_TARGET_ARCH/ApiDemos.apk'
- ),
- },
- 'teleservice': {
- 'build': 'TeleService',
- 'update': 'adb shell am force-stop com.android.phone',
- },
- 'managed_provisioning': {
- 'build': 'ManagedProvisioning',
- 'update': 'adb shell am force-stop com.android.managedprovisioning',
- },
- 'car_managed_provisioning': {
- 'build': 'carManagedProvisioning',
- 'update': (
- 'adb install'
- ' $OUT/anywhere/priv-app/CarManagedProvisioning/CarManagedProvisioning.apk'
- ),
- },
- 'ctsv': {
- 'build': 'ctsVerifier',
- 'update': (
- 'adb install'
- ' $OUT/testcases/CtsVerifier/$var_cache_TARGET_ARCH/CtsVerifier.apk'
- ),
- },
- 'gtsv': {
- 'build': 'gtsVerifier',
- 'update': (
- 'adb install'
- ' $OUT/testcases/GtsVerifier/$var_cache_TARGET_ARCH/GtsVerifier.apk'
- ),
- },
- 'suw': {
- 'build': 'Provision',
- 'update': 'adb shell am force-stop com.android.provision',
- },
- 'pkg_installer': {
- 'build': 'PackageInstaller',
- 'update': 'adb shell am force-stop com.android.packageinstaller',
- },
- 'pkg_installer_g': {
- 'build': 'GooglePackageInstaller',
- 'update': 'adb shell am force-stop com.google.android.packageinstaller',
- },
- 'perm_controller': {
- 'build': 'PermissionController',
- 'update': (
- 'adb install'
- ' $OUT/apex/com.android.permission/priv-app/PermissionController/PermissionController.apk'
- ),
- },
- 'perm_controller_g': {
- 'build': 'GooglePermissionController',
- 'update': (
- 'adb install -r'
- ' $OUT/apex/com.google.android.permission/priv-app/GooglePermissionController/GooglePermissionController.apk'
- ),
- },
- 'wifi': {
- 'build': 'wifi',
- 'update': (
- 'adb install -r --staged --enable-rollback'
- ' $OUT/system/apex/com.android.wifi && adb shell am force-stop'
- ' com.android.wifi'
- ),
- },
- 'vold': {'build': 'vold', 'update': 'adb shell am force-stop vold'},
- 'multidisplay': {
- 'build': 'multiDisplayProvider',
- 'update': 'adb shell am force-stop com.android.emulator.multidisplay',
- },
- 'wm_ext': {
- 'build': 'androidx.window.extensions',
- },
- 'rb': {
- 'build': 'adServicesApk',
- 'update': (
- 'adb install'
- ' $OUT/apex/com.android.adservices/priv-app/AdServices/AdServices.apk'
- ),
- },
- 'rb_g': {
- 'build': 'adServicesApkGoogle',
- 'update': (
- 'adb install'
- ' $OUT/apex/com.google.android.adservices/priv-app/AdServicesApkGoogle@MASTER/AdServicesApkGoogle.apk'
- ),
- },
- 'sdk_sandbox': {
- 'build': 'sdkSandbox',
- 'update': (
- 'adb install'
- ' $OUT/apex/com.google.android.adservices/app/SdkSandboxGoogle@MASTER/SdkSandboxGoogle.apk'
- ),
- },
-}
-
-
-# Utilities to get type of target
-def is_nexus():
- target_product = os.getenv('TARGET_PRODUCT')
- return (
- target_product.startswith('.aosp')
- or 'wembley' in target_product
- or 'gms_humuhumu' in target_product
- )
-
-
-def create_alias_from_config(config):
- """Generates a Alias class from json."""
- alias = Alias()
- build = config.get('build', None)
- if build:
- alias.build = lambda: [f'm {build}']
-
- update = config.get('update', None)
- if update:
- alias.update = lambda: [
- 'adevice update --restart=None',
- update,
- ]
- return alias
-
-
-def get_aliases():
- """Dynamically find all aliases."""
- # definitions that subclass the Alias class
- aliases = {
- name.lower(): cls()
- for name, cls in inspect.getmembers(
- sys.modules[__name__], inspect.isclass
- )
- if issubclass(cls, Alias) and cls != Alias
- }
- # definitions that are defined in alias_definitions
- for name, config in alias_definitions.items():
- aliases[name.lower()] = create_alias_from_config(config)
- return aliases
diff --git a/experiments/a/tools/update_aliases.py b/experiments/a/tools/update_aliases.py
new file mode 100644
index 0000000..e22dfee
--- /dev/null
+++ b/experiments/a/tools/update_aliases.py
@@ -0,0 +1,458 @@
+#!/usr/bin/env python3
+#
+# Copyright 2024 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Update Aliases."""
+
+import inspect
+import os
+import sys
+from core.errors import WorkflowError
+
+
+class Alias:
+ """Base class for defining an alias."""
+
+ def build(self):
+ return []
+
+ def update(self):
+ return []
+
+
+class Core(Alias):
+ """Alias for Core."""
+
+ def build(self):
+ return ['m framework framework-minus-apex']
+
+ def update(self):
+ return [
+ 'adevice update',
+ ]
+
+
+class SystemServer(Alias):
+ """Alias for SystemServer."""
+
+ def update(self):
+ return [
+ 'adevice update --restart=none',
+ 'adb kill systemserver',
+ ]
+
+
+class SysUI(Alias):
+ """Alias for SystemUI."""
+
+ def build(self):
+ if is_nexus():
+ raise WorkflowError(
+ "Target 'sysui' is not allowed on Nexus Experience devices.\n"
+ 'Try sysuig (with g at the end) or sysuititan'
+ )
+ return ['m framework framework-minus-apex SystemUI']
+
+ def update(self):
+ target = 'com.android.systemui'
+ return [
+ 'adevice update --restart=none',
+ f'adb shell "am force-stop {target}"',
+ ]
+
+
+class SysUIG(Alias):
+ """Alias for SystemUI for Google Devices."""
+
+ def build(self):
+ if not is_nexus():
+ raise WorkflowError(
+ "Target 'sysuig' is only allowed on Nexus Experience devices.\n"
+ 'Try sysui (no g at the end)'
+ )
+ return ['m framework framework-minus-apex SystemUIGoogle']
+
+ def update(self):
+ target = 'com.android.systemui'
+ return [
+ 'adevice update --restart=none',
+ f'adb shell am force-stop {target}',
+ ]
+
+
+class SysUITitan(Alias):
+ """Alias for SystemUI Titan devices."""
+
+ def build(self):
+ if not is_nexus():
+ raise WorkflowError(
+ "Target 'sysuititan' is only allowed on Nexus Experience devices.\n"
+ 'Try sysui (no g at the end)'
+ )
+ return ['m framework framework-minus-apex SystemUITitan']
+
+ def update(self):
+ target = 'com.android.systemui'
+ return [
+ 'adevice update --restart=none',
+ f'adb shell am force-stop {target}',
+ ]
+
+
+class SysUIGo(Alias):
+ """Alias for SystemUI."""
+
+ def build(self):
+ if not is_nexus():
+ raise WorkflowError(
+ "Target 'sysuigo' is only allowed on Nexus Experience devices.\n"
+ 'Try sysui (no go at the end)'
+ )
+ return ['m framework framework-minus-apex SystemUIGo']
+
+ def update(self):
+ target = 'com.android.systemui'
+ return [
+ 'adevice update --restart=none',
+ f'adb shell am force-stop {target}',
+ ]
+
+
+class CarSysUI(Alias):
+ """Alias for CarSystemUI."""
+
+ def build(self):
+ return ['m framework framework-minus-apex CarSystemUI']
+
+ def update(self):
+ target = 'com.android.systemui'
+ return [
+ 'adevice update --restart=none',
+ f'adb shell am force-stop {target}',
+ ]
+
+
+class CarSysUIG(Alias):
+ """Alias for CarSystemUI."""
+
+ def build(self):
+ return ['m framework framework-minus-apex AAECarSystemUI']
+
+ def update(self):
+ target = 'com.android.systemui'
+ return [
+ 'adevice update --restart=none',
+ f'adb shell am force-stop {target}',
+ ]
+
+
+class Droid(Alias):
+ """Alias for Droid."""
+
+ def build(self):
+ return ['m droid']
+
+ def update(self):
+ return ['flashall']
+
+
+class Snod(Alias):
+ """Alias for Snod."""
+
+ def build(self):
+ return ['m snod']
+
+ def update(self):
+ return ['flashall']
+
+
+# These definitions are imported from makepush
+# https://team.git.corp.google.com/android-framework/toolbox/+/refs/heads/main/makepush/makepush.sh
+alias_definitions = {
+ 'core_jni': {'build': 'libandroid_runtime'},
+ 'res_jni': {'build': 'libandroidfw libidmap2'},
+ 'idmap2': {'build': 'idmap2 idmap2d'},
+ 'sf': {'build': 'surfaceflinger'},
+ 'res': {'build': 'framework-res'},
+ 'services': {'build': 'services protolog.conf.json.gz'},
+ 'inputflinger': {'build': 'libinputflinger'},
+ 'carsysui': {
+ 'build': 'carSystemUI',
+ 'update': 'adb shell am force-stop com.android.systemui',
+ },
+ 'carsysuig': {
+ 'build': 'AAECarSystemUI',
+ 'update': 'adb shell am force-stop com.android.systemui',
+ },
+ 'car-mainline': {
+ 'build': 'AAECarSystemUI',
+ 'update': (
+ 'adb install -r --staged --enable-rollback'
+ ' $OUT/system/apex/com.android.car.framework.apex'
+ ),
+ },
+ 'carfwk': {'build': 'carfwk car-frameworks-service'},
+ 'carfwk-module': {'build': 'car-frameworks-service-module'},
+ 'carsettings': {
+ 'build': 'carSettings',
+ 'update': 'adb shell am force-stop com.android.car.settings',
+ },
+ 'carks': {
+ 'build': 'EmbeddedKitchenSinkApp',
+ 'update': 'adb shell am force-stop com.google.android.car.kitchensink',
+ },
+ 'carlauncher': {
+ 'build': 'carLauncher',
+ 'update': 'adb shell am force-stop com.android.car.carlauncher',
+ },
+ 'carlauncherg': {
+ 'build': 'GASCarLauncher',
+ 'update': 'adb shell am force-stop com.android.car.carlauncher',
+ },
+ 'car-md-launcher': {
+ 'build': 'MultiDisplaySecondaryHomeTestLauncher',
+ 'update': (
+ 'adb install'
+ ' $OUT/system/priv-app/MultiDisplaySecondaryHomeTestLauncher/MultiDisplaySecondaryHomeTestLauncher.apk'
+ ),
+ },
+ 'carsuw': {
+ 'build': 'carProvision',
+ 'update': 'adb shell am force-stop com.android.car.provision',
+ },
+ 'car': {'build': 'android.car'},
+ 'car-builtin': {'build': 'android.car.builtin'},
+ 'vhal-legacy': {
+ 'build': 'android.hardware.automotive.vehicle@2.0-service',
+ 'update': (
+ 'adb shell am force-stop'
+ ' android.hardware.automotive.vehicle@2.0-service'
+ ),
+ },
+ 'vhal': {
+ 'build': 'android.hardware.automotive.vehicle@V1-default-service',
+ 'update': (
+ 'adb shell am force-stop'
+ ' android.hardware.automotive.vehicle@V1-default-service'
+ ),
+ },
+ 'vhal-pasa': {
+ 'build': 'android.hardware.automotive.vehicle@V1-pasa-service',
+ 'update': (
+ 'adb shell am force-stop'
+ ' android.hardware.automotive.vehicle@V1-pasa-service'
+ ),
+ },
+ 'launcher': {'build': 'NexusLauncherRelease'},
+ 'launcherd': {
+ 'build': 'nexusLauncherDebug',
+ 'update': (
+ 'adb install'
+ ' $OUT/anywhere/priv-app/NexusLauncherDebug/NexusLauncherDebug.apk'
+ ),
+ },
+ 'launchergo': {
+ 'build': 'launcherGoGoogle',
+ 'update': 'adb shell am force-stop com.android.launcher3',
+ },
+ 'intentresolver': {
+ 'build': 'intentResolver',
+ 'update': 'adb shell am force-stop com.android.intentresolver',
+ },
+ 'sysuig': {
+ 'build': 'systemUIGoogle',
+ 'update': 'adb shell am force-stop com.android.systemui',
+ },
+ 'sysuititan': {
+ 'build': 'systemUITitan',
+ 'update': 'adb shell am force-stop com.android.systemui',
+ },
+ 'sysuigo': {
+ 'build': 'systemUIGo',
+ 'update': 'adb shell am force-stop com.android.systemui',
+ },
+ 'flagflipper': {
+ 'build': 'theFlippinApp',
+ 'update': 'adb shell am force-stop com.android.theflippinapp',
+ },
+ 'docsui': {
+ 'build': 'documentsUI',
+ 'update': 'adb shell am force-stop com.android.documentsui',
+ },
+ 'docsuig': {
+ 'build': 'documentsUIGoogle',
+ 'update': 'adb shell am force-stop com.google.android.documentsui',
+ },
+ 'settings': {
+ 'build': 'settings',
+ 'update': 'adb shell am force-stop com.android.settings',
+ },
+ 'settingsg': {
+ 'build': 'SettingsGoogle',
+ 'update': 'adb shell am force-stop com.google.android.settings',
+ },
+ 'settingsgf': {
+ 'build': 'SettingsGoogleFutureFaceEnroll',
+ 'update': (
+ 'adb shell am force-stop'
+ ' com.google.android.settings.future.biometrics.faceenroll'
+ ),
+ },
+ 'settings_provider': {'build': 'SettingsProvider'},
+ 'apidemos': {
+ 'build': 'ApiDemos',
+ 'update': (
+ 'adb install'
+ ' $OUT/testcases/ApiDemos/$var_cache_TARGET_ARCH/ApiDemos.apk'
+ ),
+ },
+ 'teleservice': {
+ 'build': 'TeleService',
+ 'update': 'adb shell am force-stop com.android.phone',
+ },
+ 'managed_provisioning': {
+ 'build': 'ManagedProvisioning',
+ 'update': 'adb shell am force-stop com.android.managedprovisioning',
+ },
+ 'car_managed_provisioning': {
+ 'build': 'carManagedProvisioning',
+ 'update': (
+ 'adb install'
+ ' $OUT/anywhere/priv-app/CarManagedProvisioning/CarManagedProvisioning.apk'
+ ),
+ },
+ 'ctsv': {
+ 'build': 'ctsVerifier',
+ 'update': (
+ 'adb install'
+ ' $OUT/testcases/CtsVerifier/$var_cache_TARGET_ARCH/CtsVerifier.apk'
+ ),
+ },
+ 'gtsv': {
+ 'build': 'gtsVerifier',
+ 'update': (
+ 'adb install'
+ ' $OUT/testcases/GtsVerifier/$var_cache_TARGET_ARCH/GtsVerifier.apk'
+ ),
+ },
+ 'suw': {
+ 'build': 'Provision',
+ 'update': 'adb shell am force-stop com.android.provision',
+ },
+ 'pkg_installer': {
+ 'build': 'PackageInstaller',
+ 'update': 'adb shell am force-stop com.android.packageinstaller',
+ },
+ 'pkg_installer_g': {
+ 'build': 'GooglePackageInstaller',
+ 'update': 'adb shell am force-stop com.google.android.packageinstaller',
+ },
+ 'perm_controller': {
+ 'build': 'PermissionController',
+ 'update': (
+ 'adb install'
+ ' $OUT/apex/com.android.permission/priv-app/PermissionController/PermissionController.apk'
+ ),
+ },
+ 'perm_controller_g': {
+ 'build': 'GooglePermissionController',
+ 'update': (
+ 'adb install -r'
+ ' $OUT/apex/com.google.android.permission/priv-app/GooglePermissionController/GooglePermissionController.apk'
+ ),
+ },
+ 'wifi': {
+ 'build': 'wifi',
+ 'update': (
+ 'adb install -r --staged --enable-rollback'
+ ' $OUT/system/apex/com.android.wifi && adb shell am force-stop'
+ ' com.android.wifi'
+ ),
+ },
+ 'vold': {'build': 'vold', 'update': 'adb shell am force-stop vold'},
+ 'multidisplay': {
+ 'build': 'multiDisplayProvider',
+ 'update': 'adb shell am force-stop com.android.emulator.multidisplay',
+ },
+ 'wm_ext': {
+ 'build': 'androidx.window.extensions',
+ },
+ 'rb': {
+ 'build': 'adServicesApk',
+ 'update': (
+ 'adb install'
+ ' $OUT/apex/com.android.adservices/priv-app/AdServices/AdServices.apk'
+ ),
+ },
+ 'rb_g': {
+ 'build': 'adServicesApkGoogle',
+ 'update': (
+ 'adb install'
+ ' $OUT/apex/com.google.android.adservices/priv-app/AdServicesApkGoogle@MASTER/AdServicesApkGoogle.apk'
+ ),
+ },
+ 'sdk_sandbox': {
+ 'build': 'sdkSandbox',
+ 'update': (
+ 'adb install'
+ ' $OUT/apex/com.google.android.adservices/app/SdkSandboxGoogle@MASTER/SdkSandboxGoogle.apk'
+ ),
+ },
+}
+
+
+# Utilities to get type of target
+def is_nexus():
+ target_product = os.getenv('TARGET_PRODUCT')
+ return (
+ target_product.startswith('.aosp')
+ or 'wembley' in target_product
+ or 'gms_humuhumu' in target_product
+ )
+
+
+def create_alias_from_config(config):
+ """Generates a Alias class from json."""
+ alias = Alias()
+ build = config.get('build', None)
+ if build:
+ alias.build = lambda: [f'm {build}']
+
+ update = config.get('update', None)
+ if update:
+ alias.update = lambda: [
+ 'adevice update --restart=none',
+ update,
+ ]
+ else:
+ alias.update = lambda: ['adevice update']
+ return alias
+
+
+def get_aliases():
+ """Dynamically find all aliases."""
+ # definitions that subclass the Alias class
+ aliases = {
+ name.lower(): cls()
+ for name, cls in inspect.getmembers(
+ sys.modules[__name__], inspect.isclass
+ )
+ if issubclass(cls, Alias) and cls != Alias
+ }
+ # definitions that are defined in alias_definitions
+ for name, config in alias_definitions.items():
+ aliases[name.lower()] = create_alias_from_config(config)
+ return aliases
diff --git a/experiments/a/tools/update_test.py b/experiments/a/tools/update_test.py
index 5121cd4..acc0d96 100644
--- a/experiments/a/tools/update_test.py
+++ b/experiments/a/tools/update_test.py
@@ -16,11 +16,15 @@
#
import argparse
import unittest
-from .update import Core
-from .update import get_aliases
-from .update import SystemServer
-from .update import SysUI
from .update import Update
+from .update_aliases import Core
+from .update_aliases import get_aliases
+from .update_aliases import SystemServer
+from .update_aliases import SysUI
+from .update_utils import combine_build_commands
+from .update_utils import combine_update_commands
+from .update_utils import remove_commands_that_starts_with
+from .update_utils import remove_duplicates_maintain_order
class UpdateTest(unittest.TestCase):
@@ -55,10 +59,8 @@
self.assertEqual(tasks[1].fall_back_tasks, ['m droid', 'flashall'])
def test_gather_tasks_alias(self):
-
self.args.alias = ['core']
update = Update(self.args)
-
tasks = update.gather_tasks()
self.assertEqual(
tasks, ['m framework framework-minus-apex', 'adevice update']
@@ -80,6 +82,68 @@
tasks = update.gather_tasks()
self.assertEqual(tasks, ['adevice update'])
+ def test_gather_tasks_multiple_alias(self):
+ self.args.alias = ['sf', 'res']
+ update = Update(self.args)
+ tasks = update.gather_tasks()
+ self.assertEqual(
+ tasks, ['m surfaceflinger framework-res', 'adevice update']
+ )
+
+
+class UpdateUtilsTest(unittest.TestCase):
+
+ def test_remove_duplicates_maintain_order(self):
+ self.assertEqual(
+ remove_duplicates_maintain_order(['1', '2', '1', '3']), ['1', '2', '3']
+ )
+
+ def test_remove_commands_that_starts_with_no_match(self):
+ self.assertEqual(
+ remove_commands_that_starts_with(
+ commands=[
+ 'keep a',
+ 'keep b',
+ 'remove a',
+ 'remove b',
+ ],
+ cmd_to_remove='remove',
+ ),
+ ['keep a', 'keep b'],
+ )
+
+ def test_combine_build_cmd(self):
+ self.assertEqual(combine_build_commands(['m foo', 'm bar']), ['m foo bar'])
+
+ def test_combine_update_cmds_adevice_update(self):
+ # adevice update restarts so remove unneeded force-stops
+ self.assertEqual(
+ combine_update_commands([
+ 'adevice update',
+ 'adb shell "am force-stop foo"',
+ 'adevice update',
+ 'adb shell "am force-stop bar"',
+ 'adevice update restart=none',
+ ]),
+ ['adevice update'],
+ )
+
+ def test_combine_update_cmds_adevice_update_no_restart(self):
+ # adevice update will not restart so keep force-stops
+ self.assertEqual(
+ combine_update_commands([
+ 'adevice update --restart=none',
+ 'adb shell "am force-stop foo"',
+ 'adevice update --restart=none',
+ 'adb shell "am force-stop bar"',
+ ]),
+ [
+ 'adevice update --restart=none',
+ 'adb shell "am force-stop foo"',
+ 'adb shell "am force-stop bar"',
+ ],
+ )
+
if __name__ == '__main__':
unittest.main()
diff --git a/experiments/a/tools/update_utils.py b/experiments/a/tools/update_utils.py
new file mode 100644
index 0000000..7ae90c1
--- /dev/null
+++ b/experiments/a/tools/update_utils.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+#
+# Copyright 2024 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Update Utils."""
+
+
+def combine_build_commands(commands):
+ """Combines build commands so that m is called once."""
+ m_command = ''
+ result = []
+ for cmd in commands:
+ if cmd.startswith('m '):
+ m_command += cmd[2:] + ' '
+ else:
+ result.append(cmd)
+ if m_command:
+ result.insert(0, 'm ' + m_command.strip())
+ return result
+
+
+def combine_update_commands(commands):
+ """Combines update tasks so that a reboot/adevice update is called only once."""
+ commands = remove_duplicates_maintain_order(commands)
+
+ # if any command calls for a restart; just do that
+ # deduplicate and remove
+ if 'adevice update' in commands:
+ commands = remove_commands_that_starts_with(commands, 'adevice update')
+ commands = remove_commands_that_starts_with(
+ commands, 'adb shell "am force-stop'
+ )
+ commands = ['adevice update'] + commands
+ return commands
+
+
+def remove_duplicates_maintain_order(commands):
+ """Removes duplicates while maintaining order."""
+ seen = set()
+ result = []
+ for item in commands:
+ if item not in seen:
+ seen.add(item)
+ result.append(item)
+ return result
+
+
+def remove_commands_that_starts_with(commands, cmd_to_remove):
+ """Removes commands that start with a command."""
+ result = []
+ for cmd in commands:
+ if not cmd.startswith(cmd_to_remove):
+ result.append(cmd)
+ return result