fix mappings not loading & null safety

This commit is contained in:
rhunk 2023-05-16 20:38:00 +02:00
parent 185fee2027
commit 2fb9bd1f82
15 changed files with 56 additions and 57 deletions

View File

@ -3,9 +3,10 @@ package me.rhunk.snapenhance.data.wrapper
import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.XposedHelpers
abstract class AbstractWrapper( 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 { override fun hashCode(): Int {
return instance.hashCode() return instance.hashCode()
@ -15,7 +16,6 @@ abstract class AbstractWrapper(
return instance.toString() return instance.toString()
} }
fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T): T { fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T): T {
val mContentType = XposedHelpers.getObjectField(instance, fieldName) as Enum<*> val mContentType = XposedHelpers.getObjectField(instance, fieldName) as Enum<*>
return java.lang.Enum.valueOf(defaultValue::class.java, mContentType.name) as T return java.lang.Enum.valueOf(defaultValue::class.java, mContentType.name) as T
@ -23,7 +23,7 @@ abstract class AbstractWrapper(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun setEnumValue(fieldName: String, value: Enum<*>) { 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)) XposedHelpers.setObjectField(instance, fieldName, java.lang.Enum.valueOf(type, value.name))
} }
} }

View File

@ -4,11 +4,11 @@ import me.rhunk.snapenhance.data.MessageState
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
import me.rhunk.snapenhance.util.getObjectField import me.rhunk.snapenhance.util.getObjectField
class Message(obj: Any) : AbstractWrapper(obj) { class Message(obj: Any?) : AbstractWrapper(obj) {
val orderKey get() = instance.getObjectField("mOrderKey") as Long val orderKey get() = instanceNonNull().getObjectField("mOrderKey") as Long
val senderId get() = SnapUUID(instance.getObjectField("mSenderId")) val senderId get() = SnapUUID(instanceNonNull().getObjectField("mSenderId"))
val messageContent get() = MessageContent(instance.getObjectField("mMessageContent")) val messageContent get() = MessageContent(instanceNonNull().getObjectField("mMessageContent"))
val messageDescriptor get() = MessageDescriptor(instance.getObjectField("mDescriptor")) val messageDescriptor get() = MessageDescriptor(instanceNonNull().getObjectField("mDescriptor"))
val messageMetadata get() = MessageMetadata(instance.getObjectField("mMetadata")) val messageMetadata get() = MessageMetadata(instanceNonNull().getObjectField("mMetadata"))
val messageState get() = getEnumValue("mState", MessageState.COMMITTED) val messageState get() = getEnumValue("mState", MessageState.COMMITTED)
} }

View File

