forked from openkylin/platform_build
releasetools: Skip signing APKs with given prefixes.
We may pack prebuilts that end with ".apk" into target_files zip, via PRODUCT_COPY_FILES. META/apkcerts.txt won't contain the cert info for such files, and we want to keep them as is while signing, despite of the ".apk" extension. This CL adds "--skip_apks_with_path_prefix" option to sign_target_files_apks.py. APKs with matching prefixes will be copied verbatim into the signed images. The prefix should match the entry names in the target_files (e.g. "SYSTEM_OTHER/preloads/"). The option may be repeated to specify multiple prefixes. Note that although we may skip signing an APK file with "-e ApkName=". This would skip *all* the APK files with the matching basename. "--skip_apks_with_path_prefix" allows matching the exact prefix. For example: $ ./build/make/tools/releasetools/sign_target_files_apks.py \ --skip_apks_with_path_prefix SYSTEM_OTHER/preloads/ \ --skip_apks_with_path_prefix PRODUCT/prebuilts/PrebuiltApp1 \ --skip_apks_with_path_prefix VENDOR/app/PrebuiltApp2.apk \ target_files.zip \ signed-target_files.zip Bug: 110201128 Test: Run the command above and check the logs. Test: `python -m unittest test_sign_target_files_apks` Change-Id: I7bd80b360917cef137cf1e7e8cfa796968831f47
This commit is contained in:
parent
0816dc15cc
commit
93c2a01268
|
@ -27,6 +27,12 @@ Usage: sign_target_files_apks [flags] input_target_files output_target_files
|
|||
in the apkcerts.txt file. Option may be repeated to give
|
||||
multiple extra packages.
|
||||
|
||||
--skip_apks_with_path_prefix <prefix>
|
||||
Skip signing an APK if it has the matching prefix in its path. The prefix
|
||||
should be matching the entry name, which has partition names in upper
|
||||
case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be
|
||||
repeated to give multiple prefixes.
|
||||
|
||||
-k (--key_mapping) <src_key=dest_key>
|
||||
Add a mapping from the key name as specified in apkcerts.txt (the
|
||||
src_key) to the real key you wish to sign the package with
|
||||
|
@ -118,6 +124,7 @@ if sys.hexversion < 0x02070000:
|
|||
OPTIONS = common.OPTIONS
|
||||
|
||||
OPTIONS.extra_apks = {}
|
||||
OPTIONS.skip_apks_with_path_prefix = set()
|
||||
OPTIONS.key_map = {}
|
||||
OPTIONS.rebuild_recovery = False
|
||||
OPTIONS.replace_ota_keys = False
|
||||
|
@ -144,39 +151,53 @@ def GetApkCerts(certmap):
|
|||
return certmap
|
||||
|
||||
|
||||
def GetApkFileInfo(filename, compressed_extension):
|
||||
def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
|
||||
"""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.
|
||||
compressed extension into consideration. If it appears to be an APK file,
|
||||
further checks if the APK file should be skipped when signing, based on the
|
||||
given path prefixes.
|
||||
|
||||
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.
|
||||
skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
|
||||
|
||||
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).
|
||||
(is_apk, is_compressed, should_be_skipped): 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). should_be_skipped
|
||||
indicates whether the filename matches any of the given prefixes to be
|
||||
skipped.
|
||||
|
||||
Raises:
|
||||
AssertionError: On invalid compressed_extension input.
|
||||
AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
|
||||
"""
|
||||
assert compressed_extension is None or compressed_extension.startswith('.'), \
|
||||
"Invalid compressed_extension arg: '{}'".format(compressed_extension)
|
||||
|
||||
# skipped_prefixes should be one of set/list/tuple types. Other types such as
|
||||
# str shouldn't be accepted.
|
||||
assert (isinstance(skipped_prefixes, tuple) or
|
||||
isinstance(skipped_prefixes, set) or
|
||||
isinstance(skipped_prefixes, list)), \
|
||||
"Invalid skipped_prefixes input type: {}".format(
|
||||
type(skipped_prefixes))
|
||||
|
||||
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)
|
||||
return (False, False, False)
|
||||
|
||||
is_compressed = (compressed_apk_extension and
|
||||
filename.endswith(compressed_apk_extension))
|
||||
return (True, is_compressed)
|
||||
should_be_skipped = filename.startswith(tuple(skipped_prefixes))
|
||||
return (True, is_compressed, should_be_skipped)
|
||||
|
||||
|
||||
def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
|
||||
|
@ -193,9 +214,9 @@ def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
|
|||
"""
|
||||
unknown_apks = []
|
||||
for info in input_tf_zip.infolist():
|
||||
(is_apk, is_compressed) = GetApkFileInfo(
|
||||
info.filename, compressed_extension)
|
||||
if not is_apk:
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
|
||||
if not is_apk or should_be_skipped:
|
||||
continue
|
||||
name = os.path.basename(info.filename)
|
||||
if is_compressed:
|
||||
|
@ -276,9 +297,11 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
|
|||
apk_key_map, key_passwords, platform_api_level,
|
||||
codename_to_api_level_map,
|
||||
compressed_extension):
|
||||
# maxsize measures the maximum filename length, including the ones to be
|
||||
# skipped.
|
||||
maxsize = max(
|
||||
[len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
|
||||
if GetApkFileInfo(i.filename, compressed_extension)[0]])
|
||||
if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
|
||||
system_root_image = misc_info.get("system_root_image") == "true"
|
||||
|
||||
for info in input_tf_zip.infolist():
|
||||
|
@ -288,10 +311,18 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
|
|||
|
||||
data = input_tf_zip.read(filename)
|
||||
out_info = copy.copy(info)
|
||||
(is_apk, is_compressed) = GetApkFileInfo(filename, compressed_extension)
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
|
||||
|
||||
if is_apk and should_be_skipped:
|
||||
# Copy skipped APKs verbatim.
|
||||
print(
|
||||
"NOT signing: %s\n"
|
||||
" (skipped due to matching prefix)" % (filename,))
|
||||
common.ZipWriteStr(output_tf_zip, out_info, data)
|
||||
|
||||
# Sign APKs.
|
||||
if is_apk:
|
||||
elif is_apk:
|
||||
name = os.path.basename(filename)
|
||||
if is_compressed:
|
||||
name = name[:-len(compressed_extension)]
|
||||
|
@ -304,7 +335,9 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
|
|||
common.ZipWriteStr(output_tf_zip, out_info, signed_data)
|
||||
else:
|
||||
# an APK we're not supposed to sign.
|
||||
print("NOT signing: %s" % (name,))
|
||||
print(
|
||||
"NOT signing: %s\n"
|
||||
" (skipped due to special cert string)" % (name,))
|
||||
common.ZipWriteStr(output_tf_zip, out_info, data)
|
||||
|
||||
# System properties.
|
||||
|
@ -794,6 +827,12 @@ def main(argv):
|
|||
names = names.split(",")
|
||||
for n in names:
|
||||
OPTIONS.extra_apks[n] = key
|
||||
elif o == "--skip_apks_with_path_prefix":
|
||||
# Sanity check the prefix, which must be in all upper case.
|
||||
prefix = a.split('/')[0]
|
||||
if not prefix or prefix != prefix.upper():
|
||||
raise ValueError("Invalid path prefix '%s'" % (a,))
|
||||
OPTIONS.skip_apks_with_path_prefix.add(a)
|
||||
elif o in ("-d", "--default_key_mappings"):
|
||||
key_mapping_options.append((None, a))
|
||||
elif o in ("-k", "--key_mapping"):
|
||||
|
@ -853,6 +892,7 @@ def main(argv):
|
|||
extra_opts="e:d:k:ot:",
|
||||
extra_long_opts=[
|
||||
"extra_apks=",
|
||||
"skip_apks_with_path_prefix=",
|
||||
"default_key_mappings=",
|
||||
"key_mapping=",
|
||||
"replace_ota_keys",
|
||||
|
|
|
@ -237,25 +237,116 @@ class SignTargetFilesApksTest(unittest.TestCase):
|
|||
AssertionError, CheckAllApksSigned, input_zip, apk_key_map, '.gz')
|
||||
|
||||
def test_GetApkFileInfo(self):
|
||||
(is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk", None)
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/apps/Chats.apk", None, [])
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.dat", None)
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/apps/Chats.apk", None, [])
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/apps/Chats.dat", None, [])
|
||||
self.assertFalse(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
def test_GetApkFileInfo_withCompressedApks(self):
|
||||
(is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk.gz", ".gz")
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/apps/Chats.apk.gz", ".gz", [])
|
||||
self.assertTrue(is_apk)
|
||||
self.assertTrue(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed) = GetApkFileInfo("PRODUCT/apps/Chats.apk.gz", ".xz")
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/apps/Chats.apk.gz", ".xz", [])
|
||||
self.assertFalse(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
self.assertRaises(
|
||||
AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "")
|
||||
AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "", [])
|
||||
|
||||
self.assertRaises(
|
||||
AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk")
|
||||
AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk", [])
|
||||
|
||||
def test_GetApkFileInfo_withSkippedPrefixes(self):
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/preloads/apps/Chats.apk", None, set())
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"PRODUCT/preloads/apps/Chats.apk",
|
||||
None,
|
||||
set(["PRODUCT/preloads/"]))
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertTrue(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"SYSTEM_OTHER/preloads/apps/Chats.apk",
|
||||
None,
|
||||
set(["SYSTEM/preloads/", "SYSTEM_OTHER/preloads/"]))
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertTrue(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"SYSTEM_OTHER/preloads/apps/Chats.apk.gz",
|
||||
".gz",
|
||||
set(["PRODUCT/prebuilts/", "SYSTEM_OTHER/preloads/"]))
|
||||
self.assertTrue(is_apk)
|
||||
self.assertTrue(is_compressed)
|
||||
self.assertTrue(should_be_skipped)
|
||||
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"SYSTEM_OTHER/preloads/apps/Chats.dat",
|
||||
None,
|
||||
set(["SYSTEM_OTHER/preloads/"]))
|
||||
self.assertFalse(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertFalse(should_be_skipped)
|
||||
|
||||
def test_GetApkFileInfo_checkSkippedPrefixesInput(self):
|
||||
# set
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"SYSTEM_OTHER/preloads/apps/Chats.apk",
|
||||
None,
|
||||
set(["SYSTEM_OTHER/preloads/"]))
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertTrue(should_be_skipped)
|
||||
|
||||
# tuple
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"SYSTEM_OTHER/preloads/apps/Chats.apk",
|
||||
None,
|
||||
("SYSTEM_OTHER/preloads/",))
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertTrue(should_be_skipped)
|
||||
|
||||
# list
|
||||
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
|
||||
"SYSTEM_OTHER/preloads/apps/Chats.apk",
|
||||
None,
|
||||
["SYSTEM_OTHER/preloads/"])
|
||||
self.assertTrue(is_apk)
|
||||
self.assertFalse(is_compressed)
|
||||
self.assertTrue(should_be_skipped)
|
||||
|
||||
# str is invalid.
|
||||
self.assertRaises(
|
||||
AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
|
||||
None, "SYSTEM_OTHER/preloads/")
|
||||
|
||||
# None is invalid.
|
||||
self.assertRaises(
|
||||
AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
|
||||
None, None)
|
||||
|
|
Loading…
Reference in New Issue