All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jayway.maven.plugins.android.phase09package.ApkBuilder Maven / Gradle / Ivy

There is a newer version: 4.0.0-rc.2
Show newest version
/*
 * Copyright (C) 2009 Jayway AB
 * Copyright (C) 2007-2008 JVending Masa
 * Copyright (C) 2010 akquinet A.G.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jayway.maven.plugins.android.phase09package;

import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;

/**
 * This class is a front end on the APKBuilder contained in the
 * {@code sdklib.jar}. as there is now easy way to use this jar, it it
 * dynamically loaded and reflection is used to delegate the methods.
 */
public class ApkBuilder {

    /**
     * The ApkBuilder class object.
     */
    @SuppressWarnings("rawtypes")
    private static Class apkBuilderClass;

    /**
     * The Mojo logger.
     */
    private static Log log;

    /**
     * Before being able to use the ApkBuilder, an initialization is required.
     *
     * @param log
     *            the Mojo Logger
     * @param sdkLibs
     *            the File pointing on {@code sdklib.jar}
     * @throws MojoExecutionException
     *             if the ApkBuilder class cannot be loaded
     */
    @SuppressWarnings("unchecked")
    public static void initialize(Log log, File sdkLibs)
            throws MojoExecutionException {
        if (apkBuilderClass != null) {
            // Already initialized
            return;
        }

        ApkBuilder.log = log;

        // Loads the ApkBuilder class
        try {
            URLClassLoader child = new URLClassLoader(new URL[] { sdkLibs
                    .toURI().toURL() }, ApkBuilder.class.getClassLoader());
            apkBuilderClass = child
                    .loadClass("com.android.sdklib.build.ApkBuilder");
        } catch (MalformedURLException e) {
            // This one cannot happen.
            throw new RuntimeException("Cannot create a correct URL from file "
                    + sdkLibs.getAbsolutePath());
        } catch (ClassNotFoundException e) {
            log.error(e);
            throw new MojoExecutionException(
                    "Cannot load 'com.android.sdklib.build.ApkBuilder'");
        }
        log.debug("ApkBuilder loaded " + apkBuilderClass);

        // In order to improve performence and to check that all methods are available
        // we cache used methods.


        try {
            apkBuilderConstructor = apkBuilderClass
            .getConstructor(new Class[] { File.class, File.class,
                    File.class, String.class, PrintStream.class });

            setDebugMethod = apkBuilderClass.getMethod("setDebugMode",
                    new Class[] { Boolean.TYPE });

            addResourcesFromJarMethod = apkBuilderClass.getMethod(
                    "addResourcesFromJar", new Class[] { File.class });

            addNativeLibrariesMethod = apkBuilderClass.getMethod(
                    "addNativeLibraries", new Class[] { File.class, String.class });

            addSourceFolderMethod = apkBuilderClass.getMethod(
                    "addSourceFolder", new Class[] { File.class });

            sealApkMethod = apkBuilderClass.getMethod("sealApk", new Class[0]);

            getDebugKeyStoreMethod = apkBuilderClass.getMethod("getDebugKeystore", new Class[0]);
        } catch (Exception e) {
            log.error("Cannot find required method", e);
            throw new MojoExecutionException(
                    "Cannot find the required method", e);
        }
    }

    /**
     * The APK Builder.
     */
    private Object builder;

    /**
     * The APKBuilder Constructor Method.
     */
    @SuppressWarnings("rawtypes")
    private static Constructor apkBuilderConstructor;

    /**
     * The ApkBuilder.addSourceFolder Method.
     */
    private static Method addSourceFolderMethod;

    /**
     * The ApkBuilder.addResourcesFromJar Method.
     */
    private static Method addResourcesFromJarMethod;

    /**
     * The ApkBuilder.addNativeLibraries Method.
     */
    private static Method addNativeLibrariesMethod;

    /**
     * The ApkBuilder.setDebug Method.
     */
    private static Method setDebugMethod;

    /**
     * The ApkBuilder.sealApk Method.
     */
    private static Method sealApkMethod;

    /**
     * The ApkBuilder.getDebugKeyStore Method.
     */
    private static Method getDebugKeyStoreMethod;

