Add a relayout.py script to relayout an existing APK using a profile

Change-Id: Idd2518909b129d4d1af3b750873441422d897f69
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index ee3f27a..d0af556 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -30,7 +30,7 @@
         profile_classes_and_methods, iteration, options)
     current_startup_descriptors = \
         profile_utils.transform_art_profile_to_r8_startup_list(
-            profile_classes_and_methods)
+            profile_classes_and_methods, options.generalize_synthetics)
   write_tmp_startup_descriptors(current_startup_descriptors, iteration, options)
   new_startup_descriptors = add_r8_startup_descriptors(
       startup_descriptors, current_startup_descriptors)
@@ -307,6 +307,11 @@
   result.add_argument('--device-pin',
                       help='Device pin code (e.g., 1234)',
                       action='append')
+  result.add_argument('--generalize-synthetics',
+                      help='Whether synthetics should be abstracted into their '
+                           'synthetic contexts',
+                      action='store_true',
+                      default=False)
   result.add_argument('--logcat',
                       action='store_true',
                       default=False)
diff --git a/tools/startup/profile_utils.py b/tools/startup/profile_utils.py
index 0e69397..923326f 100755
--- a/tools/startup/profile_utils.py
+++ b/tools/startup/profile_utils.py
@@ -41,11 +41,12 @@
     art_profile[descriptor] = flags
   return art_profile
 
-def transform_art_profile_to_r8_startup_list(art_profile):
+def transform_art_profile_to_r8_startup_list(
+    art_profile, generalize_synthetics=False):
   r8_startup_list = {}
   for startup_descriptor, flags in art_profile.items():
     transformed_startup_descriptor = transform_synthetic_descriptor(
-        startup_descriptor)
+        startup_descriptor) if generalize_synthetics else startup_descriptor
     r8_startup_list[transformed_startup_descriptor] = {
       'conditional_startup': False,
       'post_startup': flags['post_startup'],
diff --git a/tools/startup/relayout.py b/tools/startup/relayout.py
new file mode 100755
index 0000000..6220336
--- /dev/null
+++ b/tools/startup/relayout.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import argparse
+import os
+import subprocess
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+import apk_masseur
+import toolhelper
+import utils
+
+def parse_options(argv):
+  result = argparse.ArgumentParser(
+      description='Relayout a given APK using a startup profile.')
+  result.add_argument('--apk',
+                      help='Path to the .apk',
+                      required=True)
+  result.add_argument('--out',
+                      help='Destination of resulting apk',
+                      required=True)
+  result.add_argument('--profile',
+                      help='Path to the startup profile',
+                      required=True)
+  options, args = result.parse_known_args(argv)
+  return options, args
+
+def get_min_api(apk):
+  aapt = os.path.join(utils.getAndroidBuildTools(), 'aapt')
+  cmd = [aapt, 'dump', 'badging', apk]
+  stdout = subprocess.check_output(cmd).decode('utf-8').strip()
+  for line in stdout.splitlines():
+    if line.startswith('sdkVersion:\''):
+      return int(line[len('sdkVersion:\''): -1])
+  raise ValueError('Unexpected stdout: %s' % stdout)
+
+def main(argv):
+  (options, args) = parse_options(argv)
+  with utils.TempDir() as temp:
+    dex = os.path.join(temp, 'dex.zip')
+    d8_args = [
+        '--min-api', str(get_min_api(options.apk)),
+        '--output', dex,
+        '--no-desugaring',
+        '--release',
+        options.apk]
+    extra_args = ['-Dcom.android.tools.r8.startup.profile=%s' % options.profile]
+    toolhelper.run(
+        'd8',
+        d8_args,
+        extra_args=extra_args,
+        main='com.android.tools.r8.D8')
+    apk_masseur.masseur(options.apk, dex=dex, out=options.out)
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))