DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (777e60ca8853)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
#!/usr/bin/env python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Sign Android packages using an Android debug keystore, creating the
keystore if it does not exist.

This and |zip| can be combined to replace the Android |apkbuilder|
tool, which was deprecated in SDK r22.

Exits with code 0 if creating the keystore and every signing succeeded,
or with code 1 if any creation or signing failed.
"""

from argparse import ArgumentParser
import errno
import logging
import os
import subprocess
import sys


log = logging.getLogger(os.path.basename(__file__))
log.setLevel(logging.INFO)
sh = logging.StreamHandler(stream=sys.stdout)
sh.setFormatter(logging.Formatter('%(name)s: %(message)s'))
log.addHandler(sh)


class DebugKeystore:
    """
    A thin abstraction on top of an Android debug key store.
    """
    def __init__(self, keystore):
        self._keystore = os.path.abspath(os.path.expanduser(keystore))
        self._alias = 'androiddebugkey'
        self.verbose = False
        self.keytool = 'keytool'
        self.jarsigner = 'jarsigner'

    @property
    def keystore(self):
        return self._keystore

    @property
    def alias(self):
        return self._alias

    def _check(self, args):
        try:
            if self.verbose:
                subprocess.check_call(args)
            else:
                subprocess.check_output(args)
        except OSError as ex:
            if ex.errno != errno.ENOENT:
                raise
            raise Exception("Could not find executable '%s'" % args[0])

    def keystore_contains_alias(self):
        args = [ self.keytool,
                 '-list',
                 '-keystore', self.keystore,
                 '-storepass', 'android',
                 '-alias', self.alias,
               ]
        if self.verbose:
            args.append('-v')
        contains = True
        try:
            self._check(args)
        except subprocess.CalledProcessError as e:
            contains = False
        if self.verbose:
            log.info('Keystore %s %s alias %s' %
                     (self.keystore,
                      'contains' if contains else 'does not contain',
                      self.alias))
        return contains

    def create_alias_in_keystore(self):
        try:
            path = os.path.dirname(self.keystore)
            os.makedirs(path)
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

        args = [ self.keytool,
                 '-genkeypair',
                 '-keystore', self.keystore,
                 '-storepass', 'android',
                 '-alias', self.alias,
                 '-keypass', 'android',
                 '-dname', 'CN=Android Debug,O=Android,C=US',
                 '-keyalg', 'RSA',
                 '-validity', '365',
               ]
        if self.verbose:
            args.append('-v')
        self._check(args)
        if self.verbose:
            log.info('Created alias %s in keystore %s' %
                     (self.alias, self.keystore))

    def sign(self, apk):
        if not self.keystore_contains_alias():
            self.create_alias_in_keystore()

        args = [ self.jarsigner,
                 '-digestalg', 'SHA1',
                 '-sigalg', 'MD5withRSA',
                 '-keystore', self.keystore,
                 '-storepass', 'android',
                 apk,
                 self.alias,
               ]
        if self.verbose:
            args.append('-verbose')
        self._check(args)
        if self.verbose:
            log.info('Signed %s with alias %s from keystore %s' %
                     (apk, self.alias, self.keystore))


def parse_args(argv):
    parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.')
    parser.add_argument('apks', nargs='+',
                        metavar='APK',
                        help='Android packages to be signed')
    parser.add_argument('-v', '--verbose',
                        dest='verbose',
                        default=False,
                        action='store_true',
                        help='verbose output')
    parser.add_argument('--keytool',
                        metavar='PATH',
                        default='keytool',
                        help='path to Java keytool')
    parser.add_argument('--jarsigner',
                        metavar='PATH',
                        default='jarsigner',
                        help='path to Java jarsigner')
    parser.add_argument('--keystore',
                        metavar='PATH',
                        default='~/.android/debug.keystore',
                        help='path to keystore (default: ~/.android/debug.keystore)')
    parser.add_argument('-f', '--force-create-keystore',
                        dest='force',
                        default=False,
                        action='store_true',
                        help='force creating keystore')
    return parser.parse_args(argv)


def main():
    args = parse_args(sys.argv[1:])

    keystore = DebugKeystore(args.keystore)
    keystore.verbose = args.verbose
    keystore.keytool = args.keytool
    keystore.jarsigner = args.jarsigner

    if args.force:
        try:
            keystore.create_alias_in_keystore()
        except subprocess.CalledProcessError as e:
            log.error('Failed to force-create alias %s in keystore %s' %
                      (keystore.alias, keystore.keystore))
            log.error(e)
            return 1

    for apk in args.apks:
        try:
            keystore.sign(apk)
        except subprocess.CalledProcessError as e:
            log.error('Failed to sign %s', apk)
            log.error(e)
            return 1

    return 0

if __name__ == '__main__':
    sys.exit(main())