Commit 9f9de8c4 authored by topjohnwu's avatar topjohnwu

Obfuscate WorkManager components

Remove unused components and hack the context sent into WorkManager
parent a910c8cc
...@@ -97,6 +97,12 @@ ...@@ -97,6 +97,12 @@
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
android:value="12451000" /> android:value="12451000" />
<!-- Initialize WorkManager on-demand -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application> </application>
</manifest> </manifest>
...@@ -6,8 +6,8 @@ import androidx.annotation.NonNull; ...@@ -6,8 +6,8 @@ import androidx.annotation.NonNull;
import androidx.work.Worker; import androidx.work.Worker;
import androidx.work.WorkerParameters; import androidx.work.WorkerParameters;
import com.topjohnwu.magisk.HacksKt;
import com.topjohnwu.magisk.base.DelegateWorker; import com.topjohnwu.magisk.base.DelegateWorker;
import com.topjohnwu.magisk.utils.ResourceMgrKt;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
...@@ -19,7 +19,7 @@ public abstract class w<T extends DelegateWorker> extends Worker { ...@@ -19,7 +19,7 @@ public abstract class w<T extends DelegateWorker> extends Worker {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
w(@NonNull Context context, @NonNull WorkerParameters workerParams) { w(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(ResourceMgrKt.wrap(context, false), workerParams); super(HacksKt.wrap(context, false), workerParams);
try { try {
base = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()) base = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]).newInstance(); .getActualTypeArguments()[0]).newInstance();
......
...@@ -6,6 +6,7 @@ import android.content.res.Configuration ...@@ -6,6 +6,7 @@ import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import androidx.room.Room import androidx.room.Room
import androidx.work.WorkManager
import androidx.work.impl.WorkDatabase import androidx.work.impl.WorkDatabase
import androidx.work.impl.WorkDatabase_Impl import androidx.work.impl.WorkDatabase_Impl
import com.topjohnwu.magisk.data.database.RepoDatabase import com.topjohnwu.magisk.data.database.RepoDatabase
...@@ -14,10 +15,7 @@ import com.topjohnwu.magisk.di.ActivityTracker ...@@ -14,10 +15,7 @@ import com.topjohnwu.magisk.di.ActivityTracker
import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.extensions.unwrap
import com.topjohnwu.magisk.utils.ResourceMgr
import com.topjohnwu.magisk.utils.RootInit import com.topjohnwu.magisk.utils.RootInit
import com.topjohnwu.magisk.utils.isRunningAsStub
import com.topjohnwu.magisk.utils.wrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
...@@ -72,6 +70,7 @@ open class App() : Application() { ...@@ -72,6 +70,7 @@ open class App() : Application() {
} }
ResourceMgr.reload() ResourceMgr.reload()
app.registerActivityLifecycleCallbacks(get<ActivityTracker>()) app.registerActivityLifecycleCallbacks(get<ActivityTracker>())
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
} }
// This is required as some platforms expect ContextImpl // This is required as some platforms expect ContextImpl
......
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<String, String>? = 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)
}
@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<JobInfo> {
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<String, String>? = null
operator fun get(c: Class<*>) = classMap.getOrElse(c) { throw IllegalArgumentException() }
}
...@@ -21,7 +21,7 @@ import com.topjohnwu.magisk.extensions.set ...@@ -21,7 +21,7 @@ import com.topjohnwu.magisk.extensions.set
import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.events.EventHandler
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.wrap
import kotlin.random.Random import kotlin.random.Random
typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit
......
...@@ -4,7 +4,7 @@ import android.content.BroadcastReceiver ...@@ -4,7 +4,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.wrap
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
abstract class BaseReceiver : BroadcastReceiver(), KoinComponent { abstract class BaseReceiver : BroadcastReceiver(), KoinComponent {
......
...@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.base ...@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.base
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.wrap
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
abstract class BaseService : Service(), KoinComponent { abstract class BaseService : Service(), KoinComponent {
......
...@@ -20,7 +20,7 @@ import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.* ...@@ -20,7 +20,7 @@ import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.utils.APKInstall import com.topjohnwu.magisk.utils.APKInstall
import com.topjohnwu.magisk.utils.DynAPK import com.topjohnwu.magisk.utils.DynAPK
import com.topjohnwu.magisk.utils.isRunningAsStub import com.topjohnwu.magisk.isRunningAsStub
import org.koin.core.get import org.koin.core.get
import java.io.File import java.io.File
import kotlin.random.Random.Default.nextInt import kotlin.random.Random.Default.nextInt
......
...@@ -12,7 +12,7 @@ import com.topjohnwu.magisk.extensions.writeTo ...@@ -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.model.entity.internal.DownloadSubject.* import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import com.topjohnwu.magisk.utils.ProgressInputStream 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.magisk.view.Notifications
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import io.reactivex.Completable import io.reactivex.Completable
......
...@@ -7,7 +7,7 @@ import android.text.TextUtils ...@@ -7,7 +7,7 @@ import android.text.TextUtils
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.utils.Utils 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.Notifications
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
......
...@@ -13,10 +13,7 @@ import androidx.preference.ListPreference ...@@ -13,10 +13,7 @@ import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BasePreferenceFragment import com.topjohnwu.magisk.base.BasePreferenceFragment
import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.data.database.RepoDao
import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding
......
@file:Suppress("DEPRECATION")
package com.topjohnwu.magisk.utils package com.topjohnwu.magisk.utils
import android.annotation.SuppressLint 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.Configuration
import android.content.res.Resources import android.content.res.Resources
import androidx.annotation.StringRes
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.ResourceMgr
import com.topjohnwu.magisk.extensions.langTagToLocale import com.topjohnwu.magisk.extensions.langTagToLocale
import io.reactivex.Single import io.reactivex.Single
import java.util.* import java.util.*
import kotlin.Comparator
var isRunningAsStub = false
var currentLocale: Locale = Locale.getDefault() var currentLocale: Locale = Locale.getDefault()
private set
@SuppressLint("ConstantLocale") @SuppressLint("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault() val defaultLocale: Locale = Locale.getDefault()
@Suppress("DEPRECATION")
val availableLocales = Single.fromCallable { val availableLocales = Single.fromCallable {
val compareId = R.string.app_changelog val compareId = R.string.app_changelog
mutableListOf<Locale>().apply { mutableListOf<Locale>().apply {
...@@ -54,73 +47,3 @@ val availableLocales = Single.fromCallable { ...@@ -54,73 +47,3 @@ val availableLocales = Single.fromCallable {
.compareTo(b.getDisplayName(b).toLowerCase(b)) .compareTo(b.getDisplayName(b).toLowerCase(b))
}) })
}.cache()!! }.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() }
}
}
...@@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Const ...@@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.rawResource import com.topjohnwu.magisk.extensions.rawResource
import com.topjohnwu.magisk.wrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
......
...@@ -75,36 +75,14 @@ ...@@ -75,36 +75,14 @@
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
android:value="12451000" /> android:value="12451000" />
<!-- WorkManager --> <!-- WorkManager SystemJobService -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:directBootAware="false"
android:exported="false"
android:multiprocess="true"
tools:targetApi="n" />
<service <service
android:name="androidx.work.impl.background.systemjob.SystemJobService" android:name="a.j"
android:directBootAware="false" android:directBootAware="false"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" android:permission="android.permission.BIND_JOB_SERVICE" />
tools:targetApi="n" />
<receiver
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:directBootAware="false"
android:enabled="true"
android:exported="false"
tools:targetApi="n" />
<!-- Room -->
<service
android:name="androidx.room.MultiInstanceInvalidationService"
android:exported="false" />
</application> </application>
......
...@@ -4,7 +4,7 @@ import java.util.HashMap; ...@@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
class ComponentMap { class ComponentMap {
private static Map<String, String> map = new HashMap<>(6); private static Map<String, String> map = new HashMap<>();
// This mapping will be sent into the guest app // This mapping will be sent into the guest app
static Map<String, String> inverseMap; static Map<String, String> inverseMap;
...@@ -16,7 +16,7 @@ class ComponentMap { ...@@ -16,7 +16,7 @@ class ComponentMap {
map.put("a.g", "a.m"); map.put("a.g", "a.m");
map.put(a.w.class.getName(), "a.h"); map.put(a.w.class.getName(), "a.h");
map.put("a.v", "a.j"); 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()); inverseMap = new HashMap<>(map.size());
for (Map.Entry<String, String> e : map.entrySet()) { for (Map.Entry<String, String> e : map.entrySet()) {
......
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