main.java revision 4b171afedb983fb811990beeec6a15e30a90b455
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.com * [The "BSD licence"]
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright (c) 2010 Ben Gruver (JesusFreke)
4a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.com * All rights reserved.
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Redistribution and use in source and binary forms, with or without
7a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.com * modification, are permitted provided that the following conditions
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * are met:
9ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * 1. Redistributions of source code must retain the above copyright
1064a0ec36555352ec31aa7c5a7630a5d042b010badjsollen@google.com *    notice, this list of conditions and the following disclaimer.
118b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org * 2. Redistributions in binary form must reproduce the above copyright
128b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org *    notice, this list of conditions and the following disclaimer in the
138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com *    documentation and/or other materials provided with the distribution.
148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * 3. The name of the author may not be used to endorse or promote products
158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com *    derived from this software without specific prior written permission.
162999f789c7e66a31066414f4b8fffb3148b677c5reed@google.com *
172999f789c7e66a31066414f4b8fffb3148b677c5reed@google.com * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
182999f789c7e66a31066414f4b8fffb3148b677c5reed@google.com * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.com * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
200a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.com * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.com * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.com * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.com * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */
288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compackage org.jf.baksmali;
308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
310a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport com.google.common.collect.Lists;
320a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport org.apache.commons.cli.*;
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comimport org.jf.dexlib2.DexFileFactory;
3449f085dddff10473b6ebf832a974288300224e60bsalomonimport org.jf.dexlib2.analysis.CustomInlineMethodResolver;
350a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport org.jf.dexlib2.dexbacked.DexBackedDexFile;
360a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport org.jf.dexlib2.dexbacked.DexBackedOdexFile;
378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comimport org.jf.util.ConsoleUtil;
380a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport org.jf.util.SmaliHelpFormatter;
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
400a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport javax.annotation.Nonnull;
41f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.comimport java.io.File;
428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comimport java.io.IOException;
43a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.comimport java.io.InputStream;
44a6260ff5696e3ae45f295463eaeada2cd75a8b85reed@android.comimport java.util.List;
45f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.comimport java.util.Locale;
460a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.comimport java.util.Properties;
47f9ab99aaade8c451c0e9309b4c61a448373019e3weita@google.com
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compublic class main {
498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    public static final String VERSION;
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    private static final Options basicOptions;
538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    private static final Options debugOptions;
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    private static final Options options;
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    static {
578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        options = new Options();
588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        basicOptions = new Options();
598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        debugOptions = new Options();
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        buildOptions();
610a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
63c9ab987efcb7e8b69237d565f73c28c137610232djsollen@google.com        Properties properties = new Properties();
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        String version = "(unknown)";
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        try {
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            properties.load(templateStream);
678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            version = properties.getProperty("application.version");
680a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com        } catch (IOException ex) {
690a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com            // ignore
700a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com        }
718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        VERSION = version;
720a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com    }
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    /**
750a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com     * This class is uninstantiable.
760a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com     */
770a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com    private main() {
780a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com    }
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    /**
818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com     * Run!
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com     */
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    public static void main(String[] args) throws IOException {
848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        Locale locale = new Locale("en", "US");
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        Locale.setDefault(locale);
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
878b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org        CommandLineParser parser = new PosixParser();
888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        CommandLine commandLine;
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        try {
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            commandLine = parser.parse(options, args);
920a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com        } catch (ParseException ex) {
93c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com            usage();
94ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org            return;
95ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org        }
96ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org
97ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org        baksmaliOptions options = new baksmaliOptions();
98ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org
99ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org        boolean disassemble = true;
100ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org        boolean doDump = false;
101ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org        String dumpFileName = null;
102ef74fa189b738e13295d6a96f86a6e10223505a8commit-bot@chromium.org
10357313f6472684b14570562e43e84f5e2d6fcf278bsalomon@google.com        String[] remainingArgs = commandLine.getArgs();
104c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com
105025128811219dc45fd99b6c4d1d14f833cf7a26ecommit-bot@chromium.org        Option[] clOptions = commandLine.getOptions();
10657313f6472684b14570562e43e84f5e2d6fcf278bsalomon@google.com
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        for (int i=0; i<clOptions.length; i++) {
1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            Option option = clOptions[i];
1098b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org            String opt = option.getOpt();
1100a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com
111c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com            switch (opt.charAt(0)) {
1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                case 'v':
113                    version();
114                    return;
115                case '?':
116                    while (++i < clOptions.length) {
117                        if (clOptions[i].getOpt().charAt(0) == '?') {
118                            usage(true);
119                            return;
120                        }
121                    }
122                    usage(false);
123                    return;
124                case 'o':
125                    options.outputDirectory = commandLine.getOptionValue("o");
126                    break;
127                case 'p':
128                    options.noParameterRegisters = true;
129                    break;
130                case 'l':
131                    options.useLocalsDirective = true;
132                    break;
133                case 's':
134                    options.useSequentialLabels = true;
135                    break;
136                case 'b':
137                    options.outputDebugInfo = false;
138                    break;
139                case 'd':
140                    options.bootClassPathDirs.add(option.getValue());
141                    break;
142                case 'f':
143                    options.addCodeOffsets = true;
144                    break;
145                case 'r':
146                    String[] values = commandLine.getOptionValues('r');
147                    int registerInfo = 0;
148
149                    if (values == null || values.length == 0) {
150                        registerInfo = baksmaliOptions.ARGS | baksmaliOptions.DEST;
151                    } else {
152                        for (String value: values) {
153                            if (value.equalsIgnoreCase("ALL")) {
154                                registerInfo |= baksmaliOptions.ALL;
155                            } else if (value.equalsIgnoreCase("ALLPRE")) {
156                                registerInfo |= baksmaliOptions.ALLPRE;
157                            } else if (value.equalsIgnoreCase("ALLPOST")) {
158                                registerInfo |= baksmaliOptions.ALLPOST;
159                            } else if (value.equalsIgnoreCase("ARGS")) {
160                                registerInfo |= baksmaliOptions.ARGS;
161                            } else if (value.equalsIgnoreCase("DEST")) {
162                                registerInfo |= baksmaliOptions.DEST;
163                            } else if (value.equalsIgnoreCase("MERGE")) {
164                                registerInfo |= baksmaliOptions.MERGE;
165                            } else if (value.equalsIgnoreCase("FULLMERGE")) {
166                                registerInfo |= baksmaliOptions.FULLMERGE;
167                            } else {
168                                usage();
169                                return;
170                            }
171                        }
172
173                        if ((registerInfo & baksmaliOptions.FULLMERGE) != 0) {
174                            registerInfo &= ~baksmaliOptions.MERGE;
175                        }
176                    }
177                    options.registerInfo = registerInfo;
178                    break;
179                case 'c':
180                    String bcp = commandLine.getOptionValue("c");
181                    if (bcp != null && bcp.charAt(0) == ':') {
182                        options.addExtraClassPath(bcp);
183                    } else {
184                        options.setBootClassPath(bcp);
185                    }
186                    break;
187                case 'x':
188                    options.deodex = true;
189                    break;
190                case 'm':
191                    options.noAccessorComments = true;
192                    break;
193                case 'a':
194                    options.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
195                    break;
196                case 'N':
197                    disassemble = false;
198                    break;
199                case 'D':
200                    doDump = true;
201                    dumpFileName = commandLine.getOptionValue("D");
202                    break;
203                case 'I':
204                    options.ignoreErrors = true;
205                    break;
206                case 'T':
207                    options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(commandLine.getOptionValue("T")));
208                    break;
209                case 'K':
210                    options.checkPackagePrivateAccess = true;
211                    break;
212                default:
213                    assert false;
214            }
215        }
216
217        if (remainingArgs.length != 1) {
218            usage();
219            return;
220        }
221
222        String inputDexFileName = remainingArgs[0];
223
224        File dexFileFile = new File(inputDexFileName);
225        if (!dexFileFile.exists()) {
226            System.err.println("Can't find the file " + inputDexFileName);
227            System.exit(1);
228        }
229
230        //Read in and parse the dex file
231        DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.apiLevel);
232
233        if (dexFile.isOdexFile()) {
234            if (!options.deodex) {
235                System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
236                System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
237                System.err.println("option");
238            }
239        } else {
240            options.deodex = false;
241        }
242
243
244        if ((options.deodex || options.registerInfo != 0) && options.bootClassPathEntries == null) {
245            if (dexFile instanceof DexBackedOdexFile) {
246                ((DexBackedOdexFile)dexFile).getDependencies();
247            } else {
248                options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
249            }
250        }
251
252        if (disassemble) {
253            baksmali.disassembleDexFile(dexFile, options);
254        }
255
256        if (doDump) {
257            if (dumpFileName == null) {
258                dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
259            }
260            dump.dump(dexFile, dumpFileName, options.apiLevel);
261        }
262    }
263
264    /**
265     * Prints the usage message.
266     */
267    private static void usage(boolean printDebugOptions) {
268        SmaliHelpFormatter formatter = new SmaliHelpFormatter();
269        int consoleWidth = ConsoleUtil.getConsoleWidth();
270        if (consoleWidth <= 0) {
271            consoleWidth = 80;
272        }
273
274        formatter.setWidth(consoleWidth);
275
276        formatter.printHelp("java -jar baksmali.jar [options] <dex-file>",
277                "disassembles and/or dumps a dex file", basicOptions, printDebugOptions?debugOptions:null);
278    }
279
280    private static void usage() {
281        usage(false);
282    }
283
284    /**
285     * Prints the version message.
286     */
287    protected static void version() {
288        System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
289        System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
290        System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
291        System.exit(0);
292    }
293
294    @SuppressWarnings("AccessStaticViaInstance")
295    private static void buildOptions() {
296        Option versionOption = OptionBuilder.withLongOpt("version")
297                .withDescription("prints the version then exits")
298                .create("v");
299
300        Option helpOption = OptionBuilder.withLongOpt("help")
301                .withDescription("prints the help message then exits. Specify twice for debug options")
302                .create("?");
303
304        Option outputDirOption = OptionBuilder.withLongOpt("output")
305                .withDescription("the directory where the disassembled files will be placed. The default is out")
306                .hasArg()
307                .withArgName("DIR")
308                .create("o");
309
310        Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers")
311                .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method " +
312                        "parameters")
313                .create("p");
314
315        Option deodexerantOption = OptionBuilder.withLongOpt("deodex")
316                .withDescription("deodex the given odex file. This option is ignored if the input file is not an " +
317                        "odex file")
318                .create("x");
319
320        Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
321                .withDescription("output the .locals directive with the number of non-parameter registers, rather" +
322                        " than the .register directive with the total number of register")
323                .create("l");
324
325        Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels")
326                .withDescription("create label names using a sequential numbering scheme per label type, rather than " +
327                        "using the bytecode address")
328                .create("s");
329
330        Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info")
331                .withDescription("don't write out debug info (.local, .param, .line, etc.)")
332                .create("b");
333
334        Option registerInfoOption = OptionBuilder.withLongOpt("register-info")
335                .hasOptionalArgs()
336                .withArgName("REGISTER_INFO_TYPES")
337                .withValueSeparator(',')
338                .withDescription("print the specificed type(s) of register information for each instruction. " +
339                        "\"ARGS,DEST\" is the default if no types are specified.\nValid values are:\nALL: all " +
340                        "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " +
341                        "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " +
342                        "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " +
343                        "pre-instruction register has been merged from more than 1 different post-instruction " +
344                        "register from its predecessors\nFULLMERGE: For each register that would be printed by " +
345                        "MERGE, also show the incoming register types that were merged")
346                .create("r");
347
348        Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
349                .withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
350                        "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If the value begins with a " +
351                        ":, it will be appended to the default bootclasspath instead of replacing it")
352                .hasOptionalArg()
353                .withArgName("BOOTCLASSPATH")
354                .create("c");
355
356        Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
357                .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
358                        "directory")
359                .hasArg()
360                .withArgName("DIR")
361                .create("d");
362
363        Option codeOffsetOption = OptionBuilder.withLongOpt("code-offsets")
364                .withDescription("add comments to the disassembly containing the code offset for each address")
365                .create("f");
366
367        Option noAccessorCommentsOption = OptionBuilder.withLongOpt("no-accessor-comments")
368                .withDescription("don't output helper comments for synthetic accessors")
369                .create("m");
370
371        Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
372                .withDescription("The numeric api-level of the file being disassembled. If not " +
373                        "specified, it defaults to 15 (ICS).")
374                .hasArg()
375                .withArgName("API_LEVEL")
376                .create("a");
377
378        Option dumpOption = OptionBuilder.withLongOpt("dump-to")
379                .withDescription("dumps the given dex file into a single annotated dump file named FILE" +
380                        " (<dexfile>.dump by default), along with the normal disassembly")
381                .hasOptionalArg()
382                .withArgName("FILE")
383                .create("D");
384
385        Option ignoreErrorsOption = OptionBuilder.withLongOpt("ignore-errors")
386                .withDescription("ignores any non-fatal errors that occur while disassembling/deodexing," +
387                        " ignoring the class if needed, and continuing with the next class. The default" +
388                        " behavior is to stop disassembling and exit once an error is encountered")
389                .create("I");
390
391        Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly")
392                .withDescription("suppresses the output of the disassembly")
393                .create("N");
394
395        Option inlineTableOption = OptionBuilder.withLongOpt("inline-table")
396                .withDescription("specify a file containing a custom inline method table to use for deodexing")
397                .hasArg()
398                .withArgName("FILE")
399                .create("T");
400
401        Option checkPackagePrivateAccess = OptionBuilder.withLongOpt("check-package-private-access")
402                .withDescription("When deodexing, use the new virtual table generation logic that " +
403                        "prevents overriding an inaccessible package private method. This is a temporary option " +
404                        "that will be removed once this new functionality can be tied to a specific api level.")
405                .create("K");
406
407        basicOptions.addOption(versionOption);
408        basicOptions.addOption(helpOption);
409        basicOptions.addOption(outputDirOption);
410        basicOptions.addOption(noParameterRegistersOption);
411        basicOptions.addOption(deodexerantOption);
412        basicOptions.addOption(useLocalsOption);
413        basicOptions.addOption(sequentialLabelsOption);
414        basicOptions.addOption(noDebugInfoOption);
415        basicOptions.addOption(registerInfoOption);
416        basicOptions.addOption(classPathOption);
417        basicOptions.addOption(classPathDirOption);
418        basicOptions.addOption(codeOffsetOption);
419        basicOptions.addOption(noAccessorCommentsOption);
420        basicOptions.addOption(apiLevelOption);
421
422        debugOptions.addOption(dumpOption);
423        debugOptions.addOption(ignoreErrorsOption);
424        debugOptions.addOption(noDisassemblyOption);
425        debugOptions.addOption(inlineTableOption);
426        debugOptions.addOption(checkPackagePrivateAccess);
427
428        for (Object option: basicOptions.getOptions()) {
429            options.addOption((Option)option);
430        }
431        for (Object option: debugOptions.getOptions()) {
432            options.addOption((Option)option);
433        }
434    }
435
436    @Nonnull
437    private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
438        if (apiLevel < 9) {
439            return Lists.newArrayList(
440                    "/system/framework/core.jar",
441                    "/system/framework/ext.jar",
442                    "/system/framework/framework.jar",
443                    "/system/framework/android.policy.jar",
444                    "/system/framework/services.jar");
445        } else if (apiLevel < 12) {
446            return Lists.newArrayList(
447                    "/system/framework/core.jar",
448                    "/system/framework/bouncycastle.jar",
449                    "/system/framework/ext.jar",
450                    "/system/framework/framework.jar",
451                    "/system/framework/android.policy.jar",
452                    "/system/framework/services.jar",
453                    "/system/framework/core-junit.jar");
454        } else if (apiLevel < 14) {
455            return Lists.newArrayList(
456                    "/system/framework/core.jar",
457                    "/system/framework/apache-xml.jar",
458                    "/system/framework/bouncycastle.jar",
459                    "/system/framework/ext.jar",
460                    "/system/framework/framework.jar",
461                    "/system/framework/android.policy.jar",
462                    "/system/framework/services.jar",
463                    "/system/framework/core-junit.jar");
464        } else if (apiLevel < 16) {
465            return Lists.newArrayList(
466                    "/system/framework/core.jar",
467                    "/system/framework/core-junit.jar",
468                    "/system/framework/bouncycastle.jar",
469                    "/system/framework/ext.jar",
470                    "/system/framework/framework.jar",
471                    "/system/framework/android.policy.jar",
472                    "/system/framework/services.jar",
473                    "/system/framework/apache-xml.jar",
474                    "/system/framework/filterfw.jar");
475
476        } else {
477            // this is correct as of api 17/4.2.2
478            return Lists.newArrayList(
479                    "/system/framework/core.jar",
480                    "/system/framework/core-junit.jar",
481                    "/system/framework/bouncycastle.jar",
482                    "/system/framework/ext.jar",
483                    "/system/framework/framework.jar",
484                    "/system/framework/telephony-common.jar",
485                    "/system/framework/mms-common.jar",
486                    "/system/framework/android.policy.jar",
487                    "/system/framework/services.jar",
488                    "/system/framework/apache-xml.jar");
489        }
490    }
491}