com.apkTool.Main.java Source code

Java tutorial

Introduction

Here is the source code for com.apkTool.Main.java

Source

package com.apkTool;

import com.libs.brut.androlib.Androlib;
import com.libs.brut.androlib.AndrolibException;
import com.libs.brut.androlib.ApkDecoder;
import com.libs.brut.androlib.ApkOptions;
import com.libs.brut.androlib.ApktoolProperties;
import com.libs.brut.androlib.err.CantFindFrameworkResException;
import com.libs.brut.androlib.err.InFileNotFoundException;
import com.libs.brut.androlib.err.OutDirExistsException;

import J.common.BrutException;
import J.dir.DirectoryException;

import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;

import java.io.File;
import java.io.IOException;
import java.util.logging.*;

public class Main {

    public static void main(String[] args) throws IOException, InterruptedException, BrutException {

        /**
         * ?apk 
         * ?.apk  
         *  + ExtDataInput.javaskipCheckChunkTypeInt 73
         *
         * .apk 
         * qq.apk  
         *  + ResTypeSpec.javaaddResSpec78
         *
         * .apk 
         * ?.apk
         */

        // ??
        args = new String[] { "d", "-f", "ApkTool/apk/qq.apk", "-o", "ApkTool/out" };

        // 
        // args = new String[]{"b", "out", "-o", "apk/zhifubao_build.apk"};

        // set verbosity default
        Verbosity verbosity = Verbosity.NORMAL;

        // cli parser
        CommandLineParser parser = new PosixParser();
        CommandLine commandLine;

        // load options
        _Options();

        try {
            commandLine = parser.parse(allOptions, args, false);
        } catch (ParseException ex) {
            System.err.println(ex.getMessage());
            usage();
            return;
        }

        // check for verbose / quiet
        if (commandLine.hasOption("-v") || commandLine.hasOption("--verbose")) {
            verbosity = Verbosity.VERBOSE;
        } else if (commandLine.hasOption("-q") || commandLine.hasOption("--quiet")) {
            verbosity = Verbosity.QUIET;
        }
        setupLogging(verbosity);

        // check for advance mode
        if (commandLine.hasOption("advance") || commandLine.hasOption("advanced")) {
            setAdvanceMode(true);
        }

        // @todo use new ability of apache-commons-cli to check hasOption for non-prefixed items
        boolean cmdFound = false;
        for (String opt : commandLine.getArgs()) {
            if (opt.equalsIgnoreCase("d") || opt.equalsIgnoreCase("decode")) {
                cmdDecode(commandLine);
                cmdFound = true;
            } else if (opt.equalsIgnoreCase("b") || opt.equalsIgnoreCase("build")) {
                cmdBuild(commandLine);
                cmdFound = true;
            } else if (opt.equalsIgnoreCase("if") || opt.equalsIgnoreCase("install-framework")) {
                cmdInstallFramework(commandLine);
                cmdFound = true;
            } else if (opt.equalsIgnoreCase("empty-framework-dir")) {
                cmdEmptyFrameworkDirectory(commandLine);
                cmdFound = true;
            } else if (opt.equalsIgnoreCase("publicize-resources")) {
                cmdPublicizeResources(commandLine);
                cmdFound = true;
            }
        }

        // if no commands ran, run the version / usage check.
        if (!cmdFound) {
            if (commandLine.hasOption("version")) {
                _version();
            } else {
                usage();
            }
        }
    }

