Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Rico Wind | 0ed24cc | 2018-03-23 07:16:35 +0100 | [diff] [blame] | 2 | # Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file |
| 3 | # for details. All rights reserved. Use of this source code is governed by a |
| 4 | # BSD-style license that can be found in the LICENSE file. |
| 5 | |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 6 | import optparse |
Søren Gjesse | cdae879 | 2018-12-12 09:02:43 +0100 | [diff] [blame] | 7 | import os |
Christoffer Quist Adamsen | 3f7e428 | 2022-04-21 13:12:31 +0200 | [diff] [blame] | 8 | import shutil |
Rico Wind | 0ed24cc | 2018-03-23 07:16:35 +0100 | [diff] [blame] | 9 | import subprocess |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 10 | import sys |
Christoffer Quist Adamsen | 3f7e428 | 2022-04-21 13:12:31 +0200 | [diff] [blame] | 11 | import time |
| 12 | |
Rico Wind | 0ed24cc | 2018-03-23 07:16:35 +0100 | [diff] [blame] | 13 | import utils |
Christoffer Quist Adamsen | 3f7e428 | 2022-04-21 13:12:31 +0200 | [diff] [blame] | 14 | import zip_utils |
Rico Wind | 0ed24cc | 2018-03-23 07:16:35 +0100 | [diff] [blame] | 15 | |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 16 | USAGE = 'usage: %prog [options] <apk>' |
| 17 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 18 | |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 19 | def parse_options(): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 20 | parser = optparse.OptionParser(usage=USAGE) |
| 21 | parser.add_option('--keystore', |
| 22 | help='keystore file (default ~/.android/app.keystore)', |
| 23 | default='~/.android/app.keystore') |
| 24 | parser.add_option('--sign', |
| 25 | help='Sign the passed in apk.', |
| 26 | default=False, |
| 27 | action='store_true') |
| 28 | parser.add_option('--use_apksigner', |
| 29 | help='Use apksigner to sign.', |
| 30 | default=False, |
| 31 | action='store_true') |
| 32 | parser.add_option('--output', |
| 33 | help='Where to put the signed apk.)', |
| 34 | default=None) |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 35 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 36 | (options, args) = parser.parse_args() |
| 37 | if len(args) != 1: |
| 38 | parser.error('Expected <apk> argument, got: ' + ' '.join(args)) |
| 39 | apk = args[0] |
| 40 | return (options, apk) |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 41 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 42 | |
| 43 | def add_baseline_profile_to_apk(apk, baseline_profile, |
| 44 | baseline_profile_metadata, tmp_dir): |
| 45 | if baseline_profile is None: |
| 46 | return apk |
| 47 | ts = time.time_ns() |
| 48 | dest_apk = os.path.join(tmp_dir, 'app-%s.apk' % ts) |
| 49 | dest_apk_aligned = os.path.join(tmp_dir, 'app-aligned-%s.apk' % ts) |
| 50 | dest_apk_signed = os.path.join(tmp_dir, 'app-signed-%s.apk' % ts) |
| 51 | shutil.copy2(apk, dest_apk) |
| 52 | zip_utils.remove_files_from_zip( |
| 53 | ['assets/dexopt/baseline.prof', 'assets/dexopt/baseline.profm'], |
| 54 | dest_apk) |
| 55 | zip_utils.add_file_to_zip(baseline_profile, 'assets/dexopt/baseline.prof', |
| 56 | dest_apk) |
| 57 | if baseline_profile_metadata is not None: |
| 58 | zip_utils.add_file_to_zip(baseline_profile_metadata, |
| 59 | 'assets/dexopt/baseline.profm', dest_apk) |
| 60 | align(dest_apk, dest_apk_aligned) |
| 61 | sign_with_apksigner(dest_apk_aligned, dest_apk_signed) |
| 62 | return dest_apk_signed |
| 63 | |
Christoffer Quist Adamsen | 3f7e428 | 2022-04-21 13:12:31 +0200 | [diff] [blame] | 64 | |
| 65 | def align(apk, aligned_apk): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 66 | zipalign_path = ('zipalign' if 'build_tools' in os.environ.get('PATH') else |
| 67 | os.path.join(utils.getAndroidBuildTools(), 'zipalign')) |
| 68 | cmd = [zipalign_path, '-f', '-p', '4', apk, aligned_apk] |
| 69 | utils.RunCmd(cmd, quiet=True, logging=False) |
| 70 | return aligned_apk |
| 71 | |
Christoffer Quist Adamsen | 3f7e428 | 2022-04-21 13:12:31 +0200 | [diff] [blame] | 72 | |
| 73 | def default_keystore(): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 74 | return os.path.join(os.getenv('HOME'), '.android', 'app.keystore') |
| 75 | |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 76 | |
Christoffer Quist Adamsen | eb29bfe | 2023-02-21 14:50:22 +0100 | [diff] [blame] | 77 | def get_min_api(apk): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 78 | aapt = os.path.join(utils.getAndroidBuildTools(), 'aapt') |
| 79 | cmd = [aapt, 'dump', 'badging', apk] |
| 80 | stdout = subprocess.check_output(cmd).decode('utf-8').strip() |
| 81 | for line in stdout.splitlines(): |
| 82 | if line.startswith('sdkVersion:\''): |
| 83 | return int(line[len('sdkVersion:\''):-1]) |
| 84 | raise ValueError('Unexpected stdout: %s' % stdout) |
| 85 | |
Christoffer Quist Adamsen | eb29bfe | 2023-02-21 14:50:22 +0100 | [diff] [blame] | 86 | |
Christoffer Quist Adamsen | 41cbdca | 2019-04-12 08:52:03 +0200 | [diff] [blame] | 87 | def sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 88 | utils.Print('Signing (ignore the warnings)', quiet=quiet) |
| 89 | cmd = ['zip', '-d', unsigned_apk, 'META-INF/*'] |
| 90 | utils.RunCmd(cmd, quiet=quiet, logging=logging, fail=False) |
| 91 | cmd = [ |
| 92 | 'jarsigner', '-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1', |
| 93 | '-keystore', keystore, '-storepass', 'android', '-signedjar', |
| 94 | signed_apk, unsigned_apk, 'androiddebugkey' |
| 95 | ] |
| 96 | utils.RunCmd(cmd, quiet=quiet) |
Søren Gjesse | cdae879 | 2018-12-12 09:02:43 +0100 | [diff] [blame] | 97 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 98 | |
| 99 | def sign_with_apksigner(unsigned_apk, |
| 100 | signed_apk, |
| 101 | keystore=None, |
| 102 | password='android', |
| 103 | quiet=False, |
| 104 | logging=True): |
| 105 | cmd = [ |
| 106 | os.path.join(utils.getAndroidBuildTools(), 'apksigner'), 'sign', '-v', |
| 107 | '--ks', keystore or default_keystore(), '--ks-pass', 'pass:' + password, |
| 108 | '--min-sdk-version', '19', '--out', signed_apk, '--v2-signing-enabled', |
| 109 | unsigned_apk |
| 110 | ] |
| 111 | utils.RunCmd(cmd, quiet=quiet, logging=logging) |
| 112 | return signed_apk |
| 113 | |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 114 | |
| 115 | def main(): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 116 | (options, apk) = parse_options() |
| 117 | if options.sign: |
| 118 | if not options.output: |
| 119 | print('When signing you must specify an output apk') |
| 120 | return 1 |
| 121 | if not options.keystore: |
| 122 | print('When signing you must specify a keystore') |
| 123 | return 1 |
| 124 | if options.use_apksigner: |
| 125 | sign_with_apksigner(apk, options.output, options.keystore) |
| 126 | else: |
| 127 | sign(apk, options.output, options.keystore) |
| 128 | return 0 |
| 129 | |
Rico Wind | 744ba75 | 2021-01-22 06:24:49 +0100 | [diff] [blame] | 130 | |
| 131 | if __name__ == '__main__': |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 132 | sys.exit(main()) |