Simplify several hacks

This commit is contained in:
topjohnwu 2021-12-13 19:48:17 -08:00
parent b4120cddfb
commit 48e2d6a8da
7 changed files with 64 additions and 88 deletions

View File

@ -7,12 +7,8 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.utils.DispatcherExecutor import com.topjohnwu.magisk.core.utils.*
import com.topjohnwu.magisk.core.utils.RootRegistry
import com.topjohnwu.magisk.core.utils.ShellInit
import com.topjohnwu.magisk.core.utils.updateConfig
import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ktx.unwrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.superuser.ipc.RootService import com.topjohnwu.superuser.ipc.RootService
@ -40,28 +36,34 @@ open class App() : Application() {
} }
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(context: Context) {
Shell.setDefaultBuilder(Shell.Builder.create() Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_MOUNT_MASTER) .setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(ShellInit::class.java) .setInitializers(ShellInit::class.java)
.setTimeout(2)) .setTimeout(2))
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO) Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
// Some context magic // Get the actual ContextImpl
val app: Application val app: Application
val impl: Context val base: Context
if (base is Application) { if (context is Application) {
app = base app = context
impl = base.baseContext base = context.baseContext
} else { } else {
app = this app = this
impl = base base = context
} }
val wrapped = impl.wrap() super.attachBaseContext(base)
super.attachBaseContext(wrapped) ServiceLocator.context = base
ServiceLocator.context = wrapped refreshLocale()
AssetHack.init(impl) AppApkPath = if (isRunningAsStub) {
DynAPK.current(base).path
} else {
base.packageResourcePath
}
base.resources.patch()
app.registerActivityLifecycleCallbacks(ForegroundTracker) app.registerActivityLifecycleCallbacks(ForegroundTracker)
} }
@ -74,14 +76,9 @@ open class App() : Application() {
) )
} }
// This is required as some platforms expect ContextImpl
override fun getBaseContext(): Context {
return super.getBaseContext().unwrap()
}
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
if (resources.configuration.diff(newConfig) != 0) { if (resources.configuration.diff(newConfig) != 0) {
resources.updateConfig(newConfig) resources.setConfig(newConfig)
} }
if (!isRunningAsStub) if (!isRunningAsStub)
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)

View File