    @SuppressWarnings("static-access")
    private static void _Options() {
        // create options
        Option versionOption = OptionBuilder.withLongOpt("version").withDescription("prints the version then exits")
                .create("version");

        Option advanceOption = OptionBuilder.withLongOpt("advanced").withDescription("prints advance information.")
                .create("advance");

        Option noSrcOption = OptionBuilder.withLongOpt("no-src").withDescription("Do not decode sources.")
                .create("s");

        Option noResOption = OptionBuilder.withLongOpt("no-res").withDescription("Do not decode resources.")
                .create("r");

        Option debugDecOption = OptionBuilder.withLongOpt("debug")
                .withDescription("REMOVED (DOES NOT WORK): Decode in debug mode.").create("d");

        Option analysisOption = OptionBuilder.withLongOpt("match-original")
                .withDescription("Keeps files to closest to original as possible. Prevents rebuild.").create("m");

        Option apiLevelOption = OptionBuilder.withLongOpt("api")
                .withDescription("The numeric api-level of the file to generate, e.g. 14 for ICS.").hasArg(true)
                .withArgName("API").create();

        Option debugBuiOption = OptionBuilder.withLongOpt("debug")
                .withDescription("Sets android:debuggable to \"true\" in the APK's compiled manifest").create("d");

        Option noDbgOption = OptionBuilder.withLongOpt("no-debug-info")
                .withDescription("don't write out debug info (.local, .param, .line, etc.)").create("b");

        Option forceDecOption = OptionBuilder.withLongOpt("force")
                .withDescription("Force delete destination directory.").create("f");

        Option frameTagOption = OptionBuilder.withLongOpt("frame-tag")
                .withDescription("Uses framework files tagged by <tag>.").hasArg(true).withArgName("tag")
                .create("t");

        Option frameDirOption = OptionBuilder.withLongOpt("frame-path")
                .withDescription("Uses framework files located in <dir>.").hasArg(true).withArgName("dir")
                .create("p");

        Option frameIfDirOption = OptionBuilder.withLongOpt("frame-path")
                .withDescription("Stores framework files into <dir>.").hasArg(true).withArgName("dir").create("p");

        Option keepResOption = OptionBuilder.withLongOpt("keep-broken-res")
                .withDescription("Use if there was an error and some resources were dropped, e.g.\n"
                        + "            \"Invalid config flags detected. Dropping resources\", but you\n"
                        + "            want to decode them anyway, even with errors. You will have to\n"
                        + "            fix them manually before building.")
                .create("k");

        Option forceBuiOption = OptionBuilder.withLongOpt("force-all")
                .withDescription("Skip changes detection and build all files.").create("f");

        Option aaptOption = OptionBuilder.withLongOpt("aapt").hasArg(true).withArgName("loc")
                .withDescription("Loads aapt from specified location.").create("a");

        Option originalOption = OptionBuilder.withLongOpt("copy-original")
                .withDescription(
                        "Copies original AndroidManifest.xml and META-INF. See project page for more info.")
                .create("c");

        Option tagOption = OptionBuilder.withLongOpt("tag").withDescription("Tag frameworks using <tag>.")
                .hasArg(true).withArgName("tag").create("t");

        Option outputBuiOption = OptionBuilder.withLongOpt("output")
                .withDescription("The name of apk that gets written. Default is dist/name.apk").hasArg(true)
                .withArgName("dir").create("o");

        Option outputDecOption = OptionBuilder.withLongOpt("output")
                .withDescription("The name of folder that gets written. Default is apk.out").hasArg(true)
                .withArgName("dir").create("o");

        Option quietOption = OptionBuilder.withLongOpt("quiet").create("q");

        Option verboseOption = OptionBuilder.withLongOpt("verbose").create("v");

        // check for advance mode
        if (isAdvanceMode()) {
            DecodeOptions.addOption(noDbgOption);
            DecodeOptions.addOption(keepResOption);
            DecodeOptions.addOption(analysisOption);
            DecodeOptions.addOption(apiLevelOption);

            BuildOptions.addOption(debugBuiOption);
            BuildOptions.addOption(aaptOption);
            BuildOptions.addOption(originalOption);
        }

        // add global options
        normalOptions.addOption(versionOption);
        normalOptions.addOption(advanceOption);

        // add basic decode options
        DecodeOptions.addOption(frameTagOption);
        DecodeOptions.addOption(outputDecOption);
        DecodeOptions.addOption(frameDirOption);
        DecodeOptions.addOption(forceDecOption);
        DecodeOptions.addOption(noSrcOption);
        DecodeOptions.addOption(noResOption);

        // add basic build options
        BuildOptions.addOption(outputBuiOption);
        BuildOptions.addOption(frameDirOption);
        BuildOptions.addOption(forceBuiOption);

        // add basic framework options
        frameOptions.addOption(tagOption);
        frameOptions.addOption(frameIfDirOption);

        // add empty framework options
        emptyFrameworkOptions.addOption(forceDecOption);
        emptyFrameworkOptions.addOption(frameIfDirOption);

        // add all, loop existing cats then manually add advance
        for (Object op : normalOptions.getOptions()) {
            allOptions.addOption((Option) op);
        }
        for (Object op : DecodeOptions.getOptions()) {
            allOptions.addOption((Option) op);
        }
        for (Object op : BuildOptions.getOptions()) {
            allOptions.addOption((Option) op);
        }
        for (Object op : frameOptions.getOptions()) {
            allOptions.addOption((Option) op);
        }
        allOptions.addOption(analysisOption);
        allOptions.addOption(debugDecOption);
        allOptions.addOption(noDbgOption);
        allOptions.addOption(keepResOption);
        allOptions.addOption(debugBuiOption);
        allOptions.addOption(aaptOption);
        allOptions.addOption(originalOption);
        allOptions.addOption(verboseOption);
        allOptions.addOption(quietOption);
    }

