xiongzhu пре 1 година
родитељ
комит
0262ff5e4f

+ 1 - 1
app/build.gradle

@@ -24,7 +24,7 @@ android {
         applicationId "com.example.modifier"
         minSdk 29
         targetSdk 34
-        versionCode 154
+        versionCode 155
         versionName "1.0.1"
         archivesBaseName = "modifier-${versionCode}"
 

+ 2 - 3
app/src/main/java/com/example/modifier/http/api/RcsNumberApi.kt

@@ -128,9 +128,8 @@ class RcsNumberApi() {
                                 .body<RcsNumberResponse>()
                         Log.i(TAG, "wait for otp response: $rcsNumber")
                         if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS) {
-                            val match =
-                                Regex("Your Messenger verification code is G-(\\d{6})")
-                                    .find(rcsNumber.message!!)
+                            val match = Regex("Your Messenger verification code is G-(\\d{6})")
+                                .find(rcsNumber.message!!)
                             if (match != null) {
                                 val otp = match.groupValues[1]
                                 Log.i(TAG, "OTP: $otp")

+ 1 - 2
app/src/main/java/com/example/modifier/model/TaskAction.kt

@@ -45,5 +45,4 @@ class TaskItem(
     val status: String,
     @Serializable(with = LocalDateTimeSerializer::class)
     val sendAt: LocalDateTime?
-) {
-}
+)

+ 6 - 6
app/src/main/java/com/example/modifier/model/TaskExecutionResult.kt

@@ -4,9 +4,9 @@ import kotlinx.serialization.Serializable
 
 @Serializable
 data class TaskExecutionResult(
-    val success: List<Int>,
-    val fail: List<Int>,
-    val retry: List<Int>? = null
-) {
-
-}
+    val id: Int,
+    var messageId: String? = null,
+    var sent: Boolean = false,
+    var delivery: Int = 0,
+    var numberId: Int? = null,
+)

+ 2 - 6
app/src/main/java/com/example/modifier/repo/BackupRepository.kt

@@ -25,17 +25,13 @@ import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.getContext
 import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
 import java.io.File
 import java.util.Date
 import java.util.Optional
 import kotlin.coroutines.coroutineContext
 import kotlin.time.Duration.Companion.minutes
-import kotlin.time.Duration.Companion.seconds
 
 class BackupRepository(
     private val context: Context,
@@ -232,7 +228,7 @@ class BackupRepository(
             }
 
             val job = CoroutineScope(coroutineContext).launch {
-                taskRunner.gmsgStateRepository.rcsConfigureState.collect {
+                taskRunner.gmsgStateRepo.rcsConfigureState.collect {
                     if (it == RcsConfigureState.RETRY) {
                         shellRun(
                             PACKAGE_GMS.kill(),
@@ -244,7 +240,7 @@ class BackupRepository(
                 }
             }
 
-            val state = taskRunner.gmsgStateRepository.waitForRcsState(
+            val state = taskRunner.gmsgStateRepo.waitForRcsState(
                 arrayOf(RcsConfigureState.CONFIGURED, RcsConfigureState.WAITING_FOR_OTP), 5.minutes
             )
             job.cancel()

+ 42 - 6
app/src/main/java/com/example/modifier/repo/GmsgStateRepository.kt → app/src/main/java/com/example/modifier/repo/GmsgStateRepo.kt

@@ -1,24 +1,32 @@
 package com.example.modifier.repo
 
 import android.util.Log
+import androidx.compose.runtime.mutableStateListOf
 import com.example.modifier.baseTag
 import com.example.modifier.enums.RcsConfigureState
+import com.example.modifier.model.TaskExecutionResult
+import com.example.modifier.model.TaskItem
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.cancel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeoutOrNull
 import org.apache.commons.collections4.queue.CircularFifoQueue
 import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
 
-class GmsgStateRepository() {
+class GmsgStateRepo private constructor() {
     val logs = MutableStateFlow("")
     val rcsConfigureState = MutableStateFlow(RcsConfigureState.CONFIGURED)
+    val watchList = mutableStateListOf<TaskExecutionResult>()
 
     companion object {
         private const val TAG = "$baseTag/GoogleMessageStateRepository"
+        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { GmsgStateRepo() }
     }
 
     init {
@@ -37,7 +45,7 @@ class GmsgStateRepository() {
                 writer.write("logcat -c")
                 writer.newLine()
                 writer.flush()
-                writer.write("logcat BugleRcsEngine:D *:S -v time")
+                writer.write("logcat BugleRcsEngine:D BugleTachygram:D BugleDataModel:D *:S -v time")
                 writer.newLine()
                 writer.flush()
             }
@@ -64,6 +72,29 @@ class GmsgStateRepository() {
                         } else if (line.contains("destState=ReplayRequestState")) {
                             rcsConfigureState.emit(RcsConfigureState.REPLAY_REQUEST)
                         }
+                        if (line.contains("Sending message")
+                            && (line.contains("content_type=\"text/plain") ||
+                                    line.contains("content_type=\"application/vnd.google.rcs.encrypted\"") ||
+                                    line.contains("content_type=\"application/vnd.gsma.rcs-ft-http+xml\""))
+                        ) {
+                            val match = Regex("message_id=\"(.*?)\"").find(line)
+                            if (match != null) {
+                                val messageId = match.groupValues[1]
+                                Log.i(TAG, "Sending message: $messageId")
+                                if (watchList.isNotEmpty()) {
+                                    watchList.last().messageId = messageId
+                                }
+                            }
+                        } else if (line.contains("Received RCS Delivery Report message")) {
+                            val match = Regex("rcsMessage\\{id:(.*?)\\}").find(line)
+                            if (match != null) {
+                                val messageId = match.groupValues[1]
+                                Log.i(TAG, "Received RCS Delivery Report message: $messageId")
+                                watchList.find { it.messageId == messageId }?.let {
+                                    it.delivery = 1
+                                }
+                            }
+                        }
                         Regex("(?<time>\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3}) I/BugleRcsEngine\\(\\W*\\d+\\): (?<log>.*)")
                             .matchEntire(line)
                             ?.apply {
@@ -77,10 +108,7 @@ class GmsgStateRepository() {
                                     if (log.contains("destState=")) {
                                         logsCache.add(
                                             "$time\n${
-                                                log.replace(
-                                                    "transitionTo: destState=",
-                                                    ""
-                                                )
+                                                log.replace("transitionTo: destState=", "")
                                             }"
                                         )
                                         logs.emit(logsCache.joinToString("\n"))
@@ -120,4 +148,12 @@ class GmsgStateRepository() {
     suspend fun updateRcsState(state: RcsConfigureState) {
         rcsConfigureState.emit(state)
     }
+
+    fun addToWatchList(res: TaskExecutionResult) {
+        watchList.add(res)
+        CoroutineScope(Dispatchers.IO).launch {
+            delay(60.seconds)
+            watchList.remove(res)
+        }
+    }
 }

+ 4 - 6
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -28,11 +28,10 @@ import com.example.modifier.baseTag
 import com.example.modifier.data.AppDatabase
 import com.example.modifier.databinding.FloatingWindowBinding
 import com.example.modifier.enums.ReqState
-import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.repo.AppPrefsRepo
 import com.example.modifier.repo.AppStateRepo
 import com.example.modifier.repo.BackupRepository
-import com.example.modifier.repo.GmsgStateRepository
+import com.example.modifier.repo.GmsgStateRepo
 import com.example.modifier.repo.SpoofedSimInfoRepo
 import com.example.modifier.utils.checkPif
 import com.example.modifier.utils.clearConv
@@ -48,7 +47,6 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -74,7 +72,7 @@ class ModifierService : AccessibilityService() {
     private val backupItemDao by lazy { AppDatabase.getDatabase(this).itemDao() }
     private val appPrefsRepo = AppPrefsRepo.instance
     private val appStateRepo = AppStateRepo.instance
-    private val gmsgStateRepository by lazy { GmsgStateRepository() }
+    private val gmsgStateRepo = GmsgStateRepo.instance
     private val screenInspector by lazy { ScreenInspector(this) }
     private val screenController by lazy { ScreenController(this, screenInspector) }
     private val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
@@ -173,7 +171,7 @@ class ModifierService : AccessibilityService() {
                 appStateRepo,
                 appPrefsRepo,
                 spoofedSimInfoRepo,
-                gmsgStateRepository,
+                gmsgStateRepo,
                 backupRepository,
             )
             launch {
@@ -269,7 +267,7 @@ class ModifierService : AccessibilityService() {
                 }
             }
             launch {
-                gmsgStateRepository.logs.debounce(200).collect {
+                gmsgStateRepo.logs.debounce(200).collect {
                     withContext(Dispatchers.Main) {
                         binding.tvLog.text = it
                         delay(100)

+ 1 - 1
app/src/main/java/com/example/modifier/service/SocketClient.kt

@@ -306,7 +306,7 @@ class SocketClient(
         }
     }
 
-    private fun socketCallback(id: String, data: TaskExecutionResult) {
+    private fun socketCallback(id: String, data: List<TaskExecutionResult>) {
         runCatching {
             mSocket.emit(
                 "callback", JSONObject(

+ 30 - 35
app/src/main/java/com/example/modifier/service/TaskRunner.kt

@@ -4,8 +4,6 @@ import android.content.Context
 import android.util.Log
 import android.view.accessibility.AccessibilityNodeInfo
 import coil3.ImageLoader
-import coil3.SingletonImageLoader
-import coil3.request.ImageRequest
 import coil3.request.crossfade
 import com.example.modifier.baseTag
 import com.example.modifier.TraverseResult
@@ -37,9 +35,8 @@ import com.example.modifier.model.UpdateDeviceAction
 import com.example.modifier.repo.AppPrefsRepo
 import com.example.modifier.repo.AppStateRepo
 import com.example.modifier.repo.BackupRepository
-import com.example.modifier.repo.GmsgStateRepository
+import com.example.modifier.repo.GmsgStateRepo
 import com.example.modifier.repo.SpoofedSimInfoRepo
-import com.example.modifier.service.ModifierService.Companion
 import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.genICCID
 import com.example.modifier.utils.genIMEI
@@ -63,12 +60,9 @@ import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeout
 import kotlinx.coroutines.withTimeoutOrNull
@@ -87,7 +81,7 @@ class TaskRunner(
     private val appStateRepo: AppStateRepo,
     private val appPrefsRepo: AppPrefsRepo,
     private val spoofedSimInfoRepo: SpoofedSimInfoRepo,
-    val gmsgStateRepository: GmsgStateRepository,
+    val gmsgStateRepo: GmsgStateRepo,
     private val backupRepository: BackupRepository
 ) {
     companion object {
@@ -168,7 +162,7 @@ class TaskRunner(
                 TAG,
                 "executedNum: ${appStateRepo.appState.value.executedNum}, successNum: ${appStateRepo.appState.value.successNum}"
             )
-            delay(1000)
+            delay(2000)
             return success
         } catch (e: Exception) {
             e.printStackTrace()
@@ -178,7 +172,7 @@ class TaskRunner(
 
     suspend fun runTask(
         taskAction: TaskAction,
-        onSuccess: (TaskExecutionResult) -> Unit,
+        onSuccess: (List<TaskExecutionResult>) -> Unit,
         onError: (Exception) -> Unit
     ) {
         if (appStateRepo.appState.value.busy) {
@@ -205,23 +199,24 @@ class TaskRunner(
             }
 
             appStateRepo.updateRuntimeFlags(running = true, checkingConnection = false)
-            val success = ArrayList<Int>()
-            val fail = ArrayList<Int>()
+            val results = mutableListOf<TaskExecutionResult>()
             for (i in 0 until taskAction.data.tasks.size) {
                 val taskItem = taskAction.data.tasks[i]
+                val result = TaskExecutionResult(
+                    id = taskItem.id,
+                    numberId = spoofedSimInfoRepo.spoofedSimInfo.value.numberId
+                )
+                results.add(result)
                 try {
-                    if (send(taskItem.number, taskItem.message, taskItem.img, taskConfig)) {
-                        success.add(taskItem.id)
-                    } else {
-                        fail.add(taskItem.id)
-                    }
+                    gmsgStateRepo.addToWatchList(result)
+                    result.sent = send(taskItem.number, taskItem.message, taskItem.img, taskConfig)
                 } catch (e: Exception) {
                     Log.e(TAG, "runTaskError: ${e.message}", e)
-                    fail.add(taskItem.id)
                 }
             }
             shellRun(CMD_BACK)
-            onSuccess(TaskExecutionResult(success, fail))
+            delay(10000)
+            onSuccess(results)
             if (taskConfig.requestNumberInterval in 1..appStateRepo.appState.value.successNum) {
                 delay(3000)
                 requestNumberOnTask()
@@ -235,7 +230,7 @@ class TaskRunner(
                 delay(2000)
             }
             appStateRepo.updateRuntimeFlags(running = false)
-            if (taskAction.data.config.checkConnection && success.isEmpty()) {
+            if (taskAction.data.config.checkConnection && results.none { it.sent }) {
                 appStateRepo.updateRuntimeFlags(checkingConnection = true)
                 if (!checkRcsA10y()) {
                     onError(Exception("RCS not available"))
@@ -256,10 +251,10 @@ class TaskRunner(
                 while (true) {
                     delay(100)
                     appStateRepo.updateRuntimeFlags(reqState = ReqState.RESET)
-                    gmsgStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
+                    gmsgStateRepo.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
                     spoofedSimInfoRepo.mock()
                     resetAll()
-                    var switchAppear = gmsgStateRepository.waitForRcsState(
+                    var switchAppear = gmsgStateRepo.waitForRcsState(
                         arrayOf(RcsConfigureState.WAITING_FOR_TOS),
                         2.minutes
                     )?.let {
@@ -270,7 +265,7 @@ class TaskRunner(
                             PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
                             CMD_MESSAGING_APP
                         )
-                        switchAppear = gmsgStateRepository.waitForRcsState(
+                        switchAppear = gmsgStateRepo.waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_TOS),
                             5.minutes
                         )?.let {
@@ -289,7 +284,7 @@ class TaskRunner(
                         Log.e(TAG, "RCS switch not turned on, retrying...")
                         continue
                     }
-                    var resetSuccess = gmsgStateRepository.waitForRcsState(
+                    var resetSuccess = gmsgStateRepo.waitForRcsState(
                         arrayOf(
                             RcsConfigureState.READY
                         ), 30.seconds
@@ -298,7 +293,7 @@ class TaskRunner(
                         screenController.toggleRcsSwitch(false)
                         delay(1000)
                         screenController.toggleRcsSwitch(true)
-                        resetSuccess = gmsgStateRepository.waitForRcsState(
+                        resetSuccess = gmsgStateRepo.waitForRcsState(
                             arrayOf(
                                 RcsConfigureState.READY
                             ), 1.minutes
@@ -317,10 +312,10 @@ class TaskRunner(
                 while (true) {
                     delay(100)
                     appStateRepo.updateRuntimeFlags(reqState = ReqState.RESET)
-                    gmsgStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
+                    gmsgStateRepo.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
                     spoofedSimInfoRepo.mock()
                     resetAll()
-                    var switchAppear = gmsgStateRepository.waitForRcsState(
+                    var switchAppear = gmsgStateRepo.waitForRcsState(
                         arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
                         1.minutes
                     )?.let {
@@ -331,7 +326,7 @@ class TaskRunner(
                             PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
                             CMD_MESSAGING_APP
                         )
-                        switchAppear = gmsgStateRepository.waitForRcsState(
+                        switchAppear = gmsgStateRepo.waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
                             2.minutes
                         )?.let {
@@ -347,7 +342,7 @@ class TaskRunner(
                         Log.e(TAG, "RCS switch not turned on, retrying...")
                         continue
                     }
-                    var resetSuccess = gmsgStateRepository.waitForRcsState(
+                    var resetSuccess = gmsgStateRepo.waitForRcsState(
                         arrayOf(
                             RcsConfigureState.READY
                         ), 30.seconds
@@ -356,7 +351,7 @@ class TaskRunner(
                         screenController.toggleRcsSwitch(false)
                         delay(1000)
                         screenController.toggleRcsSwitch(true)
-                        resetSuccess = gmsgStateRepository.waitForRcsState(
+                        resetSuccess = gmsgStateRepo.waitForRcsState(
                             arrayOf(
                                 RcsConfigureState.READY
                             ), 1.minutes
@@ -376,7 +371,7 @@ class TaskRunner(
 
     private suspend fun requestNumberAtomic(store: Boolean? = false) {
         appStateRepo.updateRuntimeFlags(reqState = ReqState.REQUEST)
-        gmsgStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
+        gmsgStateRepo.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
         val device = DeviceApi.getDevice(appPrefsRepo.appPrefs.value.id)
         val rcsNumber = RcsNumberApi.getRcsNumber(
             deviceId = appPrefsRepo.appPrefs.value.id,
@@ -431,7 +426,7 @@ class TaskRunner(
         if (sendOtpTimeout > 2.minutes) {
             sendOtpTimeout = 2.minutes
         }
-        if (gmsgStateRepository.waitForRcsState(
+        if (gmsgStateRepo.waitForRcsState(
                 arrayOf(RcsConfigureState.WAITING_FOR_OTP),
                 sendOtpTimeout
             ) != RcsConfigureState.WAITING_FOR_OTP
@@ -440,7 +435,7 @@ class TaskRunner(
             if (!screenController.toggleRcsSwitch(true)) {
                 throw RequestNumberException(ErrorCode.CODE_RCS_TOGGLED_OFF)
             }
-            if (RcsConfigureState.REPLAY_REQUEST == gmsgStateRepository.rcsConfigureState.value) {
+            if (RcsConfigureState.REPLAY_REQUEST == gmsgStateRepo.rcsConfigureState.value) {
                 throw RequestNumberException(ErrorCode.CODE_REPLAY_RETRY)
             }
             throw RequestNumberException(ErrorCode.CODE_OTP_NOT_SENT)
@@ -462,7 +457,7 @@ class TaskRunner(
             repeat(2) {
                 injectOTP(otp)
                 val state =
-                    gmsgStateRepository.waitForRcsState(
+                    gmsgStateRepo.waitForRcsState(
                         arrayOf(
                             RcsConfigureState.CONFIGURED,
                             RcsConfigureState.RETRY
@@ -474,7 +469,7 @@ class TaskRunner(
                     }
 
                     RcsConfigureState.RETRY -> {
-                        gmsgStateRepository.waitForRcsState(
+                        gmsgStateRepo.waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_OTP),
                             60.seconds
                         )