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.os.Bundle
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.utils.DispatcherExecutor
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.core.utils.*
import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ktx.unwrap
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.UiThreadHandler
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()
.setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(ShellInit::class.java)
.setTimeout(2))
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
// Some context magic
// Get the actual ContextImpl
val app: Application
val impl: Context
if (base is Application) {
app = base
impl = base.baseContext
val base: Context
if (context is Application) {
app = context
base = context.baseContext
} else {
app = this
impl = base
base = context
}
val wrapped = impl.wrap()
super.attachBaseContext(wrapped)
super.attachBaseContext(base)
ServiceLocator.context = base
ServiceLocator.context = wrapped
AssetHack.init(impl)
refreshLocale()
AppApkPath = if (isRunningAsStub) {
DynAPK.current(base).path
} else {
base.packageResourcePath
}
base.resources.patch()
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) {
if (resources.configuration.diff(newConfig) != 0) {
resources.updateConfig(newConfig)
resources.setConfig(newConfig)
}
if (!isRunningAsStub)
super.onConfigurationChanged(newConfig)

View File

@ -13,15 +13,37 @@ import android.content.res.Resources
import android.util.DisplayMetrics
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.utils.refreshLocale
import com.topjohnwu.magisk.core.utils.updateConfig
import com.topjohnwu.magisk.core.utils.syncLocale
import com.topjohnwu.magisk.di.AppContext
fun AssetManager.addAssetPath(path: String) {
DynAPK.addAssetPath(this, path)
lateinit var AppApkPath: String
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 =
if (inject) ReInjectedContext(this) else InjectedContext(this)
fun Resources.patch(): Resources {
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) =
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))
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
// being removed when running "remove unused resources"
val shouldKeepResources = listOf(

View File

@ -36,7 +36,7 @@ abstract class BaseActivity : AppCompatActivity() {
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base.wrap(true))
super.attachBaseContext(base.wrap())
}
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 uninstall() = "run_uninstaller ${AssetHack.apk}".sh().isSuccess
protected fun uninstall() = "run_uninstaller $AppApkPath".sh().isSuccess
@WorkerThread
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.Resources
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
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.withContext
import java.util.*
@ -25,7 +26,7 @@ withContext(Dispatchers.Default) {
val compareId = R.string.app_changelog
// Create a completely new resource to prevent cross talk over active configs
val res = AssetHack.newResource()
val res = createNewResources()
val locales = ArrayList<String>().apply {
// Add default locale
@ -40,13 +41,13 @@ withContext(Dispatchers.Default) {
}.map {
Locale.forLanguageTag(it)
}.distinctBy {
res.updateLocale(it)
res.setLocale(it)
res.getString(compareId)
}.sortedWith { a, b ->
a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
}
res.updateLocale(defaultLocale)
res.setLocale(defaultLocale)
val defName = res.getString(R.string.system_default)
val names = ArrayList<String>(locales.size + 1)
@ -63,12 +64,14 @@ withContext(Dispatchers.Default) {
(names.toTypedArray() to values.toTypedArray()).also { cachedLocales = it }
}
fun Resources.updateConfig(config: Configuration = configuration) {
fun Resources.setConfig(config: Configuration) {
config.setLocale(currentLocale)
updateConfiguration(config, displayMetrics)
}
fun Resources.updateLocale(locale: Locale) {
fun Resources.syncLocale() = setConfig(configuration)
fun Resources.setLocale(locale: Locale) {
configuration.setLocale(locale)
updateConfiguration(configuration, displayMetrics)
}
@ -80,5 +83,5 @@ fun refreshLocale() {
else -> Locale.forLanguageTag(localeConfig)
}
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.TransitionManager
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.di.AppContext
import com.topjohnwu.magisk.utils.DynamicClassLoader
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell
@ -342,7 +342,7 @@ var TextView.precomputedText: CharSequence
}
fun Int.dpInPx(): Int {
val scale = AssetHack.resource.displayMetrics.density
val scale = AppContext.resources.displayMetrics.density
return (this * scale + 0.5).toInt()
}

View File

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