@ -13,15 +13,37 @@ import android.content.res.Resources
import android.util.DisplayMetrics import android.util.DisplayMetrics
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.utils.refreshLocale import com.topjohnwu.magisk.core.utils.syncLocale
import com.topjohnwu.magisk.core.utils.updateConfig import com.topjohnwu.magisk.di.AppContext
fun AssetManager.addAssetPath(path: String) { lateinit var AppApkPath: String
DynAPK.addAssetPath(this, path)
fun AssetManager.addAssetPath(path: String) = DynAPK.addAssetPath(this, path)
fun Context.wrap(): Context = if (this is PatchedContext) this else PatchedContext(this)
private class PatchedContext(base: Context) : ContextWrapper(base) {
init { base.resources.patch() }
override fun getClassLoader() = javaClass.classLoader!!
override fun createConfigurationContext(config: Configuration) =
super.createConfigurationContext(config).wrap()
} }
fun Context.wrap(inject: Boolean = false): Context = fun Resources.patch(): Resources {
if (inject) ReInjectedContext(this) else InjectedContext(this) syncLocale()
if (isRunningAsStub)
assets.addAssetPath(AppApkPath)
return this
}
fun createNewResources(): Resources {
val asset = AssetManager::class.java.newInstance()
asset.addAssetPath(AppApkPath)
val config = Configuration(AppContext.resources.configuration)
val metrics = DisplayMetrics()
metrics.setTo(AppContext.resources.displayMetrics)
return Resources(asset, metrics, config)
}
fun Class<*>.cmp(pkg: String) = fun Class<*>.cmp(pkg: String) =
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name) ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
@ -32,52 +54,6 @@ inline fun <reified T> Activity.redirect() = Intent(intent)
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName)) inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
private open class InjectedContext(base: Context) : ContextWrapper(base) {
open val res: Resources get() = AssetHack.resource
override fun getAssets(): AssetManager = res.assets
override fun getResources() = res
override fun getClassLoader() = javaClass.classLoader!!
override fun createConfigurationContext(config: Configuration): Context {
return super.createConfigurationContext(config).wrap(true)
}
}
private class ReInjectedContext(base: Context) : InjectedContext(base) {
override val res by lazy { base.resources.patch() }
private fun Resources.patch(): Resources {
updateConfig()
if (isRunningAsStub)
assets.addAssetPath(AssetHack.apk)
return this
}
}
object AssetHack {
lateinit var resource: Resources
lateinit var apk: String
fun init(context: Context) {
resource = context.resources
refreshLocale()
if (isRunningAsStub) {
apk = DynAPK.current(context).path
resource.assets.addAssetPath(apk)
} else {
apk = context.packageResourcePath
}
}
fun newResource(): Resources {
val asset = AssetManager::class.java.newInstance()
asset.addAssetPath(apk)
val config = Configuration(resource.configuration)
val metrics = DisplayMetrics()
metrics.setTo(resource.displayMetrics)
return Resources(asset, metrics, config)
}
}
// Keep a reference to these resources to prevent it from // Keep a reference to these resources to prevent it from
// being removed when running "remove unused resources" // being removed when running "remove unused resources"
val shouldKeepResources = listOf( val shouldKeepResources = listOf(

View File

@ -36,7 +36,7 @@ abstract class BaseActivity : AppCompatActivity() {
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
super.attachBaseContext(base.wrap(true)) super.attachBaseContext(base.wrap())
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -422,7 +422,7 @@ abstract class MagiskInstallImpl protected constructor(
protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess
protected fun uninstall() = "run_uninstaller ${AssetHack.apk}".sh().isSuccess protected fun uninstall() = "run_uninstaller $AppApkPath".sh().isSuccess
@WorkerThread @WorkerThread
protected abstract suspend fun operations(): Boolean protected abstract suspend fun operations(): Boolean

View File

@ -6,8 +6,9 @@ import android.annotation.SuppressLint
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.createNewResources
import com.topjohnwu.magisk.di.AppContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.* import java.util.*
@ -25,7 +26,7 @@ withContext(Dispatchers.Default) {
val compareId = R.string.app_changelog val compareId = R.string.app_changelog
// Create a completely new resource to prevent cross talk over active configs // Create a completely new resource to prevent cross talk over active configs
val res = AssetHack.newResource() val res = createNewResources()
val locales = ArrayList<String>().apply { val locales = ArrayList<String>().apply {
// Add default locale // Add default locale
@ -40,13 +41,13 @@ withContext(Dispatchers.Default) {
}.map { }.map {
Locale.forLanguageTag(it) Locale.forLanguageTag(it)
}.distinctBy { }.distinctBy {
res.updateLocale(it) res.setLocale(it)
res.getString(compareId) res.getString(compareId)
}.sortedWith { a, b -> }.sortedWith { a, b ->
a.getDisplayName(a).compareTo(b.getDisplayName(b), true) a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
} }
res.updateLocale(defaultLocale) res.setLocale(defaultLocale)
val defName = res.getString(R.string.system_default) val defName = res.getString(R.string.system_default)
val names = ArrayList<String>(locales.size + 1) val names = ArrayList<String>(locales.size + 1)
@ -63,12 +64,14 @@ withContext(Dispatchers.Default) {
(names.toTypedArray() to values.toTypedArray()).also { cachedLocales = it } (names.toTypedArray() to values.toTypedArray()).also { cachedLocales = it }
} }
fun Resources.updateConfig(config: Configuration = configuration) { fun Resources.setConfig(config: Configuration) {
config.setLocale(currentLocale) config.setLocale(currentLocale)
updateConfiguration(config, displayMetrics) updateConfiguration(config, displayMetrics)
} }
fun Resources.updateLocale(locale: Locale) { fun Resources.syncLocale() = setConfig(configuration)
fun Resources.setLocale(locale: Locale) {
configuration.setLocale(locale) configuration.setLocale(locale)
updateConfiguration(configuration, displayMetrics) updateConfiguration(configuration, displayMetrics)
} }
@ -80,5 +83,5 @@ fun refreshLocale() {
else -> Locale.forLanguageTag(localeConfig) else -> Locale.forLanguageTag(localeConfig)
} }
Locale.setDefault(currentLocale) Locale.setDefault(currentLocale)
AssetHack.resource.updateConfig() AppContext.resources.syncLocale()
} }

View File

@ -41,10 +41,10 @@ import androidx.lifecycle.lifecycleScope
import androidx.transition.AutoTransition import androidx.transition.AutoTransition
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.di.AppContext
import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.DynamicClassLoader
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -342,7 +342,7 @@ var TextView.precomputedText: CharSequence
} }
fun Int.dpInPx(): Int { fun Int.dpInPx(): Int {
val scale = AssetHack.resource.displayMetrics.density val scale = AppContext.resources.displayMetrics.density
return (this * scale + 0.5).toInt() return (this * scale + 0.5).toInt()
} }

View File

@ -15,7 +15,7 @@ import android.widget.TextView
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import com.caverock.androidsvg.SVG import com.caverock.androidsvg.SVG
import com.caverock.androidsvg.SVGParseException import com.caverock.androidsvg.SVGParseException
import com.topjohnwu.magisk.core.AssetHack import com.topjohnwu.magisk.di.AppContext
import com.topjohnwu.superuser.internal.WaitRunnable import com.topjohnwu.superuser.internal.WaitRunnable
import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.MarkwonSpansFactory import io.noties.markwon.MarkwonSpansFactory
@ -216,7 +216,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
return PictureDrawable(picture) return PictureDrawable(picture)
} }
val density: Float = AssetHack.resource.displayMetrics.density val density: Float = AppContext.resources.displayMetrics.density
val width = (w * density + .5f).toInt() val width = (w * density + .5f).toInt()
val height = (h * density + .5f).toInt() val height = (h * density + .5f).toInt()
@ -226,7 +226,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
canvas.scale(density, density) canvas.scale(density, density)
svg.renderToCanvas(canvas) svg.renderToCanvas(canvas)
return BitmapDrawable(AssetHack.resource, bitmap) return BitmapDrawable(AppContext.resources, bitmap)
} }
} }