tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

android_chrome_version.py (15037B)


      1 #!/usr/bin/env python3
      2 # Copyright 2019 The Chromium Authors
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 """Different build variants of Chrome for Android have different version codes.
      6 
      7 For targets that have the same package name (e.g. Chrome, Chrome Modern,
      8 Monochrome, Trichrome), Play Store considers them the same app and will push the
      9 supported app with the highest version code to devices. Note that Play Store
     10 does not support hosting two different apps with same version code and package
     11 name.
     12 
     13 Each version code generated by this script will be used by one or more APKs.
     14 
     15 Webview channels must have unique version codes for a couple reasons:
     16 a) Play Store does not support having the same version code for different
     17   versions of a package. Without unique codes, promoting a beta apk to stable
     18   would require first removing the beta version.
     19 b) Firebase project support (used by official builders) requires unique
     20   [version code + package name].
     21   We cannot add new webview package names for new channels because webview
     22   packages are allowlisted by Android as webview providers.
     23 
     24 WEBVIEW_STABLE, WEBVIEW_BETA, WEBVIEW_DEV are all used for standalone webview,
     25 whereas the others are used for various chrome APKs.
     26 
     27 TRICHROME_BETA is used for TrichromeChrome, TrichromeWebView, and
     28 TrichromeLibrary when these are compiled to use the stable package name. Similar
     29 to how WEBVIEW_STABLE/WEBVIEW_BETA work, this allows users to opt into the open
     30 Beta Track for the stable package. When Trichrome is configured to use a
     31 distinct package name for the Beta package, the version code will use TRICHROME
     32 instead of TRICHROME_BETA.
     33 
     34 Note that a package digit of '3' for Webview is reserved for Trichrome Webview.
     35 The same versionCode is used for both Trichrome Chrome and Trichrome Webview.
     36 
     37 Version code values are constructed like this:
     38 
     39  {full BUILD number}{3 digits: PATCH}{1 digit: package}{1 digit: ABIs}.
     40 
     41 For example:
     42 
     43  Build 3721, patch 0, ChromeModern (1), on ARM64 (5): 372100015
     44  Build 3721, patch 9, Monochrome (2), on ARM (0): 372100920
     45 
     46 """
     47 
     48 import argparse
     49 from collections import namedtuple
     50 
     51 # Package name version bits.
     52 _PACKAGE_NAMES = {
     53    'CHROME': 0,
     54    'CHROME_MODERN': 10,
     55    'MONOCHROME': 20,
     56    'TRICHROME': 30,
     57    'TRICHROME_BETA': 40,
     58    'TRICHROME_AUTO': 50,
     59    'TRICHROME_DESKTOP': 60,
     60    'WEBVIEW_STABLE': 0,
     61    'WEBVIEW_BETA': 10,
     62    'WEBVIEW_DEV': 20,
     63 }
     64 """ "Next" builds get +500 on their patch number.
     65 
     66 This ensures that they are considered "newer" than any non-next build of the
     67 same branch number; this is a workaround for Android requiring a total ordering
     68 of versions when we only really have a partial ordering. This assumes that the
     69 actual patch number will never reach 500, which has never even come close in
     70 the past.
     71 """
     72 _NEXT_BUILD_VERSION_CODE_DIFF = 50000
     73 """List of version numbers to be created for each build configuration.
     74 Tuple format:
     75 
     76  (version code name), (package name), (supported ABIs)
     77 
     78 Here, (supported ABIs) is referring to the combination of browser ABI and
     79 webview library ABI present in a particular APK. For example, 64_32 implies a
     80 64-bit browser with an extra 32-bit Webview library. See also
     81 _ABIS_TO_DIGIT_MASK.
     82 """
     83 _APKS = {
     84    '32': [
     85        ('CHROME', 'CHROME', '32'),
     86        ('CHROME_MODERN', 'CHROME_MODERN', '32'),
     87        ('MONOCHROME', 'MONOCHROME', '32'),
     88        ('TRICHROME', 'TRICHROME', '32'),
     89        ('TRICHROME_AUTO', 'TRICHROME_AUTO', '32'),
     90        ('TRICHROME_BETA', 'TRICHROME_BETA', '32'),
     91        ('WEBVIEW_STABLE', 'WEBVIEW_STABLE', '32'),
     92        ('WEBVIEW_BETA', 'WEBVIEW_BETA', '32'),
     93        ('WEBVIEW_DEV', 'WEBVIEW_DEV', '32'),
     94    ],
     95    '64': [
     96        ('CHROME', 'CHROME', '64'),
     97        ('CHROME_MODERN', 'CHROME_MODERN', '64'),
     98        ('MONOCHROME', 'MONOCHROME', '64'),
     99        ('TRICHROME', 'TRICHROME', '64'),
    100        ('TRICHROME_AUTO', 'TRICHROME_AUTO', '64'),
    101        ('TRICHROME_BETA', 'TRICHROME_BETA', '64'),
    102        ('TRICHROME_DESKTOP', 'TRICHROME_DESKTOP', '64'),
    103        ('WEBVIEW_STABLE', 'WEBVIEW_STABLE', '64'),
    104        ('WEBVIEW_BETA', 'WEBVIEW_BETA', '64'),
    105        ('WEBVIEW_DEV', 'WEBVIEW_DEV', '64'),
    106    ],
    107    'hybrid': [
    108        ('CHROME', 'CHROME', '64'),
    109        ('CHROME_32', 'CHROME', '32'),
    110        ('CHROME_MODERN', 'CHROME_MODERN', '64'),
    111        ('MONOCHROME', 'MONOCHROME', '32_64'),
    112        ('MONOCHROME_32', 'MONOCHROME', '32'),
    113        ('MONOCHROME_32_64', 'MONOCHROME', '32_64'),
    114        ('MONOCHROME_64_32', 'MONOCHROME', '64_32'),
    115        ('MONOCHROME_64', 'MONOCHROME', '64'),
    116        ('TRICHROME', 'TRICHROME', '32_64'),
    117        ('TRICHROME_32', 'TRICHROME', '32'),
    118        ('TRICHROME_32_64', 'TRICHROME', '32_64'),
    119        ('TRICHROME_64_32', 'TRICHROME', '64_32'),
    120        ('TRICHROME_64_32_HIGH', 'TRICHROME', '64_32_high'),
    121        ('TRICHROME_64', 'TRICHROME', '64'),
    122        ('TRICHROME_AUTO', 'TRICHROME_AUTO', '32_64'),
    123        ('TRICHROME_AUTO_32', 'TRICHROME_AUTO', '32'),
    124        ('TRICHROME_AUTO_32_64', 'TRICHROME_AUTO', '32_64'),
    125        ('TRICHROME_AUTO_64', 'TRICHROME_AUTO', '64'),
    126        ('TRICHROME_AUTO_64_32', 'TRICHROME_AUTO', '64_32'),
    127        ('TRICHROME_AUTO_64_32_HIGH', 'TRICHROME_AUTO', '64_32_high'),
    128        ('TRICHROME_BETA', 'TRICHROME_BETA', '32_64'),
    129        ('TRICHROME_32_BETA', 'TRICHROME_BETA', '32'),
    130        ('TRICHROME_32_64_BETA', 'TRICHROME_BETA', '32_64'),
    131        ('TRICHROME_64_32_BETA', 'TRICHROME_BETA', '64_32'),
    132        ('TRICHROME_64_32_HIGH_BETA', 'TRICHROME_BETA', '64_32_high'),
    133        ('TRICHROME_DESKTOP_64', 'TRICHROME_DESKTOP', '64'),
    134        ('TRICHROME_64_BETA', 'TRICHROME_BETA', '64'),
    135        ('WEBVIEW_STABLE', 'WEBVIEW_STABLE', '32_64'),
    136        ('WEBVIEW_BETA', 'WEBVIEW_BETA', '32_64'),
    137        ('WEBVIEW_DEV', 'WEBVIEW_DEV', '32_64'),
    138        ('WEBVIEW_32_STABLE', 'WEBVIEW_STABLE', '32'),
    139        ('WEBVIEW_32_BETA', 'WEBVIEW_BETA', '32'),
    140        ('WEBVIEW_32_DEV', 'WEBVIEW_DEV', '32'),
    141        ('WEBVIEW_64_STABLE', 'WEBVIEW_STABLE', '64'),
    142        ('WEBVIEW_64_BETA', 'WEBVIEW_BETA', '64'),
    143        ('WEBVIEW_64_DEV', 'WEBVIEW_DEV', '64'),
    144    ]
    145 }
    146 
    147 # Splits input build config architecture to manufacturer and bitness.
    148 _ARCH_TO_MFG_AND_BITNESS = {
    149    'arm': ('arm', '32'),
    150    'arm64': ('arm', 'hybrid'),
    151    # Until riscv64 needs a unique version code to ship APKs to the store,
    152    # point to the 'arm' bitmask.
    153    'riscv64': ('arm', '64'),
    154    'x86': ('intel', '32'),
    155    'x64': ('intel', 'hybrid'),
    156 }
    157 
    158 # Expose the available choices to other scripts.
    159 ARCH_CHOICES = _ARCH_TO_MFG_AND_BITNESS.keys()
    160 """
    161 The architecture preference is encoded into the version_code for devices
    162 that support multiple architectures. (exploiting play store logic that pushes
    163 apk with highest version code)
    164 
    165 Detail:
    166 Many Android devices support multiple architectures, and can run applications
    167 built for any of them; the Play Store considers all of the supported
    168 architectures compatible and does not, itself, have any preference for which
    169 is "better". The common cases here:
    170 
    171 - All production arm64 devices can also run arm
    172 - All production x64 devices can also run x86
    173 - Pretty much all production x86/x64 devices can also run arm (via a binary
    174  translator)
    175 
    176 Since the Play Store has no particular preferences, you have to encode your own
    177 preferences into the ordering of the version codes. There's a few relevant
    178 things here:
    179 
    180 - For any android app, it's theoretically preferable to ship a 64-bit version to
    181  64-bit devices if it exists, because the 64-bit architectures are supposed to
    182  be "better" than their 32-bit predecessors (unfortunately this is not always
    183  true due to the effect on memory usage, but we currently deal with this by
    184  simply not shipping a 64-bit version *at all* on the configurations where we
    185  want the 32-bit version to be used).
    186 - For any android app, it's definitely preferable to ship an x86 version to x86
    187  devices if it exists instead of an arm version, because running things through
    188  the binary translator is a performance hit.
    189 - For WebView, Monochrome, and Trichrome specifically, they are a special class
    190  of APK called "multiarch" which means that they actually need to *use* more
    191  than one architecture at runtime (rather than simply being compatible with
    192  more than one). The 64-bit builds of these multiarch APKs contain both 32-bit
    193  and 64-bit code, so that Webview is available for both ABIs. If you're
    194  multiarch you *must* have a version that supports both 32-bit and 64-bit
    195  version on a 64-bit device, otherwise it won't work properly. So, the 64-bit
    196  version needs to be a higher versionCode, as otherwise a 64-bit device would
    197  prefer the 32-bit version that does not include any 64-bit code, and fail.
    198 """
    199 
    200 
    201 def _GetAbisToDigitMask(build_number, patch_number):
    202  """Return the correct digit mask based on build number.
    203 
    204  Updated from build 5750: Some intel devices advertise support for arm,
    205  so arm codes must be lower than x86 codes to prevent providing an
    206  arm-optimized build to intel devices.
    207 
    208  Returns:
    209    A dictionary of architecture mapped to bitness
    210    mapped to version code suffix.
    211  """
    212  # Scheme change was made directly to M113 and M114 branches.
    213  use_new_scheme = (build_number >= 5750
    214                    or (build_number == 5672 and patch_number >= 176)
    215                    or (build_number == 5735 and patch_number >= 53))
    216  if use_new_scheme:
    217    return {
    218        'arm': {
    219            '32': 0,
    220            '32_64': 1,
    221            '64_32': 2,
    222            '64_32_high': 3,
    223            '64': 4,
    224        },
    225        'intel': {
    226            '32': 6,
    227            '32_64': 7,
    228            '64_32': 8,
    229            '64': 9,
    230        },
    231    }
    232  return {
    233      'arm': {
    234          '32': 0,
    235          '32_64': 3,
    236          '64_32': 4,
    237          '64': 5,
    238          '64_32_high': 9,
    239      },
    240      'intel': {
    241          '32': 1,
    242          '32_64': 6,
    243          '64_32': 7,
    244          '64': 8,
    245      },
    246  }
    247 
    248 
    249 VersionCodeComponents = namedtuple('VersionCodeComponents', [
    250    'build_number',
    251    'patch_number',
    252    'package_name',
    253    'abi',
    254    'is_next_build',
    255 ])
    256 
    257 
    258 def TranslateVersionCode(version_code, is_webview=False):
    259  """Translates a version code to its component parts.
    260 
    261  Returns:
    262    A 5-tuple (VersionCodeComponents) with the form:
    263      - Build number - integer
    264      - Patch number - integer
    265      - Package name - string
    266      - ABI - string : if the build is 32_64 or 64_32 or 64, that is just
    267                       appended to 'arm' or 'x86' with an underscore
    268      - Whether the build is a "next" build - boolean
    269 
    270    So, for build 100.0.5678.99, built for Monochrome on arm 64_32, not a next
    271    build, you should get:
    272      5678, 99, 'MONOCHROME', 'arm_64_32', False
    273  """
    274  if len(version_code) == 9:
    275    build_number = int(version_code[:4])
    276  else:
    277    # At one branch per day, we'll hit 5 digits in the year 2035.
    278    build_number = int(version_code[:5])
    279 
    280  is_next_build = False
    281  patch_number_plus_extra = int(version_code[-5:])
    282  if patch_number_plus_extra >= _NEXT_BUILD_VERSION_CODE_DIFF:
    283    is_next_build = True
    284    patch_number_plus_extra -= _NEXT_BUILD_VERSION_CODE_DIFF
    285  patch_number = patch_number_plus_extra // 100
    286 
    287  # From branch 3992 the name and abi bits in the version code are swapped.
    288  if build_number >= 3992:
    289    abi_digit = int(version_code[-1])
    290    package_digit = int(version_code[-2])
    291  else:
    292    abi_digit = int(version_code[-2])
    293    package_digit = int(version_code[-1])
    294 
    295  # Before branch 4844 we added 5 to the package digit to indicate a 'next'
    296  # build.
    297  if build_number < 4844 and package_digit >= 5:
    298    is_next_build = True
    299    package_digit -= 5
    300 
    301  for package, number in _PACKAGE_NAMES.items():
    302    if number == package_digit * 10:
    303      if is_webview == ('WEBVIEW' in package):
    304        package_name = package
    305        break
    306 
    307  for arch, bitness_to_number in (_GetAbisToDigitMask(build_number,
    308                                                      patch_number).items()):
    309    for bitness, number in bitness_to_number.items():
    310      if abi_digit == number:
    311        abi = arch if arch != 'intel' else 'x86'
    312        if bitness != '32':
    313          abi += '_' + bitness
    314        break
    315 
    316  return VersionCodeComponents(build_number, patch_number, package_name, abi,
    317                               is_next_build)
    318 
    319 
    320 def GenerateVersionCodes(build_number, patch_number, arch):
    321  """Build dict of version codes for the specified build architecture. Eg:
    322 
    323  {
    324    'CHROME_VERSION_CODE': '378100010',
    325    'MONOCHROME_VERSION_CODE': '378100013',
    326    ...
    327  }
    328 
    329  versionCode values are built like this:
    330  {full BUILD int}{3 digits: PATCH}{1 digit: package}{1 digit: ABIs}.
    331 
    332  MAJOR and MINOR values are not used for generating versionCode.
    333  - MINOR is always 0. It was used for something long ago in Chrome's history
    334    but has not been used since, and has never been nonzero on Android.
    335  - MAJOR is cosmetic and controlled by the release managers. MAJOR and BUILD
    336    always have reasonable sort ordering: for two version codes A and B, it's
    337    always the case that (A.MAJOR < B.MAJOR) implies (A.BUILD < B.BUILD), and
    338    that (A.MAJOR > B.MAJOR) implies (A.BUILD > B.BUILD). This property is just
    339    maintained by the humans who set MAJOR.
    340 
    341  Thus, this method is responsible for the final two digits of versionCode.
    342  """
    343  base_version_code = (build_number * 1000 + patch_number) * 100
    344 
    345  mfg, bitness = _ARCH_TO_MFG_AND_BITNESS[arch]
    346 
    347  version_codes = {}
    348 
    349  abi_to_digit_mask = _GetAbisToDigitMask(build_number, patch_number)
    350  for apk, package, abis in _APKS[bitness]:
    351    if abis == '64_32_high' and arch != 'arm64':
    352      continue
    353    abi_part = abi_to_digit_mask[mfg][abis]
    354    package_part = _PACKAGE_NAMES[package]
    355 
    356    version_code_name = apk + '_VERSION_CODE'
    357    version_code_val = base_version_code + package_part + abi_part
    358    version_codes[version_code_name] = str(version_code_val)
    359 
    360  return version_codes
    361 
    362 
    363 def main():
    364  parser = argparse.ArgumentParser(description='Parses version codes.')
    365  g1 = parser.add_argument_group('To Generate Version Name')
    366  g1.add_argument('--version-code', help='Version code (e.g. 529700010).')
    367  g1.add_argument('--webview',
    368                  action='store_true',
    369                  help='Whether this is a webview version code.')
    370  g2 = parser.add_argument_group('To Generate Version Code')
    371  g2.add_argument('--version-name', help='Version name (e.g. 124.0.6355.0).')
    372  g2.add_argument('--arch',
    373                  choices=ARCH_CHOICES,
    374                  help='Set which cpu architecture the build is for.')
    375  args = parser.parse_args()
    376  if args.version_code:
    377    print(TranslateVersionCode(args.version_code, is_webview=args.webview))
    378  elif args.version_name:
    379    if not args.arch:
    380      parser.error('Required --arch')
    381    _, _, build, patch = args.version_name.split('.')
    382    values = GenerateVersionCodes(int(build), int(patch), args.arch)
    383    for k, v in values.items():
    384      print(f'{k}={v}')
    385  else:
    386    parser.print_help()
    387 
    388 
    389 
    390 if __name__ == '__main__':
    391  main()