blob: 8ced4918e6c2d04940678cd20fa9bfd4140d1fe2 [file] [log] [blame]
Rico Wind744ba752021-01-22 06:24:49 +01001#!/usr/bin/env python3
Rico Wind0ed24cc2018-03-23 07:16:35 +01002# 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 Wind744ba752021-01-22 06:24:49 +01006import optparse
Søren Gjessecdae8792018-12-12 09:02:43 +01007import os
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +02008import shutil
Rico Wind0ed24cc2018-03-23 07:16:35 +01009import subprocess
Rico Wind744ba752021-01-22 06:24:49 +010010import sys
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +020011import time
12
Rico Wind0ed24cc2018-03-23 07:16:35 +010013import utils
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +020014import zip_utils
Rico Wind0ed24cc2018-03-23 07:16:35 +010015
Rico Wind744ba752021-01-22 06:24:49 +010016USAGE = 'usage: %prog [options] <apk>'
17
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020018
Rico Wind744ba752021-01-22 06:24:49 +010019def parse_options():
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020020 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 Wind744ba752021-01-22 06:24:49 +010035
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020036 (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 Wind744ba752021-01-22 06:24:49 +010041
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020042
43def 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 Adamsen3f7e4282022-04-21 13:12:31 +020064
65def align(apk, aligned_apk):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020066 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 Adamsen3f7e4282022-04-21 13:12:31 +020072
73def default_keystore():
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020074 return os.path.join(os.getenv('HOME'), '.android', 'app.keystore')
75
Rico Wind744ba752021-01-22 06:24:49 +010076
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +010077def get_min_api(apk):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020078 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 Adamseneb29bfe2023-02-21 14:50:22 +010086
Christoffer Quist Adamsen41cbdca2019-04-12 08:52:03 +020087def sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020088 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 Gjessecdae8792018-12-12 09:02:43 +010097
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020098
99def 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 Wind744ba752021-01-22 06:24:49 +0100114
115def main():
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200116 (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 Wind744ba752021-01-22 06:24:49 +0100130
131if __name__ == '__main__':
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200132 sys.exit(main())