FileDocCategorySizeDatePackage
ApkBuilder.javaAPI DocAndroid 1.5 API18890Wed May 06 22:41:08 BST 2009com.android.apkbuilder

ApkBuilder

public 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
mStoreType
the optional type of the debug keystore. If null, the default
Constructors Summary
Methods Summary
public voidcreatePackage(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

param
outFile
param
zipArchives
param
resourcesJars
param
files
param
javaResources keystore type of the Java VM is used.

        
        // 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.FilegetInputFile(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.FilegetOutFile(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 voidmain(java.lang.String[] args)

param
args


           
         
        new ApkBuilder().run(args);
    
private voidprintAndExit(java.lang.String messages)

        for (String message : messages) {
            System.err.println(message);
        }
        System.exit(1);
    
private voidprintUsageAndQuit()

        // 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 voidprocessFileForResource(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.

param
file the {@link File} to process.
param
path the relative path of this file to the source folder. Can be null to identify a root file.
param
javaResources the list of {@link ApkFile} object to fill.

        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 voidprocessJarFolder(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 voidprocessNativeFolder(int offset, java.io.File f, java.util.ArrayList nativeLibraries)
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.

        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 voidprocessSourceFolderForResource(java.lang.String folderPath, java.util.ArrayList javaResources)
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.

        
        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 voidrun(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 voidsetSignedPackage(boolean signedPackage)

        mSignedPackage = signedPackage;
    
public voidsetVerbose(boolean verbose)

        mVerbose = verbose;