Update hacks

This commit is contained in:
topjohnwu 2019-11-09 18:17:16 -05:00
parent a2ddf362d8
commit ad40e53349
3 changed files with 41 additions and 45 deletions

View File

@ -14,6 +14,7 @@ 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.RequiresApi import androidx.annotation.RequiresApi
import com.topjohnwu.magisk.extensions.forceGetDeclaredField
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.model.update.UpdateCheckService import com.topjohnwu.magisk.model.update.UpdateCheckService
@ -57,14 +58,13 @@ inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cm
private open class GlobalResContext(base: Context) : ContextWrapper(base) { private open class GlobalResContext(base: Context) : ContextWrapper(base) {
open val mRes: Resources get() = ResourceMgr.resource open val mRes: Resources get() = ResourceMgr.resource
private val loader by lazy { javaClass.classLoader!! }
override fun getResources(): Resources { override fun getResources(): Resources {
return mRes return mRes
} }
override fun getClassLoader(): ClassLoader { override fun getClassLoader(): ClassLoader {
return loader return javaClass.classLoader!!
} }
override fun createConfigurationContext(config: Configuration): Context { override fun createConfigurationContext(config: Configuration): Context {
@ -98,7 +98,7 @@ object ResourceMgr {
} }
} }
@RequiresApi(api = 28) @RequiresApi(28)
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() { private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
override fun schedule(job: JobInfo): Int { override fun schedule(job: JobInfo): Int {
@ -126,48 +126,14 @@ private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler
} }
private fun JobInfo.patch(): JobInfo { private fun JobInfo.patch(): JobInfo {
// We need to patch the component of JobInfo to access WorkManager SystemJobService // We need to swap out the service of JobInfo
val name = service.className val name = service.className
val component = ComponentName( val component = ComponentName(
service.packageName, service.packageName,
Info.stub!!.classToComponent[name] ?: name) Info.stub!!.classToComponent[name] ?: name)
// Clone the JobInfo except component javaClass.forceGetDeclaredField("service")?.set(this, component)
val builder = JobInfo.Builder(id, component) return this
.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()
} }
} }

View File

@ -26,6 +26,7 @@ import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toFile
import androidx.core.net.toUri import androidx.core.net.toUri
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.FileProvider import com.topjohnwu.magisk.FileProvider
@ -306,3 +307,5 @@ fun Context.unwrap() : Context {
} }
return context return context
} }
fun Uri.writeTo(file: File) = toFile().copyTo(file)

View File

@ -1,11 +1,11 @@
package com.topjohnwu.magisk.extensions package com.topjohnwu.magisk.extensions
import android.net.Uri
import android.os.Build import android.os.Build
import androidx.core.net.toFile
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.* import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
@ -19,8 +19,6 @@ fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
} }
} }
fun Uri.writeTo(file: File) = toFile().copyTo(file)
fun InputStream.writeTo(file: File) = fun InputStream.writeTo(file: File) =
withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) } withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) }
@ -101,3 +99,32 @@ fun Locale.toLangTag(): String {
return tag.toString() return tag.toString()
} }
} }
// Reflection hacks
private val loadClass = ClassLoader::class.java.getMethod("loadClass", String::class.java)
private val getDeclaredMethod = Class::class.java.getMethod("getDeclaredMethod",
String::class.java, arrayOf<Class<*>>()::class.java)
private val getDeclaredField = Class::class.java.getMethod("getDeclaredField", String::class.java)
fun ClassLoader.forceLoadClass(name: String) =
runCatching { loadClass.invoke(this, name) }.getOrNull() as Class<*>?
fun Class<*>.forceGetDeclaredMethod(name: String, vararg types: Class<*>) =
(runCatching { getDeclaredMethod.invoke(this, name, *types) }.getOrNull() as Method?)?.also {
it.isAccessible = true
}
fun Class<*>.forceGetDeclaredField(name: String) =
(runCatching { getDeclaredField.invoke(this, name) }.getOrNull() as Field?)?.also {
it.isAccessible = true
}
inline fun <reified T> T.forceGetClass(name: String) =
T::class.java.classLoader?.forceLoadClass(name)
fun Class<*>.forceGetField(name: String): Field? =
forceGetDeclaredField(name) ?: superclass?.forceGetField(name)
fun Class<*>.forceGetMethod(name: String, vararg types: Class<*>): Method? =
forceGetDeclaredMethod(name, *types) ?: superclass?.forceGetMethod(name, *types)