mRoots = new HashMap<>();
@@ -263,7 +249,6 @@ public class FileProvider extends ContentProvider {
mAuthority = authority;
}
-
void addRoot(String name, File root) {
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("Name must not be empty");
diff --git a/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java b/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java
index 4bc113f0a..78196b573 100644
--- a/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java
+++ b/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.os.Bundle;
import java.util.ArrayList;
-import java.util.Arrays;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -37,27 +36,11 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
* Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance.
*/
public class ProcessPhoenix extends Activity {
- private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents";
+ private static final String KEY_RESTART_INTENT = "phoenix_restart_intent";
- /**
- * Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default}
- * activity as an intent.
- *
- * Behavior of the current process after invoking this method is undefined.
- */
- public static void triggerRebirth(Context context) {
- triggerRebirth(context, getRestartIntent(context));
- }
-
- /**
- * Call to restart the application process using the specified intents.
- *
- * Behavior of the current process after invoking this method is undefined.
- */
- public static void triggerRebirth(Context context, Intent... nextIntents) {
- Intent intent = new Intent(context, a.r.class);
+ public static void triggerRebirth(Context context, Intent intent) {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context.
- intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents)));
+ intent.putExtra(KEY_RESTART_INTENT, getRestartIntent(context));
context.startActivity(intent);
if (context instanceof Activity) {
((Activity) context).finish();
@@ -73,17 +56,15 @@ public class ProcessPhoenix extends Activity {
return defaultIntent;
}
- throw new IllegalStateException("Unable to determine default activity for "
- + packageName
- + ". Does an activity specify the DEFAULT category in its intent filter?");
+ throw new IllegalStateException();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- ArrayList intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS);
- startActivities(intents.toArray(new Intent[0]));
+ Intent intent = getIntent().getParcelableExtra(KEY_RESTART_INTENT);
+ startActivity(intent);
finish();
Runtime.getRuntime().exit(0);
}
diff --git a/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java b/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java
new file mode 100644
index 000000000..c3ff5c447
--- /dev/null
+++ b/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java
@@ -0,0 +1,8 @@
+package com.topjohnwu.magisk;
+
+import android.content.Context;
+import android.os.Bundle;
+
+public interface ProviderCallHandler {
+ Bundle call(Context context, String method, String arg, Bundle extras);
+}
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/SignBoot.java b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java
index ce1494ff5..3936f8cf3 100644
--- a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java
+++ b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java
@@ -33,6 +33,12 @@ public class SignBoot {
private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632;
private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648;
+ // Arbitrary maximum header version value; when greater assume the field is dt/extra size
+ private static final int BOOT_IMAGE_HEADER_VERSION_MAXIMUM = 8;
+
+ // Maximum header size byte value to read (currently the bootimg minimum page size)
+ private static final int BOOT_IMAGE_HEADER_SIZE_MAXIMUM = 2048;
+
private static class PushBackRWStream extends FilterInputStream {
private OutputStream out;
private int pos = 0;
@@ -82,7 +88,7 @@ public class SignBoot {
InputStream cert, InputStream key) {
try {
PushBackRWStream in = new PushBackRWStream(imgIn, imgOut);
- byte[] hdr = new byte[1024];
+ byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
// First read the header
in.read(hdr);
int signableSize = getSignableImageSize(hdr);
@@ -113,22 +119,27 @@ public class SignBoot {
public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
try {
// Read the header for size
- byte[] hdr = new byte[1024];
- if (imgIn.read(hdr) != hdr.length)
+ byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
+ if (imgIn.read(hdr) != hdr.length) {
+ System.err.println("Unable to read image header");
return false;
+ }
int signableSize = getSignableImageSize(hdr);
// Read the rest of the image
byte[] rawImg = Arrays.copyOf(hdr, signableSize);
int remain = signableSize - hdr.length;
if (imgIn.read(rawImg, hdr.length, remain) != remain) {
- System.err.println("Invalid image: not signed");
+ System.err.println("Unable to read image");
return false;
}
// Read footer, which contains the signature
byte[] signature = new byte[4096];
- imgIn.read(signature);
+ if (imgIn.read(signature) == -1 || Arrays.equals(signature, new byte [signature.length])) {
+ System.err.println("Invalid image: not signed");
+ return false;
+ }
BootSignature bootsig = new BootSignature(signature);
if (certIn != null) {
@@ -141,7 +152,8 @@ public class SignBoot {
System.err.println("Signature is INVALID");
}
} catch (Exception e) {
- System.err.println("Invalid image: not signed");
+ e.printStackTrace();
+ return false;
}
return false;
}
@@ -165,8 +177,8 @@ public class SignBoot {
+ ((kernelSize + pageSize - 1) / pageSize) * pageSize
+ ((ramdskSize + pageSize - 1) / pageSize) * pageSize
+ ((secondSize + pageSize - 1) / pageSize) * pageSize;
- int headerVersion = image.getInt(); // boot image header version or extra size
- if (headerVersion > 0 && headerVersion < 4) {
+ int headerVersion = image.getInt(); // boot image header version or dt/extra size
+ if (headerVersion > 0 && headerVersion < BOOT_IMAGE_HEADER_VERSION_MAXIMUM) {
image.position(BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET);
int recoveryDtboLength = image.getInt();
length += ((recoveryDtboLength + pageSize - 1) / pageSize) * pageSize;
@@ -183,7 +195,7 @@ public class SignBoot {
"Invalid image header: invalid header length");
}
} else {
- // headerVersion is 0 or actually extra size in this case
+ // headerVersion is 0 or actually dt/extra size in this case
length += ((headerVersion + pageSize - 1) / pageSize) * pageSize;
}
length = ((length + pageSize - 1) / pageSize) * pageSize;
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);
diff --git a/stub/build.gradle b/stub/build.gradle
index c43fcc5a8..932b547fc 100644
--- a/stub/build.gradle
+++ b/stub/build.gradle
@@ -4,7 +4,7 @@ android {
defaultConfig {
applicationId 'com.topjohnwu.magisk'
versionCode 1
- versionName "1.0"
+ versionName props['appVersion']
buildConfigField 'String', 'DEV_CHANNEL', props['DEV_CHANNEL'] ?: 'null'
}
diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml
index 729d39a19..8222e9ca7 100644
--- a/stub/src/main/AndroidManifest.xml
+++ b/stub/src/main/AndroidManifest.xml
@@ -1,32 +1,25 @@
-
-