baksmali.java revision 93aa50139c4641d931b05608f73af8879c0de1c2
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * [The "BSD licence"]
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (c) 2010 Ben Gruver (JesusFreke)
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * All rights reserved.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Redistribution and use in source and binary forms, with or without
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * modification, are permitted provided that the following conditions
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * are met:
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1. Redistributions of source code must retain the above copyright
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    notice, this list of conditions and the following disclaimer.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2. Redistributions in binary form must reproduce the above copyright
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    notice, this list of conditions and the following disclaimer in the
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    documentation and/or other materials provided with the distribution.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3. The name of the author may not be used to endorse or promote products
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    derived from this software without specific prior written permission.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)package org.jf.baksmali;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.google.common.base.Splitter;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.google.common.collect.ImmutableList;
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import com.google.common.collect.Iterables;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.jf.baksmali.Adaptors.ClassDefinition;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.jf.dexlib2.analysis.ClassPath;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import org.jf.dexlib2.iface.ClassDef;
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.jf.dexlib2.iface.DexFile;
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.jf.dexlib2.util.SyntheticAccessorResolver;
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.jf.util.ClassFileNameHandler;
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.jf.util.IndentingWriter;
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.io.*;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.*;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.regex.Matcher;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.regex.Pattern;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public class baksmali {
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory,
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          String[] classPathDirs, String bootClassPath, String extraBootClassPath,
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          boolean noParameterRegisters, boolean useLocalsDirective,
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets,
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          boolean noAccessorComments, int registerInfo, boolean ignoreErrors,
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          String inlineTable, boolean checkPackagePrivateAccess)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        baksmaliOptions options = new baksmaliOptions();
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        options.noParameterRegisters = noParameterRegisters;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        options.useLocalsDirective = useLocalsDirective;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        options.useSequentialLabels = useSequentialLabels;
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        options.outputDebugInfo = outputDebugInfo;
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        options.addCodeOffsets = addCodeOffsets;
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        options.noAccessorComments = noAccessorComments;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        options.deodex = deodex;
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        options.registerInfo = registerInfo;
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        options.bootClassPath = bootClassPath;
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (registerInfo != 0 || deodex) {
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            try {
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                Iterable<String> extraBootClassPaths = null;
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                if (extraBootClassPath != null && extraBootClassPath.length() > 0) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    assert extraBootClassPath.charAt(0) == ':';
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    extraBootClassPaths = Splitter.on(':').split(extraBootClassPath.substring(1));
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                } else {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    extraBootClassPaths = ImmutableList.of();
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                Iterable<String> bootClassPaths = null;
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                if (bootClassPath != null) {
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    bootClassPaths = Splitter.on(':').split(bootClassPath);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                }
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                options.classPath = ClassPath.fromClassPath(Arrays.asList(classPathDirs),
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        Iterables.concat(bootClassPaths, extraBootClassPaths), dexFile);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                // TODO: uncomment
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                /*if (inlineTable != null) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    inlineResolver = new CustomInlineMethodResolver(inlineTable);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }*/
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            } catch (Exception ex) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                System.err.println("\n\nError occured while loading boot class path files. Aborting.");
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ex.printStackTrace(System.err);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                System.exit(1);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        File outputDirectoryFile = new File(outputDirectory);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!outputDirectoryFile.exists()) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if (!outputDirectoryFile.mkdirs()) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                System.err.println("Can't create the output directory " + outputDirectory);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                System.exit(1);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        //name collisions, then we'll use the same name for each class, if the dex file goes through multiple
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        //may still change of course
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        List<ClassDef> classDefs = new ArrayList<ClassDef>(dexFile.getClasses());
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Collections.sort(classDefs, new Comparator<ClassDef>() {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            public int compare(ClassDef classDef1, ClassDef classDef2) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                return classDef1.getType().compareTo(classDef2.getType());
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        });
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        classDefs = ImmutableList.copyOf(classDefs);
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!noAccessorComments) {
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs);
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (ClassDef classDef: classDefs) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            /**
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             * The path for the disassembly file is based on the package name
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             * The class descriptor will look something like:
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             * Ljava/lang/Object;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             * Where the there is leading 'L' and a trailing ';', and the parts of the
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             * package name are separated by '/'
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             */
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            String classDescriptor = classDef.getType();
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            //validate that the descriptor is formatted like we expect
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if (classDescriptor.charAt(0) != 'L' ||
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                classDescriptor.charAt(classDescriptor.length()-1) != ';') {
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                continue;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            //create and initialize the top level string template
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ClassDefinition classDefinition = new ClassDefinition(options, classDef);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            //write the disassembly
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            Writer writer = null;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            try
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                File smaliParent = smaliFile.getParentFile();
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                if (!smaliParent.exists()) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    if (!smaliParent.mkdirs()) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        continue;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                if (!smaliFile.exists()){
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    if (!smaliFile.createNewFile()) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        continue;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        new FileOutputStream(smaliFile), "UTF8"));
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                writer = new IndentingWriter(bufWriter);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                classDefinition.writeTo((IndentingWriter)writer);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            } catch (Exception ex) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ex.printStackTrace();
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                smaliFile.delete();
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            finally
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                if (writer != null) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    try {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        writer.close();
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    } catch (Throwable ex) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        System.err.println("\n\nError occured while closing file " + smaliFile.toString());
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        ex.printStackTrace();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    }
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                }
184868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if (!ignoreErrors && classDefinition.hadValidationErrors()) {
1873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                System.exit(1);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
192868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$");
193868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private static boolean isExtJar(String dexFilePath) {
1943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        Matcher m = extJarPattern.matcher(dexFilePath);
1953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        return m.find();
196868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)