Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ba547a1 authored by Tao Bao's avatar Tao Bao Committed by Gerrit Code Review
Browse files

Merge "releasetools: Factor out the check for (compressed) APK file."

parents dabf1a0c 11f955c5
Loading
Loading
Loading
Loading
+87 −56
Original line number Original line Diff line number Diff line
@@ -144,28 +144,69 @@ def GetApkCerts(certmap):
  return certmap
  return certmap




def GetApkFileInfo(filename, compressed_extension):
  """Returns the APK info based on the given filename.

  Checks if the given filename (with path) looks like an APK file, by taking the
  compressed extension into consideration.

  Args:
    filename: Path to the file.
    compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
        or None if there's no compressed APKs.

  Returns:
    (is_apk, is_compressed): is_apk indicates whether the given filename is an
    APK file. is_compressed indicates whether the APK file is compressed (only
    meaningful when is_apk is True).

  Raises:
    AssertionError: On invalid compressed_extension input.
  """
  assert compressed_extension is None or compressed_extension.startswith('.'), \
      "Invalid compressed_extension arg: '{}'".format(compressed_extension)

  compressed_apk_extension = (
      ".apk" + compressed_extension if compressed_extension else None)
  is_apk = (filename.endswith(".apk") or
            (compressed_apk_extension and
             filename.endswith(compressed_apk_extension)))
  if not is_apk:
    return (False, False)

  is_compressed = (compressed_apk_extension and
                   filename.endswith(compressed_apk_extension))
  return (True, is_compressed)


