Revert of Android: Delete rezip in favor of zipalign -p (patchset #1 id:1 of https://codereview.chromium.org/2594803002/ )
Reason for revert: Broke downstream monochrome Original issue's description: > Android: Delete rezip in favor of zipalign -p > > There's no need for a custom alignment tool now that zipalign supports > page-aligning libraries. > > BUG=676029 > > Committed: https://crrev.com/5e7b486a42ae172313d28a6ef9c5e4bba38cd74f > Cr-Commit-Position: refs/heads/master@{#440140} TBR=michaelbai@chromium.org,jbudorick@chromium.org # Not skipping CQ checks because original CL landed more than 1 days ago. BUG=676029,676589 Review-Url: https://codereview.chromium.org/2595233003 Cr-Original-Commit-Position: refs/heads/master@{#440457} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 8f43f71ff691157cd0db2725f527dc197c89685c
This commit is contained in:
Родитель
a9e7851a16
Коммит
2693c961c5
|
@ -163,15 +163,12 @@ def _AddNativeLibraries(out_apk, native_libs, android_abi, uncompress):
|
||||||
"""Add native libraries to APK."""
|
"""Add native libraries to APK."""
|
||||||
for path in native_libs:
|
for path in native_libs:
|
||||||
basename = os.path.basename(path)
|
basename = os.path.basename(path)
|
||||||
|
apk_path = 'lib/%s/%s' % (android_abi, basename)
|
||||||
|
|
||||||
compress = None
|
compress = None
|
||||||
if (uncompress and os.path.splitext(basename)[1] == '.so'
|
if (uncompress and os.path.splitext(basename)[1] == '.so'):
|
||||||
and 'android_linker' not in basename):
|
|
||||||
compress = False
|
compress = False
|
||||||
# Add prefix to prevent android install from extracting upon install.
|
|
||||||
basename = 'crazy.' + basename
|
|
||||||
|
|
||||||
apk_path = 'lib/%s/%s' % (android_abi, basename)
|
|
||||||
build_utils.AddToZipHermetic(out_apk,
|
build_utils.AddToZipHermetic(out_apk,
|
||||||
apk_path,
|
apk_path,
|
||||||
src_path=path,
|
src_path=path,
|
||||||
|
@ -268,9 +265,8 @@ def main(args):
|
||||||
options.uncompress_shared_libraries)
|
options.uncompress_shared_libraries)
|
||||||
|
|
||||||
for name in sorted(options.native_lib_placeholders):
|
for name in sorted(options.native_lib_placeholders):
|
||||||
# Note: Empty libs files are ignored by md5check (can cause issues
|
# Empty libs files are ignored by md5check, but rezip requires them
|
||||||
# with stale builds when the only change is adding/removing
|
# to be empty in order to identify them as placeholders.
|
||||||
# placeholders).
|
|
||||||
apk_path = 'lib/%s/%s' % (options.android_abi, name)
|
apk_path = 'lib/%s/%s' % (options.android_abi, name)
|
||||||
build_utils.AddToZipHermetic(out_apk, apk_path, data='')
|
build_utils.AddToZipHermetic(out_apk, apk_path, data='')
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,32 @@ import resource_sizes # pylint: disable=unused-import
|
||||||
|
|
||||||
from util import build_utils
|
from util import build_utils
|
||||||
|
|
||||||
|
def RenameInflateAndAddPageAlignment(
|
||||||
|
rezip_apk_jar_path, in_zip_file, out_zip_file):
|
||||||
|
rezip_apk_cmd = [
|
||||||
|
'java',
|
||||||
|
'-classpath',
|
||||||
|
rezip_apk_jar_path,
|
||||||
|
'RezipApk',
|
||||||
|
'renamealign',
|
||||||
|
in_zip_file,
|
||||||
|
out_zip_file,
|
||||||
|
]
|
||||||
|
build_utils.CheckOutput(rezip_apk_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def ReorderAndAlignApk(rezip_apk_jar_path, in_zip_file, out_zip_file):
|
||||||
|
rezip_apk_cmd = [
|
||||||
|
'java',
|
||||||
|
'-classpath',
|
||||||
|
rezip_apk_jar_path,
|
||||||
|
'RezipApk',
|
||||||
|
'reorder',
|
||||||
|
in_zip_file,
|
||||||
|
out_zip_file,
|
||||||
|
]
|
||||||
|
build_utils.CheckOutput(rezip_apk_cmd)
|
||||||
|
|
||||||
|
|
||||||
def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path):
|
def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path):
|
||||||
shutil.copy(unsigned_path, signed_path)
|
shutil.copy(unsigned_path, signed_path)
|
||||||
|
@ -36,15 +62,14 @@ def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path):
|
||||||
build_utils.CheckOutput(sign_cmd)
|
build_utils.CheckOutput(sign_cmd)
|
||||||
|
|
||||||
|
|
||||||
def AlignApk(zipalign_path, unaligned_path, final_path):
|
def AlignApk(zipalign_path, package_align, unaligned_path, final_path):
|
||||||
# Note -p will page align native libraries (files ending with .so), but
|
|
||||||
# only those that are stored uncompressed.
|
|
||||||
align_cmd = [
|
align_cmd = [
|
||||||
zipalign_path,
|
zipalign_path,
|
||||||
'-p',
|
'-f'
|
||||||
'-f',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if package_align:
|
||||||
|
align_cmd += ['-p']
|
||||||
|
|
||||||
align_cmd += [
|
align_cmd += [
|
||||||
'4', # 4 bytes
|
'4', # 4 bytes
|
||||||
|
@ -60,13 +85,23 @@ def main(args):
|
||||||
parser = optparse.OptionParser()
|
parser = optparse.OptionParser()
|
||||||
build_utils.AddDepfileOption(parser)
|
build_utils.AddDepfileOption(parser)
|
||||||
|
|
||||||
|
parser.add_option('--rezip-apk-jar-path',
|
||||||
|
help='Path to the RezipApk jar file.')
|
||||||
parser.add_option('--zipalign-path', help='Path to the zipalign tool.')
|
parser.add_option('--zipalign-path', help='Path to the zipalign tool.')
|
||||||
|
parser.add_option('--page-align-shared-libraries',
|
||||||
|
action='store_true',
|
||||||
|
help='Page align shared libraries.')
|
||||||
parser.add_option('--unsigned-apk-path', help='Path to input unsigned APK.')
|
parser.add_option('--unsigned-apk-path', help='Path to input unsigned APK.')
|
||||||
parser.add_option('--final-apk-path',
|
parser.add_option('--final-apk-path',
|
||||||
help='Path to output signed and aligned APK.')
|
help='Path to output signed and aligned APK.')
|
||||||
parser.add_option('--key-path', help='Path to keystore for signing.')
|
parser.add_option('--key-path', help='Path to keystore for signing.')
|
||||||
parser.add_option('--key-passwd', help='Keystore password')
|
parser.add_option('--key-passwd', help='Keystore password')
|
||||||
parser.add_option('--key-name', help='Keystore name')
|
parser.add_option('--key-name', help='Keystore name')
|
||||||
|
parser.add_option('--stamp', help='Path to touch on success.')
|
||||||
|
parser.add_option('--load-library-from-zip', type='int',
|
||||||
|
help='If non-zero, build the APK such that the library can be loaded ' +
|
||||||
|
'directly from the zip file using the crazy linker. The library ' +
|
||||||
|
'will be renamed, uncompressed and page aligned.')
|
||||||
|
|
||||||
options, _ = parser.parse_args()
|
options, _ = parser.parse_args()
|
||||||
|
|
||||||
|
@ -75,9 +110,14 @@ def main(args):
|
||||||
options.key_path,
|
options.key_path,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if options.load_library_from_zip:
|
||||||
|
input_paths.append(options.rezip_apk_jar_path)
|
||||||
|
|
||||||
input_strings = [
|
input_strings = [
|
||||||
|
options.load_library_from_zip,
|
||||||
options.key_name,
|
options.key_name,
|
||||||
options.key_passwd,
|
options.key_passwd,
|
||||||
|
options.page_align_shared_libraries,
|
||||||
]
|
]
|
||||||
|
|
||||||
build_utils.CallAndWriteDepfileIfStale(
|
build_utils.CallAndWriteDepfileIfStale(
|
||||||
|
@ -89,34 +129,57 @@ def main(args):
|
||||||
output_paths=[options.final_apk_path])
|
output_paths=[options.final_apk_path])
|
||||||
|
|
||||||
|
|
||||||
def _NormalizeZip(path):
|
|
||||||
with tempfile.NamedTemporaryFile(suffix='.zip') as hermetic_signed_apk:
|
|
||||||
with zipfile.ZipFile(path, 'r') as zi:
|
|
||||||
with zipfile.ZipFile(hermetic_signed_apk, 'w') as zo:
|
|
||||||
for info in zi.infolist():
|
|
||||||
# Ignore 'extended local file headers'. Python doesn't write them
|
|
||||||
# properly (see https://bugs.python.org/issue1742205) which causes
|
|
||||||
# zipalign to miscalculate alignment. Since we don't use them except
|
|
||||||
# for alignment anyway, we write a stripped file here and let
|
|
||||||
# zipalign add them properly later. eLFHs are controlled by 'general
|
|
||||||
# purpose bit flag 03' (0x08) so we mask that out.
|
|
||||||
info.flag_bits = info.flag_bits & 0xF7
|
|
||||||
|
|
||||||
info.date_time = build_utils.HERMETIC_TIMESTAMP
|
|
||||||
zo.writestr(info, zi.read(info.filename))
|
|
||||||
|
|
||||||
shutil.copy(hermetic_signed_apk.name, path)
|
|
||||||
|
|
||||||
|
|
||||||
def FinalizeApk(options):
|
def FinalizeApk(options):
|
||||||
with tempfile.NamedTemporaryFile() as signed_apk_path_tmp:
|
with tempfile.NamedTemporaryFile() as signed_apk_path_tmp, \
|
||||||
|
tempfile.NamedTemporaryFile() as apk_to_sign_tmp:
|
||||||
|
|
||||||
|
if options.load_library_from_zip:
|
||||||
|
# We alter the name of the library so that the Android Package Manager
|
||||||
|
# does not extract it into a separate file. This must be done before
|
||||||
|
# signing, as the filename is part of the signed manifest. At the same
|
||||||
|
# time we uncompress the library, which is necessary so that it can be
|
||||||
|
# loaded directly from the APK.
|
||||||
|
# Move the library to a page boundary by adding a page alignment file.
|
||||||
|
apk_to_sign = apk_to_sign_tmp.name
|
||||||
|
RenameInflateAndAddPageAlignment(
|
||||||
|
options.rezip_apk_jar_path, options.unsigned_apk_path, apk_to_sign)
|
||||||
|
else:
|
||||||
|
apk_to_sign = options.unsigned_apk_path
|
||||||
|
|
||||||
signed_apk_path = signed_apk_path_tmp.name
|
signed_apk_path = signed_apk_path_tmp.name
|
||||||
JarSigner(options.key_path, options.key_name, options.key_passwd,
|
JarSigner(options.key_path, options.key_name, options.key_passwd,
|
||||||
options.unsigned_apk_path, signed_apk_path)
|
apk_to_sign, signed_apk_path)
|
||||||
# Make the newly added signing files hermetic.
|
|
||||||
_NormalizeZip(signed_apk_path)
|
|
||||||
|
|
||||||
AlignApk(options.zipalign_path, signed_apk_path, options.final_apk_path)
|
# Make the signing files hermetic.
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.zip') as hermetic_signed_apk:
|
||||||
|
with zipfile.ZipFile(signed_apk_path, 'r') as zi:
|
||||||
|
with zipfile.ZipFile(hermetic_signed_apk, 'w') as zo:
|
||||||
|
for info in zi.infolist():
|
||||||
|
# Ignore 'extended local file headers'. Python doesn't write them
|
||||||
|
# properly (see https://bugs.python.org/issue1742205) which causes
|
||||||
|
# zipalign to miscalculate alignment. Since we don't use them except
|
||||||
|
# for alignment anyway, we write a stripped file here and let
|
||||||
|
# zipalign add them properly later. eLFHs are controlled by 'general
|
||||||
|
# purpose bit flag 03' (0x08) so we mask that out.
|
||||||
|
info.flag_bits = info.flag_bits & 0xF7
|
||||||
|
|
||||||
|
info.date_time = build_utils.HERMETIC_TIMESTAMP
|
||||||
|
zo.writestr(info, zi.read(info.filename))
|
||||||
|
|
||||||
|
shutil.copy(hermetic_signed_apk.name, signed_apk_path)
|
||||||
|
|
||||||
|
if options.load_library_from_zip:
|
||||||
|
# Reorder the contents of the APK. This re-establishes the canonical
|
||||||
|
# order which means the library will be back at its page aligned location.
|
||||||
|
# This step also aligns uncompressed items to 4 bytes.
|
||||||
|
ReorderAndAlignApk(
|
||||||
|
options.rezip_apk_jar_path, signed_apk_path, options.final_apk_path)
|
||||||
|
else:
|
||||||
|
# Align uncompressed items to 4 bytes
|
||||||
|
AlignApk(options.zipalign_path,
|
||||||
|
options.page_align_shared_libraries,
|
||||||
|
signed_apk_path,
|
||||||
|
options.final_apk_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Copyright 2014 The Chromium Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
import("//build/config/android/rules.gni")
|
||||||
|
|
||||||
|
java_library("rezip") {
|
||||||
|
jar_path = "$root_build_dir/lib.java/rezip_apk.jar"
|
||||||
|
java_files = [ "RezipApk.java" ]
|
||||||
|
}
|
|
@ -0,0 +1,448 @@
|
||||||
|
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command line tool used to build APKs which support loading the native code library
|
||||||
|
* directly from the APK file. To construct the APK we rename the native library by
|
||||||
|
* adding the prefix "crazy." to the filename. This is done to prevent the Android
|
||||||
|
* Package Manager from extracting the library. The native code must be page aligned
|
||||||
|
* and uncompressed. The page alignment is implemented by adding a zero filled file
|
||||||
|
* in front of the the native code library. This tool is designed so that running
|
||||||
|
* SignApk and/or zipalign on the resulting APK does not break the page alignment.
|
||||||
|
* This is achieved by outputing the filenames in the same canonical order used
|
||||||
|
* by SignApk and adding the same alignment fields added by zipalign.
|
||||||
|
*/
|
||||||
|
class RezipApk {
|
||||||
|
// Alignment to use for non-compressed files (must match zipalign).
|
||||||
|
private static final int ALIGNMENT = 4;
|
||||||
|
|
||||||
|
// Alignment to use for non-compressed *.so files
|
||||||
|
private static final int LIBRARY_ALIGNMENT = 4096;
|
||||||
|
|
||||||
|
// Files matching this pattern are not copied to the output when adding alignment.
|
||||||
|
// When reordering and verifying the APK they are copied to the end of the file.
|
||||||
|
private static Pattern sMetaFilePattern =
|
||||||
|
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA)|com/android/otacert))|("
|
||||||
|
+ Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
|
||||||
|
|
||||||
|
// Pattern for matching a shared library in the APK
|
||||||
|
private static Pattern sLibraryPattern = Pattern.compile("^lib/[^/]*/lib.*[.]so$");
|
||||||
|
// Pattern for match the crazy linker in the APK
|
||||||
|
private static Pattern sCrazyLinkerPattern =
|
||||||
|
Pattern.compile("^lib/[^/]*/libchromium_android_linker.so$");
|
||||||
|
// Pattern for matching a crazy loaded shared library in the APK
|
||||||
|
private static Pattern sCrazyLibraryPattern = Pattern.compile("^lib/[^/]*/crazy.lib.*[.]so$");
|
||||||
|
|
||||||
|
private static boolean isLibraryFilename(String filename) {
|
||||||
|
return sLibraryPattern.matcher(filename).matches()
|
||||||
|
&& !sCrazyLinkerPattern.matcher(filename).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCrazyLibraryFilename(String filename) {
|
||||||
|
return sCrazyLibraryPattern.matcher(filename).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String renameLibraryForCrazyLinker(String filename) {
|
||||||
|
int lastSlash = filename.lastIndexOf('/');
|
||||||
|
// We rename the library, so that the Android Package Manager
|
||||||
|
// no longer extracts the library.
|
||||||
|
return filename.substring(0, lastSlash + 1) + "crazy." + filename.substring(lastSlash + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps another output stream, counting the number of bytes written.
|
||||||
|
*/
|
||||||
|
private static class CountingOutputStream extends OutputStream {
|
||||||
|
private long mCount = 0;
|
||||||
|
private OutputStream mOut;
|
||||||
|
|
||||||
|
public CountingOutputStream(OutputStream out) {
|
||||||
|
this.mOut = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the number of bytes written. */
|
||||||
|
public long getCount() {
|
||||||
|
return mCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
mOut.write(b, off, len);
|
||||||
|
mCount += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(int b) throws IOException {
|
||||||
|
mOut.write(b);
|
||||||
|
mCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void close() throws IOException {
|
||||||
|
mOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void flush() throws IOException {
|
||||||
|
mOut.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String outputName(JarEntry entry, boolean rename) {
|
||||||
|
String inName = entry.getName();
|
||||||
|
if (rename && entry.getSize() > 0 && isLibraryFilename(inName)) {
|
||||||
|
return renameLibraryForCrazyLinker(inName);
|
||||||
|
}
|
||||||
|
return inName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator used to sort jar entries from the input file.
|
||||||
|
* Sorting is done based on the output filename (which maybe renamed).
|
||||||
|
* Filenames are in natural string order, except that filenames matching
|
||||||
|
* the meta-file pattern are always after other files. This is so the manifest
|
||||||
|
* and signature are at the end of the file after any alignment file.
|
||||||
|
*/
|
||||||
|
private static class EntryComparator implements Comparator<JarEntry> {
|
||||||
|
private boolean mRename;
|
||||||
|
|
||||||
|
public EntryComparator(boolean rename) {
|
||||||
|
mRename = rename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(JarEntry j1, JarEntry j2) {
|
||||||
|
String o1 = outputName(j1, mRename);
|
||||||
|
String o2 = outputName(j2, mRename);
|
||||||
|
boolean o1Matches = sMetaFilePattern.matcher(o1).matches();
|
||||||
|
boolean o2Matches = sMetaFilePattern.matcher(o2).matches();
|
||||||
|
if (o1Matches != o2Matches) {
|
||||||
|
return o1Matches ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return o1.compareTo(o2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build an ordered list of jar entries. The jar entries from the input are
|
||||||
|
// sorted based on the output filenames (which maybe renamed). If |omitMetaFiles|
|
||||||
|
// is true do not include the jar entries for the META-INF files.
|
||||||
|
// Entries are ordered in the deterministic order used by SignApk.
|
||||||
|
private static List<JarEntry> getOutputFileOrderEntries(
|
||||||
|
JarFile jar, boolean omitMetaFiles, boolean rename) {
|
||||||
|
List<JarEntry> entries = new ArrayList<JarEntry>();
|
||||||
|
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
|
||||||
|
JarEntry entry = e.nextElement();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (omitMetaFiles && sMetaFilePattern.matcher(entry.getName()).matches()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We sort the input entries by name. When present META-INF files
|
||||||
|
// are sorted to the end.
|
||||||
|
Collections.sort(entries, new EntryComparator(rename));
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a zero filled alignment file at this point in the zip file,
|
||||||
|
* The added file will be added before |name| and after |prevName|.
|
||||||
|
* The size of the alignment file is such that the location of the
|
||||||
|
* file |name| will be on a LIBRARY_ALIGNMENT boundary.
|
||||||
|
*
|
||||||
|
* Note this arrangement is devised so that running SignApk and/or zipalign on the resulting
|
||||||
|
* file will not alter the alignment.
|
||||||
|
*
|
||||||
|
* @param offset number of bytes into the output file at this point.
|
||||||
|
* @param timestamp time in millis since the epoch to include in the header.
|
||||||
|
* @param name the name of the library filename.
|
||||||
|
* @param prevName the name of the previous file in the archive (or null).
|
||||||
|
* @param out jar output stream to write the alignment file to.
|
||||||
|
*
|
||||||
|
* @throws IOException if the output file can not be written.
|
||||||
|
*/
|
||||||
|
private static void addAlignmentFile(
|
||||||
|
long offset, long timestamp, String name, String prevName,
|
||||||
|
JarOutputStream out) throws IOException {
|
||||||
|
|
||||||
|
// Compute the start and alignment of the library, as if it was next.
|
||||||
|
int headerSize = JarFile.LOCHDR + name.length();
|
||||||
|
long libOffset = offset + headerSize;
|
||||||
|
int libNeeded = LIBRARY_ALIGNMENT - (int) (libOffset % LIBRARY_ALIGNMENT);
|
||||||
|
if (libNeeded == LIBRARY_ALIGNMENT) {
|
||||||
|
// Already aligned, no need to added alignment file.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is not another file between the library and the
|
||||||
|
// alignment file.
|
||||||
|
String alignName = name.substring(0, name.length() - 2) + "align";
|
||||||
|
if (prevName != null && prevName.compareTo(alignName) >= 0) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Unable to insert alignment file, because there is "
|
||||||
|
+ "another file in front of the file to be aligned. "
|
||||||
|
+ "Other file: " + prevName + " Alignment file: " + alignName
|
||||||
|
+ " file: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the size of the alignment file header.
|
||||||
|
headerSize = JarFile.LOCHDR + alignName.length();
|
||||||
|
// We are going to add an alignment file of type STORED. This file
|
||||||
|
// will itself induce a zipalign alignment adjustment.
|
||||||
|
int extraNeeded =
|
||||||
|
(ALIGNMENT - (int) ((offset + headerSize) % ALIGNMENT)) % ALIGNMENT;
|
||||||
|
headerSize += extraNeeded;
|
||||||
|
|
||||||
|
if (libNeeded < headerSize + 1) {
|
||||||
|
// The header was bigger than the alignment that we need, add another page.
|
||||||
|
libNeeded += LIBRARY_ALIGNMENT;
|
||||||
|
}
|
||||||
|
// Compute the size of the alignment file.
|
||||||
|
libNeeded -= headerSize;
|
||||||
|
|
||||||
|
// Build the header for the alignment file.
|
||||||
|
byte[] zeroBuffer = new byte[libNeeded];
|
||||||
|
JarEntry alignEntry = new JarEntry(alignName);
|
||||||
|
alignEntry.setMethod(JarEntry.STORED);
|
||||||
|
alignEntry.setSize(libNeeded);
|
||||||
|
alignEntry.setTime(timestamp);
|
||||||
|
CRC32 crc = new CRC32();
|
||||||
|
crc.update(zeroBuffer);
|
||||||
|
alignEntry.setCrc(crc.getValue());
|
||||||
|
|
||||||
|
if (extraNeeded != 0) {
|
||||||
|
alignEntry.setExtra(new byte[extraNeeded]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output the alignment file.
|
||||||
|
out.putNextEntry(alignEntry);
|
||||||
|
out.write(zeroBuffer);
|
||||||
|
out.closeEntry();
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a JarEntry for the output file which corresponds to the input
|
||||||
|
// file. The output file will be called |name|. The output file will always
|
||||||
|
// be uncompressed (STORED). If the input is not STORED it is necessary to inflate
|
||||||
|
// it to compute the CRC and size of the output entry.
|
||||||
|
private static JarEntry makeStoredEntry(String name, JarEntry inEntry, JarFile in)
|
||||||
|
throws IOException {
|
||||||
|
JarEntry outEntry = new JarEntry(name);
|
||||||
|
outEntry.setMethod(JarEntry.STORED);
|
||||||
|
|
||||||
|
if (inEntry.getMethod() == JarEntry.STORED) {
|
||||||
|
outEntry.setCrc(inEntry.getCrc());
|
||||||
|
outEntry.setSize(inEntry.getSize());
|
||||||
|
} else {
|
||||||
|
// We are inflating the file. We need to compute the CRC and size.
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
CRC32 crc = new CRC32();
|
||||||
|
int size = 0;
|
||||||
|
int num;
|
||||||
|
InputStream data = in.getInputStream(inEntry);
|
||||||
|
while ((num = data.read(buffer)) > 0) {
|
||||||
|
crc.update(buffer, 0, num);
|
||||||
|
size += num;
|
||||||
|
}
|
||||||
|
data.close();
|
||||||
|
outEntry.setCrc(crc.getValue());
|
||||||
|
outEntry.setSize(size);
|
||||||
|
}
|
||||||
|
return outEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the contents of the input APK file to the output APK file. If |rename| is
|
||||||
|
* true then non-empty libraries (*.so) in the input will be renamed by prefixing
|
||||||
|
* "crazy.". This is done to prevent the Android Package Manager extracting the
|
||||||
|
* library. Note the crazy linker itself is not renamed, for bootstrapping reasons.
|
||||||
|
* Empty libraries are not renamed (they are in the APK to workaround a bug where
|
||||||
|
* the Android Package Manager fails to delete old versions when upgrading).
|
||||||
|
* There must be exactly one "crazy" library in the output stream. The "crazy"
|
||||||
|
* library will be uncompressed and page aligned in the output stream. Page
|
||||||
|
* alignment is implemented by adding a zero filled file, regular alignment is
|
||||||
|
* implemented by adding a zero filled extra field to the zip file header. If
|
||||||
|
* |addAlignment| is true a page alignment file is added, otherwise the "crazy"
|
||||||
|
* library must already be page aligned. Care is taken so that the output is generated
|
||||||
|
* in the same way as SignApk. This is important so that running SignApk and
|
||||||
|
* zipalign on the output does not break the page alignment. The archive may not
|
||||||
|
* contain a "*.apk" as SignApk has special nested signing logic that we do not
|
||||||
|
* support.
|
||||||
|
*
|
||||||
|
* @param in The input APK File.
|
||||||
|
* @param out The output APK stream.
|
||||||
|
* @param countOut Counting output stream (to measure the current offset).
|
||||||
|
* @param addAlignment Whether to add the alignment file or just check.
|
||||||
|
* @param rename Whether to rename libraries to be "crazy".
|
||||||
|
*
|
||||||
|
* @throws IOException if the output file can not be written.
|
||||||
|
*/
|
||||||
|
private static void rezip(
|
||||||
|
JarFile in, JarOutputStream out, CountingOutputStream countOut,
|
||||||
|
boolean addAlignment, boolean rename) throws IOException {
|
||||||
|
|
||||||
|
List<JarEntry> entries = getOutputFileOrderEntries(in, addAlignment, rename);
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
boolean firstEntry = true;
|
||||||
|
String prevName = null;
|
||||||
|
int numCrazy = 0;
|
||||||
|
for (JarEntry inEntry : entries) {
|
||||||
|
// Rename files, if specied.
|
||||||
|
String name = outputName(inEntry, rename);
|
||||||
|
if (name.endsWith(".apk")) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Nested APKs are not supported: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the header.
|
||||||
|
JarEntry outEntry = null;
|
||||||
|
boolean isCrazy = isCrazyLibraryFilename(name);
|
||||||
|
if (isCrazy) {
|
||||||
|
// "crazy" libraries are alway output uncompressed (STORED).
|
||||||
|
outEntry = makeStoredEntry(name, inEntry, in);
|
||||||
|
numCrazy++;
|
||||||
|
if (numCrazy > 1) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Found more than one library\n"
|
||||||
|
+ "Multiple libraries are not supported for APKs that use "
|
||||||
|
+ "'load_library_from_zip'.\n"
|
||||||
|
+ "See crbug/388223.\n"
|
||||||
|
+ "Note, check that your build is clean.\n"
|
||||||
|
+ "An unclean build can incorrectly incorporate old "
|
||||||
|
+ "libraries in the APK.");
|
||||||
|
}
|
||||||
|
} else if (inEntry.getMethod() == JarEntry.STORED) {
|
||||||
|
// Preserve the STORED method of the input entry.
|
||||||
|
outEntry = new JarEntry(inEntry);
|
||||||
|
outEntry.setExtra(null);
|
||||||
|
} else {
|
||||||
|
// Create a new entry so that the compressed len is recomputed.
|
||||||
|
outEntry = new JarEntry(name);
|
||||||
|
}
|
||||||
|
outEntry.setTime(timestamp);
|
||||||
|
|
||||||
|
// Compute and add alignment
|
||||||
|
long offset = countOut.getCount();
|
||||||
|
if (firstEntry) {
|
||||||
|
// The first entry in a jar file has an extra field of
|
||||||
|
// four bytes that you can't get rid of; any extra
|
||||||
|
// data you specify in the JarEntry is appended to
|
||||||
|
// these forced four bytes. This is JAR_MAGIC in
|
||||||
|
// JarOutputStream; the bytes are 0xfeca0000.
|
||||||
|
firstEntry = false;
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
if (outEntry.getMethod() == JarEntry.STORED) {
|
||||||
|
if (isCrazy) {
|
||||||
|
if (addAlignment) {
|
||||||
|
addAlignmentFile(offset, timestamp, name, prevName, out);
|
||||||
|
}
|
||||||
|
// We check that we did indeed get to a page boundary.
|
||||||
|
offset = countOut.getCount() + JarFile.LOCHDR + name.length();
|
||||||
|
if ((offset % LIBRARY_ALIGNMENT) != 0) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"Library was not page aligned when verifying page alignment. "
|
||||||
|
+ "Library name: " + name + " Expected alignment: "
|
||||||
|
+ LIBRARY_ALIGNMENT + "Offset: " + offset + " Error: "
|
||||||
|
+ (offset % LIBRARY_ALIGNMENT));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is equivalent to zipalign.
|
||||||
|
offset += JarFile.LOCHDR + name.length();
|
||||||
|
int needed = (ALIGNMENT - (int) (offset % ALIGNMENT)) % ALIGNMENT;
|
||||||
|
if (needed != 0) {
|
||||||
|
outEntry.setExtra(new byte[needed]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.putNextEntry(outEntry);
|
||||||
|
|
||||||
|
// Copy the data from the input to the output
|
||||||
|
int num;
|
||||||
|
InputStream data = in.getInputStream(inEntry);
|
||||||
|
while ((num = data.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, num);
|
||||||
|
}
|
||||||
|
data.close();
|
||||||
|
out.closeEntry();
|
||||||
|
out.flush();
|
||||||
|
prevName = name;
|
||||||
|
}
|
||||||
|
if (numCrazy == 0) {
|
||||||
|
throw new AssertionError("There was no crazy library in the archive");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage() {
|
||||||
|
System.err.println("Usage: prealignapk (addalignment|reorder) input.apk output.apk");
|
||||||
|
System.err.println("\"crazy\" libraries are always inflated in the output");
|
||||||
|
System.err.println(
|
||||||
|
" renamealign - rename libraries with \"crazy.\" prefix and add alignment file");
|
||||||
|
System.err.println(" align - add alignment file");
|
||||||
|
System.err.println(" reorder - re-creates canonical ordering and checks alignment");
|
||||||
|
System.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
if (args.length != 3) usage();
|
||||||
|
|
||||||
|
boolean addAlignment = false;
|
||||||
|
boolean rename = false;
|
||||||
|
if (args[0].equals("renamealign")) {
|
||||||
|
// Normal case. Before signing we rename the library and add an alignment file.
|
||||||
|
addAlignment = true;
|
||||||
|
rename = true;
|
||||||
|
} else if (args[0].equals("align")) {
|
||||||
|
// LGPL compliance case. Before signing, we add an alignment file to a
|
||||||
|
// reconstructed APK which already contains the "crazy" library.
|
||||||
|
addAlignment = true;
|
||||||
|
rename = false;
|
||||||
|
} else if (args[0].equals("reorder")) {
|
||||||
|
// Normal case. After jarsigning we write the file in the canonical order and check.
|
||||||
|
addAlignment = false;
|
||||||
|
} else {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
String inputFilename = args[1];
|
||||||
|
String outputFilename = args[2];
|
||||||
|
|
||||||
|
JarFile inputJar = null;
|
||||||
|
FileOutputStream outputFile = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputJar = new JarFile(new File(inputFilename), true);
|
||||||
|
outputFile = new FileOutputStream(outputFilename);
|
||||||
|
|
||||||
|
CountingOutputStream outCount = new CountingOutputStream(outputFile);
|
||||||
|
JarOutputStream outputJar = new JarOutputStream(outCount);
|
||||||
|
|
||||||
|
// Match the compression level used by SignApk.
|
||||||
|
outputJar.setLevel(9);
|
||||||
|
|
||||||
|
rezip(inputJar, outputJar, outCount, addAlignment, rename);
|
||||||
|
outputJar.close();
|
||||||
|
} finally {
|
||||||
|
if (inputJar != null) inputJar.close();
|
||||||
|
if (outputFile != null) outputFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ _java_target_whitelist = [
|
||||||
|
|
||||||
# TODO(agrieve): Rename targets below to match above patterns.
|
# TODO(agrieve): Rename targets below to match above patterns.
|
||||||
"*android_webview/glue:glue",
|
"*android_webview/glue:glue",
|
||||||
|
"//build/android/rezip:rezip",
|
||||||
"//chrome/test/android/cast_emulator:cast_emulator",
|
"//chrome/test/android/cast_emulator:cast_emulator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1384,6 +1385,7 @@ if (enable_java_templates) {
|
||||||
# keystore_path: Path to keystore to use for signing.
|
# keystore_path: Path to keystore to use for signing.
|
||||||
# keystore_name: Key alias to use.
|
# keystore_name: Key alias to use.
|
||||||
# keystore_password: Keystore password.
|
# keystore_password: Keystore password.
|
||||||
|
# rezip_apk: Whether to add crazy-linker alignment.
|
||||||
template("finalize_apk") {
|
template("finalize_apk") {
|
||||||
action(target_name) {
|
action(target_name) {
|
||||||
deps = []
|
deps = []
|
||||||
|
@ -1426,6 +1428,20 @@ if (enable_java_templates) {
|
||||||
"--key-passwd",
|
"--key-passwd",
|
||||||
invoker.keystore_password,
|
invoker.keystore_password,
|
||||||
]
|
]
|
||||||
|
if (defined(invoker.rezip_apk) && invoker.rezip_apk) {
|
||||||
|
deps += [ "//build/android/rezip" ]
|
||||||
|
_rezip_jar_path = "$root_build_dir/lib.java/rezip_apk.jar"
|
||||||
|
args += [
|
||||||
|
"--load-library-from-zip=1",
|
||||||
|
"--rezip-apk-jar-path",
|
||||||
|
rebase_path(_rezip_jar_path, root_build_dir),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined(invoker.page_align_shared_libraries) &&
|
||||||
|
invoker.page_align_shared_libraries) {
|
||||||
|
args += [ "--page-align-shared-libraries" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1668,9 +1684,6 @@ if (enable_java_templates) {
|
||||||
"uncompress_shared_libraries",
|
"uncompress_shared_libraries",
|
||||||
"write_asset_list",
|
"write_asset_list",
|
||||||
])
|
])
|
||||||
if (!defined(uncompress_shared_libraries)) {
|
|
||||||
uncompress_shared_libraries = _load_library_from_apk
|
|
||||||
}
|
|
||||||
deps = _deps + [ ":${_package_resources_target_name}" ]
|
deps = _deps + [ ":${_package_resources_target_name}" ]
|
||||||
native_libs = _native_libs + _native_libs_even_when_incremental
|
native_libs = _native_libs + _native_libs_even_when_incremental
|
||||||
|
|
||||||
|
@ -1691,9 +1704,6 @@ if (enable_java_templates) {
|
||||||
"secondary_native_libs",
|
"secondary_native_libs",
|
||||||
"uncompress_shared_libraries",
|
"uncompress_shared_libraries",
|
||||||
])
|
])
|
||||||
if (!defined(uncompress_shared_libraries)) {
|
|
||||||
uncompress_shared_libraries = _load_library_from_apk
|
|
||||||
}
|
|
||||||
_dex_target = "//build/android/incremental_install:bootstrap_java__dex"
|
_dex_target = "//build/android/incremental_install:bootstrap_java__dex"
|
||||||
deps = _incremental_deps + [
|
deps = _incremental_deps + [
|
||||||
":${_incremental_package_resources_target_name}",
|
":${_incremental_package_resources_target_name}",
|
||||||
|
@ -1720,11 +1730,14 @@ if (enable_java_templates) {
|
||||||
|
|
||||||
_finalize_apk_rule_name = "${target_name}__finalize"
|
_finalize_apk_rule_name = "${target_name}__finalize"
|
||||||
finalize_apk(_finalize_apk_rule_name) {
|
finalize_apk(_finalize_apk_rule_name) {
|
||||||
|
forward_variables_from(invoker, [ "page_align_shared_libraries" ])
|
||||||
|
|
||||||
input_apk_path = _packaged_apk_path
|
input_apk_path = _packaged_apk_path
|
||||||
output_apk_path = _final_apk_path
|
output_apk_path = _final_apk_path
|
||||||
keystore_path = _keystore_path
|
keystore_path = _keystore_path
|
||||||
keystore_name = _keystore_name
|
keystore_name = _keystore_name
|
||||||
keystore_password = _keystore_password
|
keystore_password = _keystore_password
|
||||||
|
rezip_apk = _load_library_from_apk
|
||||||
|
|
||||||
public_deps = [
|
public_deps = [
|
||||||
# Generator of the _packaged_apk_path this target takes as input.
|
# Generator of the _packaged_apk_path this target takes as input.
|
||||||
|
|
|
@ -2007,7 +2007,15 @@ if (enable_java_templates) {
|
||||||
_extra_native_libs_deps = []
|
_extra_native_libs_deps = []
|
||||||
assert(_extra_native_libs_deps == []) # Mark as used.
|
assert(_extra_native_libs_deps == []) # Mark as used.
|
||||||
_extra_native_libs_even_when_incremental = []
|
_extra_native_libs_even_when_incremental = []
|
||||||
|
_extra_native_libs_even_when_incremental_deps = []
|
||||||
|
assert(_extra_native_libs_even_when_incremental_deps == []) # Mark as used.
|
||||||
if (_native_libs_deps != []) {
|
if (_native_libs_deps != []) {
|
||||||
|
# zipalign can't align gdb_server, don't pack gdbserver temporarily.
|
||||||
|
if (is_debug && (!defined(invoker.page_align_shared_libraries) ||
|
||||||
|
!invoker.page_align_shared_libraries)) {
|
||||||
|
_extra_native_libs_even_when_incremental = [ android_gdbserver ]
|
||||||
|
}
|
||||||
|
|
||||||
if (_use_chromium_linker) {
|
if (_use_chromium_linker) {
|
||||||
_extra_native_libs =
|
_extra_native_libs =
|
||||||
[ "$root_shlib_dir/libchromium_android_linker$shlib_extension" ]
|
[ "$root_shlib_dir/libchromium_android_linker$shlib_extension" ]
|
||||||
|
@ -2029,6 +2037,7 @@ if (enable_java_templates) {
|
||||||
"deps",
|
"deps",
|
||||||
"extensions_to_not_compress",
|
"extensions_to_not_compress",
|
||||||
"language_splits",
|
"language_splits",
|
||||||
|
"page_align_shared_libraries",
|
||||||
"public_deps",
|
"public_deps",
|
||||||
"secondary_native_libs",
|
"secondary_native_libs",
|
||||||
"shared_resources",
|
"shared_resources",
|
||||||
|
@ -2078,6 +2087,7 @@ if (enable_java_templates) {
|
||||||
_extra_native_libs_even_when_incremental != []) &&
|
_extra_native_libs_even_when_incremental != []) &&
|
||||||
!_create_abi_split) {
|
!_create_abi_split) {
|
||||||
deps += _native_libs_deps + _extra_native_libs_deps +
|
deps += _native_libs_deps + _extra_native_libs_deps +
|
||||||
|
_extra_native_libs_even_when_incremental_deps +
|
||||||
[ _native_libs_file_arg_dep ]
|
[ _native_libs_file_arg_dep ]
|
||||||
native_libs_filearg = _native_libs_file_arg
|
native_libs_filearg = _native_libs_file_arg
|
||||||
native_libs = _extra_native_libs
|
native_libs = _extra_native_libs
|
||||||
|
@ -2138,7 +2148,9 @@ if (enable_java_templates) {
|
||||||
"public_deps",
|
"public_deps",
|
||||||
])
|
])
|
||||||
|
|
||||||
incremental_deps = deps + [ ":$_manifest_rule" ]
|
incremental_deps =
|
||||||
|
deps + _extra_native_libs_even_when_incremental_deps +
|
||||||
|
[ ":$_manifest_rule" ]
|
||||||
deps = []
|
deps = []
|
||||||
deps = incremental_deps + _native_libs_deps + _extra_native_libs_deps +
|
deps = incremental_deps + _native_libs_deps + _extra_native_libs_deps +
|
||||||
[ _native_libs_file_arg_dep ]
|
[ _native_libs_file_arg_dep ]
|
||||||
|
|
Загрузка…
Ссылка в новой задаче