    private static void usage() {
        // load basicOptions
        _Options();
        HelpFormatter formatter = new HelpFormatter();
        formatter.setWidth(120);

        // print out license info prior to formatter.
        System.out.println("Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n"
                + "with smali v" + ApktoolProperties.get("smaliVersion") + " and baksmali v"
                + ApktoolProperties.get("baksmaliVersion") + "\n"
                + "Copyright 2014 Ryszard Winiewski <brut.alll@gmail.com>\n"
                + "Updated by Connor Tumbleson <connor.tumbleson@gmail.com>");
        if (isAdvanceMode()) {
            System.out.println("Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n");
        } else {
            System.out.println("");
        }

        // 4 usage outputs (general, frameworks, decode, build)
        formatter.printHelp("apktool " + verbosityHelp(), normalOptions);
        formatter.printHelp("apktool " + verbosityHelp() + "if|install-framework [options] <framework.apk>",
                frameOptions);
        formatter.printHelp("apktool " + verbosityHelp() + "d[ecode] [options] <file_apk>", DecodeOptions);
        formatter.printHelp("apktool " + verbosityHelp() + "b[uild] [options] <app_path>", BuildOptions);
        if (isAdvanceMode()) {
            formatter.printHelp("apktool " + verbosityHelp() + "publicize-resources <file_path>", emptyOptions);
            formatter.printHelp("apktool " + verbosityHelp() + "empty-framework-dir [options]",
                    emptyFrameworkOptions);
            System.out.println("");
        } else {
            System.out.println("");
        }

        // print out more information
        System.out.println("For additional info, see: http://ibotpeaches.github.io/Apktool/ \n"
                + "For smali/baksmali info, see: https://github.com/JesusFreke/smali");
    }

    private static String verbosityHelp() {
        if (isAdvanceMode()) {
            return "[-q|--quiet OR -v|--verbose] ";
        } else {
            return "";
        }
    }

    private static void setupLogging(Verbosity verbosity) {
        Logger logger = Logger.getLogger("");
        for (Handler handler : logger.getHandlers()) {
            logger.removeHandler(handler);
        }
        LogManager.getLogManager().reset();

        if (verbosity == Verbosity.QUIET) {
            return;
        }

        Handler handler = new Handler() {
            @Override
            public void publish(LogRecord record) {
                if (getFormatter() == null) {
                    setFormatter(new SimpleFormatter());
                }

                try {
                    String message = getFormatter().format(record);
                    if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
                        System.err.write(message.getBytes());
                    } else {
                        System.out.write(message.getBytes());
                    }
                } catch (Exception exception) {
                    reportError(null, exception, ErrorManager.FORMAT_FAILURE);
                }
            }

            @Override
            public void close() throws SecurityException {
            }

            @Override
            public void flush() {
            }
        };

