Modify change YukiHookDataChannel usages

- Fix data channel transaction data parse maybe failure on some devices
- Added dataMaxByteSize, dataMaxByteCompressionFactor functions
This commit is contained in:
2023-04-09 23:57:05 +08:00
parent 914b059d9c
commit a8121d8735
3 changed files with 225 additions and 79 deletions

View File

@@ -70,17 +70,73 @@ inline fun with(initiate: NameSpace.() -> Unit): NameSpace
> 创建一个调用空间。
### dataMaxByteSize <span class="symbol">- field</span>
```kotlin:no-line-numbers
var dataMaxByteSize: Int
```
**Change Records**
`v1.1.9` `added`
**Function Illustrate**
> `YukiHookDataChannel` 允许发送的最大数据字节大小。
默认为 `500 KB (500 * 1024)`,详情请参考 `receiverDataMaxByteSize` 的注释。
最小不能低于 `100 KB (100 * 1024)`,否则会被重新设置为 `100 KB (100 * 1024)`。
设置后将在全局生效,直到当前进程结束。
超出最大数据字节大小后的数据将被自动分段发送。
::: danger
请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
:::
### dataMaxByteCompressionFactor <span class="symbol">- field</span>
```kotlin:no-line-numbers
var dataMaxByteCompressionFactor: Int
```
**Change Records**
`v1.1.9` `added`
**Function Illustrate**
> `YukiHookDataChannel` 允许发送的最大数据字节大小倍数 (分段数据)。
默认为 `3`,详情请参考 `receiverDataMaxByteCompressionFactor` 的注释。
最小不能低于 `2`,否则会被重新设置为 `2`。
设置后将在全局生效,直到当前进程结束。
超出最大数据字节大小后的数据将按照此倍数自动划分 `receiverDataMaxByteSize` 的大小。
::: danger
请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
:::
### allowSendTooLargeData <span class="symbol">- method</span>
```kotlin:no-line-numbers
fun allowSendTooLargeData(): NameSpace
```
**变更记录**
**Change Records**
`v1.1.5` `added`
**功能描述**
**Function Illustrate**
> 解除发送数据的大小限制并禁止开启分段发送功能。

View File

