From 6f7c13b8145c9765a21f2ea3a4965c8e0719e240 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 3 Nov 2019 04:45:35 -0500 Subject: [PATCH] Refactor JarMap --- .../com/topjohnwu/magisk/utils/PatchAPK.kt | 2 +- .../java/com/topjohnwu/signing/JarMap.java | 178 +++++++++++------- .../java/com/topjohnwu/signing/SignAPK.java | 2 +- .../java/com/topjohnwu/signing/ZipSigner.java | 2 +- 4 files changed, 108 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index fa1cf6aa2..ebc4a8c94 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -118,7 +118,7 @@ object PatchAPK { @JvmOverloads fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean { try { - val jar = JarMap(apk) + val jar = JarMap.open(apk) val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val xml = jar.getRawData(je) diff --git a/signing/src/main/java/com/topjohnwu/signing/JarMap.java b/signing/src/main/java/com/topjohnwu/signing/JarMap.java index e9677e7e0..2524c5a0b 100644 --- a/signing/src/main/java/com/topjohnwu/signing/JarMap.java +++ b/signing/src/main/java/com/topjohnwu/signing/JarMap.java @@ -15,117 +15,149 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -/* -* A universal random access interface for both JarFile and JarInputStream -* -* In the case when JarInputStream is provided to constructor, the whole stream -* will be loaded into memory for random access purposes. -* On the other hand, when a JarFile is provided, it simply works as a wrapper. -* */ +public abstract class JarMap implements Closeable { -public class JarMap implements Closeable { + LinkedHashMap entryMap; - private JarFile jarFile; - private JarInputStream jis; - private LinkedHashMap bufMap; - private Manifest manifest; - - public JarMap(File file) throws IOException { - this(file, true); + public static JarMap open(String file) throws IOException { + return new FileMap(new File(file), true, ZipFile.OPEN_READ); } - public JarMap(File file, boolean verify) throws IOException { - this(file, verify, ZipFile.OPEN_READ); + public static JarMap open(File file, boolean verify) throws IOException { + return new FileMap(file, verify, ZipFile.OPEN_READ); } - public JarMap(File file, boolean verify, int mode) throws IOException { - jarFile = new JarFile(file, verify, mode); - manifest = jarFile.getManifest(); + public static JarMap open(String file, boolean verify) throws IOException { + return new FileMap(new File(file), verify, ZipFile.OPEN_READ); } - public JarMap(String name) throws IOException { - this(new File(name)); - } - - public JarMap(String name, boolean verify) throws IOException { - this(new File(name), verify); - } - - public JarMap(InputStream is) throws IOException { - this(is, true); - } - - public JarMap(InputStream is, boolean verify) throws IOException { - jis = new JarInputStream(is, verify); - bufMap = new LinkedHashMap<>(); - JarEntry entry; - while ((entry = jis.getNextJarEntry()) != null) { - bufMap.put(entry.getName(), new JarMapEntry(entry, jis)); - } - manifest = jis.getManifest(); + public static JarMap open(InputStream is, boolean verify) throws IOException { + return new StreamMap(is, verify); } public File getFile() { - return jarFile == null ? null : new File(jarFile.getName()); + return null; } - public Manifest getManifest() { - return manifest; - } + public abstract Manifest getManifest() throws IOException; public InputStream getInputStream(ZipEntry ze) throws IOException { - if (bufMap != null) { - JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); - if (e != null) - return e.data.getInputStream(); - } - return jarFile.getInputStream(ze); + JarMapEntry e = getMapEntry(ze.getName()); + return e != null ? e.data.getInputStream() : null; } public OutputStream getOutputStream(ZipEntry ze) { - manifest = null; /* Invalidate the manifest */ - if (bufMap == null) - bufMap = new LinkedHashMap<>(); + if (entryMap == null) + entryMap = new LinkedHashMap<>(); JarMapEntry e = new JarMapEntry(ze.getName()); - bufMap.put(ze.getName(), e); + entryMap.put(ze.getName(), e); return e.data; } public byte[] getRawData(ZipEntry ze) throws IOException { - if (bufMap != null) { - JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); - if (e != null) - return e.data.toByteArray(); - } - ByteArrayStream bytes = new ByteArrayStream(); - bytes.readFrom(jarFile.getInputStream(ze)); - return bytes.toByteArray(); + JarMapEntry e = getMapEntry(ze.getName()); + return e != null ? e.data.toByteArray() : null; } - public Enumeration entries() { - return jarFile == null ? Collections.enumeration(bufMap.values()) : jarFile.entries(); - } + public abstract Enumeration entries(); - public ZipEntry getEntry(String name) { + public final ZipEntry getEntry(String name) { return getJarEntry(name); } public JarEntry getJarEntry(String name) { - JarEntry e = jarFile == null ? bufMap.get(name) : jarFile.getJarEntry(name); - if (e == null && bufMap != null) - return bufMap.get(name); + return getMapEntry(name); + } + + JarMapEntry getMapEntry(String name) { + JarMapEntry e = null; + if (entryMap != null) + e = (JarMapEntry) entryMap.get(name); return e; } - @Override - public void close() throws IOException { - if (jarFile != null) + private static class FileMap extends JarMap { + + private JarFile jarFile; + + FileMap(File file, boolean verify, int mode) throws IOException { + jarFile = new JarFile(file, verify, mode); + } + + @Override + public File getFile() { + return new File(jarFile.getName()); + } + + @Override + public Manifest getManifest() throws IOException { + return jarFile.getManifest(); + } + + @Override + public InputStream getInputStream(ZipEntry ze) throws IOException { + InputStream is = super.getInputStream(ze); + return is != null ? is : jarFile.getInputStream(ze); + } + + @Override + public byte[] getRawData(ZipEntry ze) throws IOException { + byte[] b = super.getRawData(ze); + if (b != null) + return b; + ByteArrayStream bytes = new ByteArrayStream(); + bytes.readFrom(jarFile.getInputStream(ze)); + return bytes.toByteArray(); + } + + @Override + public Enumeration entries() { + return jarFile.entries(); + } + + @Override + public JarEntry getJarEntry(String name) { + JarEntry e = getMapEntry(name); + return e != null ? e : jarFile.getJarEntry(name); + } + + @Override + public void close() throws IOException { jarFile.close(); - else + } + } + + private static class StreamMap extends JarMap { + + private JarInputStream jis; + + StreamMap(InputStream is, boolean verify) throws IOException { + jis = new JarInputStream(is, verify); + entryMap = new LinkedHashMap<>(); + JarEntry entry; + while ((entry = jis.getNextJarEntry()) != null) { + entryMap.put(entry.getName(), new JarMapEntry(entry, jis)); + } + } + + @Override + public Manifest getManifest() { + return jis.getManifest(); + } + + @Override + public Enumeration entries() { + return Collections.enumeration(entryMap.values()); + } + + @Override + public void close() throws IOException { jis.close(); + } } private static class JarMapEntry extends JarEntry { + ByteArrayStream data; JarMapEntry(JarEntry je, InputStream is) { diff --git a/signing/src/main/java/com/topjohnwu/signing/SignAPK.java b/signing/src/main/java/com/topjohnwu/signing/SignAPK.java index 1c84f33de..f84779666 100644 --- a/signing/src/main/java/com/topjohnwu/signing/SignAPK.java +++ b/signing/src/main/java/com/topjohnwu/signing/SignAPK.java @@ -72,7 +72,7 @@ public class SignAPK { ZipAdjust.adjust(temp1, temp2); - try (JarMap map = new JarMap(temp2, false)) { + try (JarMap map = JarMap.open(temp2, false)) { sign(cert, key, map, output, true); } } finally { diff --git a/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java b/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java index 9e3fce6ed..53b6b9f1f 100644 --- a/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java +++ b/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java @@ -65,7 +65,7 @@ public class ZipSigner { Security.insertProviderAt(new BouncyCastleProvider(), 1); - try (JarMap in = new JarMap(args[args.length - 2], false); + try (JarMap in = JarMap.open(args[args.length - 2], false); OutputStream out = new FileOutputStream(args[args.length - 1])) { if (args.length == 2) { sign(in, out);