        logger.addHandler(handler);

        if (verbosity == Verbosity.VERBOSE) {
            handler.setLevel(Level.ALL);
            logger.setLevel(Level.ALL);
        } else {
            handler.setFormatter(new Formatter() {
                @Override
                public String format(LogRecord record) {
                    return record.getLevel().toString().charAt(0) + ": " + record.getMessage()
                            + System.getProperty("line.separator");
                }
            });
        }
    }

    private static void cmdDecode(CommandLine cli) throws AndrolibException {
        // ??
        ApkDecoder decoder = new ApkDecoder();

        int paraCount = cli.getArgList().size();
        String apkName = (String) cli.getArgList().get(paraCount - 1);
        File outDir;

        // check for options
        if (cli.hasOption("s") || cli.hasOption("no-src")) {
            decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE);
        }
        if (cli.hasOption("d") || cli.hasOption("debug")) {
            System.err.println(
                    "SmaliDebugging has been removed in 2.1.0 onward. Please see: https://github.com/iBotPeaches/Apktool/issues/1061");
            System.exit(1);
        }
        if (cli.hasOption("b") || cli.hasOption("no-debug-info")) {
            decoder.setBaksmaliDebugMode(false);
        }
        if (cli.hasOption("t") || cli.hasOption("frame-tag")) {
            decoder.setFrameworkTag(cli.getOptionValue("t"));
        }
        if (cli.hasOption("f") || cli.hasOption("force")) {
            decoder.setForceDelete(true);
        }
        if (cli.hasOption("r") || cli.hasOption("no-res")) {
            decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE);
        }
        if (cli.hasOption("k") || cli.hasOption("keep-broken-res")) {
            decoder.setKeepBrokenResources(true);
        }
        if (cli.hasOption("p") || cli.hasOption("frame-path")) {
            decoder.setFrameworkDir(cli.getOptionValue("p"));
        }
        if (cli.hasOption("m") || cli.hasOption("match-original")) {
            decoder.setAnalysisMode(true, false);
        }
        if (cli.hasOption("api")) {
            decoder.setApi(Integer.parseInt(cli.getOptionValue("api")));
        }
        if (cli.hasOption("o") || cli.hasOption("output")) {
            outDir = new File(cli.getOptionValue("o"));
            decoder.setOutDir(outDir);
        } else {

            // make out folder manually using name of apk
            String outName = apkName;
            outName = outName.endsWith(".apk") ? outName.substring(0, outName.length() - 4).trim()
                    : outName + ".out";

            // make file from path
            outName = new File(outName).getName();
            outDir = new File(outName);
            decoder.setOutDir(outDir);
        }

        decoder.setApkFile(new File(apkName));

        try {
            decoder.decode();
        } catch (OutDirExistsException ex) {
            System.err.println("Destination directory (" + outDir.getAbsolutePath() + ") "
                    + "already exists. Use -f switch if you want to overwrite it.");
            System.exit(1);
        } catch (InFileNotFoundException ex) {
            System.err.println("Input file (" + apkName + ") " + "was not found or was not readable.");
            System.exit(1);
        } catch (CantFindFrameworkResException ex) {
            System.err.println("Can't find framework resources for package of id: " + String.valueOf(ex.getPkgId())
                    + ". You must install proper " + "framework files, see project website for more info.");
            System.exit(1);
        } catch (IOException ex) {
            System.err.println("Could not modify file. Please ensure you have permission.");
            System.exit(1);
        } catch (DirectoryException ex) {
            System.err.println("Could not modify internal dex files. Please ensure you have permission.");
            System.exit(1);
        }
    }

    private static void cmdBuild(CommandLine cli) throws BrutException {
        String[] args = cli.getArgs();
        String appDirName = args.length < 2 ? "." : args[1];
        File outFile;
        ApkOptions apkOptions = new ApkOptions();

        // check for build options
        if (cli.hasOption("f") || cli.hasOption("force-all")) {
            apkOptions.forceBuildAll = true;
        }
        if (cli.hasOption("d") || cli.hasOption("debug")) {
            System.out.println(
                    "SmaliDebugging has been removed in 2.1.0 onward. Please see: https://github.com/iBotPeaches/Apktool/issues/1061");
            apkOptions.debugMode = true;
        }
        if (cli.hasOption("v") || cli.hasOption("verbose")) {
            apkOptions.verbose = true;
        }
        if (cli.hasOption("a") || cli.hasOption("aapt")) {
            apkOptions.aaptPath = cli.getOptionValue("a");
        }
        if (cli.hasOption("c") || cli.hasOption("copy-original")) {
            apkOptions.copyOriginalFiles = true;
        }
        if (cli.hasOption("p") || cli.hasOption("frame-path")) {
            apkOptions.frameworkFolderLocation = cli.getOptionValue("p");
        }
        if (cli.hasOption("o") || cli.hasOption("output")) {
            outFile = new File(cli.getOptionValue("o"));
        } else {
            outFile = null;
        }

        // try and build apk
        new Androlib(apkOptions).build(new File(appDirName), outFile);
    }

    private static void cmdInstallFramework(CommandLine cli) throws AndrolibException {
        int paraCount = cli.getArgList().size();
        String apkName = (String) cli.getArgList().get(paraCount - 1);

        ApkOptions apkOptions = new ApkOptions();
        if (cli.hasOption("p") || cli.hasOption("frame-path")) {
            apkOptions.frameworkFolderLocation = cli.getOptionValue("p");
        }
        if (cli.hasOption("t") || cli.hasOption("tag")) {
            apkOptions.frameworkTag = cli.getOptionValue("t");
        }
        new Androlib(apkOptions).installFramework(new File(apkName));
    }

    private static void cmdEmptyFrameworkDirectory(CommandLine cli) throws AndrolibException {
        ApkOptions apkOptions = new ApkOptions();

        if (cli.hasOption("f") || cli.hasOption("force")) {
            apkOptions.forceDeleteFramework = true;
        }
        if (cli.hasOption("p") || cli.hasOption("frame-path")) {
            apkOptions.frameworkFolderLocation = cli.getOptionValue("p");
        }

        new Androlib(apkOptions).emptyFrameworkDirectory();
    }

    private static void cmdPublicizeResources(CommandLine cli) throws AndrolibException {
        int paraCount = cli.getArgList().size();
        String apkName = (String) cli.getArgList().get(paraCount - 1);

        new Androlib().publicizeResources(new File(apkName));
    }

    private static void _version() {
        System.out.println(Androlib.getVersion());
    }

    public static boolean isAdvanceMode() {
        return advanceMode;
    }

    public static void setAdvanceMode(boolean advanceMode) {
        Main.advanceMode = advanceMode;
    }

    private static boolean advanceMode = false;

    private final static Options normalOptions;
    private final static Options DecodeOptions;
    private final static Options BuildOptions;
    private final static Options frameOptions;
    private final static Options allOptions;
    private final static Options emptyOptions;
    private final static Options emptyFrameworkOptions;

    static {
        // normal and advance usage output
        normalOptions = new Options();
        BuildOptions = new Options();
        DecodeOptions = new Options();
        frameOptions = new Options();
        allOptions = new Options();
        emptyOptions = new Options();
        emptyFrameworkOptions = new Options();
    }

    private enum Verbosity {
        NORMAL, VERBOSE, QUIET
    }
}