Commit 9016e672 authored by topjohnwu's avatar topjohnwu

Fix stub app loading on older Android versions

parent a3381da7
...@@ -9,10 +9,8 @@ import dalvik.system.BaseDexClassLoader; ...@@ -9,10 +9,8 @@ import dalvik.system.BaseDexClassLoader;
public class DynamicClassLoader extends BaseDexClassLoader { public class DynamicClassLoader extends BaseDexClassLoader {
private static final ClassLoader base = Object.class.getClassLoader();
public DynamicClassLoader(File apk) { public DynamicClassLoader(File apk) {
this(apk, base); this(apk, getSystemClassLoader());
} }
public DynamicClassLoader(File apk, ClassLoader parent) { public DynamicClassLoader(File apk, ClassLoader parent) {
...@@ -29,7 +27,7 @@ public class DynamicClassLoader extends BaseDexClassLoader { ...@@ -29,7 +27,7 @@ public class DynamicClassLoader extends BaseDexClassLoader {
try { try {
// Then check boot classpath // Then check boot classpath
return base.loadClass(name); return getSystemClassLoader().loadClass(name);
} catch (ClassNotFoundException ignored) { } catch (ClassNotFoundException ignored) {
try { try {
// Next try current dex // Next try current dex
...@@ -47,7 +45,7 @@ public class DynamicClassLoader extends BaseDexClassLoader { ...@@ -47,7 +45,7 @@ public class DynamicClassLoader extends BaseDexClassLoader {
@Override @Override
public URL getResource(String name) { public URL getResource(String name) {
URL resource = base.getResource(name); URL resource = getSystemClassLoader().getResource(name);
if (resource != null) if (resource != null)
return resource; return resource;
resource = findResource(name); resource = findResource(name);
...@@ -59,7 +57,7 @@ public class DynamicClassLoader extends BaseDexClassLoader { ...@@ -59,7 +57,7 @@ public class DynamicClassLoader extends BaseDexClassLoader {
@Override @Override
public Enumeration<URL> getResources(String name) throws IOException { public Enumeration<URL> getResources(String name) throws IOException {
return new CompoundEnumeration<>(base.getResources(name), return new CompoundEnumeration<>(getSystemClassLoader().getResources(name),
findResources(name), getParent().getResources(name)); findResources(name), getParent().getResources(name));
} }
} }
...@@ -4,6 +4,7 @@ package com.topjohnwu.magisk.core ...@@ -4,6 +4,7 @@ package com.topjohnwu.magisk.core
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import android.content.res.AssetManager import android.content.res.AssetManager
import android.content.res.Configuration import android.content.res.Configuration
...@@ -13,16 +14,12 @@ import com.topjohnwu.magisk.R ...@@ -13,16 +14,12 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.utils.syncLocale import com.topjohnwu.magisk.core.utils.syncLocale
import com.topjohnwu.magisk.ktx.unwrap
lateinit var AppApkPath: String lateinit var AppApkPath: String
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path) fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
fun Context.patch(): Context {
resources.patch()
return this
}
fun Resources.patch(): Resources { fun Resources.patch(): Resources {
if (isRunningAsStub) if (isRunningAsStub)
addAssetPath(AppApkPath) addAssetPath(AppApkPath)
...@@ -30,6 +27,21 @@ fun Resources.patch(): Resources { ...@@ -30,6 +27,21 @@ fun Resources.patch(): Resources {
return this 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 { fun createNewResources(): Resources {
val asset = AssetManager::class.java.newInstance() val asset = AssetManager::class.java.newInstance()
val config = Configuration(AppContext.resources.configuration) val config = Configuration(AppContext.resources.configuration)
......
...@@ -5,7 +5,6 @@ import android.Manifest.permission.WRITE_EXTERNAL_STORAGE ...@@ -5,7 +5,6 @@ import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
...@@ -18,9 +17,9 @@ import androidx.annotation.WorkerThread ...@@ -18,9 +17,9 @@ import androidx.annotation.WorkerThread
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.isRunningAsStub 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.RequestInstall
import com.topjohnwu.magisk.core.utils.UninstallPackage import com.topjohnwu.magisk.core.utils.UninstallPackage
import com.topjohnwu.magisk.core.wrap
import com.topjohnwu.magisk.ktx.reflectField import com.topjohnwu.magisk.ktx.reflectField
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
...@@ -56,11 +55,7 @@ abstract class BaseActivity : AppCompatActivity() { ...@@ -56,11 +55,7 @@ abstract class BaseActivity : AppCompatActivity() {
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
super.attachBaseContext(base.patch()) super.attachBaseContext(base.wrap())
}
override fun createConfigurationContext(config: Configuration): Context {
return super.createConfigurationContext(config).patch()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
......
...@@ -12,12 +12,12 @@ import org.gradle.api.tasks.Sync ...@@ -12,12 +12,12 @@ import org.gradle.api.tasks.Sync
import org.gradle.kotlin.dsl.filter import org.gradle.kotlin.dsl.filter
import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.named
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions 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.*
import java.util.zip.Deflater import java.util.zip.*
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
private fun Project.androidBase(configure: Action<BaseExtension>) = private fun Project.androidBase(configure: Action<BaseExtension>) =
extensions.configure("android", configure) extensions.configure("android", configure)
...@@ -219,22 +219,23 @@ fun Project.setupStub() { ...@@ -219,22 +219,23 @@ fun Project.setupStub() {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk) commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
} }
val buffer = ByteArrayOutputStream(apk.length().toInt()) val buffer = ByteArrayOutputStream()
val newApk = ZipOutputStream(FileOutputStream(apk)) apkTmp.inputStream().use {
ZipFile(apkTmp).use { object : GZIPOutputStream(buffer) {
newApk.use { new -> init {
new.setLevel(Deflater.BEST_COMPRESSION) def.setLevel(Deflater.BEST_COMPRESSION)
new.putNextEntry(ZipEntry("AndroidManifest.xml")) }
it.getInputStream(it.getEntry("AndroidManifest.xml")).transferTo(new) }.use { o ->
new.closeEntry() it.transferTo(o)
new.finish() }
} }
ZipOutputStream(buffer).use { arsc -> ZipFile(apkTmp).use { o ->
arsc.setLevel(Deflater.BEST_COMPRESSION) ZipOutputStream(apk.outputStream()).use { n ->
arsc.putNextEntry(ZipEntry("resources.arsc")) n.setLevel(Deflater.BEST_COMPRESSION)
it.getInputStream(it.getEntry("resources.arsc")).transferTo(arsc) n.putNextEntry(ZipEntry("AndroidManifest.xml"))
arsc.closeEntry() o.getInputStream(o.getEntry("AndroidManifest.xml")).transferTo(n)
arsc.finish() n.closeEntry()
n.finish()
} }
} }
apkTmp.delete() apkTmp.delete()
......
...@@ -131,7 +131,7 @@ class StubClassLoader extends ClassLoader { ...@@ -131,7 +131,7 @@ class StubClassLoader extends ClassLoader {
class DelegateClassLoader extends ClassLoader { class DelegateClassLoader extends ClassLoader {
DelegateClassLoader() { DelegateClassLoader() {
super(null); super();
} }
@Override @Override
......
...@@ -34,6 +34,7 @@ import java.io.File; ...@@ -34,6 +34,7 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.CipherInputStream; import javax.crypto.CipherInputStream;
...@@ -95,6 +96,12 @@ public class DownloadActivity extends Activity { ...@@ -95,6 +96,12 @@ public class DownloadActivity extends Activity {
} }
} }
@Override
public void finish() {
super.finish();
Runtime.getRuntime().exit(0);
}
private void error(Throwable e) { private void error(Throwable e) {
Log.e(getClass().getSimpleName(), "", e); Log.e(getClass().getSimpleName(), "", e);
finish(); finish();
...@@ -154,7 +161,8 @@ public class DownloadActivity extends Activity { ...@@ -154,7 +161,8 @@ public class DownloadActivity extends Activity {
SecretKey key = new SecretKeySpec(Bytes.key(), "AES"); SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
IvParameterSpec iv = new IvParameterSpec(Bytes.iv()); IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
cipher.init(Cipher.DECRYPT_MODE, key, 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) { try (is; out) {
APKInstall.transfer(is, out); APKInstall.transfer(is, out);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment