| #!/usr/bin/env python3 | 
 | # Copyright (c) 2018, 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 optparse | 
 | import os | 
 | import shutil | 
 | import subprocess | 
 | import sys | 
 | import time | 
 |  | 
 | import utils | 
 | import zip_utils | 
 |  | 
 | USAGE = 'usage: %prog [options] <apk>' | 
 |  | 
 |  | 
 | def parse_options(): | 
 |     parser = optparse.OptionParser(usage=USAGE) | 
 |     parser.add_option('--keystore', | 
 |                       help='keystore file (default ~/.android/app.keystore)', | 
 |                       default='~/.android/app.keystore') | 
 |     parser.add_option('--sign', | 
 |                       help='Sign the passed in apk.', | 
 |                       default=False, | 
 |                       action='store_true') | 
 |     parser.add_option('--use_apksigner', | 
 |                       help='Use apksigner to sign.', | 
 |                       default=False, | 
 |                       action='store_true') | 
 |     parser.add_option('--output', | 
 |                       help='Where to put the signed apk.)', | 
 |                       default=None) | 
 |  | 
 |     (options, args) = parser.parse_args() | 
 |     if len(args) != 1: | 
 |         parser.error('Expected <apk> argument, got: ' + ' '.join(args)) | 
 |     apk = args[0] | 
 |     return (options, apk) | 
 |  | 
 |  | 
 | def add_baseline_profile_to_apk(apk, baseline_profile, | 
 |                                 baseline_profile_metadata, tmp_dir): | 
 |     if baseline_profile is None: | 
 |         return apk | 
 |     ts = time.time_ns() | 
 |     dest_apk = os.path.join(tmp_dir, 'app-%s.apk' % ts) | 
 |     dest_apk_aligned = os.path.join(tmp_dir, 'app-aligned-%s.apk' % ts) | 
 |     dest_apk_signed = os.path.join(tmp_dir, 'app-signed-%s.apk' % ts) | 
 |     shutil.copy2(apk, dest_apk) | 
 |     zip_utils.remove_files_from_zip( | 
 |         ['assets/dexopt/baseline.prof', 'assets/dexopt/baseline.profm'], | 
 |         dest_apk) | 
 |     zip_utils.add_file_to_zip(baseline_profile, 'assets/dexopt/baseline.prof', | 
 |                               dest_apk) | 
 |     if baseline_profile_metadata is not None: | 
 |         zip_utils.add_file_to_zip(baseline_profile_metadata, | 
 |                                   'assets/dexopt/baseline.profm', dest_apk) | 
 |     align(dest_apk, dest_apk_aligned) | 
 |     sign_with_apksigner(dest_apk_aligned, dest_apk_signed) | 
 |     return dest_apk_signed | 
 |  | 
 |  | 
 | def align(apk, aligned_apk): | 
 |     zipalign_path = ('zipalign' if 'build_tools' in os.environ.get('PATH') else | 
 |                      os.path.join(utils.getAndroidBuildTools(), 'zipalign')) | 
 |     cmd = [zipalign_path, '-f', '-p', '4', apk, aligned_apk] | 
 |     utils.RunCmd(cmd, quiet=True, logging=False) | 
 |     return aligned_apk | 
 |  | 
 |  | 
 | def default_keystore(): | 
 |     return os.path.join(os.getenv('HOME'), '.android', 'app.keystore') | 
 |  | 
 |  | 
 | 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 sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True): | 
 |     utils.Print('Signing (ignore the warnings)', quiet=quiet) | 
 |     cmd = ['zip', '-d', unsigned_apk, 'META-INF/*'] | 
 |     utils.RunCmd(cmd, quiet=quiet, logging=logging, fail=False) | 
 |     cmd = [ | 
 |         'jarsigner', '-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1', | 
 |         '-keystore', keystore, '-storepass', 'android', '-signedjar', | 
 |         signed_apk, unsigned_apk, 'androiddebugkey' | 
 |     ] | 
 |     utils.RunCmd(cmd, quiet=quiet) | 
 |  | 
 |  | 
 | def sign_with_apksigner(unsigned_apk, | 
 |                         signed_apk, | 
 |                         keystore=None, | 
 |                         password='android', | 
 |                         quiet=False, | 
 |                         logging=True): | 
 |     cmd = [ | 
 |         os.path.join(utils.getAndroidBuildTools(), 'apksigner'), 'sign', '-v', | 
 |         '--ks', keystore or default_keystore(), '--ks-pass', 'pass:' + password, | 
 |         '--min-sdk-version', '19', '--out', signed_apk, '--v2-signing-enabled', | 
 |         unsigned_apk | 
 |     ] | 
 |     utils.RunCmd(cmd, quiet=quiet, logging=logging) | 
 |     return signed_apk | 
 |  | 
 |  | 
 | def main(): | 
 |     (options, apk) = parse_options() | 
 |     if options.sign: | 
 |         if not options.output: | 
 |             print('When signing you must specify an output apk') | 
 |             return 1 | 
 |         if not options.keystore: | 
 |             print('When signing you must specify a keystore') | 
 |             return 1 | 
 |         if options.use_apksigner: | 
 |             sign_with_apksigner(apk, options.output, options.keystore) | 
 |         else: | 
 |             sign(apk, options.output, options.keystore) | 
 |     return 0 | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     sys.exit(main()) |