diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.java b/app/shared/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.java index 71b61f4756807..42b6ba97f9fac 100644 --- a/app/shared/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.java +++ b/app/shared/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.java @@ -9,10 +9,8 @@ public class DynamicClassLoader extends BaseDexClassLoader { - private static final ClassLoader base = Object.class.getClassLoader(); - public DynamicClassLoader(File apk) { - this(apk, base); + this(apk, getSystemClassLoader()); } public DynamicClassLoader(File apk, ClassLoader parent) { @@ -29,7 +27,7 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE try { // Then check boot classpath - return base.loadClass(name); + return getSystemClassLoader().loadClass(name); } catch (ClassNotFoundException ignored) { try { // Next try current dex @@ -47,7 +45,7 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE @Override public URL getResource(String name) { - URL resource = base.getResource(name); + URL resource = getSystemClassLoader().getResource(name); if (resource != null) return resource; resource = findResource(name); @@ -59,7 +57,7 @@ public URL getResource(String name) { @Override public Enumeration getResources(String name) throws IOException { - return new CompoundEnumeration<>(base.getResources(name), + return new CompoundEnumeration<>(getSystemClassLoader().getResources(name), findResources(name), getParent().getResources(name)); } } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt index 279be90ce6248..bc5a67eadf249 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt @@ -4,6 +4,7 @@ package com.topjohnwu.magisk.core import android.content.ComponentName import android.content.Context +import android.content.ContextWrapper import android.content.Intent import android.content.res.AssetManager import android.content.res.Configuration @@ -13,16 +14,12 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.utils.syncLocale +import com.topjohnwu.magisk.ktx.unwrap lateinit var AppApkPath: String fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path) -fun Context.patch(): Context { - resources.patch() - return this -} - fun Resources.patch(): Resources { if (isRunningAsStub) addAssetPath(AppApkPath) @@ -30,6 +27,21 @@ fun Resources.patch(): Resources { return this } +fun Context.patch(): Context { + unwrap().resources.patch() + return this +} + +// Wrapping is only necessary for ContextThemeWrapper to support configuration overrides +fun Context.wrap(): Context { + patch() + return object : ContextWrapper(this) { + override fun createConfigurationContext(config: Configuration): Context { + return super.createConfigurationContext(config).wrap() + } + } +} + fun createNewResources(): Resources { val asset = AssetManager::class.java.newInstance() val config = Configuration(AppContext.resources.configuration) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt index a389036335447..aa07052beab26 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt @@ -5,7 +5,6 @@ import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent -import android.content.res.Configuration import android.net.Uri import android.os.Build import android.os.Bundle @@ -18,9 +17,9 @@ import androidx.annotation.WorkerThread import androidx.appcompat.app.AppCompatActivity import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.isRunningAsStub -import com.topjohnwu.magisk.core.patch import com.topjohnwu.magisk.core.utils.RequestInstall import com.topjohnwu.magisk.core.utils.UninstallPackage +import com.topjohnwu.magisk.core.wrap import com.topjohnwu.magisk.ktx.reflectField import com.topjohnwu.magisk.utils.Utils import java.util.concurrent.CountDownLatch @@ -56,11 +55,7 @@ abstract class BaseActivity : AppCompatActivity() { } override fun attachBaseContext(base: Context) { - super.attachBaseContext(base.patch()) - } - - override fun createConfigurationContext(config: Configuration): Context { - return super.createConfigurationContext(config).patch() + super.attachBaseContext(base.wrap()) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/buildSrc/src/main/java/Setup.kt b/buildSrc/src/main/java/Setup.kt index 567ce4c36f8bb..a4ade9b3e4c3e 100644 --- a/buildSrc/src/main/java/Setup.kt +++ b/buildSrc/src/main/java/Setup.kt @@ -12,12 +12,12 @@ import org.gradle.api.tasks.Sync import org.gradle.kotlin.dsl.filter import org.gradle.kotlin.dsl.named import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions -import java.io.* +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream import java.util.* -import java.util.zip.Deflater -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream +import java.util.zip.* private fun Project.androidBase(configure: Action) = extensions.configure("android", configure) @@ -219,22 +219,23 @@ fun Project.setupStub() { commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk) } - val buffer = ByteArrayOutputStream(apk.length().toInt()) - val newApk = ZipOutputStream(FileOutputStream(apk)) - ZipFile(apkTmp).use { - newApk.use { new -> - new.setLevel(Deflater.BEST_COMPRESSION) - new.putNextEntry(ZipEntry("AndroidManifest.xml")) - it.getInputStream(it.getEntry("AndroidManifest.xml")).transferTo(new) - new.closeEntry() - new.finish() + val buffer = ByteArrayOutputStream() + apkTmp.inputStream().use { + object : GZIPOutputStream(buffer) { + init { + def.setLevel(Deflater.BEST_COMPRESSION) + } + }.use { o -> + it.transferTo(o) } - ZipOutputStream(buffer).use { arsc -> - arsc.setLevel(Deflater.BEST_COMPRESSION) - arsc.putNextEntry(ZipEntry("resources.arsc")) - it.getInputStream(it.getEntry("resources.arsc")).transferTo(arsc) - arsc.closeEntry() - arsc.finish() + } + ZipFile(apkTmp).use { o -> + ZipOutputStream(apk.outputStream()).use { n -> + n.setLevel(Deflater.BEST_COMPRESSION) + n.putNextEntry(ZipEntry("AndroidManifest.xml")) + o.getInputStream(o.getEntry("AndroidManifest.xml")).transferTo(n) + n.closeEntry() + n.finish() } } apkTmp.delete() diff --git a/stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java b/stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java index 9e2c5da014759..ef34023e3d5aa 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java +++ b/stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java @@ -131,7 +131,7 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE class DelegateClassLoader extends ClassLoader { DelegateClassLoader() { - super(null); + super(); } @Override diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java index 15cb2e61c34b1..573dcba4cfa56 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java @@ -34,6 +34,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.zip.GZIPInputStream; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -95,6 +96,12 @@ protected void onCreate(Bundle savedInstanceState) { } } + @Override + public void finish() { + super.finish(); + Runtime.getRuntime().exit(0); + } + private void error(Throwable e) { Log.e(getClass().getSimpleName(), "", e); finish(); @@ -154,7 +161,8 @@ private void decryptResources(OutputStream out) throws Exception { SecretKey key = new SecretKeySpec(Bytes.key(), "AES"); IvParameterSpec iv = new IvParameterSpec(Bytes.iv()); cipher.init(Cipher.DECRYPT_MODE, key, iv); - var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher); + var is = new GZIPInputStream(new CipherInputStream( + new ByteArrayInputStream(Bytes.res()), cipher)); try (is; out) { APKInstall.transfer(is, out); }