    /**
     * Creates a new APKBuilder. The class must be initialized before calling
     * this constructor. This creates a new builder that will create the
     * specified output file, using the two mandatory given input files. An
     * optional debug keystore can be provided. If set, it is expected that the
     * store password is 'android' and the key alias and password are
     * 'androiddebugkey' and 'android'.
     *
     * An optional {@link PrintStream} can also be provided for verbose output.
     * If null, there will be no output.
     *
     * @param apkFile
     *            the file to create
     * @param resFile
     *            the file representing the packaged resource file.
     * @param dexFile
     *            the file representing the dex file. This can be null for apk
     *            with no code.
     * @param signed
     *            if the APK must be signed using the debug store
     * @param verboseStream
     *            the stream to which verbose output should go. If null, verbose
     *            mode is not enabled.
     * @throws MojoExecutionException
     *             if the class was not initialized, or if the reflective calls
     *             failed.
     */
    public ApkBuilder(File apkFile, File resFile, File dexFile, boolean signed,
            PrintStream verboseStream) throws MojoExecutionException {
        if (apkBuilderClass == null) {
            throw new MojoExecutionException(
                    "The APKBuilder class was not initialized");
        }

        try {
            // We need to first get the debug key store
            Object debugKeyStore = getDebugKeyStoreMethod.invoke(null, new Object[0]);

            builder = apkBuilderConstructor.newInstance(new Object[] { apkFile, resFile,
                    dexFile, (signed) ? debugKeyStore : null, verboseStream });
        } catch (InvocationTargetException e) {
            log.error("Cannot create the APKBuilder object", e.getCause());
            throw new MojoExecutionException(
                    "Cannot create the APKBuilder object", e.getCause());
        } catch (Exception e) {
            log.error("Cannot create the APKBuilder object", e);
            throw new MojoExecutionException(
                    "Cannot create the APKBuilder object", e);
        }
    }

    /**
     * Enables / Disables the debug mode.
     *
     * @param debug
     *            does the debug mode need to be enabled?
     * @throws MojoExecutionException
     *             if the debug mode cannot be set
     */
    public void setDebugMode(boolean debug) throws MojoExecutionException {
        try {
            setDebugMethod.invoke(builder,
                    new Object[] { debug });
        } catch (InvocationTargetException e) {
            log.error("Cannot set the debug mode", e.getCause());
            throw new MojoExecutionException(
                    "Cannot create the APKBuilder object", e.getCause());
        } catch (Exception e) {
            log.error("Cannot set the debug mode", e);
            throw new MojoExecutionException(
                    "Cannot create the APKBuilder object", e);
        }
    }

    /**
     * Adds the resources from a source folder to the APK.
     *
     * @param sourceFolder
     *            the source folder
     * @throws MojoExecutionException
     *             if the source folder cannot be added
     */
    public void addSourceFolder(File sourceFolder)
            throws MojoExecutionException {
        try {
            addSourceFolderMethod
                    .invoke(builder, new Object[] { sourceFolder });
        } catch (InvocationTargetException e) {
            log.error("Cannot add source folder", e.getCause());
            throw new MojoExecutionException("Cannot add source folder",
                    e.getCause());
        } catch (Exception e) {
            log.error("Cannot add source folder", e);
            throw new MojoExecutionException("Cannot add source folder", e);
        }
    }

    /**
     * Adds the resources from a jar file.
     *
     * @param jarFile
     *            the jar File.
     * @throws MojoExecutionException
     *             if the resources cannot be added
     */
    public void addResourcesFromJar(File jarFile) throws MojoExecutionException {
        try {
            addResourcesFromJarMethod.invoke(builder, new Object[] { jarFile });
        } catch (InvocationTargetException e) {
            log.error("Cannot add resources from jar", e.getCause());
            throw new MojoExecutionException("Cannot add resources from jar",
                    e.getCause());
        } catch (Exception e) {
            log.error("Cannot add source folder", e);
            throw new MojoExecutionException("Cannot add resources from jar", e);
        }
    }

    /**
     * Adds the native libraries from the top native folder. The content of this
     * folder must be the various ABI folders.
     *
     * This may or may not copy gdbserver into the apk based on whether the
     * debug mode is set.
     *
     * @param nativeFolder
     *            the native folder.
     * @param abiFilter
     *            an optional filter. If not null, then only the matching ABI is
     *            included in the final archive
     * @throws MojoExecutionException if the library cannot be added.
     */
    public void addNativeLibraries(File nativeFolder, String abiFilter) throws MojoExecutionException {
        try {
            addNativeLibrariesMethod.invoke(builder, new Object[] { nativeFolder, abiFilter });
        } catch (InvocationTargetException e) {
            log.error("Cannot add native libraries", e.getCause());
            throw new MojoExecutionException("Cannot add native libraries",
                    e.getCause());
        } catch (Exception e) {
            log.error("Cannot add native libraries", e);
            throw new MojoExecutionException("Cannot add native libraries", e);
        }
    }

    /**
     * Seals the APK. Once called the APK cannot be modified.
     * @throws MojoExecutionException if the APK cannot be sealed
     */
    public void sealApk() throws MojoExecutionException {
        try {
            sealApkMethod.invoke(builder, new Object[0]);
        } catch (InvocationTargetException e) {
            log.error("Cannot seal the APK", e.getCause());
            throw new MojoExecutionException("Cannot seal the APK",
                    e.getCause());
        } catch (Exception e) {
            log.error("Cannot seal the APK", e);
            throw new MojoExecutionException("Cannot seal the APK", e);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy