mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-12 13:17:42 +02:00
feat: LSPatchUpdater
This commit is contained in:
@ -20,6 +20,7 @@ import me.rhunk.snapenhance.core.data.SnapClassCache
|
||||
import me.rhunk.snapenhance.core.event.events.impl.SnapWidgetBroadcastReceiveEvent
|
||||
import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
|
||||
import me.rhunk.snapenhance.core.messaging.CoreMessagingBridge
|
||||
import me.rhunk.snapenhance.core.util.LSPatchUpdater
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
import kotlin.system.measureTimeMillis
|
||||
@ -48,22 +49,21 @@ class SnapEnhance {
|
||||
init {
|
||||
Application::class.java.hook("attach", HookStage.BEFORE) { param ->
|
||||
appContext.apply {
|
||||
androidContext = param.arg<Context>(0).also {
|
||||
classLoader = it.classLoader
|
||||
}
|
||||
androidContext = param.arg<Context>(0).also { classLoader = it.classLoader }
|
||||
bridgeClient = BridgeClient(appContext)
|
||||
bridgeClient.apply {
|
||||
connect(
|
||||
timeout = {
|
||||
crash("SnapEnhance bridge service is not responding. Please download stable version from https://github.com/rhunk/SnapEnhance/releases", it)
|
||||
onFailure = {
|
||||
crash("Snapchat can't connect to the SnapEnhance app. Please download stable version from https://github.com/rhunk/SnapEnhance/releases", it)
|
||||
}
|
||||
) { bridgeResult ->
|
||||
if (!bridgeResult) {
|
||||
logCritical("Cannot connect to bridge service")
|
||||
logCritical("Cannot connect to the SnapEnhance app")
|
||||
softRestartApp()
|
||||
return@connect
|
||||
}
|
||||
runCatching {
|
||||
LSPatchUpdater.onBridgeConnected(appContext, bridgeClient)
|
||||
measureTimeMillis {
|
||||
runBlocking {
|
||||
init(this)
|
||||
|
@ -44,43 +44,45 @@ class BridgeClient(
|
||||
private lateinit var future: CompletableFuture<Boolean>
|
||||
private lateinit var service: BridgeInterface
|
||||
|
||||
fun connect(timeout: (Throwable) -> Unit, onResult: (Boolean) -> Unit) {
|
||||
fun connect(onFailure: (Throwable) -> Unit, onResult: (Boolean) -> Unit) {
|
||||
this.future = CompletableFuture()
|
||||
|
||||
with(context.androidContext) {
|
||||
//ensure the remote process is running
|
||||
startActivity(Intent()
|
||||
.setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.ForceStartActivity")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
|
||||
)
|
||||
|
||||
val intent = Intent()
|
||||
.setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.BridgeService")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
bindService(
|
||||
intent,
|
||||
Context.BIND_AUTO_CREATE,
|
||||
Executors.newSingleThreadExecutor(),
|
||||
this@BridgeClient
|
||||
)
|
||||
} else {
|
||||
XposedHelpers.callMethod(
|
||||
this,
|
||||
"bindServiceAsUser",
|
||||
intent,
|
||||
this@BridgeClient,
|
||||
Context.BIND_AUTO_CREATE,
|
||||
Handler(HandlerThread("BridgeClient").apply {
|
||||
start()
|
||||
}.looper),
|
||||
android.os.Process.myUserHandle()
|
||||
runCatching {
|
||||
startActivity(Intent()
|
||||
.setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.ForceStartActivity")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
|
||||
)
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
onResult(future.get(15, TimeUnit.SECONDS))
|
||||
}.onFailure {
|
||||
timeout(it)
|
||||
|
||||
//ensure the remote process is running
|
||||
runCatching {
|
||||
val intent = Intent()
|
||||
.setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.BridgeService")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
bindService(
|
||||
intent,
|
||||
Context.BIND_AUTO_CREATE,
|
||||
Executors.newSingleThreadExecutor(),
|
||||
this@BridgeClient
|
||||
)
|
||||
} else {
|
||||
XposedHelpers.callMethod(
|
||||
this,
|
||||
"bindServiceAsUser",
|
||||
intent,
|
||||
this@BridgeClient,
|
||||
Context.BIND_AUTO_CREATE,
|
||||
Handler(HandlerThread("BridgeClient").apply {
|
||||
start()
|
||||
}.looper),
|
||||
android.os.Process.myUserHandle()
|
||||
)
|
||||
}
|
||||
onResult(future.get(15, TimeUnit.SECONDS))
|
||||
}.onFailure {
|
||||
onFailure(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,62 @@
|
||||
package me.rhunk.snapenhance.core.util
|
||||
|
||||
import me.rhunk.snapenhance.common.BuildConfig
|
||||
import me.rhunk.snapenhance.core.ModContext
|
||||
import me.rhunk.snapenhance.core.bridge.BridgeClient
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
object LSPatchUpdater {
|
||||
private const val TAG = "LSPatchUpdater"
|
||||
|
||||
private fun getModuleUniqueHash(module: ZipFile): String {
|
||||
return module.entries().asSequence()
|
||||
.filter { !it.isDirectory }
|
||||
.map { it.crc }
|
||||
.reduce { acc, crc -> acc xor crc }
|
||||
.toString(16)
|
||||
}
|
||||
|
||||
fun onBridgeConnected(context: ModContext, bridgeClient: BridgeClient) {
|
||||
val embeddedModule = context.androidContext.cacheDir
|
||||
.resolve("lspatch")
|
||||
.resolve(BuildConfig.APPLICATION_ID).let { moduleDir ->
|
||||
if (!moduleDir.exists()) return@let null
|
||||
moduleDir.listFiles()?.firstOrNull { it.extension == "apk" }
|
||||
} ?: return
|
||||
|
||||
context.log.verbose("Found embedded SE at ${embeddedModule.absolutePath}", TAG)
|
||||
|
||||
val seAppApk = File(bridgeClient.getApplicationApkPath()).also {
|
||||
if (!it.canRead()) {
|
||||
throw IllegalStateException("Cannot read SnapEnhance apk")
|
||||
}
|
||||
}
|
||||
|
||||
runCatching {
|
||||
if (getModuleUniqueHash(ZipFile(embeddedModule)) == getModuleUniqueHash(ZipFile(seAppApk))) {
|
||||
context.log.verbose("Embedded SE is up to date", TAG)
|
||||
return
|
||||
}
|
||||
}.onFailure {
|
||||
throw IllegalStateException("Failed to compare module signature", it)
|
||||
}
|
||||
|
||||
context.log.verbose("updating", TAG)
|
||||
context.shortToast("Updating SnapEnhance. Please wait...")
|
||||
// copy embedded module to cache
|
||||
runCatching {
|
||||
seAppApk.copyTo(embeddedModule, overwrite = true)
|
||||
}.onFailure {
|
||||
seAppApk.delete()
|
||||
context.log.error("Failed to copy embedded module", it, TAG)
|
||||
context.longToast("Failed to update SnapEnhance. Please check logcat for more details.")
|
||||
context.forceCloseApp()
|
||||
return
|
||||
}
|
||||
|
||||
context.longToast("SnapEnhance updated!")
|
||||
context.log.verbose("updated", TAG)
|
||||
context.forceCloseApp()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user