Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 import java.io.File; | 5 import java.io.File; |
| 6 import java.io.FileOutputStream; | 6 import java.io.FileOutputStream; |
| 7 import java.io.IOException; | 7 import java.io.IOException; |
| 8 import java.io.InputStream; | 8 import java.io.InputStream; |
| 9 import java.io.OutputStream; | 9 import java.io.OutputStream; |
| 10 import java.util.ArrayList; | 10 import java.util.ArrayList; |
| 11 import java.util.Collections; | 11 import java.util.Collections; |
| 12 import java.util.Comparator; | 12 import java.util.Comparator; |
| 13 import java.util.Enumeration; | 13 import java.util.Enumeration; |
| 14 import java.util.HashMap; | |
| 14 import java.util.List; | 15 import java.util.List; |
| 16 import java.util.Map; | |
| 15 import java.util.jar.JarEntry; | 17 import java.util.jar.JarEntry; |
| 16 import java.util.jar.JarFile; | 18 import java.util.jar.JarFile; |
| 17 import java.util.jar.JarOutputStream; | 19 import java.util.jar.JarOutputStream; |
| 18 import java.util.regex.Pattern; | 20 import java.util.regex.Pattern; |
| 19 import java.util.zip.CRC32; | 21 import java.util.zip.CRC32; |
| 20 | 22 |
| 21 /** | 23 /** |
| 22 * Command line tool used to page align non-compressed libraries (*.so) in APK f iles. | 24 * Command line tool used to build APKs which support loading the native code li brary |
| 23 * Tool is designed so that running SignApk and/or zipalign on the resulting APK does not | 25 * directly from the APK file. To construct the APK we rename the native library by |
| 24 * break the page alignment. | 26 * adding the prefix "crazy." to the filename. This is done to prevent the Andro id |
| 27 * Package Manager from extracting the library. The native code must be page ali gned | |
| 28 * and uncompressed. The page alignment is implemented by adding a zero filled f ile | |
| 29 * in front of the the native code library. This tool is designed so that runnin g | |
| 30 * SignApk and/or zipalign on the resulting APK does not break the page alignmen t. | |
| 31 * This is achieved by outputing the filenames in the same canonical order used | |
| 32 * by SignApk and adding the same alignment fields added by zipalign. | |
| 25 */ | 33 */ |
| 26 class RezipApk { | 34 class RezipApk { |
| 27 // Alignment to use for non-compressed files (must match zipalign). | 35 // Alignment to use for non-compressed files (must match zipalign). |
| 28 private static final int ALIGNMENT = 4; | 36 private static final int ALIGNMENT = 4; |
| 29 | 37 |
| 30 // Alignment to use for non-compressed *.so files | 38 // Alignment to use for non-compressed *.so files |
| 31 private static final int LIBRARY_ALIGNMENT = 4096; | 39 private static final int LIBRARY_ALIGNMENT = 4096; |
| 32 | 40 |
| 33 // Files matching this pattern are not copied to the output when adding alig nment. | 41 // Files matching this pattern are not copied to the output when adding alig nment. |
| 34 // When reordering and verifying the APK they are copied to the end of the f ile. | 42 // When reordering and verifying the APK they are copied to the end of the f ile. |
| 35 private static Pattern sMetaFilePattern = | 43 private static Pattern sMetaFilePattern = |
| 36 Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA)|com/android/otacert ))|(" + | 44 Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA)|com/android/otacert ))|(" + |
| 37 Pattern.quote(JarFile.MANIFEST_NAME) + ")$"); | 45 Pattern.quote(JarFile.MANIFEST_NAME) + ")$"); |
| 38 | 46 |
| 47 // Pattern for matching a shared library in the APK | |
| 48 private static Pattern sLibraryPattern = Pattern.compile("^lib/[^/]*/lib.*[. ]so$"); | |
| 49 // Pattern for match the crazy linker in the APK | |
| 50 private static Pattern sCrazyLinkerPattern = | |
| 51 Pattern.compile("^lib/[^/]*/libchromium_android_linker.so$"); | |
| 52 // Pattern for matching a crazy loaded shared library in the APK | |
| 53 private static Pattern sCrazyLibraryPattern = | |
| 54 Pattern.compile("^lib/[^/]*/crazy.lib.*[.]so$"); | |
| 55 | |
| 56 private static boolean isLibraryFilename(String filename) { | |
| 57 return sLibraryPattern.matcher(filename).matches() && | |
| 58 !sCrazyLinkerPattern.matcher(filename).matches(); | |
| 59 } | |
| 60 | |
| 61 private static boolean isCrazyLibraryFilename(String filename) { | |
| 62 return sCrazyLibraryPattern.matcher(filename).matches(); | |
| 63 } | |
| 64 | |
| 65 private static String renameLibraryForCrazyLinker(String filename) { | |
| 66 int lastSlash = filename.lastIndexOf('/'); | |
| 67 if (lastSlash == -1) { | |
| 68 return filename; | |
|
rmcilroy
2014/10/02 12:22:36
I think this should be an exceptino rather than ju
Anton
2014/10/02 14:43:54
I just removed it. This kind of defensive programm
| |
| 69 } | |
| 70 | |
| 71 // We rename the library, so that the Android Package Manager | |
| 72 // no longer extracts the library. | |
| 73 return filename.substring(0, lastSlash + 1) + "crazy." + filename.substr ing(lastSlash + 1); | |
| 74 } | |
| 75 | |
| 39 /** | 76 /** |
| 40 * Wraps another output stream, counting the number of bytes written. | 77 * Wraps another output stream, counting the number of bytes written. |
| 41 */ | 78 */ |
| 42 private static class CountingOutputStream extends OutputStream { | 79 private static class CountingOutputStream extends OutputStream { |
| 43 private long mCount = 0; | 80 private long mCount = 0; |
| 44 private OutputStream mOut; | 81 private OutputStream mOut; |
| 45 | 82 |
| 46 public CountingOutputStream(OutputStream out) { | 83 public CountingOutputStream(OutputStream out) { |
| 47 this.mOut = out; | 84 this.mOut = out; |
| 48 } | 85 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 if (o1Matches != o2Matches) { | 121 if (o1Matches != o2Matches) { |
| 85 return o1Matches ? 1 : -1; | 122 return o1Matches ? 1 : -1; |
| 86 } else { | 123 } else { |
| 87 return o1.compareTo(o2); | 124 return o1.compareTo(o2); |
| 88 } | 125 } |
| 89 } | 126 } |
| 90 } | 127 } |
| 91 | 128 |
| 92 // Build an ordered list of filenames. Using the same deterministic ordering used | 129 // Build an ordered list of filenames. Using the same deterministic ordering used |
| 93 // by SignApk. If omitMetaFiles is true do not include the META-INF files. | 130 // by SignApk. If omitMetaFiles is true do not include the META-INF files. |
| 94 private static List<String> orderFilenames(JarFile jar, boolean omitMetaFile s) { | 131 // Note we are sorting based on the output filenames (which maybe renamed), but |
| 95 List<String> names = new ArrayList<String>(); | 132 // we are returning the input filenames. |
| 133 private static List<String> orderFilenames( | |
|
rmcilroy
2014/10/02 12:22:36
nit - rename to getOutputFileOrder()
Anton
2014/10/02 14:43:54
Done. Now getOutputFileOrderEntries() as it now re
| |
| 134 JarFile jar, boolean omitMetaFiles, boolean rename) { | |
|
rmcilroy
2014/10/02 12:22:36
alignment...
Anton
2014/10/02 14:43:54
Done.
| |
| 135 List<String> outNames = new ArrayList<String>(); | |
| 136 Map<String, String> renameMap = new HashMap<String, String>(); | |
| 96 for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) { | 137 for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) { |
| 97 JarEntry entry = e.nextElement(); | 138 JarEntry entry = e.nextElement(); |
| 98 if (entry.isDirectory()) { | 139 if (entry.isDirectory()) { |
| 99 continue; | 140 continue; |
| 100 } | 141 } |
| 101 if (omitMetaFiles && | 142 if (omitMetaFiles && |
| 102 sMetaFilePattern.matcher(entry.getName()).matches()) { | 143 sMetaFilePattern.matcher(entry.getName()).matches()) { |
| 103 continue; | 144 continue; |
| 104 } | 145 } |
| 105 names.add(entry.getName()); | 146 String inName = entry.getName(); |
| 147 String outName = inName; | |
| 148 if (rename && entry.getSize() > 0 && isLibraryFilename(inName)) { | |
| 149 outName = renameLibraryForCrazyLinker(inName); | |
| 150 renameMap.put(outName, inName); | |
| 151 } | |
| 152 outNames.add(outName); | |
|
rmcilroy
2014/10/02 12:22:36
Could you not do this in the Comparator (e.g., com
Anton
2014/10/02 14:43:54
I have done this, but I don't know whether you rea
rmcilroy
2014/10/02 15:29:37
I think this is still better, thanks.
| |
| 106 } | 153 } |
| 107 | 154 |
| 108 // We sort the input entries by name. When present META-INF files | 155 // We sort the input entries by name. When present META-INF files |
| 109 // are sorted to the end. | 156 // are sorted to the end. |
| 110 Collections.sort(names, new FilenameComparator()); | 157 Collections.sort(outNames, new FilenameComparator()); |
| 111 return names; | 158 |
| 159 if (!rename || renameMap.isEmpty()) { | |
| 160 return outNames; | |
| 161 } | |
| 162 | |
| 163 // Return the input filenames names. | |
| 164 List<String> inNames = new ArrayList<String>(); | |
| 165 for (String outName : outNames) { | |
| 166 String inName = renameMap.get(outName); | |
| 167 if (inName == null) { | |
| 168 // Not renamed. | |
| 169 inName = outName; | |
| 170 } | |
| 171 inNames.add(inName); | |
| 172 } | |
| 173 return inNames; | |
| 112 } | 174 } |
| 113 | 175 |
| 114 /** | 176 /** |
| 115 * Add a zero filled alignment file at this point in the zip file, | 177 * Add a zero filled alignment file at this point in the zip file, |
| 116 * The added file will be added before |name| and after |prevName|. | 178 * The added file will be added before |name| and after |prevName|. |
| 117 * The size of the alignment file is such that the location of the | 179 * The size of the alignment file is such that the location of the |
| 118 * file |name| will be on a LIBRARY_ALIGNMENT boundary. | 180 * file |name| will be on a LIBRARY_ALIGNMENT boundary. |
| 119 * | 181 * |
| 120 * Note this arrangement is devised so that running SignApk and/or zipalign on the resulting | 182 * Note this arrangement is devised so that running SignApk and/or zipalign on the resulting |
| 121 * file will not alter the alignment. | 183 * file will not alter the alignment. |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 141 return; | 203 return; |
| 142 } | 204 } |
| 143 | 205 |
| 144 // Check that there is not another file between the library and the | 206 // Check that there is not another file between the library and the |
| 145 // alignment file. | 207 // alignment file. |
| 146 String alignName = name.substring(0, name.length() - 2) + "align"; | 208 String alignName = name.substring(0, name.length() - 2) + "align"; |
| 147 if (prevName != null && prevName.compareTo(alignName) >= 0) { | 209 if (prevName != null && prevName.compareTo(alignName) >= 0) { |
| 148 throw new UnsupportedOperationException( | 210 throw new UnsupportedOperationException( |
| 149 "Unable to insert alignment file, because there is " | 211 "Unable to insert alignment file, because there is " |
| 150 + "another file in front of the file to be aligned. " | 212 + "another file in front of the file to be aligned. " |
| 151 + "Other file: " + prevName + " Alignment file: " + alignName); | 213 + "Other file: " + prevName + " Alignment file: " + alignName |
| 214 + " file: " + name); | |
| 152 } | 215 } |
| 153 | 216 |
| 154 // Compute the size of the alignment file header. | 217 // Compute the size of the alignment file header. |
| 155 headerSize = JarFile.LOCHDR + alignName.length(); | 218 headerSize = JarFile.LOCHDR + alignName.length(); |
| 156 // We are going to add an alignment file of type STORED. This file | 219 // We are going to add an alignment file of type STORED. This file |
| 157 // will itself induce a zipalign alignment adjustment. | 220 // will itself induce a zipalign alignment adjustment. |
| 158 int extraNeeded = | 221 int extraNeeded = |
| 159 (ALIGNMENT - (int) ((offset + headerSize) % ALIGNMENT)) % ALIGNM ENT; | 222 (ALIGNMENT - (int) ((offset + headerSize) % ALIGNMENT)) % ALIGNM ENT; |
| 160 headerSize += extraNeeded; | 223 headerSize += extraNeeded; |
| 161 | 224 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 180 alignEntry.setExtra(new byte[extraNeeded]); | 243 alignEntry.setExtra(new byte[extraNeeded]); |
| 181 } | 244 } |
| 182 | 245 |
| 183 // Output the alignment file. | 246 // Output the alignment file. |
| 184 out.putNextEntry(alignEntry); | 247 out.putNextEntry(alignEntry); |
| 185 out.write(zeroBuffer); | 248 out.write(zeroBuffer); |
| 186 out.closeEntry(); | 249 out.closeEntry(); |
| 187 out.flush(); | 250 out.flush(); |
| 188 } | 251 } |
| 189 | 252 |
| 253 // Make a JarEntry for the output file which corresponds to the input | |
| 254 // file. The output file will be called |name|. The output file will always | |
| 255 // be uncompressed (STORED). If the input is not STORED it is necessary to i nflate | |
| 256 // it to compute the CRC and size of the output entry. | |
| 257 private static JarEntry makeStoredEntry(String name, JarEntry inEntry, JarFi le in) | |
| 258 throws IOException { | |
| 259 JarEntry outEntry = new JarEntry(name); | |
| 260 outEntry.setMethod(JarEntry.STORED); | |
| 261 | |
| 262 if (inEntry.getMethod() == JarEntry.STORED) { | |
| 263 outEntry.setCrc(inEntry.getCrc()); | |
| 264 outEntry.setSize(inEntry.getSize()); | |
| 265 } else { | |
| 266 // We are inflating the file. We need to compute the CRC and size. | |
| 267 byte[] buffer = new byte[4096]; | |
| 268 CRC32 crc = new CRC32(); | |
| 269 int size = 0; | |
| 270 int num; | |
| 271 InputStream data = in.getInputStream(inEntry); | |
| 272 while ((num = data.read(buffer)) > 0) { | |
| 273 crc.update(buffer, 0, num); | |
| 274 size += num; | |
| 275 } | |
| 276 data.close(); | |
| 277 outEntry.setCrc(crc.getValue()); | |
| 278 outEntry.setSize(size); | |
| 279 } | |
| 280 return outEntry; | |
| 281 } | |
| 282 | |
| 190 /** | 283 /** |
| 191 * Copy the contents of the input APK file to the output APK file. Uncompres sed files | 284 * Copy the contents of the input APK file to the output APK file. If |renam e| is |
| 192 * will be aligned in the output stream. Uncompressed native code libraries (*.so) | 285 * true then non-empty libraries (*.so) in the input will be renamed by pref ixing |
| 193 * will be aligned on a page boundary. Page alignment is implemented by addi ng a | 286 * "crazy.". This is done to prevent the Android Package Manager extracting the |
| 194 * zero filled file, regular alignment is implemented by adding a zero fille d extra | 287 * library. Note the crazy linker itself is not renamed, for bootstrapping r easons. |
| 195 * field to the zip file header. Care is take so that the output generated i n the | 288 * Empty libraries are not renamed (they are in the APK to workaround a bug where |
| 196 * same way as SignApk. This is important so that running SignApk and zipali gn on | 289 * the Android Package Manager fails to delete old versions when upgrading). |
| 197 * the output does not break the page alignment. The archive may not contain a "*.apk" | 290 * There must be exactly one "crazy" library in the output stream. The "craz y" |
| 198 * as SignApk has special nested signing logic that we do not support. | 291 * library will be uncompressed and page aligned in the output stream. Page |
| 292 * alignment is implemented by adding a zero filled file, regular alignment is | |
| 293 * implemented by adding a zero filled extra field to the zip file header. I f | |
| 294 * |addAlignment| is true a page alignment file is added, otherwise the "cra zy" | |
| 295 * library must already be page aligned. Care is taken so that the output is generated | |
| 296 * in the same way as SignApk. This is important so that running SignApk and | |
| 297 * zipalign on the output does not break the page alignment. The archive may not | |
| 298 * contain a "*.apk" as SignApk has special nested signing logic that we do not | |
| 299 * support. | |
| 199 * | 300 * |
| 200 * @param in The input APK File. | 301 * @param in The input APK File. |
| 201 * @param out The output APK stream. | 302 * @param out The output APK stream. |
| 202 * @param countOut Counting output stream (to measure the current offset). | 303 * @param countOut Counting output stream (to measure the current offset). |
| 203 * @param addAlignment Whether to add the alignment file or just check. | 304 * @param addAlignment Whether to add the alignment file or just check. |
| 305 * @param rename Whether to rename libraries to be "crazy". | |
| 204 * | 306 * |
| 205 * @throws IOException if the output file can not be written. | 307 * @throws IOException if the output file can not be written. |
| 206 */ | 308 */ |
| 207 private static void copyAndAlignFiles( | 309 private static void rezip( |
| 208 JarFile in, JarOutputStream out, CountingOutputStream countOut, | 310 JarFile in, JarOutputStream out, CountingOutputStream countOut, |
| 209 boolean addAlignment) throws IOException { | 311 boolean addAlignment, boolean rename) throws IOException { |
| 210 | 312 |
| 211 List<String> names = orderFilenames(in, addAlignment); | 313 List<String> names = orderFilenames(in, addAlignment, rename); |
| 212 long timestamp = System.currentTimeMillis(); | 314 long timestamp = System.currentTimeMillis(); |
| 213 byte[] buffer = new byte[4096]; | 315 byte[] buffer = new byte[4096]; |
| 214 boolean firstEntry = true; | 316 boolean firstEntry = true; |
| 215 String prevName = null; | 317 String prevName = null; |
| 318 int numCrazy = 0; | |
| 319 boolean isCrazy = false; | |
|
rmcilroy
2014/10/02 12:22:36
This should just be local to the loop I think.
Anton
2014/10/02 14:43:54
Done.
| |
| 216 for (String name : names) { | 320 for (String name : names) { |
| 217 JarEntry inEntry = in.getJarEntry(name); | 321 JarEntry inEntry = in.getJarEntry(name); |
| 218 JarEntry outEntry = null; | 322 JarEntry outEntry = null; |
| 219 if (name.endsWith(".apk")) { | 323 if (name.endsWith(".apk")) { |
| 220 throw new UnsupportedOperationException( | 324 throw new UnsupportedOperationException( |
| 221 "Nested APKs are not supported: " + name); | 325 "Nested APKs are not supported: " + name); |
| 222 } | 326 } |
| 223 if (inEntry.getMethod() == JarEntry.STORED) { | 327 |
| 328 // Rename files, if specied. | |
| 329 if (rename && isLibraryFilename(name) && inEntry.getSize() > 0) { | |
| 330 name = renameLibraryForCrazyLinker(name); | |
| 331 } | |
| 332 | |
| 333 // Build the header. | |
| 334 isCrazy = isCrazyLibraryFilename(name); | |
| 335 if (isCrazy) { | |
| 336 // "crazy" libraries are alway output uncompressed (STORED). | |
| 337 outEntry = makeStoredEntry(name, inEntry, in); | |
| 338 numCrazy++; | |
| 339 if (numCrazy > 1) { | |
| 340 throw new UnsupportedOperationException( | |
| 341 "Found more than one library\n" | |
| 342 + "Multiple libraries are not supported for APKs tha t use " | |
| 343 + "'load_library_from_zip_file'.\n" | |
| 344 + "See crbug/388223.\n" | |
| 345 + "Note, check that your build is clean.\n" | |
| 346 + "An unclean build can incorrectly incorporate old " | |
| 347 + "libraries in the APK."); | |
| 348 } | |
| 349 } else if (inEntry.getMethod() == JarEntry.STORED) { | |
| 224 // Preserve the STORED method of the input entry. | 350 // Preserve the STORED method of the input entry. |
| 225 outEntry = new JarEntry(inEntry); | 351 outEntry = new JarEntry(inEntry); |
| 226 outEntry.setExtra(null); | 352 outEntry.setExtra(null); |
| 227 } else { | 353 } else { |
| 228 // Create a new entry so that the compressed len is recomputed. | 354 // Create a new entry so that the compressed len is recomputed. |
| 229 outEntry = new JarEntry(name); | 355 outEntry = new JarEntry(name); |
| 230 } | 356 } |
| 231 outEntry.setTime(timestamp); | 357 outEntry.setTime(timestamp); |
| 232 | 358 |
| 359 // Compute and add alignment | |
| 233 long offset = countOut.getCount(); | 360 long offset = countOut.getCount(); |
| 234 if (firstEntry) { | 361 if (firstEntry) { |
| 235 // The first entry in a jar file has an extra field of | 362 // The first entry in a jar file has an extra field of |
| 236 // four bytes that you can't get rid of; any extra | 363 // four bytes that you can't get rid of; any extra |
| 237 // data you specify in the JarEntry is appended to | 364 // data you specify in the JarEntry is appended to |
| 238 // these forced four bytes. This is JAR_MAGIC in | 365 // these forced four bytes. This is JAR_MAGIC in |
| 239 // JarOutputStream; the bytes are 0xfeca0000. | 366 // JarOutputStream; the bytes are 0xfeca0000. |
| 240 firstEntry = false; | 367 firstEntry = false; |
| 241 offset += 4; | 368 offset += 4; |
| 242 } | 369 } |
| 243 if (inEntry.getMethod() == JarEntry.STORED) { | 370 if (outEntry.getMethod() == JarEntry.STORED) { |
| 244 if (name.endsWith(".so")) { | 371 if (isCrazy) { |
| 245 if (addAlignment) { | 372 if (addAlignment) { |
| 246 addAlignmentFile(offset, timestamp, name, prevName, out) ; | 373 addAlignmentFile(offset, timestamp, name, prevName, out) ; |
| 247 } | 374 } |
| 248 // We check that we did indeed get to a page boundary. | 375 // We check that we did indeed get to a page boundary. |
| 249 offset = countOut.getCount() + JarFile.LOCHDR + name.length( ); | 376 offset = countOut.getCount() + JarFile.LOCHDR + name.length( ); |
| 250 if ((offset % LIBRARY_ALIGNMENT) != 0) { | 377 if ((offset % LIBRARY_ALIGNMENT) != 0) { |
| 251 throw new AssertionError( | 378 throw new AssertionError( |
| 252 "Library was not page aligned when verifying page al ignment. " | 379 "Library was not page aligned when verifying pag e alignment. " |
| 253 + "Library name: " + name + " Expected alignment: " + LIBRARY_ALIGNMENT | 380 + "Library name: " + name + " Expected alignment : " |
| 254 + "Offset: " + offset + " Error: " + (offset % LIBRA RY_ALIGNMENT)); | 381 + LIBRARY_ALIGNMENT + "Offset: " + offset + " Er ror: " |
| 382 + (offset % LIBRARY_ALIGNMENT)); | |
| 255 } | 383 } |
| 256 } else { | 384 } else { |
| 385 // This is equivalent to zipalign. | |
| 257 offset += JarFile.LOCHDR + name.length(); | 386 offset += JarFile.LOCHDR + name.length(); |
| 258 int needed = (ALIGNMENT - (int) (offset % ALIGNMENT)) % ALIG NMENT; | 387 int needed = (ALIGNMENT - (int) (offset % ALIGNMENT)) % ALIG NMENT; |
| 259 if (needed != 0) { | 388 if (needed != 0) { |
| 260 outEntry.setExtra(new byte[needed]); | 389 outEntry.setExtra(new byte[needed]); |
| 261 } | 390 } |
| 262 } | 391 } |
| 263 } | 392 } |
| 264 out.putNextEntry(outEntry); | 393 out.putNextEntry(outEntry); |
| 265 | 394 |
| 395 // Copy the data from the input to the output | |
| 266 int num; | 396 int num; |
| 267 InputStream data = in.getInputStream(inEntry); | 397 InputStream data = in.getInputStream(inEntry); |
| 268 while ((num = data.read(buffer)) > 0) { | 398 while ((num = data.read(buffer)) > 0) { |
| 269 out.write(buffer, 0, num); | 399 out.write(buffer, 0, num); |
| 270 } | 400 } |
| 401 data.close(); | |
| 271 out.closeEntry(); | 402 out.closeEntry(); |
| 272 out.flush(); | 403 out.flush(); |
| 273 prevName = name; | 404 prevName = name; |
| 274 } | 405 } |
| 406 if (numCrazy == 0) { | |
| 407 throw new AssertionError("There was no crazy library in the archive" ); | |
| 408 } | |
| 275 } | 409 } |
| 276 | 410 |
| 277 private static void usage() { | 411 private static void usage() { |
| 278 System.err.println("Usage: prealignapk (addalignment|reorder) input.apk output.apk"); | 412 System.err.println( |
| 279 System.err.println(" addalignment - adds alignment file removes manifes t and signature"); | 413 "Usage: prealignapk (addalignment|reorder) input.apk output.apk" ); |
| 280 System.err.println(" reorder - re-creates canonical ordering and c hecks alignment"); | 414 System.err.println( |
| 415 "\"crazy\" libraries are always inflated in the output"); | |
| 416 System.err.println( | |
| 417 " renamealign - rename libraries with \"crazy.\" prefix and ad d alignment file"); | |
| 418 System.err.println( | |
| 419 " align - add alignment file"); | |
| 420 System.err.println( | |
| 421 " reorder - re-creates canonical ordering and checks align ment"); | |
| 281 System.exit(2); | 422 System.exit(2); |
| 282 } | 423 } |
| 283 | 424 |
| 284 public static void main(String[] args) throws IOException { | 425 public static void main(String[] args) throws IOException { |
| 285 if (args.length != 3) usage(); | 426 if (args.length != 3) usage(); |
| 286 | 427 |
| 287 boolean addAlignment = false; | 428 boolean addAlignment = false; |
| 288 if (args[0].equals("addalignment")) { | 429 boolean rename = false; |
| 430 if (args[0].equals("renamealign")) { | |
| 431 // Normal case. Before signing we rename the library and add an alig nment file. | |
| 289 addAlignment = true; | 432 addAlignment = true; |
| 433 rename = true; | |
| 434 } else if (args[0].equals("align")) { | |
| 435 // LGPL compliance case. Before signing, we add an alignment file to a | |
| 436 // reconstructed APK which already contains the "crazy" library. | |
| 437 addAlignment = true; | |
| 438 rename = false; | |
| 290 } else if (args[0].equals("reorder")) { | 439 } else if (args[0].equals("reorder")) { |
| 440 // Normal case. After jarsigning we write the file in the canonical order and check. | |
| 291 addAlignment = false; | 441 addAlignment = false; |
| 292 } else { | 442 } else { |
| 293 usage(); | 443 usage(); |
| 294 } | 444 } |
| 295 | 445 |
| 296 String inputFilename = args[1]; | 446 String inputFilename = args[1]; |
| 297 String outputFilename = args[2]; | 447 String outputFilename = args[2]; |
| 298 | 448 |
| 299 JarFile inputJar = null; | 449 JarFile inputJar = null; |
| 300 FileOutputStream outputFile = null; | 450 FileOutputStream outputFile = null; |
| 301 | 451 |
| 302 try { | 452 try { |
| 303 inputJar = new JarFile(new File(inputFilename), true); | 453 inputJar = new JarFile(new File(inputFilename), true); |
| 304 outputFile = new FileOutputStream(outputFilename); | 454 outputFile = new FileOutputStream(outputFilename); |
| 305 | 455 |
| 306 CountingOutputStream outCount = new CountingOutputStream(outputFile) ; | 456 CountingOutputStream outCount = new CountingOutputStream(outputFile) ; |
| 307 JarOutputStream outputJar = new JarOutputStream(outCount); | 457 JarOutputStream outputJar = new JarOutputStream(outCount); |
| 308 | 458 |
| 309 // Match the compression level used by SignApk. | 459 // Match the compression level used by SignApk. |
| 310 outputJar.setLevel(9); | 460 outputJar.setLevel(9); |
| 311 | 461 |
| 312 copyAndAlignFiles(inputJar, outputJar, outCount, addAlignment); | 462 rezip(inputJar, outputJar, outCount, addAlignment, rename); |
| 313 outputJar.close(); | 463 outputJar.close(); |
| 314 } finally { | 464 } finally { |
| 315 if (inputJar != null) inputJar.close(); | 465 if (inputJar != null) inputJar.close(); |
| 316 if (outputFile != null) outputFile.close(); | 466 if (outputFile != null) outputFile.close(); |
| 317 } | 467 } |
| 318 } | 468 } |
| 319 } | 469 } |
| OLD | NEW |