@@ -62,6 +62,62 @@ inline fun with(initiate: NameSpace.() -> Unit): NameSpace
> 创建一个调用空间。
### dataMaxByteSize <span class="symbol">- field</span>
```kotlin:no-line-numbers
var dataMaxByteSize: Int
```
**变更记录**
`v1.1.9` `新增`
**功能描述**
> `YukiHookDataChannel` 允许发送的最大数据字节大小。
默认为 `500 KB (500 * 1024)`,详情请参考 `receiverDataMaxByteSize` 的注释。
最小不能低于 `100 KB (100 * 1024)`,否则会被重新设置为 `100 KB (100 * 1024)`。
设置后将在全局生效,直到当前进程结束。
超出最大数据字节大小后的数据将被自动分段发送。
::: danger
请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
:::
### dataMaxByteCompressionFactor <span class="symbol">- field</span>
```kotlin:no-line-numbers
var dataMaxByteCompressionFactor: Int
```
**变更记录**
`v1.1.9` `新增`
**功能描述**
> `YukiHookDataChannel` 允许发送的最大数据字节大小倍数 (分段数据)。
默认为 `3`,详情请参考 `receiverDataMaxByteCompressionFactor` 的注释。
最小不能低于 `2`,否则会被重新设置为 `2`。
设置后将在全局生效,直到当前进程结束。
超出最大数据字节大小后的数据将按照此倍数自动划分 `receiverDataMaxByteSize` 的大小。
::: danger
请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
:::
### allowSendTooLargeData <span class="symbol">- method</span>
```kotlin:no-line-numbers

View File

@@ -40,12 +40,10 @@ import android.content.IntentFilter
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import android.os.TransactionTooLargeException
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
import com.highcapable.yukihookapi.hook.log.YukiHookLogger
import com.highcapable.yukihookapi.hook.log.YukiLoggerData
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.*
import com.highcapable.yukihookapi.hook.utils.RandomSeed
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
@@ -55,7 +53,6 @@ import com.highcapable.yukihookapi.hook.xposed.channel.priority.ChannelPriority
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import java.io.Serializable
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.round
/**
* 实现 Xposed 模块的数据通讯桥
@@ -80,17 +77,6 @@ class YukiHookDataChannel private constructor() {
/** 自动生成的 Xposed 模块构建版本号 */
private val moduleGeneratedVersion = YukiHookAPI.Status.compiledTimestamp.toString()
/**
* 系统广播允许发送的最大数据字节大小
*
* 标准为 1 MB - 实测不同系统目前已知能得到的数据分别有 1、2、3 MB
*
* 经过测试分段发送 900 KB 数据在 1 台 Android 13 系统的设备上依然会发生异常
*
* 综上所述 - 为了防止系统不同限制不同 - 最终决定设置为 500 KB - 超出后以此大小分段发送数据
*/
private const val RECEIVER_DATA_MAX_BYTE_SIZE = 500 * 1024
/** 模块构建版本号获取标签 */
private const val GET_MODULE_GENERATED_VERSION = "module_generated_version_get"
@@ -106,6 +92,34 @@ class YukiHookDataChannel private constructor() {
/** 仅监听结果键值 */
private const val VALUE_WAIT_FOR_LISTENER = "wait_for_listener_value"
/**
* 系统广播允许发送的最大数据字节大小
*
* 标准为 1 MB - 实测不同系统目前已知能得到的数据分别有 1、2、3 MB
*
* 经过测试分段发送 900 KB 数据在 1 台 Android 13 系统的设备上依然会发生异常
*
* 综上所述 - 为了防止系统不同限制不同 - 最终决定默认设置为 500 KB - 超出后以此大小分段发送数据
*/
private var receiverDataMaxByteSize = 500 * 1024
/**
* 系统广播允许发送的最大数据字节大小倍数 (分段数据)
*
* 分段后的数据每次也不能超出 [receiverDataMaxByteSize] 的大小
*
* 此倍数被作用于分配 [receiverDataMaxByteSize] 的大小
*
* 倍数计算公式为 [receiverDataMaxByteSize] / [receiverDataMaxByteCompressionFactor] = [receiverDataSegmentMaxByteSize]
*/
private var receiverDataMaxByteCompressionFactor = 3
/**
* 获取当前系统广播允许的最大单个分段数据字节大小
* @return [Int]
*/
private val receiverDataSegmentMaxByteSize get() = receiverDataMaxByteSize / receiverDataMaxByteCompressionFactor
/** 当前 [YukiHookDataChannel] 单例 */
private var instance: YukiHookDataChannel? = null
@@ -127,7 +141,7 @@ class YukiHookDataChannel private constructor() {
/** 当前注册广播的 [Context] */
private var receiverContext: Context? = null
/** 是否允许发送超出 [RECEIVER_DATA_MAX_BYTE_SIZE] 大小的数据 */
/** 是否允许发送超出 [receiverDataMaxByteSize] 大小的数据 */
private var isAllowSendTooLargeData = false
/** 广播接收器 */
@@ -268,6 +282,53 @@ class YukiHookDataChannel private constructor() {
*/
private val keyNonRepeatName get() = "_${packageName.hashCode()}"
/**
* 创建一个调用空间
* @param initiate 方法体
* @return [NameSpace] 可继续向下监听
*/
inline fun with(initiate: NameSpace.() -> Unit) = apply(initiate)
/**
* [YukiHookDataChannel] 允许发送的最大数据字节大小
*
* 默认为 500 KB (500 * 1024) - 详情请参考 [receiverDataMaxByteSize] 的注释
*
* 最小不能低于 100 KB (100 * 1024) - 否则会被重新设置为 100 KB (100 * 1024)
*
* 设置后将在全局生效 - 直到当前进程结束
*
* - 超出最大数据字节大小后的数据将被自动分段发送
*
* - ❗警告:请谨慎调整此参数 - 如果超出了系统能够允许的大小会引发 [TransactionTooLargeException] 异常
* @return [Int]
*/
var dataMaxByteSize
get() = receiverDataMaxByteSize
set(value) {
receiverDataMaxByteSize = if (value < 100 * 1024) 100 * 1024 else value
}
/**
* [YukiHookDataChannel] 允许发送的最大数据字节大小倍数 (分段数据)
*
* 默认为 3 - 详情请参考 [receiverDataMaxByteCompressionFactor] 的注释
*
* 最小不能低于 2 - 否则会被重新设置为 2
*
* 设置后将在全局生效 - 直到当前进程结束
*
* - 超出最大数据字节大小后的数据将按照此倍数自动划分 [receiverDataMaxByteSize] 的大小
*
* - ❗警告:请谨慎调整此参数 - 如果超出了系统能够允许的大小会引发 [TransactionTooLargeException] 异常
* @return [Int]
*/
var dataMaxByteCompressionFactor
get() = receiverDataMaxByteCompressionFactor
set(value) {
receiverDataMaxByteCompressionFactor = if (value < 2) 2 else value
}
/**
* 解除发送数据的大小限制并禁止开启分段发送功能
*
@@ -284,13 +345,6 @@ class YukiHookDataChannel private constructor() {
return this
}
/**
* 创建一个调用空间
* @param initiate 方法体
* @return [NameSpace] 可继续向下监听
*/
inline fun with(initiate: NameSpace.() -> Unit) = apply(initiate)
/**
* 发送键值数据
* @param key 键值名称
@@ -409,35 +463,37 @@ class YukiHookDataChannel private constructor() {
ChannelDataWrapper(id, size > 0, size, index, this)
/**
* 计算 [ChannelData] 所占空间的字节大小
* 计算任意类型所占空间的字节大小
* @return [Int] 字节大小
*/
private fun ChannelData<*>.calDataByteSize(): Int {
private fun Any.calDataByteSize(): Int {
val key = if (this is ChannelData<*>) key else "placeholder"
val value = if (this is ChannelData<*>) value else this
val bundle = Bundle().apply {
when (value) {
null -> Unit
is Boolean -> putBoolean(key, value as Boolean)
is BooleanArray -> putBooleanArray(key, value as BooleanArray)
is Byte -> putByte(key, value as Byte)
is ByteArray -> putByteArray(key, value as ByteArray)
is Char -> putChar(key, value as Char)
is CharArray -> putCharArray(key, value as CharArray)
is Double -> putDouble(key, value as Double)
is DoubleArray -> putDoubleArray(key, value as DoubleArray)
is Float -> putFloat(key, value as Float)
is FloatArray -> putFloatArray(key, value as FloatArray)
is Int -> putInt(key, value as Int)
is IntArray -> putIntArray(key, value as IntArray)
is Long -> putLong(key, value as Long)
is LongArray -> putLongArray(key, value as LongArray)
is Short -> putShort(key, value as Short)
is ShortArray -> putShortArray(key, value as ShortArray)
is String -> putString(key, value as String)
is Array<*> -> putSerializable(key, value as Array<*>)
is CharSequence -> putCharSequence(key, value as CharSequence)
is Parcelable -> putParcelable(key, value as Parcelable)
is Serializable -> putSerializable(key, value as Serializable)
else -> error("Key-Value type ${value?.javaClass?.name} is not allowed")
is Boolean -> putBoolean(key, value)
is BooleanArray -> putBooleanArray(key, value)
is Byte -> putByte(key, value)
is ByteArray -> putByteArray(key, value)
is Char -> putChar(key, value)
is CharArray -> putCharArray(key, value)
is Double -> putDouble(key, value)
is DoubleArray -> putDoubleArray(key, value)
is Float -> putFloat(key, value)
is FloatArray -> putFloatArray(key, value)
is Int -> putInt(key, value)
is IntArray -> putIntArray(key, value)
is Long -> putLong(key, value)
is LongArray -> putLongArray(key, value)
is Short -> putShort(key, value)
is ShortArray -> putShortArray(key, value)
is String -> putString(key, value)
is Array<*> -> putSerializable(key, value)
is CharSequence -> putCharSequence(key, value)
is Parcelable -> putParcelable(key, value)
is Serializable -> putSerializable(key, value)
else -> error("Key-Value type ${value.javaClass.name} is not allowed")
}
}
return runCatching {
@@ -529,7 +585,7 @@ class YukiHookDataChannel private constructor() {
fun loggerForTooLargeData(name: String, size: Int) {
if (YukiHookAPI.Configs.isDebug) yLoggerW(
msg = "This data key of \"${wrapper.instance.key}\" type $name is too large (total ${dataByteSize / 1024f} KB, " +
"limit ${RECEIVER_DATA_MAX_BYTE_SIZE / 1024f} KB), will be segmented to $size piece to send"
"limit ${receiverDataMaxByteSize / 1024f} KB), will be segmented to $size piece to send"
)
}
@@ -540,40 +596,20 @@ class YukiHookDataChannel private constructor() {
fun loggerForUnprocessableData(suggestionMessage: String = "") = yLoggerE(
msg = "YukiHookDataChannel cannot send this data key of \"${wrapper.instance.key}\" type ${wrapper.instance.value?.javaClass}, " +
"because it is too large (total ${dataByteSize / 1024f} KB, " +
"limit ${RECEIVER_DATA_MAX_BYTE_SIZE / 1024f} KB) and cannot be segmented\n" +
"limit ${receiverDataMaxByteSize / 1024f} KB) and cannot be segmented\n" +
(if (suggestionMessage.isNotBlank()) "$suggestionMessage\n" else "") +
"If you want to lift this restriction, use the allowSendTooLargeData function when calling, " +
"but this may cause the app crash"
)
/**
* 计算需要的大小 (预估大小)
* @param size 集合大小 (长度)
* @return [Int]
*/
fun calIntendCountBySize(size: Int) = round(size / round(dataByteSize.toFloat() / RECEIVER_DATA_MAX_BYTE_SIZE)).toInt()
/**
* 计算需要的大小 (预估大小)
* @return [Int]
*/
fun Collection<*>.calIntendCount() = calIntendCountBySize(size)
/**
* 计算需要的大小 (预估大小)
* @return [Int]
*/
fun Map<*, *>.calIntendCount() = calIntendCountBySize(size)
when {
wrapper.isSegmentsType || isAllowSendTooLargeData -> pushReceiver(wrapper)
dataByteSize >= RECEIVER_DATA_MAX_BYTE_SIZE -> when (wrapper.instance.value) {
dataByteSize >= receiverDataMaxByteSize -> when (wrapper.instance.value) {
is List<*> -> (wrapper.instance.value as List<*>).also { value ->
val segments = arrayListOf<List<*>>()
var segment = arrayListOf<Any?>()
val intendCount = value.calIntendCount()
value.forEach {
segment.add(it)
if (segment.size >= intendCount) {
if (segment.calDataByteSize() >= receiverDataSegmentMaxByteSize) {
segments.add(segment)
segment = arrayListOf()
}
@@ -587,10 +623,9 @@ class YukiHookDataChannel private constructor() {
is Map<*, *> -> (wrapper.instance.value as Map<*, *>).also { value ->
val segments = arrayListOf<Map<*, *>>()
var segment = hashMapOf<Any?, Any?>()
val intendCount = value.calIntendCount()
value.forEach { (k, v) ->
segment[k] = v
if (segment.size >= intendCount) {
if (segment.calDataByteSize() >= receiverDataSegmentMaxByteSize) {
segments.add(segment)
segment = hashMapOf()
}
@@ -604,10 +639,9 @@ class YukiHookDataChannel private constructor() {
is Set<*> -> (wrapper.instance.value as Set<*>).also { value ->
val segments = arrayListOf<Set<*>>()
var segment = hashSetOf<Any?>()
val intendCount = value.calIntendCount()
value.forEach {
segment.add(it)
if (segment.size >= intendCount) {
if (segment.calDataByteSize() >= receiverDataSegmentMaxByteSize) {
segments.add(segment)
segment = hashSetOf()
}
@@ -620,7 +654,7 @@ class YukiHookDataChannel private constructor() {
}
is String -> (wrapper.instance.value as String).also { value ->
/** 由于字符会被按照双字节计算 - 所以这里将限制字节大小除以 2 */
val twoByteMaxSize = RECEIVER_DATA_MAX_BYTE_SIZE / 2
val twoByteMaxSize = receiverDataMaxByteSize / 2
val segments = arrayListOf<String>()
for (i in 0..value.length step twoByteMaxSize)
if (i + twoByteMaxSize <= value.length)