ApkBuilderpublic final class ApkBuilder extends Object Command line APK builder with signing support. |
Fields Summary |
---|
private static final Pattern | PATTERN_JAR_EXT | private static final Pattern | PATTERN_NATIVELIB_EXT | private static final String | NATIVE_LIB_ROOT | private com.android.jarutils.JavaResourceFilter | mResourceFilter | private boolean | mVerbose | private boolean | mSignedPackage | private String | mStoreTypethe optional type of the debug keystore. If null , the default |
Methods Summary |
---|
public void | createPackage(java.io.File outFile, java.util.ArrayList zipArchives, java.util.ArrayList files, java.util.ArrayList javaResources, java.util.ArrayList resourcesJars, java.util.ArrayList nativeLibraries)Creates the application package
// 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 java.io.File | getInputFile(java.lang.String filepath)
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;
| private java.io.File | getOutFile(java.lang.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 void | main(java.lang.String[] args)
new ApkBuilder().run(args);
| private void | printAndExit(java.lang.String messages)
for (String message : messages) {
System.err.println(message);
}
System.exit(1);
| private 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]");
System.err.println(" [-f inputfile] [-rf input-folder] [-rj -input-path]");
System.err.println("");
System.err.println(" -v Verbose.");
System.err.println(" -u Creates an unsigned package.");
System.err.println(" -storetype Forces the KeyStore type. If ommited the default is used.");
System.err.println("");
System.err.println(" -z Followed by the path to a zip archive.");
System.err.println(" Adds the content of the application package.");
System.err.println("");
System.err.println(" -f Followed by the path to a file.");
System.err.println(" Adds the file to the application package.");
System.err.println("");
System.err.println(" -rf Followed by the path to a source folder.");
System.err.println(" Adds the java resources found in that folder to the application");
System.err.println(" package, while keeping their path relative to the source folder.");
System.err.println("");
System.err.println(" -rj Followed by the path to a jar file or a folder containing");
System.err.println(" jar files.");
System.err.println(" Adds the java resources found in the jar file(s) to the application");
System.err.println(" package.");
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 static void | processFileForResource(java.io.File file, java.lang.String path, java.util.ArrayList javaResources)Processes a {@link File} that could be a {@link ApkFile}, or a folder containing
java resources.
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));
}
}
| public static void | processJarFolder(java.lang.String parameter, java.util.ArrayList resourcesJars)
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);
}
| public static void | processNativeFolder(int offset, java.io.File f, java.util.ArrayList nativeLibraries)Process a {@link File} for native library inclusion.
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 | processSourceFolderForResource(java.lang.String folderPath, java.util.ArrayList javaResources)Processes a source folder and adds its java resources to a given list of {@link ApkFile}.
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!");
}
}
| private void | run(java.lang.String[] args)
if (args.length < 1) {
printUsageAndQuit();
}
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 void | setSignedPackage(boolean signedPackage)
mSignedPackage = signedPackage;
| public void | setVerbose(boolean verbose)
mVerbose = verbose;
|
|