def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
  """Check that all the APKs we want to sign have keys specified, and
  """Checks that all the APKs have keys specified, otherwise errors out.
  error out if they don't."""

  Args:
    input_tf_zip: An open target_files zip file.
    apk_key_map: A dict of known signing keys key'd by APK names.
    compressed_extension: The extension string of compressed APKs, such as
        ".gz", or None if there's no compressed APKs.

  Raises:
    AssertionError: On finding unknown APKs.
  """
  unknown_apks = []
  unknown_apks = []
  compressed_apk_extension = None
  if compressed_extension:
    compressed_apk_extension = ".apk" + compressed_extension
  for info in input_tf_zip.infolist():
  for info in input_tf_zip.infolist():
    if (info.filename.endswith(".apk") or
    (is_apk, is_compressed) = GetApkFileInfo(
        (compressed_apk_extension and
        info.filename, compressed_extension)
         info.filename.endswith(compressed_apk_extension))):
    if not is_apk:
      continue
    name = os.path.basename(info.filename)
    name = os.path.basename(info.filename)
      if compressed_apk_extension and name.endswith(compressed_apk_extension):
    if is_compressed:
      name = name[:-len(compressed_extension)]
      name = name[:-len(compressed_extension)]
    if name not in apk_key_map:
    if name not in apk_key_map:
      unknown_apks.append(name)
      unknown_apks.append(name)
  if unknown_apks:

    print("ERROR: no key specified for:\n")
  assert not unknown_apks, \
    print("  " + "\n  ".join(unknown_apks))
      ("No key specified for:\n  {}\n"
    print("\nUse '-e <apkname>=' to specify a key (which may be an empty "
       "Use '-e <apkname>=' to specify a key (which may be an empty string to "
          "string to not sign this apk).")
       "not sign this apk).".format("\n  ".join(unknown_apks)))
    sys.exit(1)




def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
@@ -235,32 +276,23 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
                       apk_key_map, key_passwords, platform_api_level,
                       apk_key_map, key_passwords, platform_api_level,
                       codename_to_api_level_map,
                       codename_to_api_level_map,
                       compressed_extension):
                       compressed_extension):

  compressed_apk_extension = None
  if compressed_extension:
    compressed_apk_extension = ".apk" + compressed_extension

  maxsize = max(
  maxsize = max(
      [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
      [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
       if (i.filename.endswith('.apk') or
       if GetApkFileInfo(i.filename, compressed_extension)[0]])
           (compressed_apk_extension and
            i.filename.endswith(compressed_apk_extension)))])
  system_root_image = misc_info.get("system_root_image") == "true"
  system_root_image = misc_info.get("system_root_image") == "true"


  for info in input_tf_zip.infolist():
  for info in input_tf_zip.infolist():
    if info.filename.startswith("IMAGES/"):
    filename = info.filename
    if filename.startswith("IMAGES/"):
      continue
      continue


    data = input_tf_zip.read(info.filename)
    data = input_tf_zip.read(filename)
    out_info = copy.copy(info)
    out_info = copy.copy(info)
    (is_apk, is_compressed) = GetApkFileInfo(filename, compressed_extension)


    # Sign APKs.
    # Sign APKs.
    if (info.filename.endswith(".apk") or
    if is_apk:
        (compressed_apk_extension and
      name = os.path.basename(filename)
         info.filename.endswith(compressed_apk_extension))):
      is_compressed = (compressed_extension and
                       info.filename.endswith(compressed_apk_extension))
      name = os.path.basename(info.filename)
      if is_compressed:
      if is_compressed:
        name = name[:-len(compressed_extension)]
        name = name[:-len(compressed_extension)]


@@ -276,7 +308,7 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
        common.ZipWriteStr(output_tf_zip, out_info, data)
        common.ZipWriteStr(output_tf_zip, out_info, data)


    # System properties.
    # System properties.
    elif info.filename in ("SYSTEM/build.prop",
    elif filename in ("SYSTEM/build.prop",
                      "VENDOR/build.prop",
                      "VENDOR/build.prop",
                      "SYSTEM/etc/prop.default",
                      "SYSTEM/etc/prop.default",
                      "BOOT/RAMDISK/prop.default",
                      "BOOT/RAMDISK/prop.default",
@@ -284,7 +316,7 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
                      "ROOT/default.prop",  # legacy
                      "ROOT/default.prop",  # legacy
                      "RECOVERY/RAMDISK/prop.default",
                      "RECOVERY/RAMDISK/prop.default",
                      "RECOVERY/RAMDISK/default.prop"):  # legacy
                      "RECOVERY/RAMDISK/default.prop"):  # legacy
      print("Rewriting %s:" % (info.filename,))
      print("Rewriting %s:" % (filename,))
      if stat.S_ISLNK(info.external_attr >> 16):
      if stat.S_ISLNK(info.external_attr >> 16):
        new_data = data
        new_data = data
      else:
      else:
@@ -293,20 +325,20 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,


    # Replace the certs in *mac_permissions.xml (there could be multiple, such
    # Replace the certs in *mac_permissions.xml (there could be multiple, such
    # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
    # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
    elif info.filename.endswith("mac_permissions.xml"):
    elif filename.endswith("mac_permissions.xml"):
      print("Rewriting %s with new keys." % (info.filename,))
      print("Rewriting %s with new keys." % (filename,))
      new_data = ReplaceCerts(data)
      new_data = ReplaceCerts(data)
      common.ZipWriteStr(output_tf_zip, out_info, new_data)
      common.ZipWriteStr(output_tf_zip, out_info, new_data)


    # Ask add_img_to_target_files to rebuild the recovery patch if needed.
    # Ask add_img_to_target_files to rebuild the recovery patch if needed.
    elif info.filename in ("SYSTEM/recovery-from-boot.p",
    elif filename in ("SYSTEM/recovery-from-boot.p",
                      "SYSTEM/etc/recovery.img",
                      "SYSTEM/etc/recovery.img",
                      "SYSTEM/bin/install-recovery.sh"):
                      "SYSTEM/bin/install-recovery.sh"):
      OPTIONS.rebuild_recovery = True
      OPTIONS.rebuild_recovery = True


    # Don't copy OTA keys if we're replacing them.
    # Don't copy OTA keys if we're replacing them.
    elif (OPTIONS.replace_ota_keys and
    elif (OPTIONS.replace_ota_keys and
          info.filename in (
          filename in (
              "BOOT/RAMDISK/res/keys",
              "BOOT/RAMDISK/res/keys",
              "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
              "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
              "RECOVERY/RAMDISK/res/keys",
              "RECOVERY/RAMDISK/res/keys",
@@ -315,22 +347,21 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
      pass
      pass


    # Skip META/misc_info.txt since we will write back the new values later.
    # Skip META/misc_info.txt since we will write back the new values later.
    elif info.filename == "META/misc_info.txt":
    elif filename == "META/misc_info.txt":
      pass
      pass


    # Skip verity public key if we will replace it.
    # Skip verity public key if we will replace it.
    elif (OPTIONS.replace_verity_public_key and
    elif (OPTIONS.replace_verity_public_key and
          info.filename in ("BOOT/RAMDISK/verity_key",
          filename in ("BOOT/RAMDISK/verity_key",
                       "ROOT/verity_key")):
                       "ROOT/verity_key")):
      pass
      pass


    # Skip verity keyid (for system_root_image use) if we will replace it.
    # Skip verity keyid (for system_root_image use) if we will replace it.
    elif (OPTIONS.replace_verity_keyid and
    elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
          info.filename == "BOOT/cmdline"):
      pass
      pass


    # Skip the care_map as we will regenerate the system/vendor images.
    # Skip the care_map as we will regenerate the system/vendor images.
    elif info.filename == "META/care_map.txt":
    elif filename == "META/care_map.txt":
      pass
      pass


    # A non-APK file; copy it verbatim.
    # A non-APK file; copy it verbatim.
+49 −1
Original line number Original line Diff line number Diff line
@@ -24,7 +24,8 @@ import zipfile
import common
import common
import test_utils
import test_utils
from sign_target_files_apks import (
from sign_target_files_apks import (
    EditTags, ReplaceCerts, ReplaceVerityKeyId, RewriteProps)
    CheckAllApksSigned, EditTags, GetApkFileInfo, ReplaceCerts,
    ReplaceVerityKeyId, RewriteProps)




class SignTargetFilesApksTest(unittest.TestCase):
class SignTargetFilesApksTest(unittest.TestCase):
@@ -211,3 +212,50 @@ class SignTargetFilesApksTest(unittest.TestCase):
        cert2_path[:-9] : 'non-existent',
        cert2_path[:-9] : 'non-existent',
    }
    }
    self.assertEqual(output_xml, ReplaceCerts(input_xml))
    self.assertEqual(output_xml, ReplaceCerts(input_xml))

  def test_CheckAllApksSigned(self):
    input_file = common.MakeTempFile(suffix='.zip')
    with zipfile.ZipFile(input_file, 'w') as input_zip:
      input_zip.writestr('SYSTEM/app/App1.apk', "App1-content")
      input_zip.writestr('SYSTEM/app/App2.apk.gz', "App2-content")

    apk_key_map = {
        'App1.apk' : 'key1',
        'App2.apk' : 'key2',
        'App3.apk' : 'key3',
    }
    with zipfile.ZipFile(input_file) as input_zip:
      CheckAllApksSigned(input_zip, apk_key_map, None)
      CheckAllApksSigned(input_zip, apk_key_map, '.gz')

      # 'App2.apk.gz' won't be considered as an APK.
      CheckAllApksSigned(input_zip, apk_key_map, None)
      CheckAllApksSigned(input_zip, apk_key_map, '.xz')

      del apk_key_map['App2.apk']
      self.assertRaises(
          AssertionError, CheckAllApksSigned, input_zip, apk_key_map, '.gz')

  def test_GetApkFileInfo(self):
    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk", None)
    self.assertTrue(is_apk)
    self.assertFalse(is_compressed)

    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.dat", None)
    self.assertFalse(is_apk)
    self.assertFalse(is_compressed)

  def test_GetApkFileInfo_withCompressedApks(self):
    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk.gz", ".gz")
    self.assertTrue(is_apk)
    self.assertTrue(is_compressed)

    (is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk.gz", ".xz")
    self.assertFalse(is_apk)
    self.assertFalse(is_compressed)

    self.assertRaises(
        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "")

    self.assertRaises(
        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk")