commit 2fb9bd1f82e796d3bdcd03bba926c78db1aa759c parent 185fee2027f0356b603dc9c5cfd0baa6c788a059 Author: rhunk <101876869+rhunk@users.noreply.github.com> Date: Tue, 16 May 2023 20:38:00 +0200 fix mappings not loading & null safety Diffstat:
15 files changed, 56 insertions(+), 57 deletions(-)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt @@ -3,9 +3,10 @@ package me.rhunk.snapenhance.data.wrapper import de.robv.android.xposed.XposedHelpers abstract class AbstractWrapper( - protected var instance: Any + protected var instance: Any? ) { - fun instance() = instance + fun instanceNonNull(): Any = instance!! + fun isPresent(): Boolean = instance == null override fun hashCode(): Int { return instance.hashCode() @@ -15,7 +16,6 @@ abstract class AbstractWrapper( return instance.toString() } - fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T): T { val mContentType = XposedHelpers.getObjectField(instance, fieldName) as Enum<*> return java.lang.Enum.valueOf(defaultValue::class.java, mContentType.name) as T @@ -23,7 +23,7 @@ abstract class AbstractWrapper( @Suppress("UNCHECKED_CAST") fun setEnumValue(fieldName: String, value: Enum<*>) { - val type = instance.javaClass.fields.find { it.name == fieldName }?.type as Class<out Enum<*>> + val type = instance!!.javaClass.fields.find { it.name == fieldName }?.type as Class<out Enum<*>> XposedHelpers.setObjectField(instance, fieldName, java.lang.Enum.valueOf(type, value.name)) } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt @@ -4,11 +4,11 @@ import me.rhunk.snapenhance.data.MessageState import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.util.getObjectField -class Message(obj: Any) : AbstractWrapper(obj) { - val orderKey get() = instance.getObjectField("mOrderKey") as Long - val senderId get() = SnapUUID(instance.getObjectField("mSenderId")) - val messageContent get() = MessageContent(instance.getObjectField("mMessageContent")) - val messageDescriptor get() = MessageDescriptor(instance.getObjectField("mDescriptor")) - val messageMetadata get() = MessageMetadata(instance.getObjectField("mMetadata")) +class Message(obj: Any?) : AbstractWrapper(obj) { + val orderKey get() = instanceNonNull().getObjectField("mOrderKey") as Long + val senderId get() = SnapUUID(instanceNonNull().getObjectField("mSenderId")) + val messageContent get() = MessageContent(instanceNonNull().getObjectField("mMessageContent")) + val messageDescriptor get() = MessageDescriptor(instanceNonNull().getObjectField("mDescriptor")) + val messageMetadata get() = MessageMetadata(instanceNonNull().getObjectField("mMetadata")) val messageState get() = getEnumValue("mState", MessageState.COMMITTED) } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt @@ -5,10 +5,10 @@ import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.util.getObjectField import me.rhunk.snapenhance.util.setObjectField -class MessageContent(obj: Any) : AbstractWrapper(obj) { +class MessageContent(obj: Any?) : AbstractWrapper(obj) { var content - get() = instance.getObjectField("mContent") as ByteArray - set(value) = instance.setObjectField("mContent", value) + get() = instanceNonNull().getObjectField("mContent") as ByteArray + set(value) = instanceNonNull().setObjectField("mContent", value) var contentType get() = getEnumValue("mContentType", ContentType.UNKNOWN) set(value) = setEnumValue("mContentType", value) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt @@ -3,7 +3,7 @@ package me.rhunk.snapenhance.data.wrapper.impl import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.util.getObjectField -class MessageDescriptor(obj: Any) : AbstractWrapper(obj) { - val messageId: Long get() = instance.getObjectField("mMessageId") as Long - val conversationId: SnapUUID get() = SnapUUID(instance.getObjectField("mConversationId")) +class MessageDescriptor(obj: Any?) : AbstractWrapper(obj) { + val messageId: Long get() = instanceNonNull().getObjectField("mMessageId") as Long + val conversationId: SnapUUID get() = SnapUUID(instanceNonNull().getObjectField("mConversationId")!!) } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt @@ -4,13 +4,13 @@ import me.rhunk.snapenhance.data.PlayableSnapState import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.util.getObjectField -class MessageMetadata(obj: Any) : AbstractWrapper(obj){ - val createdAt: Long get() = instance.getObjectField("mCreatedAt") as Long - val readAt: Long get() = instance.getObjectField("mReadAt") as Long +class MessageMetadata(obj: Any?) : AbstractWrapper(obj){ + val createdAt: Long get() = instanceNonNull().getObjectField("mCreatedAt") as Long + val readAt: Long get() = instanceNonNull().getObjectField("mReadAt") as Long var playableSnapState: PlayableSnapState get() = getEnumValue("mPlayableSnapState", PlayableSnapState.PLAYABLE) set(value) { setEnumValue("mPlayableSnapState", value) } - val savedBy: List<SnapUUID> = (instance.getObjectField("mSavedBy") as List<*>).map { SnapUUID(it!!) } + val savedBy: List<SnapUUID> = (instanceNonNull().getObjectField("mSavedBy") as List<*>).map { SnapUUID(it!!) } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt @@ -6,12 +6,10 @@ import me.rhunk.snapenhance.util.getObjectField import java.nio.ByteBuffer import java.util.* -class SnapUUID(instance: Any) : AbstractWrapper(instance) { +class SnapUUID(obj: Any?) : AbstractWrapper(obj) { private val uuidString by lazy { toUUID().toString() } - val bytes: ByteArray get() { - return instance.getObjectField("mId") as ByteArray - } + val bytes: ByteArray get() = instanceNonNull().getObjectField("mId") as ByteArray private fun toUUID(): UUID { val buffer = ByteBuffer.wrap(bytes) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt @@ -10,7 +10,7 @@ import javax.crypto.CipherOutputStream import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec -class EncryptionWrapper(instance: Any) : AbstractWrapper(instance) { +class EncryptionWrapper(instance: Any?) : AbstractWrapper(instance) { fun decrypt(data: ByteArray?): ByteArray { return newCipher(Cipher.DECRYPT_MODE).doFinal(data) } @@ -30,12 +30,12 @@ class EncryptionWrapper(instance: Any) : AbstractWrapper(instance) { * @return the field */ private fun searchByteArrayField(arrayLength: Int): Field { - return instance::class.java.fields.first { f -> + return instanceNonNull()::class.java.fields.first { f -> try { if (!f.type.isArray || f.type .componentType != Byte::class.javaPrimitiveType ) return@first false - return@first (f.get(instance) as ByteArray).size == arrayLength + return@first (f.get(instanceNonNull()) as ByteArray).size == arrayLength } catch (e: Exception) { return@first false } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt @@ -6,27 +6,27 @@ import me.rhunk.snapenhance.util.getObjectField import java.lang.reflect.Field -class MediaInfo(obj: Any) : AbstractWrapper(obj) { +class MediaInfo(obj: Any?) : AbstractWrapper(obj) { val uri: String get() { - val firstStringUriField = instance.javaClass.fields.first { f: Field -> f.type == String::class.java } - return instance.getObjectField(firstStringUriField.name) as String + val firstStringUriField = instanceNonNull().javaClass.fields.first { f: Field -> f.type == String::class.java } + return instanceNonNull().getObjectField(firstStringUriField.name) as String } init { - var mediaInfo: Any = instance - if (mediaInfo is List<*>) { - if (mediaInfo.size == 0) { - throw RuntimeException("MediaInfo is empty") + instance?.let { + if (it is List<*>) { + if (it.size == 0) { + throw RuntimeException("MediaInfo is empty") + } + instance = it[0]!! } - mediaInfo = mediaInfo[0]!! } - instance = mediaInfo } val encryption: EncryptionWrapper? get() { - val encryptionAlgorithmField = instance.javaClass.fields.first { f: Field -> + val encryptionAlgorithmField = instanceNonNull().javaClass.fields.first { f: Field -> f.type.isInterface && Parcelable::class.java.isAssignableFrom(f.type) } return encryptionAlgorithmField[instance]?.let { EncryptionWrapper(it) } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt @@ -3,11 +3,11 @@ package me.rhunk.snapenhance.data.wrapper.impl.media.opera import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.util.ReflectionHelper -class Layer(obj: Any) : AbstractWrapper(obj) { +class Layer(obj: Any?) : AbstractWrapper(obj) { val paramMap: ParamMap get() { val layerControllerField = ReflectionHelper.searchFieldContainsToString( - instance::class.java, + instanceNonNull()::class.java, instance, "OperaPageModel" )!! diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt @@ -6,11 +6,11 @@ import me.rhunk.snapenhance.util.ReflectionHelper import java.lang.reflect.Field import java.util.concurrent.ConcurrentHashMap -class LayerController(obj: Any) : AbstractWrapper(obj) { +class LayerController(obj: Any?) : AbstractWrapper(obj) { val paramMap: ParamMap get() { val paramMapField: Field = ReflectionHelper.searchFieldTypeInSuperClasses( - instance::class.java, + instanceNonNull()::class.java, ConcurrentHashMap::class.java ) ?: throw RuntimeException("Could not find paramMap field") return ParamMap(XposedHelpers.getObjectField(instance, paramMapField.name)) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt b/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt @@ -7,16 +7,16 @@ import java.lang.reflect.Field import java.util.concurrent.ConcurrentHashMap @Suppress("UNCHECKED_CAST") -class ParamMap(obj: Any) : AbstractWrapper(obj) { +class ParamMap(obj: Any?) : AbstractWrapper(obj) { private val paramMapField: Field by lazy { ReflectionHelper.searchFieldTypeInSuperClasses( - instance.javaClass, + instanceNonNull().javaClass, ConcurrentHashMap::class.java )!! } private val concurrentHashMap: ConcurrentHashMap<Any, Any> - get() = instance.getObjectField(paramMapField.name) as ConcurrentHashMap<Any, Any> + get() = instanceNonNull().getObjectField(paramMapField.name) as ConcurrentHashMap<Any, Any> operator fun get(key: String): Any? { return concurrentHashMap.keys.firstOrNull{ k: Any -> k.toString() == key }?.let { concurrentHashMap[it] } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/AutoSave.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/AutoSave.kt @@ -44,7 +44,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR runCatching { updateMessageMethod.invoke( context.feature(Messaging::class).conversationManager, - conversationId.instance(), + conversationId.instanceNonNull(), messageId, context.classCache.messageUpdateEnum.enumConstants.first { it.toString() == "SAVE" }, callback @@ -84,7 +84,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR HookStage.BEFORE, { context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()} ) { param -> - val conversationId = SnapUUID(param.arg<Any>(0).getObjectField("mConversationId")) + val conversationId = SnapUUID(param.arg<Any>(0).getObjectField("mConversationId")!!) val messages = param.arg<List<Any>>(1).map { Message(it) } messages.forEach { if (!canSaveMessage(it)) return@forEach @@ -119,7 +119,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR val callback = CallbackBuilder(fetchConversationWithMessagesCallbackClass).build() runCatching { fetchConversationWithMessagesPaginatedMethod.invoke( - messaging.conversationManager, messaging.lastOpenedConversationUUID!!.instance(), + messaging.conversationManager, messaging.lastOpenedConversationUUID!!.instanceNonNull(), Long.MAX_VALUE, 3, callback diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/Notifications.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/Notifications.kt @@ -95,7 +95,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN } ContentType.SNAP -> { //serialize the message content into a json object - val serializedMessageContent = context.gson.toJsonTree(snapMessage.messageContent.instance()).asJsonObject + val serializedMessageContent = context.gson.toJsonTree(snapMessage.messageContent.instanceNonNull()).asJsonObject val mediaReferences = serializedMessageContent["mRemoteMediaReferences"] .asJsonArray.map { it.asJsonObject["mMediaReferences"].asJsonArray } .flatten() @@ -169,7 +169,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN Logger.xposedLog("Failed to fetch message ${param.arg(0) as Any}") }.build() - fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instance(), callback) + fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instanceNonNull(), callback) it.setResult(null) } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt @@ -109,16 +109,17 @@ class MappingManager(private val context: ModContext) : Manager { val classes: MutableList<Class<*>> = ArrayList() val classLoader = context.androidContext.classLoader - val dexPathList = classLoader.getObjectField("pathList") + val dexPathList = classLoader.getObjectField("pathList")!! val dexElements = dexPathList.getObjectField("dexElements") as Array<Any> dexElements.forEach { dexElement: Any -> - val dexFile = dexElement.getObjectField("dexFile") as DexFile - dexFile.entries().toList().forEach fileList@{ className -> - //ignore classes without a dot in them - if (className.contains(".") && !className.startsWith("com.snap")) return@fileList - runCatching { - classLoader.loadClass(className)?.let { classes.add(it) } + (dexElement.getObjectField("dexFile") as DexFile?)?.apply { + entries().toList().forEach fileList@{ className -> + //ignore classes without a dot in them + if (className.contains(".") && !className.startsWith("com.snap")) return@fileList + runCatching { + classLoader.loadClass(className)?.let { classes.add(it) } + } } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt b/app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt @@ -2,7 +2,7 @@ package me.rhunk.snapenhance.util import de.robv.android.xposed.XposedHelpers -fun Any.getObjectField(fieldName: String): Any { +fun Any.getObjectField(fieldName: String): Any? { return XposedHelpers.getObjectField(this, fieldName) }