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
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 756bc8a..393c33d 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -27,6 +27,12 @@
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 @@
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 @@
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 @@
"""
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 @@
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 @@
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 @@
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 @@
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 @@
extra_opts="e:d:k:ot:",
extra_long_opts=[
"extra_apks=",
+ "skip_apks_with_path_prefix=",
"default_key_mappings=",
"key_mapping=",
"replace_ota_keys",