| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854 |
- package com.example.modifier.service
- import android.content.Context
- import android.os.Build
- import android.os.Environment
- import android.util.Log
- import android.view.accessibility.AccessibilityNodeInfo
- import androidx.core.content.ContextCompat
- import coil3.ImageLoader
- import coil3.request.crossfade
- import com.example.modifier.baseTag
- import com.example.modifier.TraverseResult
- import com.example.modifier.constants.CMD_BACK
- import com.example.modifier.constants.CMD_CONVERSATION_LIST_ACTIVITY
- import com.example.modifier.constants.CMD_MESSAGING_APP
- import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY
- import com.example.modifier.constants.PACKAGE_CLEAN_SMS
- import com.example.modifier.constants.PACKAGE_GMS
- import com.example.modifier.constants.PACKAGE_MESSAGING
- import com.example.modifier.enums.RcsConfigureState
- import com.example.modifier.enums.RcsConnectionStatus
- import com.example.modifier.enums.ReqState
- import com.example.modifier.exception.RequestNumberException
- import com.example.modifier.exception.RequestNumberException.Companion.ErrorCode
- import com.example.modifier.extension.kill
- import com.example.modifier.http.api.DeviceApi
- import com.example.modifier.http.api.RcsNumberApi
- import com.example.modifier.http.api.SysConfigApi
- import com.example.modifier.http.downloadImage
- import com.example.modifier.http.ktorClient
- import com.example.modifier.http.response.SysConfigResponse
- import com.example.modifier.model.InstallApkAction
- import com.example.modifier.model.RunScriptAction
- import com.example.modifier.model.SpoofedInfo
- import com.example.modifier.model.TaskAction
- import com.example.modifier.model.TaskConfig
- import com.example.modifier.model.TaskExecutionResult
- 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.GmsgStateRepo
- import com.example.modifier.repo.SpoofedInfoRepo
- import com.example.modifier.utils.clearConv
- import com.example.modifier.utils.genAndroidId
- import com.example.modifier.utils.genICCID
- import com.example.modifier.utils.genIMEI
- import com.example.modifier.utils.genIMSI
- import com.example.modifier.utils.genMacAddress
- import com.example.modifier.utils.genSerialNo
- import com.example.modifier.utils.getContext
- import com.example.modifier.utils.isOldVersion
- import com.example.modifier.utils.resetAll
- import com.example.modifier.utils.shellRun
- import com.example.modifier.utils.smsIntent
- import com.example.modifier.utils.injectOTP
- import io.ktor.client.HttpClient
- import io.ktor.client.call.body
- import io.ktor.client.engine.okhttp.OkHttp
- import io.ktor.client.plugins.HttpResponseValidator
- import io.ktor.client.plugins.ServerResponseException
- import io.ktor.client.plugins.resources.get
- import io.ktor.client.request.prepareGet
- import io.ktor.utils.io.ByteReadChannel
- import io.ktor.utils.io.core.isEmpty
- import io.ktor.utils.io.core.readBytes
- import kotlinx.coroutines.CancellationException
- import kotlinx.coroutines.CoroutineScope
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.Job
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.isActive
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import kotlinx.coroutines.withTimeout
- import kotlinx.coroutines.withTimeoutOrNull
- import org.apache.commons.lang3.RandomStringUtils
- import java.io.File
- import java.time.LocalDateTime
- import java.time.temporal.ChronoUnit
- import kotlin.coroutines.coroutineContext
- import kotlin.time.Duration.Companion.hours
- import kotlin.time.Duration.Companion.minutes
- import kotlin.time.Duration.Companion.seconds
- class TaskRunner(
- private val context: Context,
- private val screenInspector: ScreenInspector,
- val screenController: ScreenController,
- private val appStateRepo: AppStateRepo,
- private val appPrefsRepo: AppPrefsRepo,
- private val spoofedInfoRepo: SpoofedInfoRepo,
- val gmsgStateRepo: GmsgStateRepo,
- private val backupRepository: BackupRepository
- ) {
- companion object {
- private const val TAG = "$baseTag/TaskRunner"
- }
- private var lastSend = 0L
- private var currentTaskId = 0
- private var requestMode = 1
- private var storeNumberJob: Job? = null
- private val imageLoader = ImageLoader.Builder(context)
- .crossfade(true)
- .build()
- suspend fun send(
- to: String,
- body: String?,
- img: String?,
- taskConfig: TaskConfig
- ): Boolean {
- Log.i(TAG, "Sending SMS to $to: $body")
- var imgFile: File? = null
- if (img?.isNotBlank() == true) {
- imgFile = downloadImage(img)
- }
- context.startActivity(smsIntent(to, body, imgFile))
- try {
- Log.i(TAG, "Command executed successfully, waiting for app to open...")
- delay(1000)
- var success = false
- var traverseResult = TraverseResult()
- withTimeoutOrNull(taskConfig.singleTimeout) {
- repeat(999) {
- traverseResult = TraverseResult()
- screenInspector.traverseNode(traverseResult)
- if (traverseResult.isRcsCapable && traverseResult.sendBtn != null) {
- return@withTimeoutOrNull
- }
- delay(200)
- }
- }
- if (traverseResult.isRcsCapable && traverseResult.sendBtn != null && taskConfig.e2ee > 0) {
- withTimeoutOrNull(taskConfig.e2eeTimeout) {
- repeat(999) {
- traverseResult = TraverseResult()
- screenInspector.traverseNode(traverseResult)
- if (traverseResult.encrypted) {
- return@withTimeoutOrNull
- }
- delay(200)
- }
- }
- }
- if (traverseResult.isRcsCapable) {
- if (traverseResult.sendBtn == null) {
- Log.i(TAG, "Send button not found")
- } else if (taskConfig.e2ee == 2 && !traverseResult.encrypted) {
- Log.i(TAG, "E2EE not detected")
- } else {
- Log.i(TAG, "Clicking send button")
- val dt = System.currentTimeMillis() - lastSend
- if (taskConfig.singleDelay > 0 && dt < taskConfig.singleDelay) {
- Log.i(TAG, "Waiting for RCS interval")
- delay(taskConfig.singleDelay - dt)
- }
- traverseResult.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
- lastSend = System.currentTimeMillis()
- success = true
- }
- } else {
- Log.i(TAG, "RCS not detected")
- }
- appStateRepo.incrementExecutedNum(
- success = success,
- num = if (img?.isNotBlank() == true && body?.isNotBlank() == true) 2 else 1
- )
- Log.i(
- TAG,
- "executedNum: ${appStateRepo.appState.value.executedNum}, successNum: ${appStateRepo.appState.value.successNum}"
- )
- delay(2000)
- return success
- } catch (e: Exception) {
- e.printStackTrace()
- }
- return false
- }
- suspend fun runTask(
- taskAction: TaskAction,
- onSuccess: (List<TaskExecutionResult>) -> Unit,
- onError: (Exception) -> Unit
- ) {
- if (appStateRepo.appState.value.busy) {
- onError(Exception("device busy"))
- return
- }
- try {
- val taskConfig = taskAction.data.config
- currentTaskId = taskAction.data.taskId
- requestMode = if (taskAction.data.config.useBackup) 2 else 1
- if (!spoofedInfoRepo.spoofedInfo.value.available ||
- (taskAction.data.config.checkConnection && appStateRepo.appState.value.executedNum == 0)
- ) {
- appStateRepo.updateRuntimeFlags(checkingConnection = true)
- if (!spoofedInfoRepo.spoofedInfo.value.available || !checkRcsA10y()) {
- onError(Exception("RCS not available"))
- if (appStateRepo.appState.value.failure >= 3) {
- appStateRepo.updateRuntimeFlags(reqState = ReqState.NONE, suspended = true)
- } else {
- requestNumberOnTask()
- appStateRepo.updateRuntimeFlags(checkingConnection = false)
- }
- return
- }
- appStateRepo.updateRuntimeFlags(checkingConnection = false)
- }
- appStateRepo.updateRuntimeFlags(running = true, checkingConnection = false)
- 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 = spoofedInfoRepo.spoofedInfo.value.numberId
- )
- results.add(result)
- try {
- gmsgStateRepo.addToWatchList(result)
- result.sent = send(taskItem.number, taskItem.message, taskItem.img, taskConfig)
- } catch (e: Exception) {
- Log.e(TAG, "runTaskError: ${e.message}", e)
- }
- }
- shellRun(CMD_BACK)
- delay(5000)
- onSuccess(results)
- if (taskConfig.singleQty in 1..appStateRepo.appState.value.successNum) {
- delay(3000)
- requestNumberOnTask()
- } else if (taskConfig.cleanCount in 1..appStateRepo.appState.value.executedNum && !appPrefsRepo.appPrefs.value.preventClean) {
- delay(3000)
- clearConv();
- shellRun(CMD_MESSAGING_APP)
- delay(3000)
- appStateRepo.resetExecutedNum()
- } else {
- delay(2000)
- }
- appStateRepo.updateRuntimeFlags(running = false)
- if (taskAction.data.config.checkConnection && results.none { it.sent }) {
- appStateRepo.updateRuntimeFlags(checkingConnection = true)
- if (!checkRcsA10y()) {
- onError(Exception("RCS not available"))
- requestNumberOnTask()
- }
- appStateRepo.updateRuntimeFlags(checkingConnection = false)
- }
- } catch (e: Exception) {
- Log.e(TAG, "runTaskError: ${e.message}", e)
- onError(e)
- appStateRepo.updateRuntimeFlags(running = false)
- }
- }
- suspend fun reset(mock: Boolean = true) {
- val context = getContext()
- val dir = ContextCompat.getExternalCacheDirs(context)[0]
- // val pifPath = File(dir, "pif1.json").path
- // Utils.copyAsset(context.assets, "pif1.json", pifPath)
- // shellRun("cp $pifPath /data/adb/pif.json")
- if (isOldVersion(context)) {
- withTimeout(1.hours) {
- while (true) {
- delay(100)
- appStateRepo.updateRuntimeFlags(reqState = ReqState.RESET)
- gmsgStateRepo.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
- if (mock) {
- spoofedInfoRepo.mock()
- }
- resetAll()
- var switchAppear = gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_TOS),
- 2.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_TOS
- }
- if (switchAppear != true) {
- shellRun(
- PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
- CMD_MESSAGING_APP
- )
- switchAppear = gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_TOS),
- 3.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_TOS
- }
- if (switchAppear != true) {
- shellRun(
- PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
- CMD_MESSAGING_APP
- )
- switchAppear = gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_TOS),
- 2.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_TOS
- }
- if (switchAppear != true) {
- Log.e(TAG, "RCS not entered default on state, retrying...")
- continue
- }
- }
- }
- if (!screenController.toggleRcsSwitch(false)) {
- Log.e(TAG, "RCS switch not turned off, retrying...")
- continue
- }
- if (!screenController.toggleRcsSwitch(true)) {
- Log.e(TAG, "RCS switch not turned on, retrying...")
- continue
- }
- var resetSuccess = gmsgStateRepo.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 30.seconds
- ).let { it == RcsConfigureState.READY }
- if (!resetSuccess) {
- screenController.toggleRcsSwitch(false)
- delay(1000)
- screenController.toggleRcsSwitch(true)
- resetSuccess = gmsgStateRepo.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 1.minutes
- ).let { it == RcsConfigureState.READY }
- }
- Log.i(TAG, "waitForRcsState: $resetSuccess")
- appStateRepo.resetRequestedNum()
- if (resetSuccess) {
- delay(3000)
- break
- }
- }
- }
- } else {
- withTimeout(1.hours) {
- while (true) {
- delay(100)
- appStateRepo.updateRuntimeFlags(reqState = ReqState.RESET)
- gmsgStateRepo.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
- if (mock) {
- spoofedInfoRepo.mock()
- }
- resetAll()
- var switchAppear = gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
- 1.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
- }
- if (switchAppear != true) {
- shellRun(
- PACKAGE_GMS.kill(), PACKAGE_MESSAGING.kill(), "sleep 1",
- CMD_MESSAGING_APP
- )
- switchAppear = gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
- 2.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
- }
- if (switchAppear != true) {
- Log.e(TAG, "RCS not entered default on state, retrying...")
- continue
- }
- }
- val switchOn = screenController.toggleRcsSwitch(true)
- if (!switchOn) {
- Log.e(TAG, "RCS switch not turned on, retrying...")
- continue
- }
- var resetSuccess = gmsgStateRepo.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 30.seconds
- ).let { it == RcsConfigureState.READY }
- if (!resetSuccess) {
- screenController.toggleRcsSwitch(false)
- delay(1000)
- screenController.toggleRcsSwitch(true)
- resetSuccess = gmsgStateRepo.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 1.minutes
- ).let { it == RcsConfigureState.READY }
- }
- Log.i(TAG, "waitForRcsState: $resetSuccess")
- appStateRepo.resetRequestedNum()
- if (resetSuccess) {
- delay(3000)
- break
- }
- }
- }
- }
- appStateRepo.updateRuntimeFlags(reqState = ReqState.NONE)
- // shellRun("rm /data/adb/pif.json")
- }
- private suspend fun requestNumberAtomic(store: Boolean? = false) {
- appStateRepo.updateRuntimeFlags(reqState = ReqState.REQUEST)
- gmsgStateRepo.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
- val device = DeviceApi.getDevice(appPrefsRepo.appPrefs.value.id)
- val rcsNumber = RcsNumberApi.getRcsNumber(
- deviceId = appPrefsRepo.appPrefs.value.id,
- taskId = currentTaskId,
- pinCountry = device.pinCountry,
- store = store
- )
- Log.i(TAG, "requestNumber response: $rcsNumber")
- appStateRepo.updateRuntimeFlags(reqState = ReqState.OTP_1)
- spoofedInfoRepo.updateSpoofedSimInfo(
- spoofedInfoRepo.spoofedInfo.value.copy(
- numberId = rcsNumber.id,
- number = rcsNumber.number,
- mcc = rcsNumber.mcc,
- mnc = rcsNumber.mnc,
- iccid = genICCID(rcsNumber.mnc, rcsNumber.areaCode),
- imsi = genIMSI(rcsNumber.mcc + rcsNumber.mnc),
- imei = genIMEI(),
- country = rcsNumber.country,
- areaCode = rcsNumber.areaCode,
- available = false,
- carrierId = rcsNumber.carrierId,
- carrierName = rcsNumber.carrierName,
- serialNo = spoofedInfoRepo.spoofedInfo.value.serialNo,
- wifiMac = genMacAddress(),
- bssid = genMacAddress(),
- btMac = genMacAddress(),
- ethMac = genMacAddress(),
- androidId = genAndroidId(),
- gmsAid = genAndroidId(),
- rootAid = genAndroidId(),
- )
- )
- val tosAgreeJob = CoroutineScope(coroutineContext).launch {
- run tos@{
- repeat(60 * 1000 / 200) {
- delay(500)
- val traverseResult = TraverseResult()
- screenInspector.traverseNode(traverseResult)
- if (traverseResult.tosAgreeBtn != null) {
- traverseResult.tosAgreeBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
- return@tos
- }
- }
- }
- }
- shellRun(CMD_MESSAGING_APP)
- if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
- throw RequestNumberException(ErrorCode.CODE_NUMBER_EXPIRED)
- }
- var sendOtpTimeout =
- ChronoUnit.SECONDS.between(LocalDateTime.now(), rcsNumber.expiryTime).seconds
- if (sendOtpTimeout < 5.seconds) {
- throw RequestNumberException(ErrorCode.CODE_TIMEOUT_TOO_SHORT)
- }
- if (sendOtpTimeout > 2.minutes) {
- sendOtpTimeout = 2.minutes
- }
- if (gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_OTP),
- sendOtpTimeout
- ) != RcsConfigureState.WAITING_FOR_OTP
- ) {
- tosAgreeJob.cancel()
- if (!screenController.toggleRcsSwitch(true)) {
- throw RequestNumberException(ErrorCode.CODE_RCS_TOGGLED_OFF)
- }
- if (RcsConfigureState.REPLAY_REQUEST == gmsgStateRepo.rcsConfigureState.value) {
- throw RequestNumberException(ErrorCode.CODE_REPLAY_RETRY)
- }
- throw RequestNumberException(ErrorCode.CODE_OTP_NOT_SENT)
- }
- tosAgreeJob.cancel()
- RcsNumberApi.notifyOtpState(rcsNumber.id)
- if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
- throw RequestNumberException(ErrorCode.CODE_NUMBER_EXPIRED)
- }
- appStateRepo.updateRuntimeFlags(reqState = ReqState.OTP_2)
- val otp = RcsNumberApi.waitForOtp(rcsNumber.id)
- ?: throw RequestNumberException(ErrorCode.CODE_OTP_NOT_RECEIVED)
- appStateRepo.updateRuntimeFlags(reqState = ReqState.CONFIG)
- val configured = run configuring@{
- repeat(2) {
- injectOTP(otp)
- val state =
- gmsgStateRepo.waitForRcsState(
- arrayOf(
- RcsConfigureState.CONFIGURED,
- RcsConfigureState.RETRY
- ), 60.seconds
- )
- when (state) {
- RcsConfigureState.CONFIGURED -> {
- return@configuring true
- }
- RcsConfigureState.RETRY -> {
- gmsgStateRepo.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_OTP),
- 60.seconds
- )
- }
- else -> {
- }
- }
- }
- false
- }
- if (!configured) {
- throw RequestNumberException(ErrorCode.CODE_OTP_VERIFY_FAILED)
- } else {
- RcsNumberApi.notifyConfigured(rcsNumber.id)
- delay(4000)
- shellRun(
- PACKAGE_GMS.kill(),
- "sleep 2",
- PACKAGE_MESSAGING.kill(),
- "sleep 1",
- CMD_MESSAGING_APP
- )
- delay(2000)
- }
- }
- suspend fun requestNumberOnTask(reset: Boolean = false, noBackup: Boolean = false) {
- appStateRepo.updateRuntimeFlags(suspended = false)
- if (appPrefsRepo.appPrefs.value.preventRequest) {
- return
- }
- if (appStateRepo.appState.value.reqState != ReqState.NONE) {
- return
- }
- appStateRepo.updateRuntimeFlags(reqState = ReqState.CLEAN)
- clearConv(restoreRole = false)
- if (spoofedInfoRepo.spoofedInfo.value.available) {
- backupRepository.backup(
- type = "auto",
- sendCount = appStateRepo.appState.value.successNum
- )
- }
- var needRest = reset
- val requestSuccess = withTimeoutOrNull(2.hours) {
- while (true) {
- delay(200)
- try {
- if (requestMode == 2 && !noBackup) {
- val backup = backupRepository.findBackupForRestore(
- spoofedInfoRepo.spoofedInfo.value.number
- )
- if (backup != null) {
- AppStateRepo.instance.updateRuntimeFlags(reqState = ReqState.RESTORE)
- if (backupRepository.restore(backup)) {
- return@withTimeoutOrNull true
- } else {
- continue
- }
- }
- }
- needRest = needRest || appStateRepo.appState.value.requestedNum >= 3
- || spoofedInfoRepo.spoofedInfo.value.available
- needRest = needRest && !appPrefsRepo.appPrefs.value.preventReset
- if (needRest) {
- reset()
- needRest = false
- }
- appStateRepo.incrementRequestedNum()
- requestNumberAtomic()
- return@withTimeoutOrNull true
- } catch (e: Exception) {
- if (e is RequestNumberException) {
- Log.e(TAG, "requestNumberError: ${e.message}")
- } else {
- Log.e(TAG, "requestNumberError: ${e.message}", e)
- }
- }
- }
- false
- }
- if (requestSuccess == true) {
- spoofedInfoRepo.updateAvailable(available = true)
- appStateRepo.resetSuccessNum()
- appStateRepo.resetExecutedNum()
- Log.i(TAG, "requestNumber success")
- } else {
- Log.e(TAG, "requestNumber failed")
- appStateRepo.updateSend(false)
- }
- appStateRepo.updateRuntimeFlags(
- reqState = ReqState.NONE,
- suspended = requestSuccess != true
- )
- appStateRepo.incrementRequestedNum()
- }
- private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
- repeat(3) {
- Log.i(TAG, "Checking RCS status...")
- shellRun(
- CMD_CONVERSATION_LIST_ACTIVITY,
- CMD_RCS_SETTINGS_ACTIVITY,
- "sleep 1",
- )
- val res = TraverseResult()
- screenInspector.traverseNode(res)
- if (res.rcsConnectionStatus == RcsConnectionStatus.CONNECTED) {
- Log.i(TAG, "RCS is connected")
- shellRun(CMD_BACK)
- return@checkRcsConnection true
- } else {
- Log.i(TAG, "RCS not connected, retrying...")
- }
- shellRun(CMD_BACK, "sleep ${it * 2}")
- }
- false
- }
- suspend fun checkRcsA10y(timeout: Int = 7500, repeatNum: Int = 2): Boolean {
- val availability = run checkA10y@{
- repeat(repeatNum) {
- val rcsConnected = checkRcsConnectivity()
- if (rcsConnected) {
- val checkRcsA10yNumbers = mutableListOf<String>()
- try {
- val config = ktorClient
- .get(SysConfigApi.Id(SysConfigApi(), "check_availability_numbers"))
- .body<SysConfigResponse>()
- Log.i(TAG, "sysConfig response: $config")
- checkRcsA10yNumbers.addAll(config.value.split(",").map { it.trim() })
- } catch (exception: Exception) {
- Log.e(TAG, "sysConfig Error: ${exception.message}", exception)
- }
- if (checkRcsA10yNumbers.isEmpty()) {
- Log.e(TAG, "checkRcsA10yNumbers is empty")
- return@checkA10y true
- }
- checkRcsA10yNumbers.forEach {
- context.startActivity(smsIntent(it, "", null))
- repeat(timeout / 200) {
- delay(200)
- val traverseResult = TraverseResult()
- screenInspector.traverseNode(traverseResult)
- if (traverseResult.isRcsCapable) {
- return@checkA10y true
- } else {
- Log.i(TAG, "checkRcsA10y: RCS not detected")
- }
- }
- }
- }
- shellRun(PACKAGE_MESSAGING.kill(), CMD_MESSAGING_APP, "sleep 5")
- }
- false
- }
- if (!availability) {
- RcsNumberApi.notifyWasted(spoofedInfoRepo.spoofedInfo.value.numberId)
- appStateRepo.incrementFailure()
- } else {
- appStateRepo.resetFailure()
- }
- return availability
- }
- suspend fun installApk(
- installApkAction: InstallApkAction,
- onSuccess: () -> Unit,
- onError: (Exception) -> Unit
- ) {
- try {
- val file = withContext(Dispatchers.IO) {
- File.createTempFile(
- "temp",
- ".apk",
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
- )
- }
- HttpClient(OkHttp) {
- HttpResponseValidator {
- validateResponse { response ->
- if (response.status.value !in 200..299) {
- throw ServerResponseException(
- response,
- "Error " + response.status.value.toString()
- )
- }
- }
- }
- }.prepareGet(installApkAction.data.apkUrl)
- .execute { httpResponse ->
- val channel: ByteReadChannel = httpResponse.body()
- while (!channel.isClosedForRead) {
- val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
- while (!packet.isEmpty) {
- val bytes = packet.readBytes()
- file.appendBytes(bytes)
- }
- }
- }
- Log.i(TAG, "Apk file saved to ${file.path}")
- val installPath = "/data/local/tmp/${RandomStringUtils.randomAlphabetic(8)}.apk"
- shellRun(
- "cp ${file.path} $installPath",
- "pm install -d -r $installPath",
- "rm -f $installPath"
- )
- onSuccess()
- file.delete()
- } catch (e: Exception) {
- Log.e(TAG, "Failed to install apk", e)
- onError(e)
- }
- }
- suspend fun runScript(
- installApkAction: RunScriptAction,
- onSuccess: (String, String) -> Unit,
- onError: (Exception) -> Unit
- ) {
- try {
- val (out, err) = shellRun(*installApkAction.data.script.split("\n").toTypedArray())
- onSuccess(out, err)
- } catch (e: Exception) {
- Log.e(TAG, "Failed to run script", e)
- onError(e)
- }
- }
- suspend fun updateDevice(
- updateDeviceAction: UpdateDeviceAction,
- onSuccess: () -> Unit,
- onError: (Exception) -> Unit
- ) {
- try {
- if (updateDeviceAction.data.id != null) {
- appPrefsRepo.updateId(updateDeviceAction.data.id)
- }
- if (updateDeviceAction.data.name != null) {
- appPrefsRepo.updateName(updateDeviceAction.data.name)
- }
- onSuccess()
- } catch (e: Exception) {
- Log.e(TAG, "Failed to update device", e)
- onError(e)
- }
- }
- suspend fun storeNumbers(n: Int? = null) {
- if (storeNumberJob != null) {
- Log.e(TAG, "storeNumbers: another store number job is running")
- return
- }
- val num = n ?: appPrefsRepo.appPrefs.value.storeNum
- if (num <= 0) {
- Log.e(TAG, "storeNumbers: storeNum is 0")
- return
- }
- storeNumberJob = CoroutineScope(coroutineContext).launch {
- try {
- appStateRepo.updateRuntimeFlags(storing = true)
- if (spoofedInfoRepo.spoofedInfo.value.available && appStateRepo.appState.value.successNum <= 5) {
- screenController.toggleRcsSwitch(false)
- backupRepository.backup(
- type = "auto",
- sendCount = appStateRepo.appState.value.successNum,
- stock = 1
- )
- screenController.toggleRcsSwitch(true)
- }
- var success = true
- repeat(num) {
- if (!isActive) {
- Log.e(TAG, "storeNumbers: job is cancelled, stopping repeat")
- return@launch
- }
- Log.i(TAG, "storeNumbers: $it")
- try {
- if ((success || appStateRepo.appState.value.requestedNum > 3)
- && !(it == 0 && appStateRepo.appState.value.requestedNum == 0)
- ) {
- reset()
- }
- requestNumberAtomic(store = true)
- spoofedInfoRepo.updateAvailable(available = true)
- screenController.toggleRcsSwitch(false)
- backupRepository.backup(type = "auto", sendCount = 0, stock = 1)
- RcsNumberApi.updateStockFlag(
- id = spoofedInfoRepo.spoofedInfo.value.numberId,
- flag = 1
- )
- success = true
- } catch (e: CancellationException) {
- Log.e(TAG, "storeNumbers: job is cancelled", e)
- } catch (e: Exception) {
- success = false
- Log.e(TAG, "Error store number", e)
- }
- appStateRepo.incrementRequestedNum()
- }
- if (success) {
- reset()
- }
- appStateRepo.updateRuntimeFlags(
- reqState = ReqState.NONE,
- storing = false
- )
- storeNumberJob = null
- } catch (e: CancellationException) {
- Log.e(TAG, "storeNumbers: job is cancelled", e)
- }
- }
- }
- suspend fun cancelStoreNumber() {
- if (storeNumberJob != null) {
- if (storeNumberJob!!.isActive) {
- storeNumberJob!!.cancel()
- }
- appStateRepo.updateRuntimeFlags(
- reqState = ReqState.NONE,
- storing = false
- )
- storeNumberJob = null
- }
- }
- suspend fun cleanBackup(start: Long, end: Long) {
- backupRepository.cleanBackup(start, end)
- }
- }
|