131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver/*
231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * Copyright 2016, Google Inc.
331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * All rights reserved.
431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver *
531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * Redistribution and use in source and binary forms, with or without
631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * modification, are permitted provided that the following conditions are
731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * met:
831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver *
931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * Redistributions of source code must retain the above copyright
1031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * notice, this list of conditions and the following disclaimer.
1131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * Redistributions in binary form must reproduce the above
1231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * copyright notice, this list of conditions and the following disclaimer
1331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * in the documentation and/or other materials provided with the
1431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * distribution.
1531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * Neither the name of Google Inc. nor the names of its
1631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * contributors may be used to endorse or promote products derived from
1731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * this software without specific prior written permission.
1831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver *
1931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver */
3131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
3231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverpackage org.jf.dexlib2.dexbacked;
3331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
3431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport com.google.common.collect.Lists;
3531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport com.google.common.io.ByteStreams;
3631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport org.jf.dexlib2.Opcodes;
3731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport org.jf.dexlib2.dexbacked.DexBackedDexFile.NotADexFile;
3831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport org.jf.dexlib2.dexbacked.ZipDexContainer.ZipDexFile;
3931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport org.jf.dexlib2.iface.MultiDexContainer;
404eefe294e4c664577982283cc64c415819a30c1eBen Gruverimport org.jf.dexlib2.util.DexUtil;
414eefe294e4c664577982283cc64c415819a30c1eBen Gruverimport org.jf.dexlib2.util.DexUtil.InvalidFile;
424eefe294e4c664577982283cc64c415819a30c1eBen Gruverimport org.jf.dexlib2.util.DexUtil.UnsupportedFile;
4331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
4431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport javax.annotation.Nonnull;
4531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport javax.annotation.Nullable;
4616d4b5656b0b803689191a299d25237acf735b5bBen Gruverimport java.io.BufferedInputStream;
47dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruverimport java.io.File;
4831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport java.io.IOException;
4931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport java.io.InputStream;
5031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport java.util.Enumeration;
5131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport java.util.List;
5231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport java.util.zip.ZipEntry;
5331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruverimport java.util.zip.ZipFile;
5431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
5531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver/**
5631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver * Represents a zip file that contains dex files (i.e. an apk or jar file)
5731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver */
58dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruverpublic class ZipDexContainer implements MultiDexContainer<ZipDexFile> {
5931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
60dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver    private final File zipFilePath;
6131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    private final Opcodes opcodes;
6231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
6331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    /**
6431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * Constructs a new ZipDexContainer for the given zip file
6531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     *
66dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver     * @param zipFilePath The path to the zip file
6731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * @param opcodes The Opcodes instance to use when loading dex files from this container
6831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     */
69dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver    public ZipDexContainer(@Nonnull File zipFilePath, @Nonnull Opcodes opcodes) {
70dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        this.zipFilePath = zipFilePath;
7131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        this.opcodes = opcodes;
7231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
7331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
742996766649f09b9ce9d1a6e155897dc403b1b89dBen Gruver    @Nonnull @Override public Opcodes getOpcodes() {
752996766649f09b9ce9d1a6e155897dc403b1b89dBen Gruver        return opcodes;
762996766649f09b9ce9d1a6e155897dc403b1b89dBen Gruver    }
772996766649f09b9ce9d1a6e155897dc403b1b89dBen Gruver
7831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    /**
7931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * Gets a list of the names of dex files in this zip file.
8031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     *
8131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * @return A list of the names of dex files in this zip file
8231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     */
8331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    @Nonnull @Override public List<String> getDexEntryNames() throws IOException {
8431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        List<String> entryNames = Lists.newArrayList();
85dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        ZipFile zipFile = getZipFile();
86dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        try {
87dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            Enumeration<? extends ZipEntry> entriesEnumeration = zipFile.entries();
88dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver
89dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            while (entriesEnumeration.hasMoreElements()) {
90dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver                ZipEntry entry = entriesEnumeration.nextElement();
9131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
92dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver                if (!isDex(zipFile, entry)) {
93dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver                    continue;
94dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver                }
9531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
96dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver                entryNames.add(entry.getName());
9731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            }
9831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
99dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            return entryNames;
100dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        } finally {
101dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            zipFile.close();
10231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        }
10331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
10431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
10531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    /**
10631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * Loads a dex file from a specific named entry.
10731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     *
10831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * @param entryName The name of the entry
10931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * @return A ZipDexFile, or null if there is no entry with the given name
11031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     * @throws NotADexFile If the entry isn't a dex file
11131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver     */
11231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    @Nullable @Override public ZipDexFile getEntry(@Nonnull String entryName) throws IOException {
113dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        ZipFile zipFile = getZipFile();
114dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        try {
115dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            ZipEntry entry = zipFile.getEntry(entryName);
116dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            if (entry == null) {
117dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver                return null;
118dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            }
11931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
120dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            return loadEntry(zipFile, entry);
121dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        } finally {
122dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            zipFile.close();
123dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        }
12431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
12531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
126dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver    public boolean isZipFile() {
127e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski        ZipFile zipFile = null;
128dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        try {
129e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski            zipFile = getZipFile();
130dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            return true;
131dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        } catch (IOException ex) {
132dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            return false;
133dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        } catch (NotAZipFileException ex) {
134dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            return false;
135e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski        } finally {
136e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski            if(zipFile != null) {
137e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski                try {
138e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski                    zipFile.close();
139e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski                } catch (IOException ex) {
140e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski                    // just eat it
141e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski                }
142e75f2b230a1188ebfeffcb7737dfe94bcc0f9e44Albert Gorski            }
143dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        }
14431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
14531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
14631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    public class ZipDexFile extends DexBackedDexFile implements MultiDexContainer.MultiDexFile {
14731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
14831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        private final String entryName;
14931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
15031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        protected ZipDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, @Nonnull String entryName) {
15131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            super(opcodes, buf, 0);
15231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            this.entryName = entryName;
15331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        }
15431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
15531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        @Nonnull @Override public String getEntryName() {
15631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            return entryName;
15731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        }
15831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
15931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        @Nonnull @Override public MultiDexContainer getContainer() {
16031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            return ZipDexContainer.this;
16131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        }
16231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
16331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
164615d3833207dae4c30e559f3aae9dcf074005d49Ben Gruver    protected boolean isDex(@Nonnull ZipFile zipFile, @Nonnull ZipEntry zipEntry) throws IOException {
16516d4b5656b0b803689191a299d25237acf735b5bBen Gruver        InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
16631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        try {
1674eefe294e4c664577982283cc64c415819a30c1eBen Gruver            DexUtil.verifyDexHeader(inputStream);
1684eefe294e4c664577982283cc64c415819a30c1eBen Gruver        } catch (NotADexFile ex) {
1694eefe294e4c664577982283cc64c415819a30c1eBen Gruver            return false;
1704eefe294e4c664577982283cc64c415819a30c1eBen Gruver        } catch (InvalidFile ex) {
1714eefe294e4c664577982283cc64c415819a30c1eBen Gruver            return false;
1724eefe294e4c664577982283cc64c415819a30c1eBen Gruver        } catch (UnsupportedFile ex) {
1734eefe294e4c664577982283cc64c415819a30c1eBen Gruver            return false;
17431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        } finally {
17531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            inputStream.close();
17631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        }
1774eefe294e4c664577982283cc64c415819a30c1eBen Gruver        return true;
17831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
17931ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver
180615d3833207dae4c30e559f3aae9dcf074005d49Ben Gruver    protected ZipFile getZipFile() throws IOException {
181dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        try {
182dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            return new ZipFile(zipFilePath);
183dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        } catch (IOException ex) {
184dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver            throw new NotAZipFileException();
185dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver        }
186dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver    }
187dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver
18831ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    @Nonnull
189615d3833207dae4c30e559f3aae9dcf074005d49Ben Gruver    protected ZipDexFile loadEntry(@Nonnull ZipFile zipFile, @Nonnull ZipEntry zipEntry) throws IOException {
19031ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        InputStream inputStream = zipFile.getInputStream(zipEntry);
19131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        try {
19231ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            byte[] buf = ByteStreams.toByteArray(inputStream);
19331ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            return new ZipDexFile(opcodes, buf, zipEntry.getName());
19431ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        } finally {
19531ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver            inputStream.close();
19631ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver        }
19731ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver    }
198dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver
199dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver    public static class NotAZipFileException extends RuntimeException {
200dbd9db303a6c30edb00376de2c5ed028a8acc967Ben Gruver    }
20131ad2bc1002784161b318627f32b4df8bcb862e0Ben Gruver}
202