commit b4879c9a7f3e90d562bdfdc1cb4232f155fcbfa2
parent 91b690b01d4046fa3533a8ab297655f3805bc994
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sat,  1 Jun 2024 23:29:13 +0200

refactor: lazy bridge references

Diffstat:
Mapp/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt | 7++++---
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/bridge/BridgeFiles.kt | 18++++++++++--------
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/bridge/InternalFileWrapper.kt | 6++++--
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/bridge/wrapper/LocaleWrapper.kt | 16++++++++--------
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/bridge/wrapper/MappingsWrapper.kt | 3++-
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/ModConfig.kt | 3++-
Acommon/src/main/kotlin/me/rhunk/snapenhance/common/util/LazyBridge.kt | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt | 10+++++++---
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt | 39++++++++++++++++++---------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/BridgeFileFeature.kt | 6++++--
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/CustomEmojiFont.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/EndToEndEncryption.kt | 3++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/spying/FriendTracker.kt | 3++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/spying/MessageLogger.kt | 3++-
14 files changed, 115 insertions(+), 53 deletions(-)

diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt @@ -28,6 +28,7 @@ import me.rhunk.snapenhance.common.bridge.wrapper.LoggerWrapper import me.rhunk.snapenhance.common.bridge.wrapper.MappingsWrapper import me.rhunk.snapenhance.common.config.ModConfig import me.rhunk.snapenhance.common.logger.fatalCrash +import me.rhunk.snapenhance.common.util.constantLazyBridge import me.rhunk.snapenhance.common.util.getPurgeTime import me.rhunk.snapenhance.e2ee.E2EEImplementation import me.rhunk.snapenhance.scripting.RemoteScriptManager @@ -61,9 +62,9 @@ class RemoteSideContext( val sharedPreferences: SharedPreferences get() = androidContext.getSharedPreferences("prefs", 0) val fileHandleManager = RemoteFileHandleManager(this) - val config = ModConfig(androidContext, fileHandleManager) - val translation = LocaleWrapper(fileHandleManager) - val mappings = MappingsWrapper(fileHandleManager) + val config = ModConfig(androidContext, constantLazyBridge { fileHandleManager }) + val translation = LocaleWrapper(constantLazyBridge { fileHandleManager }) + val mappings = MappingsWrapper(constantLazyBridge { fileHandleManager }) val taskManager = TaskManager(this) val database = AppDatabase(this) val streaksReminder = StreaksReminder(this) diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/BridgeFiles.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/BridgeFiles.kt @@ -5,6 +5,8 @@ import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor.AutoCloseInputStream import android.os.ParcelFileDescriptor.AutoCloseOutputStream import me.rhunk.snapenhance.bridge.storage.FileHandle +import me.rhunk.snapenhance.common.util.LazyBridgeValue +import me.rhunk.snapenhance.common.util.lazyBridge import java.io.File @@ -43,10 +45,10 @@ enum class InternalFileHandleType( } } -fun FileHandle.toWrapper() = FileHandleWrapper(lazy { this }) +fun FileHandle.toWrapper() = FileHandleWrapper(lazyBridge { this }) open class FileHandleWrapper( - private val fileHandle: Lazy<FileHandle> + private val fileHandle: LazyBridgeValue<FileHandle> ) { fun exists() = fileHandle.value.exists() fun create() = fileHandle.value.create() @@ -54,8 +56,8 @@ open class FileHandleWrapper( fun writeBytes(data: ByteArray) = fileHandle.value.open( ParcelFileDescriptor.MODE_WRITE_ONLY or - ParcelFileDescriptor.MODE_CREATE or - ParcelFileDescriptor.MODE_TRUNCATE + ParcelFileDescriptor.MODE_CREATE or + ParcelFileDescriptor.MODE_TRUNCATE ).use { pfd -> AutoCloseOutputStream(pfd).use { it.write(data) @@ -64,7 +66,7 @@ open class FileHandleWrapper( open fun readBytes(): ByteArray = fileHandle.value.open( ParcelFileDescriptor.MODE_READ_ONLY or - ParcelFileDescriptor.MODE_CREATE + ParcelFileDescriptor.MODE_CREATE ).use { pfd -> AutoCloseInputStream(pfd).use { it.readBytes() @@ -73,7 +75,7 @@ open class FileHandleWrapper( fun inputStream(block: (AutoCloseInputStream) -> Unit) = fileHandle.value.open( ParcelFileDescriptor.MODE_READ_ONLY or - ParcelFileDescriptor.MODE_CREATE + ParcelFileDescriptor.MODE_CREATE ).use { pfd -> AutoCloseInputStream(pfd).use { block(it) @@ -82,8 +84,8 @@ open class FileHandleWrapper( fun outputStream(block: (AutoCloseOutputStream) -> Unit) = fileHandle.value.open( ParcelFileDescriptor.MODE_WRITE_ONLY or - ParcelFileDescriptor.MODE_CREATE or - ParcelFileDescriptor.MODE_TRUNCATE + ParcelFileDescriptor.MODE_CREATE or + ParcelFileDescriptor.MODE_TRUNCATE ).use { pfd -> AutoCloseOutputStream(pfd).use { block(it) diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/InternalFileWrapper.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/InternalFileWrapper.kt @@ -1,12 +1,14 @@ package me.rhunk.snapenhance.common.bridge import me.rhunk.snapenhance.bridge.storage.FileHandleManager +import me.rhunk.snapenhance.common.util.LazyBridgeValue +import me.rhunk.snapenhance.common.util.lazyBridge open class InternalFileWrapper( - fileHandleManager: FileHandleManager, + fileHandleManager: LazyBridgeValue<FileHandleManager>, private val fileType: InternalFileHandleType, val defaultValue: String? = null -): FileHandleWrapper(lazy { fileHandleManager.getFileHandle(FileHandleScope.INTERNAL.key, fileType.key)!! }) { +): FileHandleWrapper(lazyBridge { fileHandleManager.value.getFileHandle(FileHandleScope.INTERNAL.key, fileType.key)!! }) { override fun readBytes(): ByteArray { if (!exists()) { defaultValue?.toByteArray(Charsets.UTF_8)?.let { diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/wrapper/LocaleWrapper.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/wrapper/LocaleWrapper.kt @@ -8,11 +8,12 @@ import com.google.gson.JsonParser import me.rhunk.snapenhance.bridge.storage.FileHandleManager import me.rhunk.snapenhance.common.bridge.FileHandleScope import me.rhunk.snapenhance.common.logger.AbstractLogger +import me.rhunk.snapenhance.common.util.LazyBridgeValue import java.util.Locale class LocaleWrapper( - private val fileHandleManager: FileHandleManager + private val fileHandleManager: LazyBridgeValue<FileHandleManager> ) { companion object { const val DEFAULT_LOCALE = "en_US" @@ -62,15 +63,14 @@ class LocaleWrapper( } fun load() { - load( - DEFAULT_LOCALE, - fileHandleManager.getFileHandle(FileHandleScope.LOCALE.key, "$DEFAULT_LOCALE.json")?.open(ParcelFileDescriptor.MODE_READ_ONLY) ?: run { - throw IllegalStateException("Failed to load default locale") - } - ) + fileHandleManager.value.getFileHandle(FileHandleScope.LOCALE.key, "$DEFAULT_LOCALE.json")?.open(ParcelFileDescriptor.MODE_READ_ONLY)?.use { + load(DEFAULT_LOCALE, it) + } ?: run { + throw IllegalStateException("Failed to load default locale") + } if (userLocale != DEFAULT_LOCALE) { - fileHandleManager.getFileHandle(FileHandleScope.LOCALE.key, "$userLocale.json")?.open(ParcelFileDescriptor.MODE_READ_ONLY)?.let { + fileHandleManager.value.getFileHandle(FileHandleScope.LOCALE.key, "$userLocale.json")?.open(ParcelFileDescriptor.MODE_READ_ONLY)?.use { load(userLocale, it) } } diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/wrapper/MappingsWrapper.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/bridge/wrapper/MappingsWrapper.kt @@ -9,12 +9,13 @@ import me.rhunk.snapenhance.common.Constants import me.rhunk.snapenhance.common.bridge.InternalFileHandleType import me.rhunk.snapenhance.common.bridge.InternalFileWrapper import me.rhunk.snapenhance.common.logger.AbstractLogger +import me.rhunk.snapenhance.common.util.LazyBridgeValue import me.rhunk.snapenhance.mapper.AbstractClassMapper import me.rhunk.snapenhance.mapper.ClassMapper import kotlin.reflect.KClass class MappingsWrapper( - fileHandleManager: FileHandleManager + fileHandleManager: LazyBridgeValue<FileHandleManager> ): InternalFileWrapper(fileHandleManager, InternalFileHandleType.MAPPINGS, defaultValue = "{}") { private lateinit var context: Context private var mappingUniqueHash: Long = 0 diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/ModConfig.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/ModConfig.kt @@ -11,11 +11,12 @@ import me.rhunk.snapenhance.common.bridge.InternalFileWrapper import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.common.config.impl.RootConfig import me.rhunk.snapenhance.common.logger.AbstractLogger +import me.rhunk.snapenhance.common.util.LazyBridgeValue import kotlin.properties.Delegates class ModConfig( private val context: Context, - fileHandleManager: FileHandleManager + fileHandleManager: LazyBridgeValue<FileHandleManager> ) { private val fileWrapper = InternalFileWrapper(fileHandleManager, InternalFileHandleType.CONFIG, "{}") var locale: String = LocaleWrapper.DEFAULT_LOCALE diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/util/LazyBridge.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/util/LazyBridge.kt @@ -0,0 +1,49 @@ +package me.rhunk.snapenhance.common.util + +import android.os.IInterface + + +open class LazyBridgeValue<T: IInterface>( + private val block: () -> T, + private val isConstant: Boolean = false +): Lazy<T> { + private val lock = Any() + private var _value: T? = null + + override val value: T get() = run { + synchronized(lock) { + if (_value == null || (!isConstant && !_value!!.asBinder().pingBinder())) { + _value = block() + } + } + return _value!! + } + + override fun isInitialized(): Boolean { + return _value != null && (isConstant || _value!!.asBinder().pingBinder()) + } + + operator fun getValue(thisRef: Any?, property: Any?): T { + return value + } +} + + +fun <T : IInterface, R> mappedLazyBridge(lazyBridgeValue: LazyBridgeValue<T>, map: (T) -> R): Lazy<R> { + return object : Lazy<R> { + private var _value: T? = null + private var _mappedValue: R? = null + + override val value: R get() = run { + if (_value != lazyBridgeValue.value) { + _value = lazyBridgeValue.value + _mappedValue = map(_value!!) + } + return _mappedValue!! + } + override fun isInitialized(): Boolean = lazyBridgeValue.isInitialized() + } +} + +fun <T: IInterface> lazyBridge(block: () -> T) = LazyBridgeValue(block) +fun <T: IInterface> constantLazyBridge(value: () -> T) = LazyBridgeValue(value, isConstant = true) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt @@ -17,6 +17,7 @@ import me.rhunk.snapenhance.common.Constants import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.common.bridge.wrapper.MappingsWrapper import me.rhunk.snapenhance.common.config.ModConfig +import me.rhunk.snapenhance.common.util.lazyBridge import me.rhunk.snapenhance.core.action.ActionManager import me.rhunk.snapenhance.core.bridge.BridgeClient import me.rhunk.snapenhance.core.database.DatabaseAccess @@ -48,15 +49,18 @@ class ModContext( val resources: Resources get() = androidContext.resources val gson: Gson = GsonBuilder().create() - private val _config by lazy { ModConfig(androidContext, bridgeClient.getFileHandlerManager()) } + private val lazyFileHandlerManager = lazyBridge { bridgeClient.getFileHandlerManager() } + val fileHandlerManager by lazyFileHandlerManager + + private val _config by lazy { ModConfig(androidContext, lazyFileHandlerManager) } val config get() = _config.root val log by lazy { CoreLogger(this.bridgeClient) } - val translation by lazy { LocaleWrapper(bridgeClient.getFileHandlerManager()) } + val translation by lazy { LocaleWrapper(lazyFileHandlerManager) } val httpServer = HttpServer() val messageSender = MessageSender(this) val features = FeatureManager(this) - val mappings by lazy { MappingsWrapper(bridgeClient.getFileHandlerManager()) } + val mappings by lazy { MappingsWrapper(lazyFileHandlerManager) } val actionManager = ActionManager(this) val database = DatabaseAccess(this) val event = EventBus(this) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt @@ -5,23 +5,15 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection -import android.os.Build -import android.os.DeadObjectException -import android.os.Handler -import android.os.HandlerThread -import android.os.IBinder -import android.os.ParcelFileDescriptor -import android.os.Process +import android.os.* import android.util.Log import de.robv.android.xposed.XposedHelpers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.withTimeoutOrNull -import me.rhunk.snapenhance.bridge.AccountStorage -import me.rhunk.snapenhance.bridge.BridgeInterface -import me.rhunk.snapenhance.bridge.ConfigStateListener -import me.rhunk.snapenhance.bridge.DownloadCallback -import me.rhunk.snapenhance.bridge.SyncCallback +import me.rhunk.snapenhance.bridge.* import me.rhunk.snapenhance.bridge.e2ee.E2eeInterface import me.rhunk.snapenhance.bridge.logger.LoggerInterface import me.rhunk.snapenhance.bridge.logger.TrackerInterface @@ -58,7 +50,7 @@ class BridgeClient( return true } - return withTimeoutOrNull(5000L) { + return withTimeoutOrNull(10000L) { suspendCancellableCoroutine { cancellableContinuation -> continuation = cancellableContinuation with(context.androidContext) { @@ -133,16 +125,21 @@ class BridgeClient( continuation = null } + private val reconnectSemaphore = Semaphore(permits = 1) + private fun tryReconnect() { runBlocking { - Log.d("BridgeClient", "service is dead, restarting") - val canLoad = connect { - Log.e("BridgeClient", "connection failed", it) - context.softRestartApp() - } - if (canLoad != true) { - Log.e("BridgeClient", "failed to reconnect to service", Throwable()) - context.softRestartApp() + reconnectSemaphore.withPermit { + if (service.asBinder().pingBinder()) return@runBlocking + Log.d("BridgeClient", "service is dead, restarting") + val canLoad = connect { + Log.e("BridgeClient", "connection failed", it) + context.softRestartApp() + } + if (canLoad != true) { + Log.e("BridgeClient", "failed to reconnect to service, result=$canLoad") + context.softRestartApp() + } } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/BridgeFileFeature.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/BridgeFileFeature.kt @@ -3,15 +3,17 @@ package me.rhunk.snapenhance.core.features import me.rhunk.snapenhance.common.bridge.FileHandleScope import me.rhunk.snapenhance.common.bridge.InternalFileHandleType import me.rhunk.snapenhance.common.bridge.toWrapper +import me.rhunk.snapenhance.common.util.LazyBridgeValue +import me.rhunk.snapenhance.common.util.mappedLazyBridge import java.io.BufferedReader import java.io.InputStreamReader import java.nio.charset.StandardCharsets abstract class BridgeFileFeature(name: String, private val bridgeFileType: InternalFileHandleType, loadParams: Int) : Feature(name, loadParams) { private val fileLines = mutableListOf<String>() - private val fileWrapper by lazy { context.bridgeClient.getFileHandlerManager().getFileHandle(FileHandleScope.INTERNAL.key, bridgeFileType.key)!!.toWrapper() } + private val fileWrapper by mappedLazyBridge(LazyBridgeValue({ context.fileHandlerManager.getFileHandle(FileHandleScope.INTERNAL.key, bridgeFileType.key)!! }), map = { it.toWrapper() }) - protected fun readFile() { + private fun readFile() { val temporaryLines = mutableListOf<String>() fileWrapper.inputStream { stream -> with(BufferedReader(InputStreamReader(stream, StandardCharsets.UTF_8))) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/CustomEmojiFont.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/CustomEmojiFont.kt @@ -13,7 +13,7 @@ fun getCustomEmojiFontPath( val customFileName = context.config.experimental.nativeHooks.customEmojiFont.getNullable()?.takeIf { it.isNotBlank() } ?: return null if (cacheFontPath == null) { cacheFontPath = runCatching { - context.bridgeClient.getFileHandlerManager().getFileHandleLocalPath( + context.fileHandlerManager.getFileHandleLocalPath( context, FileHandleScope.USER_IMPORT, customFileName, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/EndToEndEncryption.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/EndToEndEncryption.kt @@ -25,6 +25,7 @@ import me.rhunk.snapenhance.common.data.MessagingRuleType import me.rhunk.snapenhance.common.data.RuleState import me.rhunk.snapenhance.common.database.impl.ConversationMessage import me.rhunk.snapenhance.common.ui.createComposeView +import me.rhunk.snapenhance.common.util.lazyBridge import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor import me.rhunk.snapenhance.common.util.protobuf.ProtoReader import me.rhunk.snapenhance.common.util.protobuf.ProtoWriter @@ -58,7 +59,7 @@ class EndToEndEncryption : MessagingRuleFeature( loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_SYNC or FeatureLoadParams.INIT_ASYNC ) { val isEnabled get() = context.config.experimental.e2eEncryption.globalState == true - private val e2eeInterface by lazy { context.bridgeClient.getE2eeInterface() } + private val e2eeInterface by lazyBridge { context.bridgeClient.getE2eeInterface() } private val translation by lazy { context.translation.getCategory("end_to_end_encryption") } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/spying/FriendTracker.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/spying/FriendTracker.kt @@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Info import me.rhunk.snapenhance.common.Constants import me.rhunk.snapenhance.common.data.* +import me.rhunk.snapenhance.common.util.lazyBridge import me.rhunk.snapenhance.common.util.protobuf.ProtoReader import me.rhunk.snapenhance.common.util.toParcelable import me.rhunk.snapenhance.core.features.Feature @@ -24,7 +25,7 @@ import java.nio.ByteBuffer class FriendTracker : Feature("Friend Tracker", loadParams = FeatureLoadParams.INIT_SYNC) { private val conversationPresenceState = mutableMapOf<String, MutableMap<String, FriendPresenceState?>>() // conversationId -> (userId -> state) - private val tracker by lazy { context.bridgeClient.getTracker() } + private val tracker by lazyBridge { context.bridgeClient.getTracker() } private val notificationManager by lazy { context.androidContext.getSystemService(NotificationManager::class.java).apply { createNotificationChannel(NotificationChannel( "friend_tracker", diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/spying/MessageLogger.kt @@ -11,6 +11,7 @@ import me.rhunk.snapenhance.common.data.ContentType import me.rhunk.snapenhance.common.data.MessageState import me.rhunk.snapenhance.common.data.QuotedMessageContentStatus import me.rhunk.snapenhance.common.util.ktx.longHashCode +import me.rhunk.snapenhance.common.util.lazyBridge import me.rhunk.snapenhance.common.util.protobuf.ProtoReader import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent import me.rhunk.snapenhance.core.event.events.impl.BuildMessageEvent @@ -33,7 +34,7 @@ class MessageLogger : Feature("MessageLogger", const val DELETED_MESSAGE_COLOR = 0x6Eb71c1c } - private val loggerInterface by lazy { context.bridgeClient.getMessageLogger() } + private val loggerInterface by lazyBridge { context.bridgeClient.getMessageLogger() } val isEnabled get() = context.config.messaging.messageLogger.globalState == true