aboutsummaryrefslogtreecommitdiffstats
path: root/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'apkbuilder/src/com/android/apkbuilder/ApkBuilder.java')
-rw-r--r--apkbuilder/src/com/android/apkbuilder/ApkBuilder.java426
1 files changed, 38 insertions, 388 deletions
diff --git a/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java b/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
index 40abff1..db9f355 100644
--- a/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
+++ b/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
@@ -16,419 +16,69 @@
package com.android.apkbuilder;
-import com.android.jarutils.DebugKeyProvider;
-import com.android.jarutils.JavaResourceFilter;
-import com.android.jarutils.SignedJarBuilder;
-import com.android.jarutils.DebugKeyProvider.KeytoolException;
-import com.android.prefs.AndroidLocation.AndroidLocationException;
+import com.android.apkbuilder.internal.ApkBuilderImpl;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.regex.Pattern;
+
/**
* Command line APK builder with signing support.
*/
public final class ApkBuilder {
-
- private final static Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$",
- Pattern.CASE_INSENSITIVE);
- private final static Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",
- Pattern.CASE_INSENSITIVE);
-
- private final static String NATIVE_LIB_ROOT = "lib/";
- /**
- * A File to be added to the APK archive.
- * <p/>This includes the {@link File} representing the file and its path in the archive.
- */
- public final static class ApkFile {
- String archivePath;
- File file;
+ public final static class WrongOptionException extends Exception {
+ private static final long serialVersionUID = 1L;
- ApkFile(File file, String path) {
- this.file = file;
- this.archivePath = path;
+ public WrongOptionException(String message) {
+ super(message);
}
}
- private JavaResourceFilter mResourceFilter = new JavaResourceFilter();
- private boolean mVerbose = false;
- private boolean mSignedPackage = true;
- /** the optional type of the debug keystore. If <code>null</code>, the default */
- private String mStoreType = null;
-
- /**
- * @param args
- */
- public static void main(String[] args) {
- new ApkBuilder().run(args);
- }
-
- public void setVerbose(boolean verbose) {
- mVerbose = verbose;
- }
-
- public void setSignedPackage(boolean signedPackage) {
- mSignedPackage = signedPackage;
- }
+ public final static class ApkCreationException extends Exception {
+ private static final long serialVersionUID = 1L;
- private void run(String[] args) {
- if (args.length < 1) {
- printUsageAndQuit();
+ public ApkCreationException(String message) {
+ super(message);
}
- try {
- // read the first args that should be a file path
- File outFile = getOutFile(args[0]);
-
- ArrayList<FileInputStream> zipArchives = new ArrayList<FileInputStream>();
- ArrayList<File> archiveFiles = new ArrayList<File>();
- ArrayList<ApkFile> javaResources = new ArrayList<ApkFile>();
- ArrayList<FileInputStream> resourcesJars = new ArrayList<FileInputStream>();
- ArrayList<ApkFile> nativeLibraries = new ArrayList<ApkFile>();
-
- int index = 1;
- do {
- String argument = args[index++];
-
- if ("-v".equals(argument)) {
- mVerbose = true;
- } else if ("-u".equals(argument)) {
- mSignedPackage = false;
- } else if ("-z".equals(argument)) {
- // quick check on the next argument.
- if (index == args.length) printUsageAndQuit();
-
- try {
- FileInputStream input = new FileInputStream(args[index++]);
- zipArchives.add(input);
- } catch (FileNotFoundException e) {
- printAndExit(e.getMessage());
- }
- } else if ("-f". equals(argument)) {
- // quick check on the next argument.
- if (index == args.length) printUsageAndQuit();
-
- archiveFiles.add(getInputFile(args[index++]));
- } else if ("-rf". equals(argument)) {
- // quick check on the next argument.
- if (index == args.length) printUsageAndQuit();
-
- processSourceFolderForResource(args[index++], javaResources);
- } else if ("-rj". equals(argument)) {
- // quick check on the next argument.
- if (index == args.length) printUsageAndQuit();
-
- processJarFolder(args[index++], resourcesJars);
- } else if ("-nf".equals(argument)) {
- // quick check on the next argument.
- if (index == args.length) printUsageAndQuit();
-
- String parameter = args[index++];
- File f = new File(parameter);
-
- // compute the offset to get the relative path
- int offset = parameter.length();
- if (parameter.endsWith(File.separator) == false) {
- offset++;
- }
-
- processNativeFolder(offset, f, nativeLibraries);
- } else if ("-storetype".equals(argument)) {
- // quick check on the next argument.
- if (index == args.length) printUsageAndQuit();
-
- mStoreType = args[index++];
- } else {
- printAndExit("Unknown argument: " + argument);
- }
- } while (index < args.length);
-
- createPackage(outFile, zipArchives, archiveFiles, javaResources, resourcesJars,
- nativeLibraries);
- } catch (IllegalArgumentException e) {
- printAndExit(e.getMessage());
- } catch (FileNotFoundException e) {
- printAndExit(e.getMessage());
+ public ApkCreationException(Throwable throwable) {
+ super(throwable);
}
- }
-
- private File getOutFile(String filepath) {
- File f = new File(filepath);
-
- if (f.isDirectory()) {
- printAndExit(filepath + " is a directory!");
- }
-
- if (f.exists()) { // will be a file in this case.
- if (f.canWrite() == false) {
- printAndExit("Cannot write " + filepath);
- }
- } else {
- try {
- if (f.createNewFile() == false) {
- printAndExit("Failed to create " + filepath);
- }
- } catch (IOException e) {
- printAndExit("Failed to create '" + filepath + "' : " + e.getMessage());
- }
- }
-
- return f;
- }
-
- public static File getInputFile(String filepath) throws IllegalArgumentException {
- File f = new File(filepath);
-
- if (f.isDirectory()) {
- throw new IllegalArgumentException(filepath + " is a directory!");
- }
-
- if (f.exists()) {
- if (f.canRead() == false) {
- throw new IllegalArgumentException("Cannot read " + filepath);
- }
- } else {
- throw new IllegalArgumentException(filepath + " does not exists!");
- }
-
- return f;
}
/**
- * Processes a source folder and adds its java resources to a given list of {@link ApkFile}.
- * @param folderPath the path to the source folder.
- * @param javaResources the list of {@link ApkFile} to fill.
+ * Main method. This is meant to be called from the command line through an exec.
+ * <p/>WARNING: this will call {@link System#exit(int)} if anything goes wrong.
+ * @param args command line arguments.
*/
- public static void processSourceFolderForResource(String folderPath,
- ArrayList<ApkFile> javaResources) {
-
- File folder = new File(folderPath);
-
- if (folder.isDirectory()) {
- // file is a directory, process its content.
- File[] files = folder.listFiles();
- for (File file : files) {
- processFileForResource(file, null, javaResources);
- }
- } else {
- // not a directory? output error and quit.
- if (folder.exists()) {
- throw new IllegalArgumentException(folderPath + " is not a folder!");
- } else {
- throw new IllegalArgumentException(folderPath + " does not exist!");
- }
- }
- }
-
- public static void processJarFolder(String parameter, ArrayList<FileInputStream> resourcesJars)
- throws FileNotFoundException {
- File f = new File(parameter);
- if (f.isDirectory()) {
- String[] files = f.list(new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return PATTERN_JAR_EXT.matcher(name).matches();
- }
- });
-
- for (String file : files) {
- String path = f.getAbsolutePath() + File.separator + file;
- FileInputStream input = new FileInputStream(path);
- resourcesJars.add(input);
- }
- } else {
- FileInputStream input = new FileInputStream(parameter);
- resourcesJars.add(input);
- }
- }
-
-
- /**
- * Processes a {@link File} that could be a {@link ApkFile}, or a folder containing
- * java resources.
- * @param file the {@link File} to process.
- * @param path the relative path of this file to the source folder. Can be <code>null</code> to
- * identify a root file.
- * @param javaResources the list of {@link ApkFile} object to fill.
- */
- private static void processFileForResource(File file, String path,
- ArrayList<ApkFile> javaResources) {
- if (file.isDirectory()) {
- // a directory? we check it
- if (JavaResourceFilter.checkFolderForPackaging(file.getName())) {
- // if it's valid, we append its name to the current path.
- if (path == null) {
- path = file.getName();
- } else {
- path = path + "/" + file.getName();
- }
-
- // and process its content.
- File[] files = file.listFiles();
- for (File contentFile : files) {
- processFileForResource(contentFile, path, javaResources);
- }
- }
- } else {
- // a file? we check it
- if (JavaResourceFilter.checkFileForPackaging(file.getName())) {
- // we append its name to the current path
- if (path == null) {
- path = file.getName();
- } else {
- path = path + "/" + file.getName();
- }
-
- // and add it to the list.
- javaResources.add(new ApkFile(file, path));
- }
- }
- }
-
- /**
- * Process a {@link File} for native library inclusion.
- * @param offset the length of the root folder (used to compute relative path)
- * @param f the {@link File} to process
- * @param nativeLibraries the array to add native libraries.
- */
- public static void processNativeFolder(int offset, File f, ArrayList<ApkFile> nativeLibraries) {
- if (f.isDirectory()) {
- File[] children = f.listFiles();
-
- if (children != null) {
- for (File child : children) {
- processNativeFolder(offset, child, nativeLibraries);
- }
- }
- } else if (f.isFile()) {
- if (PATTERN_NATIVELIB_EXT.matcher(f.getName()).matches()) {
- String path = NATIVE_LIB_ROOT +
- f.getAbsolutePath().substring(offset).replace('\\', '/');
-
- nativeLibraries.add(new ApkFile(f, path));
- }
+ public static void main(String[] args) {
+ try {
+ new ApkBuilderImpl().run(args);
+ } catch (WrongOptionException e) {
+ printUsageAndQuit();
+ } catch (FileNotFoundException e) {
+ printAndExit(e.getMessage());
+ } catch (ApkCreationException e) {
+ printAndExit(e.getMessage());
}
}
/**
- * Creates the application package
- * @param outFile
- * @param zipArchives
- * @param resourcesJars
- * @param files
- * @param javaResources
- * keystore type of the Java VM is used.
+ * API entry point similar to the {@link #main(String[])} method.
+ * <p/>Unlike {@link #main(String[])}, this will not call {@link System#exit(int)} and instead
+ * will throw exceptions.
+ * @param args command line arguments.
+ * @throws WrongOptionException if the command line arguments are incorrect.
+ * @throws FileNotFoundException if a required file was not found.
+ * @throws ApkCreationException if an error happened during the creation of the APK.
*/
- public void createPackage(File outFile, ArrayList<FileInputStream> zipArchives,
- ArrayList<File> files, ArrayList<ApkFile> javaResources,
- ArrayList<FileInputStream> resourcesJars, ArrayList<ApkFile> nativeLibraries) {
-
- // get the debug key
- try {
- SignedJarBuilder builder;
-
- if (mSignedPackage) {
- System.err.println(String.format("Using keystore: %s",
- DebugKeyProvider.getDefaultKeyStoreOsPath()));
-
-
- DebugKeyProvider keyProvider = new DebugKeyProvider(
- null /* osKeyPath: use default */,
- mStoreType, null /* IKeyGenOutput */);
- PrivateKey key = keyProvider.getDebugKey();
- X509Certificate certificate = (X509Certificate)keyProvider.getCertificate();
-
- if (key == null) {
- throw new IllegalArgumentException("Unable to get debug signature key");
- }
-
- // compare the certificate expiration date
- if (certificate != null && certificate.getNotAfter().compareTo(new Date()) < 0) {
- // TODO, regenerate a new one.
- throw new IllegalArgumentException("Debug Certificate expired on " +
- DateFormat.getInstance().format(certificate.getNotAfter()));
- }
-
- builder = new SignedJarBuilder(
- new FileOutputStream(outFile.getAbsolutePath(), false /* append */), key,
- certificate);
- } else {
- builder = new SignedJarBuilder(
- new FileOutputStream(outFile.getAbsolutePath(), false /* append */),
- null /* key */, null /* certificate */);
- }
-
- // add the archives
- for (FileInputStream input : zipArchives) {
- builder.writeZip(input, null /* filter */);
- }
-
- // add the single files
- for (File input : files) {
- // always put the file at the root of the archive in this case
- builder.writeFile(input, input.getName());
- if (mVerbose) {
- System.err.println(String.format("%1$s => %2$s", input.getAbsolutePath(),
- input.getName()));
- }
- }
-
- // add the java resource from the source folders.
- for (ApkFile resource : javaResources) {
- builder.writeFile(resource.file, resource.archivePath);
- if (mVerbose) {
- System.err.println(String.format("%1$s => %2$s",
- resource.file.getAbsolutePath(), resource.archivePath));
- }
- }
-
- // add the java resource from jar files.
- for (FileInputStream input : resourcesJars) {
- builder.writeZip(input, mResourceFilter);
- }
-
- // add the native files
- for (ApkFile file : nativeLibraries) {
- builder.writeFile(file.file, file.archivePath);
- if (mVerbose) {
- System.err.println(String.format("%1$s => %2$s", file.file.getAbsolutePath(),
- file.archivePath));
- }
- }
-
- // close and sign the application package.
- builder.close();
- } catch (KeytoolException e) {
- if (e.getJavaHome() == null) {
- throw new IllegalArgumentException(e.getMessage() +
- "\nJAVA_HOME seems undefined, setting it will help locating keytool automatically\n" +
- "You can also manually execute the following command\n:" +
- e.getCommandLine());
- } else {
- throw new IllegalArgumentException(e.getMessage() +
- "\nJAVA_HOME is set to: " + e.getJavaHome() +
- "\nUpdate it if necessary, or manually execute the following command:\n" +
- e.getCommandLine());
- }
- } catch (AndroidLocationException e) {
- throw new IllegalArgumentException(e);
- } catch (Exception e) {
- throw new IllegalArgumentException(e);
- }
+ public static void createApk(String[] args) throws FileNotFoundException, WrongOptionException,
+ ApkCreationException {
+ new ApkBuilderImpl().run(args);
}
- private void printUsageAndQuit() {
+ private static void printUsageAndQuit() {
// 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789
System.err.println("A command line tool to package an Android application from various sources.");
System.err.println("Usage: apkbuilder <out archive> [-v][-u][-storetype STORE_TYPE] [-z inputzip]");
@@ -455,11 +105,11 @@ public final class ApkBuilder {
System.err.println("");
System.err.println(" -nf Followed by the root folder containing native libraries to");
System.err.println(" include in the application package.");
-
+
System.exit(1);
}
-
- private void printAndExit(String... messages) {
+
+ private static void printAndExit(String... messages) {
for (String message : messages) {
System.err.println(message);
}