@ -5,10 +5,10 @@ import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
import me.rhunk.snapenhance.util.getObjectField import me.rhunk.snapenhance.util.getObjectField
import me.rhunk.snapenhance.util.setObjectField import me.rhunk.snapenhance.util.setObjectField
class MessageContent(obj: Any) : AbstractWrapper(obj) { class MessageContent(obj: Any?) : AbstractWrapper(obj) {
var content var content
get() = instance.getObjectField("mContent") as ByteArray get() = instanceNonNull().getObjectField("mContent") as ByteArray
set(value) = instance.setObjectField("mContent", value) set(value) = instanceNonNull().setObjectField("mContent", value)
var contentType var contentType
get() = getEnumValue("mContentType", ContentType.UNKNOWN) get() = getEnumValue("mContentType", ContentType.UNKNOWN)
set(value) = setEnumValue("mContentType", value) set(value) = setEnumValue("mContentType", value)

View File

@ -3,7 +3,7 @@ package me.rhunk.snapenhance.data.wrapper.impl
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
import me.rhunk.snapenhance.util.getObjectField import me.rhunk.snapenhance.util.getObjectField
class MessageDescriptor(obj: Any) : AbstractWrapper(obj) { class MessageDescriptor(obj: Any?) : AbstractWrapper(obj) {
val messageId: Long get() = instance.getObjectField("mMessageId") as Long val messageId: Long get() = instanceNonNull().getObjectField("mMessageId") as Long
val conversationId: SnapUUID get() = SnapUUID(instance.getObjectField("mConversationId")) val conversationId: SnapUUID get() = SnapUUID(instanceNonNull().getObjectField("mConversationId")!!)
} }

View File

@ -4,13 +4,13 @@ import me.rhunk.snapenhance.data.PlayableSnapState
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
import me.rhunk.snapenhance.util.getObjectField import me.rhunk.snapenhance.util.getObjectField
class MessageMetadata(obj: Any) : AbstractWrapper(obj){ class MessageMetadata(obj: Any?) : AbstractWrapper(obj){
val createdAt: Long get() = instance.getObjectField("mCreatedAt") as Long val createdAt: Long get() = instanceNonNull().getObjectField("mCreatedAt") as Long
val readAt: Long get() = instance.getObjectField("mReadAt") as Long val readAt: Long get() = instanceNonNull().getObjectField("mReadAt") as Long
var playableSnapState: PlayableSnapState var playableSnapState: PlayableSnapState
get() = getEnumValue("mPlayableSnapState", PlayableSnapState.PLAYABLE) get() = getEnumValue("mPlayableSnapState", PlayableSnapState.PLAYABLE)
set(value) { set(value) {
setEnumValue("mPlayableSnapState", 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!!) }
} }

View File

@ -6,12 +6,10 @@ import me.rhunk.snapenhance.util.getObjectField
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.* import java.util.*
class SnapUUID(instance: Any) : AbstractWrapper(instance) { class SnapUUID(obj: Any?) : AbstractWrapper(obj) {
private val uuidString by lazy { toUUID().toString() } private val uuidString by lazy { toUUID().toString() }
val bytes: ByteArray get() { val bytes: ByteArray get() = instanceNonNull().getObjectField("mId") as ByteArray
return instance.getObjectField("mId") as ByteArray
}
private fun toUUID(): UUID { private fun toUUID(): UUID {
val buffer = ByteBuffer.wrap(bytes) val buffer = ByteBuffer.wrap(bytes)

View File

@ -10,7 +10,7 @@ import javax.crypto.CipherOutputStream
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
class EncryptionWrapper(instance: Any) : AbstractWrapper(instance) { class EncryptionWrapper(instance: Any?) : AbstractWrapper(instance) {
fun decrypt(data: ByteArray?): ByteArray { fun decrypt(data: ByteArray?): ByteArray {
return newCipher(Cipher.DECRYPT_MODE).doFinal(data) return newCipher(Cipher.DECRYPT_MODE).doFinal(data)
} }
@ -30,12 +30,12 @@ class EncryptionWrapper(instance: Any) : AbstractWrapper(instance) {
* @return the field * @return the field
*/ */
private fun searchByteArrayField(arrayLength: Int): Field { private fun searchByteArrayField(arrayLength: Int): Field {
return instance::class.java.fields.first { f -> return instanceNonNull()::class.java.fields.first { f ->
try { try {
if (!f.type.isArray || f.type if (!f.type.isArray || f.type
.componentType != Byte::class.javaPrimitiveType .componentType != Byte::class.javaPrimitiveType
) return@first false ) return@first false
return@first (f.get(instance) as ByteArray).size == arrayLength return@first (f.get(instanceNonNull()) as ByteArray).size == arrayLength
} catch (e: Exception) { } catch (e: Exception) {
return@first false return@first false
} }

View File

@ -6,27 +6,27 @@ import me.rhunk.snapenhance.util.getObjectField
import java.lang.reflect.Field import java.lang.reflect.Field
class MediaInfo(obj: Any) : AbstractWrapper(obj) { class MediaInfo(obj: Any?) : AbstractWrapper(obj) {
val uri: String val uri: String
get() { get() {
val firstStringUriField = instance.javaClass.fields.first { f: Field -> f.type == String::class.java } val firstStringUriField = instanceNonNull().javaClass.fields.first { f: Field -> f.type == String::class.java }
return instance.getObjectField(firstStringUriField.name) as String return instanceNonNull().getObjectField(firstStringUriField.name) as String
} }
init { init {
var mediaInfo: Any = instance instance?.let {
if (mediaInfo is List<*>) { if (it is List<*>) {
if (mediaInfo.size == 0) { if (it.size == 0) {
throw RuntimeException("MediaInfo is empty") throw RuntimeException("MediaInfo is empty")
} }
mediaInfo = mediaInfo[0]!! instance = it[0]!!
}
} }
instance = mediaInfo
} }
val encryption: EncryptionWrapper? val encryption: EncryptionWrapper?
get() { 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) f.type.isInterface && Parcelable::class.java.isAssignableFrom(f.type)
} }
return encryptionAlgorithmField[instance]?.let { EncryptionWrapper(it) } return encryptionAlgorithmField[instance]?.let { EncryptionWrapper(it) }

View File

@ -3,11 +3,11 @@ package me.rhunk.snapenhance.data.wrapper.impl.media.opera
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
import me.rhunk.snapenhance.util.ReflectionHelper import me.rhunk.snapenhance.util.ReflectionHelper
class Layer(obj: Any) : AbstractWrapper(obj) { class Layer(obj: Any?) : AbstractWrapper(obj) {
val paramMap: ParamMap val paramMap: ParamMap
get() { get() {
val layerControllerField = ReflectionHelper.searchFieldContainsToString( val layerControllerField = ReflectionHelper.searchFieldContainsToString(
instance::class.java, instanceNonNull()::class.java,
instance, instance,
"OperaPageModel" "OperaPageModel"
)!! )!!

View File

@ -6,11 +6,11 @@ import me.rhunk.snapenhance.util.ReflectionHelper
import java.lang.reflect.Field import java.lang.reflect.Field
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class LayerController(obj: Any) : AbstractWrapper(obj) { class LayerController(obj: Any?) : AbstractWrapper(obj) {
val paramMap: ParamMap val paramMap: ParamMap
get() { get() {
val paramMapField: Field = ReflectionHelper.searchFieldTypeInSuperClasses( val paramMapField: Field = ReflectionHelper.searchFieldTypeInSuperClasses(
instance::class.java, instanceNonNull()::class.java,
ConcurrentHashMap::class.java ConcurrentHashMap::class.java
) ?: throw RuntimeException("Could not find paramMap field") ) ?: throw RuntimeException("Could not find paramMap field")
return ParamMap(XposedHelpers.getObjectField(instance, paramMapField.name)) return ParamMap(XposedHelpers.getObjectField(instance, paramMapField.name))

View File

@ -7,16 +7,16 @@ import java.lang.reflect.Field
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class ParamMap(obj: Any) : AbstractWrapper(obj) { class ParamMap(obj: Any?) : AbstractWrapper(obj) {
private val paramMapField: Field by lazy { private val paramMapField: Field by lazy {
ReflectionHelper.searchFieldTypeInSuperClasses( ReflectionHelper.searchFieldTypeInSuperClasses(
instance.javaClass, instanceNonNull().javaClass,
ConcurrentHashMap::class.java ConcurrentHashMap::class.java
)!! )!!
} }
private val concurrentHashMap: ConcurrentHashMap<Any, Any> 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? { operator fun get(key: String): Any? {
return concurrentHashMap.keys.firstOrNull{ k: Any -> k.toString() == key }?.let { concurrentHashMap[it] } return concurrentHashMap.keys.firstOrNull{ k: Any -> k.toString() == key }?.let { concurrentHashMap[it] }

View File

@ -44,7 +44,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
runCatching { runCatching {
updateMessageMethod.invoke( updateMessageMethod.invoke(
context.feature(Messaging::class).conversationManager, context.feature(Messaging::class).conversationManager,
conversationId.instance(), conversationId.instanceNonNull(),
messageId, messageId,
context.classCache.messageUpdateEnum.enumConstants.first { it.toString() == "SAVE" }, context.classCache.messageUpdateEnum.enumConstants.first { it.toString() == "SAVE" },
callback callback
@ -84,7 +84,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
HookStage.BEFORE, HookStage.BEFORE,
{ context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()} { context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()}
) { param -> ) { 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) } val messages = param.arg<List<Any>>(1).map { Message(it) }
messages.forEach { messages.forEach {
if (!canSaveMessage(it)) return@forEach if (!canSaveMessage(it)) return@forEach
@ -119,7 +119,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
val callback = CallbackBuilder(fetchConversationWithMessagesCallbackClass).build() val callback = CallbackBuilder(fetchConversationWithMessagesCallbackClass).build()
runCatching { runCatching {
fetchConversationWithMessagesPaginatedMethod.invoke( fetchConversationWithMessagesPaginatedMethod.invoke(
messaging.conversationManager, messaging.lastOpenedConversationUUID!!.instance(), messaging.conversationManager, messaging.lastOpenedConversationUUID!!.instanceNonNull(),
Long.MAX_VALUE, Long.MAX_VALUE,
3, 3,
callback callback

View File

@ -95,7 +95,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
} }
ContentType.SNAP -> { ContentType.SNAP -> {
//serialize the message content into a json object //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"] val mediaReferences = serializedMessageContent["mRemoteMediaReferences"]
.asJsonArray.map { it.asJsonObject["mMediaReferences"].asJsonArray } .asJsonArray.map { it.asJsonObject["mMediaReferences"].asJsonArray }
.flatten() .flatten()
@ -169,7 +169,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
Logger.xposedLog("Failed to fetch message ${param.arg(0) as Any}") Logger.xposedLog("Failed to fetch message ${param.arg(0) as Any}")
}.build() }.build()
fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instance(), callback) fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instanceNonNull(), callback)
it.setResult(null) it.setResult(null)
} }
} }

View File

@ -109,12 +109,12 @@ class MappingManager(private val context: ModContext) : Manager {
val classes: MutableList<Class<*>> = ArrayList() val classes: MutableList<Class<*>> = ArrayList()
val classLoader = context.androidContext.classLoader val classLoader = context.androidContext.classLoader
val dexPathList = classLoader.getObjectField("pathList") val dexPathList = classLoader.getObjectField("pathList")!!
val dexElements = dexPathList.getObjectField("dexElements") as Array<Any> val dexElements = dexPathList.getObjectField("dexElements") as Array<Any>
dexElements.forEach { dexElement: Any -> dexElements.forEach { dexElement: Any ->
val dexFile = dexElement.getObjectField("dexFile") as DexFile (dexElement.getObjectField("dexFile") as DexFile?)?.apply {
dexFile.entries().toList().forEach fileList@{ className -> entries().toList().forEach fileList@{ className ->
//ignore classes without a dot in them //ignore classes without a dot in them
if (className.contains(".") && !className.startsWith("com.snap")) return@fileList if (className.contains(".") && !className.startsWith("com.snap")) return@fileList
runCatching { runCatching {
@ -122,6 +122,7 @@ class MappingManager(private val context: ModContext) : Manager {
} }
} }
} }
}
executeMappers(classes) executeMappers(classes)
write() write()

View File

@ -2,7 +2,7 @@ package me.rhunk.snapenhance.util
import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.XposedHelpers
fun Any.getObjectField(fieldName: String): Any { fun Any.getObjectField(fieldName: String): Any? {
return XposedHelpers.getObjectField(this, fieldName) return XposedHelpers.getObjectField(this, fieldName)
} }