From 9f9de8c43bb8669031f8a253d34fc769a69486d7 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 16 Oct 2019 17:03:55 -0400 Subject: [PATCH] Obfuscate WorkManager components Remove unused components and hack the context sent into WorkManager --- app/src/main/AndroidManifest.xml | 6 + app/src/main/java/a/w.java | 4 +- app/src/main/java/com/topjohnwu/magisk/App.kt | 5 +- .../java/com/topjohnwu/magisk/ClassMap.kt | 44 ---- .../main/java/com/topjohnwu/magisk/Hacks.kt | 223 ++++++++++++++++++ .../com/topjohnwu/magisk/base/BaseActivity.kt | 2 +- .../com/topjohnwu/magisk/base/BaseReceiver.kt | 2 +- .../com/topjohnwu/magisk/base/BaseService.kt | 2 +- .../magisk/model/download/DownloadService.kt | 2 +- .../model/download/RemoteFileService.kt | 2 +- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 2 +- .../magisk/ui/settings/SettingsFragment.kt | 5 +- .../com/topjohnwu/magisk/utils/Locales.kt | 49 ++++ .../com/topjohnwu/magisk/utils/ResourceMgr.kt | 126 ---------- .../com/topjohnwu/magisk/utils/RootInit.kt | 1 + stub/src/main/AndroidManifest.xml | 28 +-- .../com/topjohnwu/magisk/ComponentMap.java | 4 +- 17 files changed, 295 insertions(+), 212 deletions(-) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/ClassMap.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/Hacks.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4d76d7753..b4d2737fa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -97,6 +97,12 @@ android:name="com.google.android.gms.version" android:value="12451000" /> + + + diff --git a/app/src/main/java/a/w.java b/app/src/main/java/a/w.java index 6fa3e50f6..f1307a025 100644 --- a/app/src/main/java/a/w.java +++ b/app/src/main/java/a/w.java @@ -6,8 +6,8 @@ import androidx.annotation.NonNull; import androidx.work.Worker; import androidx.work.WorkerParameters; +import com.topjohnwu.magisk.HacksKt; import com.topjohnwu.magisk.base.DelegateWorker; -import com.topjohnwu.magisk.utils.ResourceMgrKt; import java.lang.reflect.ParameterizedType; @@ -19,7 +19,7 @@ public abstract class w extends Worker { @SuppressWarnings("unchecked") w(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(ResourceMgrKt.wrap(context, false), workerParams); + super(HacksKt.wrap(context, false), workerParams); try { base = ((Class) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]).newInstance(); diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 8c448a784..b70f6d753 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -6,6 +6,7 @@ import android.content.res.Configuration import androidx.appcompat.app.AppCompatDelegate import androidx.multidex.MultiDex import androidx.room.Room +import androidx.work.WorkManager import androidx.work.impl.WorkDatabase import androidx.work.impl.WorkDatabase_Impl import com.topjohnwu.magisk.data.database.RepoDatabase @@ -14,10 +15,7 @@ import com.topjohnwu.magisk.di.ActivityTracker import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.unwrap -import com.topjohnwu.magisk.utils.ResourceMgr import com.topjohnwu.magisk.utils.RootInit -import com.topjohnwu.magisk.utils.isRunningAsStub -import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -72,6 +70,7 @@ open class App() : Application() { } ResourceMgr.reload() app.registerActivityLifecycleCallbacks(get()) + WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build()) } // This is required as some platforms expect ContextImpl diff --git a/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt b/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt deleted file mode 100644 index 79217a2d2..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.topjohnwu.magisk - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import com.topjohnwu.magisk.model.download.DownloadService -import com.topjohnwu.magisk.model.receiver.GeneralReceiver -import com.topjohnwu.magisk.model.update.UpdateCheckService -import com.topjohnwu.magisk.ui.MainActivity -import com.topjohnwu.magisk.ui.SplashActivity -import com.topjohnwu.magisk.ui.flash.FlashActivity -import com.topjohnwu.magisk.ui.surequest.SuRequestActivity - -object ClassMap { - - private val classMap = mapOf( - App::class.java to a.e::class.java, - MainActivity::class.java to a.b::class.java, - SplashActivity::class.java to a.c::class.java, - FlashActivity::class.java to a.f::class.java, - UpdateCheckService::class.java to a.g::class.java, - GeneralReceiver::class.java to a.h::class.java, - DownloadService::class.java to a.j::class.java, - SuRequestActivity::class.java to a.m::class.java - ) - - // This will be set if running as guest app - var componentMap: Map? = null - - operator fun get(c: Class<*>) = classMap.getOrElse(c) { throw IllegalArgumentException() } -} - -fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName { - val name = ClassMap[this].name - return ComponentName(pkg, ClassMap.componentMap?.get(name) ?: name) -} - -fun Context.intent(c: Class<*>): Intent { - val cls = ClassMap[c] - return ClassMap.componentMap?.let { - val className = it.getOrElse(cls.name) { cls.name } - Intent().setComponent(ComponentName(this, className)) - } ?: Intent(this, cls) -} diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt new file mode 100644 index 000000000..4f12a5b56 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -0,0 +1,223 @@ +@file:Suppress("DEPRECATION") + +package com.topjohnwu.magisk + +import android.annotation.SuppressLint +import android.app.job.JobInfo +import android.app.job.JobScheduler +import android.app.job.JobWorkItem +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 +import android.content.res.Resources +import androidx.annotation.RequiresApi +import androidx.annotation.StringRes +import com.topjohnwu.magisk.extensions.langTagToLocale +import com.topjohnwu.magisk.model.download.DownloadService +import com.topjohnwu.magisk.model.receiver.GeneralReceiver +import com.topjohnwu.magisk.model.update.UpdateCheckService +import com.topjohnwu.magisk.ui.MainActivity +import com.topjohnwu.magisk.ui.SplashActivity +import com.topjohnwu.magisk.ui.flash.FlashActivity +import com.topjohnwu.magisk.ui.surequest.SuRequestActivity +import com.topjohnwu.magisk.utils.DynAPK +import com.topjohnwu.magisk.utils.currentLocale +import com.topjohnwu.magisk.utils.defaultLocale +import java.util.* + +var isRunningAsStub = false + +private val addAssetPath by lazy { + AssetManager::class.java.getMethod("addAssetPath", String::class.java) +} + +fun AssetManager.addAssetPath(path: String) { + addAssetPath.invoke(this, path) +} + +fun Context.wrap(global: Boolean = true): Context + = if (!global) ResContext(this) else GlobalResContext(this) + +fun Context.wrapJob(): Context = object : GlobalResContext(this) { + + @SuppressLint("NewApi") + override fun getSystemService(name: String): Any? { + return if (!isRunningAsStub) super.getSystemService(name) else + when (name) { + Context.JOB_SCHEDULER_SERVICE -> + JobSchedulerWrapper(super.getSystemService(name) as JobScheduler) + else -> super.getSystemService(name) + } + } +} + +// Override locale and inject resources from dynamic APK +private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources { + config.setLocale(currentLocale) + updateConfiguration(config, displayMetrics) + if (isRunningAsStub) + assets.addAssetPath(ResourceMgr.resApk) + return this +} + +fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName { + val name = ClassMap[this].name + return ComponentName(pkg, ClassMap.componentMap?.get(name) + ?: name) +} + +fun Context.intent(c: Class<*>): Intent { + val cls = ClassMap[c] + return ClassMap.componentMap?.let { + val className = it.getOrElse(cls.name) { cls.name } + Intent().setComponent(ComponentName(this, className)) + } ?: Intent(this, cls) +} + +private open class GlobalResContext(base: Context) : ContextWrapper(base) { + open val mRes: Resources get() = ResourceMgr.resource + private val loader by lazy { javaClass.classLoader!! } + + override fun getApplicationContext(): Context { + return this + } + + override fun getResources(): Resources { + return mRes + } + + override fun getClassLoader(): ClassLoader { + return loader + } + + override fun createConfigurationContext(config: Configuration): Context { + return ResContext(super.createConfigurationContext(config)) + } +} + +private class ResContext(base: Context) : GlobalResContext(base) { + override val mRes by lazy { base.resources.patch() } +} + +object ResourceMgr { + + internal lateinit var resource: Resources + internal lateinit var resApk: String + + fun init(context: Context) { + resource = context.resources + if (isRunningAsStub) + resApk = DynAPK.current(context).path + } + + fun reload(config: Configuration = Configuration(resource.configuration)) { + val localeConfig = Config.locale + currentLocale = when { + localeConfig.isEmpty() -> defaultLocale + else -> localeConfig.langTagToLocale() + } + Locale.setDefault(currentLocale) + resource.patch(config) + } + + fun getString(locale: Locale, @StringRes id: Int): String { + val config = Configuration() + config.setLocale(locale) + return Resources(resource.assets, resource.displayMetrics, config).getString(id) + } + +} + +@RequiresApi(api = 28) +private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() { + + override fun schedule(job: JobInfo): Int { + return base.schedule(job.patch()) + } + + override fun enqueue(job: JobInfo, work: JobWorkItem): Int { + return base.enqueue(job.patch(), work) + } + + override fun cancel(jobId: Int) { + base.cancel(jobId) + } + + override fun cancelAll() { + base.cancelAll() + } + + override fun getAllPendingJobs(): List { + return base.allPendingJobs + } + + override fun getPendingJob(jobId: Int): JobInfo? { + return base.getPendingJob(jobId) + } + + fun JobInfo.patch(): JobInfo { + // We need to patch the component of JobInfo to access WorkManager SystemJobService + + val name = service.className + val component = ComponentName(service.packageName, ClassMap.componentMap?.get(name) + ?: name) + + // Clone the JobInfo except component + val builder = JobInfo.Builder(id, component) + .setExtras(extras) + .setTransientExtras(transientExtras) + .setClipData(clipData, clipGrantFlags) + .setRequiredNetwork(requiredNetwork) + .setEstimatedNetworkBytes(estimatedNetworkDownloadBytes, estimatedNetworkUploadBytes) + .setRequiresCharging(isRequireCharging) + .setRequiresDeviceIdle(isRequireDeviceIdle) + .setRequiresBatteryNotLow(isRequireBatteryNotLow) + .setRequiresStorageNotLow(isRequireStorageNotLow) + .also { + triggerContentUris?.let { uris -> + for (uri in uris) + it.addTriggerContentUri(uri) + } + } + .setTriggerContentUpdateDelay(triggerContentUpdateDelay) + .setTriggerContentMaxDelay(triggerContentMaxDelay) + .setImportantWhileForeground(isImportantWhileForeground) + .setPrefetch(isPrefetch) + .setPersisted(isPersisted) + + if (isPeriodic) { + builder.setPeriodic(intervalMillis, flexMillis) + } else { + if (minLatencyMillis > 0) + builder.setMinimumLatency(minLatencyMillis) + if (maxExecutionDelayMillis > 0) + builder.setOverrideDeadline(maxExecutionDelayMillis) + } + if (!isRequireDeviceIdle) + builder.setBackoffCriteria(initialBackoffMillis, backoffPolicy) + + return builder.build() + } +} + +object ClassMap { + + private val classMap = mapOf( + App::class.java to a.e::class.java, + MainActivity::class.java to a.b::class.java, + SplashActivity::class.java to a.c::class.java, + FlashActivity::class.java to a.f::class.java, + UpdateCheckService::class.java to a.g::class.java, + GeneralReceiver::class.java to a.h::class.java, + DownloadService::class.java to a.j::class.java, + SuRequestActivity::class.java to a.m::class.java + ) + + // This will be set if running as guest app + var componentMap: Map? = null + + operator fun get(c: Class<*>) = classMap.getOrElse(c) { throw IllegalArgumentException() } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt index 0b8fed26f..0c4435dee 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt @@ -21,7 +21,7 @@ import com.topjohnwu.magisk.extensions.set import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.utils.currentLocale -import com.topjohnwu.magisk.utils.wrap +import com.topjohnwu.magisk.wrap import kotlin.random.Random typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BaseReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseReceiver.kt index 2e96a6b11..e3f07e620 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BaseReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseReceiver.kt @@ -4,7 +4,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.ContextWrapper import android.content.Intent -import com.topjohnwu.magisk.utils.wrap +import com.topjohnwu.magisk.wrap import org.koin.core.KoinComponent abstract class BaseReceiver : BroadcastReceiver(), KoinComponent { diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BaseService.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseService.kt index dc7801664..b7a65ccc2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BaseService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseService.kt @@ -2,7 +2,7 @@ package com.topjohnwu.magisk.base import android.app.Service import android.content.Context -import com.topjohnwu.magisk.utils.wrap +import com.topjohnwu.magisk.wrap import org.koin.core.KoinComponent abstract class BaseService : Service(), KoinComponent { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt index 70adc04e3..f22ba0af7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt @@ -20,7 +20,7 @@ import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.* import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.utils.APKInstall import com.topjohnwu.magisk.utils.DynAPK -import com.topjohnwu.magisk.utils.isRunningAsStub +import com.topjohnwu.magisk.isRunningAsStub import org.koin.core.get import java.io.File import kotlin.random.Random.Default.nextInt diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt index ceb35cc85..8e6dfa6be 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt @@ -12,7 +12,7 @@ import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.* import com.topjohnwu.magisk.utils.ProgressInputStream -import com.topjohnwu.magisk.utils.isRunningAsStub +import com.topjohnwu.magisk.isRunningAsStub import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.superuser.ShellUtils import io.reactivex.Completable diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 6a43ddb73..75d814ce2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -7,7 +7,7 @@ import android.text.TextUtils import androidx.appcompat.app.AlertDialog import com.topjohnwu.magisk.* import com.topjohnwu.magisk.utils.Utils -import com.topjohnwu.magisk.utils.wrap +import com.topjohnwu.magisk.wrap import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index a3457ddd0..c67ce091a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -13,10 +13,7 @@ import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreferenceCompat -import com.topjohnwu.magisk.BuildConfig -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.* import com.topjohnwu.magisk.base.BasePreferenceFragment import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt new file mode 100644 index 000000000..f5fe0c8ea --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt @@ -0,0 +1,49 @@ +package com.topjohnwu.magisk.utils + +import android.annotation.SuppressLint +import android.content.res.Configuration +import android.content.res.Resources +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.ResourceMgr +import com.topjohnwu.magisk.extensions.langTagToLocale +import io.reactivex.Single +import java.util.* +import kotlin.Comparator + +var currentLocale: Locale = Locale.getDefault() + +@SuppressLint("ConstantLocale") +val defaultLocale: Locale = Locale.getDefault() + +@Suppress("DEPRECATION") +val availableLocales = Single.fromCallable { + val compareId = R.string.app_changelog + mutableListOf().apply { + // Add default locale + add(Locale.ENGLISH) + + // Add some special locales + add(Locale.TAIWAN) + add(Locale("pt", "BR")) + + val config = Configuration() + val metrics = ResourceMgr.resource.displayMetrics + val res = Resources(ResourceMgr.resource.assets, metrics, config) + + // Other locales + val otherLocales = ResourceMgr.resource.assets.locales + .map { it.langTagToLocale() } + .distinctBy { + config.setLocale(it) + res.updateConfiguration(config, metrics) + res.getString(compareId) + } + + listOf("", "").toTypedArray() + + addAll(otherLocales) + }.sortedWith(Comparator { a, b -> + a.getDisplayName(a).toLowerCase(a) + .compareTo(b.getDisplayName(b).toLowerCase(b)) + }) +}.cache()!! diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt b/app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt deleted file mode 100644 index fb3d7d503..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt +++ /dev/null @@ -1,126 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.topjohnwu.magisk.utils - -import android.annotation.SuppressLint -import android.content.Context -import android.content.ContextWrapper -import android.content.res.AssetManager -import android.content.res.Configuration -import android.content.res.Resources -import androidx.annotation.StringRes -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.extensions.langTagToLocale -import io.reactivex.Single -import java.util.* - -var isRunningAsStub = false - -var currentLocale: Locale = Locale.getDefault() - private set - -@SuppressLint("ConstantLocale") -val defaultLocale: Locale = Locale.getDefault() - -val availableLocales = Single.fromCallable { - val compareId = R.string.app_changelog - mutableListOf().apply { - // Add default locale - add(Locale.ENGLISH) - - // Add some special locales - add(Locale.TAIWAN) - add(Locale("pt", "BR")) - - val config = Configuration() - val metrics = ResourceMgr.resource.displayMetrics - val res = Resources(ResourceMgr.resource.assets, metrics, config) - - // Other locales - val otherLocales = ResourceMgr.resource.assets.locales - .map { it.langTagToLocale() } - .distinctBy { - config.setLocale(it) - res.updateConfiguration(config, metrics) - res.getString(compareId) - } - - listOf("", "").toTypedArray() - - addAll(otherLocales) - }.sortedWith(Comparator { a, b -> - a.getDisplayName(a).toLowerCase(a) - .compareTo(b.getDisplayName(b).toLowerCase(b)) - }) -}.cache()!! - -private val addAssetPath by lazy { - AssetManager::class.java.getMethod("addAssetPath", String::class.java) -} - -fun AssetManager.addAssetPath(path: String) { - addAssetPath.invoke(this, path) -} - -fun Context.wrap(global: Boolean = true): Context - = if (!global) ResourceMgr.ResContext(this) else ResourceMgr.GlobalResContext(this) - -object ResourceMgr { - - lateinit var resource: Resources - private lateinit var resApk: String - - fun init(context: Context) { - resource = context.resources - if (isRunningAsStub) - resApk = DynAPK.current(context).path - } - - // Override locale and inject resources from dynamic APK - private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources { - config.setLocale(currentLocale) - updateConfiguration(config, displayMetrics) - if (isRunningAsStub) - assets.addAssetPath(resApk) - return this - } - - fun reload(config: Configuration = Configuration(resource.configuration)) { - val localeConfig = Config.locale - currentLocale = when { - localeConfig.isEmpty() -> defaultLocale - else -> localeConfig.langTagToLocale() - } - Locale.setDefault(currentLocale) - resource.patch(config) - } - - fun getString(locale: Locale, @StringRes id: Int): String { - val config = Configuration() - config.setLocale(locale) - return Resources(resource.assets, resource.displayMetrics, config).getString(id) - } - - open class GlobalResContext(base: Context) : ContextWrapper(base) { - open val mRes: Resources get() = resource - private val loader by lazy { javaClass.classLoader!! } - - override fun getResources(): Resources { - return mRes - } - - override fun getClassLoader(): ClassLoader { - return loader - } - - override fun createConfigurationContext(config: Configuration): Context { - return ResContext(super.createConfigurationContext(config)) - } - } - - class ResContext(base: Context) : GlobalResContext(base) { - override val mRes by lazy { base.resources.patch() } - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt index c4bbfde4e..481ce2d02 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt @@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.rawResource +import com.topjohnwu.magisk.wrap import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.io.SuFile diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml index e29f2888d..36428315f 100644 --- a/stub/src/main/AndroidManifest.xml +++ b/stub/src/main/AndroidManifest.xml @@ -75,36 +75,14 @@ android:name="com.google.android.gms.version" android:value="12451000" /> - - - + - - - - - - + android:permission="android.permission.BIND_JOB_SERVICE" /> diff --git a/stub/src/main/java/com/topjohnwu/magisk/ComponentMap.java b/stub/src/main/java/com/topjohnwu/magisk/ComponentMap.java index dfd03b253..bc067e09b 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/ComponentMap.java +++ b/stub/src/main/java/com/topjohnwu/magisk/ComponentMap.java @@ -4,7 +4,7 @@ import java.util.HashMap; import java.util.Map; class ComponentMap { - private static Map map = new HashMap<>(6); + private static Map map = new HashMap<>(); // This mapping will be sent into the guest app static Map inverseMap; @@ -16,7 +16,7 @@ class ComponentMap { map.put("a.g", "a.m"); map.put(a.w.class.getName(), "a.h"); map.put("a.v", "a.j"); - map.put("a.s", "androidx.work.impl.WorkManagerInitializer"); + map.put("a.j", "androidx.work.impl.background.systemjob.SystemJobService"); inverseMap = new HashMap<>(map.size()); for (Map.Entry e : map.entrySet()) {