Main.java revision 12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b
112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden/* 212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Copyright (C) 2009 The Android Open Source Project 312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * 412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Licensed under the Apache License, Version 2.0 (the "License"); 512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * you may not use this file except in compliance with the License. 612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * You may obtain a copy of the License at 712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * 812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * http://www.apache.org/licenses/LICENSE-2.0 912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * 1012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Unless required by applicable law or agreed to in writing, software 1112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * distributed under the License is distributed on an "AS IS" BASIS, 1212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * See the License for the specific language governing permissions and 1412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * limitations under the License. 1512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 1612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 1712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenpackage com.android.dexdeps; 1812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 1912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.File; 2012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.FileNotFoundException; 2112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.IOException; 2212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.InputStream; 2312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.RandomAccessFile; 2412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipEntry; 2512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipException; 2612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipFile; 2712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipInputStream; 2812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 2912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenpublic class Main { 3012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden private static final String CLASSES_DEX = "classes.dex"; 3112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 3212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden private String mInputFileName; 3312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden private String mOutputFormat = "brief"; 3412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 3512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /** 3612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Entry point. 3712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 3812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden public static void main(String[] args) { 3912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden Main main = new Main(); 4012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden main.run(args); 4112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 4212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 4312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /** 4412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Start things up. 4512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 4612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden void run(String[] args) { 4712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden try { 4812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden parseArgs(args); 4912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden RandomAccessFile raf = openInputFile(); 5012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden DexData dexData = new DexData(raf); 5112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden dexData.load(); 5212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 5312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden Output.generate(dexData, mOutputFormat); 5412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } catch (UsageException ue) { 5512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden usage(); 5612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.exit(2); 5712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } catch (IOException ioe) { 5812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* a message was already reported, just bail quietly */ 5912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.exit(1); 6012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } catch (DexDataException dde) { 6112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* a message was already reported, just bail quietly */ 6212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.exit(1); 6312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 6412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 6512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 6612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /** 6712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Opens the input file, which could be a .dex or a .jar/.apk with a 6812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * classes.dex inside. If the latter, we extract the contents to a 6912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * temporary file. 7012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 7112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden RandomAccessFile openInputFile() throws IOException { 7212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden RandomAccessFile raf; 7312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 7412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden raf = openInputFileAsZip(); 7512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden if (raf == null) { 7612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden File inputFile = new File(mInputFileName); 7712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden raf = new RandomAccessFile(inputFile, "r"); 7812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 7912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 8012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden return raf; 8112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 8212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 8312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /** 8412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Tries to open the input file as a Zip archive (jar/apk) with a 8512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * "classes.dex" inside. 8612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * 8712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * @return a RandomAccessFile for classes.dex, or null if the input file 8812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * is not a zip archive 8912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * @throws IOException if the file isn't found, or it's a zip and 9012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * classes.dex isn't found inside 9112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 9212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden RandomAccessFile openInputFileAsZip() throws IOException { 9312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden ZipFile zipFile; 9412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 9512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* 9612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Try it as a zip file. 9712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 9812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden try { 9912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden zipFile = new ZipFile(mInputFileName); 10012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } catch (FileNotFoundException fnfe) { 10112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* not found, no point in retrying as non-zip */ 10212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println("Unable to open '" + mInputFileName + "': " + 10312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden fnfe.getMessage()); 10412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden throw fnfe; 10512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } catch (ZipException ze) { 10612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* not a zip */ 10712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden return null; 10812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 10912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 11012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* 11112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * We know it's a zip; see if there's anything useful inside. A 11212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * failure here results in some type of IOException (of which 11312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * ZipException is a subclass). 11412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 11512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden ZipEntry entry = zipFile.getEntry(CLASSES_DEX); 11612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden if (entry == null) { 11712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println("Unable to find '" + CLASSES_DEX + 11812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden "' in '" + mInputFileName + "'"); 11912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden zipFile.close(); 12012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden throw new ZipException(); 12112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 12212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 12312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden InputStream zis = zipFile.getInputStream(entry); 12412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 12512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* 12612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Create a temp file to hold the DEX data, open it, and delete it 12712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * to ensure it doesn't hang around if we fail. 12812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 12912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden File tempFile = File.createTempFile("dexdeps", ".dex"); 13012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.out.println("+++ using temp " + tempFile); 13112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); 13212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden tempFile.delete(); 13312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 13412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /* 13512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Copy all data from input stream to output file. 13612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 13712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden byte copyBuf[] = new byte[32768]; 13812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden int actual; 13912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 14012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden while (true) { 14112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden actual = zis.read(copyBuf); 14212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden if (actual == -1) 14312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden break; 14412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 14512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden raf.write(copyBuf, 0, actual); 14612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 14712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 14812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden zis.close(); 14912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden raf.seek(0); 15012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 15112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden return raf; 15212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 15312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 15412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 15512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /** 15612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Parses command-line arguments. 15712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * 15812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * @throws UsageException if arguments are missing or poorly formed 15912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 16012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden void parseArgs(String[] args) { 16112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden int idx; 16212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 16312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden for (idx = 0; idx < args.length; idx++) { 16412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden String arg = args[idx]; 16512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 16612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden if (arg.equals("--") || !arg.startsWith("--")) { 16712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden break; 16812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } else if (arg.startsWith("--format=")) { 16912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden mOutputFormat = arg.substring(arg.indexOf('=') + 1); 17012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden if (!mOutputFormat.equals("brief") && 17112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden !mOutputFormat.equals("xml")) 17212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden { 17312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println("Unknown format '" + mOutputFormat +"'"); 17412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden throw new UsageException(); 17512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 17612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.out.println("Using format " + mOutputFormat); 17712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } else { 17812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println("Unknown option '" + arg + "'"); 17912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden throw new UsageException(); 18012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 18112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 18212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 18312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden // expecting one argument left 18412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden if (idx != args.length - 1) { 18512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden throw new UsageException(); 18612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 18712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 18812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden mInputFileName = args[idx]; 18912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 19012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 19112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden /** 19212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Prints command-line usage info. 19312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */ 19412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden void usage() { 19512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println("\nUsage: dexdeps [options] <file.{dex,apk,jar}>"); 19612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println("Options:"); 19712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden System.err.println(" --format={brief,xml}"); 19812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden } 19912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden} 20012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden 201