1ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver/*
2ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * Copyright 2012, Google Inc.
3ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * All rights reserved.
4ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver *
5ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * Redistribution and use in source and binary forms, with or without
6ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * modification, are permitted provided that the following conditions are
7ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * met:
8ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver *
9ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver *     * Redistributions of source code must retain the above copyright
10ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * notice, this list of conditions and the following disclaimer.
11ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver *     * Redistributions in binary form must reproduce the above
12ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * copyright notice, this list of conditions and the following disclaimer
13ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * in the documentation and/or other materials provided with the
14ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * distribution.
15ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver *     * Neither the name of Google Inc. nor the names of its
16ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * contributors may be used to endorse or promote products derived from
17ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * this software without specific prior written permission.
18ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver *
19ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver */
31ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver
32ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruverpackage org.jf.dexlib2;
33ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver
34ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruverimport com.google.common.io.ByteStreams;
35ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruverimport org.jf.dexlib2.dexbacked.DexBackedDexFile;
36d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruverimport org.jf.dexlib2.dexbacked.DexBackedOdexFile;
37ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruverimport org.jf.dexlib2.iface.DexFile;
380f198bc78bbb18a2eed3b55403ce17c93ea98932Ben Gruverimport org.jf.dexlib2.writer.pool.DexPool;
39ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruverimport org.jf.util.ExceptionWithContext;
40ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver
41ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruverimport javax.annotation.Nonnull;
42d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruverimport java.io.*;
43ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruverimport java.util.zip.ZipEntry;
44ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruverimport java.util.zip.ZipFile;
45ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver
4685b31462241daf1d5d5e7b5e94ca926e46dff971Ben Gruverpublic final class DexFileFactory {
47ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver    @Nonnull
4883f77f51aa888998486c0c9ad693047480b060b0Ben Gruver    public static DexBackedDexFile loadDexFile(String path, int api) throws IOException {
4983f77f51aa888998486c0c9ad693047480b060b0Ben Gruver        return loadDexFile(new File(path), new Opcodes(api));
50ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver    }
51ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver
52ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver    @Nonnull
5383f77f51aa888998486c0c9ad693047480b060b0Ben Gruver    public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException {
5483f77f51aa888998486c0c9ad693047480b060b0Ben Gruver        return loadDexFile(dexFile, new Opcodes(api));
5583f77f51aa888998486c0c9ad693047480b060b0Ben Gruver    }
5683f77f51aa888998486c0c9ad693047480b060b0Ben Gruver
5783f77f51aa888998486c0c9ad693047480b060b0Ben Gruver    @Nonnull
5883f77f51aa888998486c0c9ad693047480b060b0Ben Gruver    public static DexBackedDexFile loadDexFile(File dexFile, @Nonnull Opcodes opcodes) throws IOException {
59ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver        ZipFile zipFile = null;
60d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        boolean isZipFile = false;
61ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver        try {
62ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            zipFile = new ZipFile(dexFile);
63ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            // if we get here, it's safe to assume we have a zip file
64ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            isZipFile = true;
65ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver
66ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            ZipEntry zipEntry = zipFile.getEntry("classes.dex");
67ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            if (zipEntry == null) {
684cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver                throw new NoClassesDexException("zip file %s does not contain a classes.dex file", dexFile.getName());
69ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            }
70ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            long fileLength = zipEntry.getSize();
71ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            if (fileLength < 40) {
72ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                throw new ExceptionWithContext(
73ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                        "The classes.dex file in %s is too small to be a valid dex file", dexFile.getName());
74ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            } else if (fileLength > Integer.MAX_VALUE) {
75ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                throw new ExceptionWithContext("The classes.dex file in %s is too large to read in", dexFile.getName());
76ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            }
77d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver            byte[] dexBytes = new byte[(int)fileLength];
78ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            ByteStreams.readFully(zipFile.getInputStream(zipEntry), dexBytes);
7983f77f51aa888998486c0c9ad693047480b060b0Ben Gruver            return new DexBackedDexFile(opcodes, dexBytes);
80ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver        } catch (IOException ex) {
81d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver            // don't continue on if we know it's a zip file
82ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            if (isZipFile) {
83ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                throw ex;
84ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            }
85ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver        } finally {
86ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            if (zipFile != null) {
87ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                try {
88ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                    zipFile.close();
89ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                } catch (IOException ex) {
90ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                    // just eat it
91ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver                }
92ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver            }
93ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver        }
94ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver
95d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        InputStream inputStream = new BufferedInputStream(new FileInputStream(dexFile));
96d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver
97d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        try {
9883f77f51aa888998486c0c9ad693047480b060b0Ben Gruver            return DexBackedDexFile.fromInputStream(opcodes, inputStream);
99d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        } catch (DexBackedDexFile.NotADexFile ex) {
100d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver            // just eat it
101d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        }
102d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver
103d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        // Note: DexBackedDexFile.fromInputStream will reset inputStream back to the same position, if it fails
104d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver
105d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        try {
10683f77f51aa888998486c0c9ad693047480b060b0Ben Gruver            return DexBackedOdexFile.fromInputStream(opcodes, inputStream);
107d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        } catch (DexBackedOdexFile.NotAnOdexFile ex) {
108d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver            // just eat it
109ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver        }
110ebc11d32160041c61c5fb5be11e3d82853486880Ben Gruver
111d45a6a60921ac27a4f13360a68e02e8f5fc28454Ben Gruver        throw new ExceptionWithContext("%s is not an apk, dex file or odex file.", dexFile.getPath());
112ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver    }
11385b31462241daf1d5d5e7b5e94ca926e46dff971Ben Gruver
1144ffbfa2e71ffdf6ecaa8429b19ce29daa28e9fc4Ben Gruver    public static void writeDexFile(String path, DexFile dexFile) throws IOException {
1150f198bc78bbb18a2eed3b55403ce17c93ea98932Ben Gruver        DexPool.writeTo(path, dexFile);
1164ffbfa2e71ffdf6ecaa8429b19ce29daa28e9fc4Ben Gruver    }
1174ffbfa2e71ffdf6ecaa8429b19ce29daa28e9fc4Ben Gruver
11885b31462241daf1d5d5e7b5e94ca926e46dff971Ben Gruver    private DexFileFactory() {}
1194cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver
1204cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver    public static class NoClassesDexException extends ExceptionWithContext {
1214cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver        public NoClassesDexException(Throwable cause) {
1224cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver            super(cause);
1234cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver        }
1244cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver
1254cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver        public NoClassesDexException(Throwable cause, String message, Object... formatArgs) {
1264cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver            super(cause, message, formatArgs);
1274cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver        }
1284cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver
1294cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver        public NoClassesDexException(String message, Object... formatArgs) {
1304cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver            super(message, formatArgs);
1314cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver        }
1324cf7845ea4d6821b435f134f6e5a73efc2cb0ebcBen Gruver    }
133ec47cb62a802276f78d09240be107f635f9d7a26Ben Gruver}
134