| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312 |
- package com.example.modifier.service
- import android.accessibilityservice.AccessibilityService
- import android.annotation.SuppressLint
- import android.content.ComponentName
- import android.content.Context
- import android.graphics.PixelFormat
- import android.os.Build
- import android.os.Handler
- import android.os.Looper
- import android.text.TextUtils
- import android.util.Base64
- import android.util.DisplayMetrics
- import android.util.Log
- import android.view.Gravity
- import android.view.LayoutInflater
- import android.view.MotionEvent
- import android.view.View
- import android.view.View.OnTouchListener
- import android.view.WindowManager
- import android.view.accessibility.AccessibilityEvent
- import android.view.accessibility.AccessibilityNodeInfo
- import android.widget.CompoundButton
- import android.widget.FrameLayout
- import androidx.annotation.MenuRes
- import androidx.appcompat.widget.PopupMenu
- import androidx.core.content.ContextCompat
- import com.example.modifier.BuildConfig
- import com.example.modifier.Global
- import com.example.modifier.Global.restartModifier
- import com.example.modifier.R
- import com.example.modifier.TraverseResult
- import com.example.modifier.Utils
- 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_GMS
- import com.example.modifier.constants.PACKAGE_MESSAGING
- import com.example.modifier.data.AppDatabase
- import com.example.modifier.data.AppPreferences
- import com.example.modifier.repo.AppPreferencesRepository
- import com.example.modifier.data.AppState
- import com.example.modifier.repo.AppStateRepository
- import com.example.modifier.data.BackupItemDao
- import com.example.modifier.repo.GoogleMessageStateRepository
- import com.example.modifier.databinding.FloatingWindowBinding
- import com.example.modifier.enums.RcsConfigureState
- import com.example.modifier.enums.RcsConnectionStatus
- 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.ktorClient
- import com.example.modifier.http.request.RcsNumberRequest
- import com.example.modifier.http.response.DeviceResponse
- import com.example.modifier.http.response.RcsNumberResponse
- import com.example.modifier.http.response.SysConfigResponse
- import com.example.modifier.model.InstallApkAction
- import com.example.modifier.model.SpoofedSimInfo
- import com.example.modifier.model.SocketCallback
- import com.example.modifier.model.TaskAction
- import com.example.modifier.model.TaskConfig
- import com.example.modifier.model.TaskExecutionResult
- import com.example.modifier.repo.BackupRepository
- import com.example.modifier.repo.SpoofedSimInfoRepository
- import com.example.modifier.serializer.Json
- import com.example.modifier.utils.changeClashProfile
- import com.example.modifier.utils.clearConv
- import com.example.modifier.utils.genICCID
- import com.example.modifier.utils.genIMEI
- import com.example.modifier.utils.genIMSI
- import com.example.modifier.utils.hasRootAccess
- import com.example.modifier.utils.isClashInstalled
- import com.example.modifier.utils.isOldVersion
- import com.example.modifier.utils.isRebooted
- import com.example.modifier.utils.killPhoneProcess
- import com.example.modifier.utils.optimize
- import com.example.modifier.utils.resetAll
- import com.example.modifier.utils.setBatteryLevel
- import com.example.modifier.utils.shellRun
- import com.example.modifier.utils.smsIntent
- import com.example.modifier.utils.stopClash
- import com.example.modifier.utils.syncTime
- import com.example.modifier.utils.uniqueId
- import com.google.android.material.color.DynamicColors
- 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.plugins.resources.post
- import io.ktor.client.plugins.resources.put
- import io.ktor.client.plugins.timeout
- import io.ktor.client.request.prepareGet
- import io.ktor.client.request.setBody
- import io.ktor.http.ContentType
- import io.ktor.http.contentType
- import io.ktor.utils.io.ByteReadChannel
- import io.ktor.utils.io.core.isEmpty
- import io.ktor.utils.io.core.readBytes
- import io.socket.client.IO
- import io.socket.client.Socket
- import io.socket.emitter.Emitter
- import kotlinx.coroutines.CoroutineScope
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.flow.StateFlow
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import kotlinx.coroutines.withTimeout
- import kotlinx.coroutines.withTimeoutOrNull
- import kotlinx.serialization.encodeToString
- import org.apache.commons.lang3.RandomStringUtils
- import org.json.JSONException
- import org.json.JSONObject
- import java.io.File
- import java.time.LocalDateTime
- import java.time.temporal.ChronoUnit
- import java.util.Timer
- import java.util.TimerTask
- import java.util.concurrent.atomic.AtomicReference
- import kotlin.math.max
- import kotlin.math.min
- import kotlin.time.Duration.Companion.hours
- import kotlin.time.Duration.Companion.minutes
- import kotlin.time.Duration.Companion.seconds
- @SuppressLint("SetTextI18n")
- class ModifierService : AccessibilityService(), Emitter.Listener {
- companion object {
- const val NAME: String = BuildConfig.APPLICATION_ID + ".service.ModifierService"
- @JvmStatic
- var instance: ModifierService? = null
- private set
- }
- private val handler = Handler(Looper.getMainLooper())
- private val mSocketOpts = IO.Options()
- private lateinit var mSocket: Socket
- private lateinit var binding: FloatingWindowBinding
- private var currentTaskId = 0
- private var lastSend = 0L
- private val backupItemDao by lazy {
- AppDatabase.getDatabase(this).itemDao()
- }
- private val appPreferencesRepository by lazy {
- AppPreferencesRepository(this)
- }
- private lateinit var appPreferences: StateFlow<AppPreferences>
- private val appStateRepository: AppStateRepository by lazy {
- AppStateRepository(this)
- }
- private lateinit var appState: StateFlow<AppState>
- private val googleMessageStateRepository by lazy {
- GoogleMessageStateRepository(this)
- }
- private var requestMode = 1;
- private var currentActivity = ""
- private val screenInspector by lazy {
- ScreenInspector(this)
- }
- private val screenController by lazy {
- ScreenController(this, screenInspector)
- }
- private val spoofedSimInfoRepository by lazy {
- SpoofedSimInfoRepository(this)
- }
- private lateinit var spoofedSimInfo: StateFlow<SpoofedSimInfo>
- private val backupRepository by lazy {
- BackupRepository(this, backupItemDao, spoofedSimInfoRepository)
- }
- fun connect() {
- try {
- if (this@ModifierService::mSocket.isInitialized) {
- mSocket.disconnect()
- }
- mSocketOpts.query =
- "model=${Build.MODEL}&name=${appPreferences.value.name}&id=${uniqueId}&version=${BuildConfig.VERSION_CODE}"
- mSocketOpts.transports = arrayOf("websocket")
- Log.i(com.example.modifier.TAG, "Connection query: ${mSocketOpts.query}")
- mSocket = IO.socket(appPreferences.value.server, mSocketOpts)
- mSocket.on("message", this@ModifierService)
- mSocket.on(Socket.EVENT_CONNECT) {
- Log.i(com.example.modifier.TAG, "Connected to server")
- CoroutineScope(Dispatchers.IO).launch {
- delay(500)
- reportDeviceStatues()
- }
- }
- mSocket.on(Socket.EVENT_DISCONNECT) {
- Log.i(com.example.modifier.TAG, "Disconnected from server")
- }
- mSocket.on(Socket.EVENT_CONNECT_ERROR) { args ->
- Log.i(com.example.modifier.TAG, "Connection error: " + args[0])
- if (args[0] is Exception) {
- val e = args[0] as Exception
- e.printStackTrace()
- }
- }
- mSocket.connect()
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- override fun onCreate() {
- super.onCreate()
- Log.i(com.example.modifier.TAG, "Starting ModifierService")
- CoroutineScope(Dispatchers.IO).launch {
- appState = appStateRepository.getAppState()
- appPreferences = appPreferencesRepository.getAppPreferences()
- spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
- appStateRepository.updateRuntimeFlags(preparing = true)
- val hasRoot = run checkRoot@{
- repeat(30) {
- if (hasRootAccess()) {
- return@checkRoot true
- }
- delay(1000)
- }
- false
- }
- if (!hasRoot) {
- System.exit(0)
- }
- launch {
- googleMessageStateRepository.startLogging()
- }
- if (isRebooted()) {
- delay(2.minutes)
- } else {
- delay(5000)
- }
- optimize()
- syncTime()
- if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
- killPhoneProcess(force = false)
- }
- appStateRepository.updateRuntimeFlags(preparing = false)
- connect()
- val timer = Timer()
- timer.schedule(object : TimerTask() {
- override fun run() {
- reportDeviceStatues()
- }
- }, 0, 3.seconds.inWholeMilliseconds)
- if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
- timer.schedule(object : TimerTask() {
- override fun run() {
- CoroutineScope(Dispatchers.IO).launch {
- try {
- setBatteryLevel(100)
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- }
- }, 0, 30.minutes.inWholeMilliseconds)
- }
- }
- }
- override fun onAccessibilityEvent(event: AccessibilityEvent) {
- Log.d(
- com.example.modifier.TAG,
- "eventType: ${event.eventType}, packageName: ${event.packageName}, className: ${event.className}"
- )
- if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- if (event.packageName != null && event.className != null) {
- val componentName = ComponentName(
- event.packageName.toString(),
- event.className.toString()
- )
- try {
- packageManager.getActivityInfo(componentName, 0)
- currentActivity = componentName.flattenToShortString()
- Log.d(com.example.modifier.TAG, "Activity: $currentActivity")
- } catch (_: Exception) {
- }
- }
- }
- }
- override fun onInterrupt() {
- }
- override fun call(vararg args: Any) {
- if (args.isNotEmpty()) {
- Log.i(com.example.modifier.TAG, "Received message: " + args[0])
- if (args[0] is JSONObject) {
- val json = args[0] as JSONObject
- val action = json.optString("action")
- if ("send" == action) {
- val data = json.optJSONObject("data")
- if (data != null) {
- val to = data.optString("to")
- val body = data.optString("body")
- CoroutineScope(Dispatchers.IO).launch {
- send(
- to, body, TaskConfig(
- rcsWait = 3000,
- rcsInterval = 1000,
- cleanCount = 10,
- requestNumberInterval = 50,
- checkConnection = true,
- useBackup = false,
- endToEndEncryption = true
- )
- )
- }
- }
- } else if ("task" == action) {
- val taskAction = Json.decodeFromString<TaskAction>(json.toString())
- CoroutineScope(Dispatchers.IO).launch {
- runTask(taskAction)
- }
- } else if ("installApk" == action) {
- val installApkAction = Json.decodeFromString<InstallApkAction>(json.toString())
- CoroutineScope(Dispatchers.IO).launch {
- installApk(installApkAction)
- }
- }
- }
- }
- }
- private suspend fun installApk(installApkAction: InstallApkAction) {
- try {
- val file = withContext(Dispatchers.IO) {
- File.createTempFile("files", ".apk")
- }
- 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(com.example.modifier.TAG, "Apk file saved to ${file.path}")
- shellRun("pm install -d -r ${file.path}")
- mSocket.emit(
- "callback",
- JSONObject(
- Json.encodeToString(
- SocketCallback<String>(
- id = installApkAction.id,
- status = 0,
- )
- )
- )
- )
- } catch (e: Exception) {
- Log.e("Modifier", "Failed to install apk", e)
- mSocket.emit(
- "callback",
- JSONObject(
- Json.encodeToString(
- SocketCallback<String>(
- id = installApkAction.id,
- status = -1,
- error = e.message
- )
- )
- )
- )
- }
- }
- private suspend fun runTask(taskAction: TaskAction) {
- if (appState.value.busy) {
- mSocket.emit(
- "callback",
- JSONObject(
- Json.encodeToString(
- SocketCallback<String>(
- id = taskAction.id,
- status = -1,
- error = "another task is running"
- )
- )
- )
- )
- return
- }
- try {
- val taskConfig = taskAction.data.config
- currentTaskId = taskAction.data.taskId
- requestMode = if (taskAction.data.config.useBackup) 2 else 1
- if (taskAction.data.config.checkConnection) {
- appStateRepository.updateRuntimeFlags(checkingConnection = true)
- if (!checkRcsAvailability()) {
- mSocket.emit(
- "callback",
- JSONObject(
- Json.encodeToString(
- SocketCallback<String>(
- id = taskAction.id,
- status = -1,
- error = "RCS not available"
- )
- )
- )
- )
- requestNumber()
- appStateRepository.updateRuntimeFlags(checkingConnection = false)
- return
- }
- appStateRepository.updateRuntimeFlags(checkingConnection = false)
- }
- appStateRepository.updateRuntimeFlags(running = true)
- val success = ArrayList<Int>()
- val fail = ArrayList<Int>()
- for (i in 0 until taskAction.data.tasks.size) {
- val taskItem = taskAction.data.tasks[i]
- try {
- if (send(
- taskItem.number,
- taskItem.message,
- taskConfig
- )
- ) {
- success.add(taskItem.id)
- } else {
- fail.add(taskItem.id)
- }
- } catch (e: Exception) {
- Log.e(com.example.modifier.TAG, "runTaskError: ${e.message}", e)
- fail.add(taskItem.id)
- }
- }
- shellRun(CMD_BACK)
- mSocket.emit(
- "callback",
- JSONObject(
- Json.encodeToString(
- SocketCallback(
- id = taskAction.id,
- status = 0,
- data = TaskExecutionResult(success, fail)
- )
- )
- )
- )
- if (taskConfig.requestNumberInterval in 1..appState.value.successNum) {
- delay(3000)
- requestNumber()
- } else if (taskConfig.cleanCount in 1..appState.value.executedNum && !appPreferences.value.preventClean) {
- delay(3000)
- clearConv();
- shellRun(CMD_MESSAGING_APP)
- delay(3000)
- appStateRepository.resetExecutedNum()
- } else {
- delay(2000)
- }
- appStateRepository.updateRuntimeFlags(running = false)
- } catch (e: Exception) {
- Log.e(com.example.modifier.TAG, "runTaskError: ${e.message}", e)
- mSocket.emit(
- "callback",
- JSONObject(
- Json.encodeToString(
- SocketCallback<String>(
- id = taskAction.id,
- status = -1,
- error = e.message
- )
- )
- )
- )
- appStateRepository.updateRuntimeFlags(running = false)
- }
- }
- private suspend fun send(
- to: String,
- body: String,
- taskConfig: TaskConfig
- ): Boolean {
- Log.i(com.example.modifier.TAG, "Sending SMS to $to: $body")
- startActivity(smsIntent(to, body))
- try {
- Log.i(
- com.example.modifier.TAG,
- "Command executed successfully, waiting for app to open..."
- )
- delay(1000)
- var success = false
- var traverseResult = TraverseResult()
- withTimeoutOrNull(taskConfig.rcsWait) {
- while (true) {
- traverseResult = TraverseResult()
- screenInspector.traverseNode(traverseResult)
- if (traverseResult.isRcsCapable && traverseResult.sendBtn != null) {
- if (!taskConfig.endToEndEncryption || traverseResult.encrypted) {
- break
- }
- }
- delay(200)
- }
- }
- if (traverseResult.isRcsCapable) {
- if (traverseResult.sendBtn == null) {
- Log.i(com.example.modifier.TAG, "Send button not found")
- } else {
- Log.i(com.example.modifier.TAG, "Clicking send button")
- val dt = System.currentTimeMillis() - lastSend
- if (taskConfig.rcsInterval > 0 && dt < taskConfig.rcsInterval) {
- Log.i(com.example.modifier.TAG, "Waiting for RCS interval")
- delay(taskConfig.rcsInterval - dt)
- }
- traverseResult.sendBtn!!.performAction(AccessibilityNodeInfo.ACTION_CLICK)
- lastSend = System.currentTimeMillis()
- success = true
- }
- } else {
- Log.i(com.example.modifier.TAG, "RCS not detected")
- }
- appStateRepository.incrementExecutedNum(success)
- Log.i(
- com.example.modifier.TAG,
- "executedNum: ${appState.value.executedNum}, successNum: ${appState.value.successNum}"
- )
- delay(1000)
- return success
- } catch (e: Exception) {
- e.printStackTrace()
- }
- return false
- }
- @SuppressLint("ClickableViewAccessibility")
- override fun onServiceConnected() {
- super.onServiceConnected()
- instance = this
- val displayMetrics = DisplayMetrics()
- val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
- windowManager.defaultDisplay.getMetrics(displayMetrics)
- val height = displayMetrics.heightPixels
- val width = displayMetrics.widthPixels
- val mLayout = FrameLayout(this)
- val layoutParams = WindowManager.LayoutParams()
- layoutParams.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
- layoutParams.format = PixelFormat.TRANSLUCENT
- layoutParams.flags = layoutParams.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- layoutParams.flags = layoutParams.flags or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT
- layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
- layoutParams.x = 0
- layoutParams.y = 800
- layoutParams.gravity = Gravity.START or Gravity.TOP
- val newContext = DynamicColors.wrapContextIfAvailable(applicationContext, R.style.AppTheme)
- val inflater = LayoutInflater.from(newContext)
- binding = FloatingWindowBinding.inflate(inflater, mLayout, true)
- binding.tvVersion.text = "v${BuildConfig.VERSION_CODE}"
- windowManager.addView(mLayout, layoutParams)
- var maxX = 0
- var maxY = 0
- binding.root.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY)
- binding.root.post {
- maxX = width - binding.root.measuredWidth
- maxY = height - binding.root.measuredHeight
- Log.i(com.example.modifier.TAG, "measured: $maxX, $maxY")
- layoutParams.x = maxX
- windowManager.updateViewLayout(mLayout, layoutParams)
- }
- val downX = AtomicReference(0f)
- val downY = AtomicReference(0f)
- val downParamX = AtomicReference(0)
- val downParamY = AtomicReference(0)
- val touchListener = OnTouchListener { v, event ->
- when (event.action) {
- MotionEvent.ACTION_DOWN -> {
- downX.set(event.rawX)
- downY.set(event.rawY)
- downParamX.set(layoutParams.x)
- downParamY.set(layoutParams.y)
- }
- MotionEvent.ACTION_MOVE -> {
- layoutParams.x = min(
- max((downParamX.get() + (event.rawX - downX.get())).toDouble(), 0.0),
- maxX.toDouble()
- )
- .toInt()
- layoutParams.y = min(
- max((downParamY.get() + (event.rawY - downY.get())).toDouble(), 0.0),
- maxY.toDouble()
- )
- .toInt()
- windowManager.updateViewLayout(mLayout, layoutParams)
- }
- MotionEvent.ACTION_UP -> {
- return@OnTouchListener event.eventTime - event.downTime >= 200
- }
- }
- false
- }
- CoroutineScope(Dispatchers.Main).launch {
- appState.collect {
- binding.swSend.isChecked = it.send
- binding.btnReq.isEnabled = !it.requesting
- binding.tvCount.text = "${it.successNum} / ${it.executedNum}"
- withContext(Dispatchers.IO) {
- reportDeviceStatues()
- }
- }
- }
- CoroutineScope(Dispatchers.Main).launch {
- appPreferences.collect {
- binding.swSend.text = it.name
- }
- }
- binding.swSend.setOnTouchListener(touchListener)
- binding.swSend.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean ->
- CoroutineScope(Dispatchers.IO).launch {
- appStateRepository.updateSend(isChecked)
- }
- }
- googleMessageStateRepository.logs.observeForever {
- binding.tvLog.text = it
- binding.scroll.fullScroll(View.FOCUS_DOWN)
- }
- binding.btnReq.setOnClickListener {
- CoroutineScope(Dispatchers.IO).launch {
- requestNumber(reset = false, noBackup = true)
- }
- }
- binding.btnReset.setOnClickListener {
- binding.btnReset.isEnabled = false
- CoroutineScope(Dispatchers.IO).launch {
- reset()
- withContext(Dispatchers.Main) {
- binding.btnReset.isEnabled = true
- }
- }
- }
- binding.btnMore.setOnClickListener {
- showMenu(newContext, binding.btnMore, R.menu.more)
- }
- }
- private fun showMenu(context: Context, v: View, @MenuRes menuRes: Int) {
- val popup = PopupMenu(context, v)
- popup.menuInflater.inflate(menuRes, popup.menu)
- popup.setOnMenuItemClickListener { item ->
- binding.btnMore.isEnabled = false
- CoroutineScope(Dispatchers.IO).launch {
- when (item.itemId) {
- R.id.inspect -> {
- delay(1500)
- screenInspector.traverseNode(TraverseResult())
- }
- R.id.check_availability -> {
- checkRcsAvailability()
- }
- R.id.toggle_on -> {
- screenController.toggleRcsSwitch(true)
- }
- R.id.toggle_off -> {
- screenController.toggleRcsSwitch(false)
- }
- R.id.clear_conv -> {
- clearConv()
- }
- R.id.store_numbers -> {
- storeNumbers()
- }
- R.id.change_profile -> {
- }
- R.id.restart_modifier -> {
- restartModifier()
- }
- R.id.reset_counter -> {
- CoroutineScope(Dispatchers.IO).launch {
- appStateRepository.resetExecutedNum()
- appStateRepository.resetSuccessNum()
- appStateRepository.resetRequestedNum()
- }
- }
- }
- withContext(Dispatchers.Main) {
- binding.btnMore.isEnabled = true
- }
- }
- true
- }
- popup.setOnDismissListener {
- // Respond to popup being dismissed.
- }
- // Show the popup menu.
- popup.show()
- }
- private fun reportDeviceStatues() {
- if (this::mSocket.isInitialized) {
- val data = JSONObject()
- try {
- data.put("action", "updateDevice")
- val dataObj = JSONObject()
- dataObj.put("canSend", appState.value.send)
- dataObj.put("busy", appState.value.busy)
- dataObj.put("currentCountry", spoofedSimInfo.value.country)
- data.put("data", dataObj)
- mSocket.emit("message", data)
- } catch (e: JSONException) {
- e.printStackTrace()
- }
- }
- }
- private suspend fun reset() {
- if (isOldVersion(this)) {
- withTimeout(1.hours) {
- while (true) {
- delay(100)
- withContext(Dispatchers.Main) {
- binding.tvLog.text = "Waiting for RCS switch on..."
- }
- googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
- spoofedSimInfoRepository.mock()
- resetAll()
- var switchAppear = googleMessageStateRepository.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 = googleMessageStateRepository.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_TOS),
- 5.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_TOS
- }
- if (switchAppear != true) {
- Log.e(
- com.example.modifier.TAG,
- "RCS not entered default on state, retrying..."
- )
- continue
- }
- }
- if (!screenController.toggleRcsSwitch(false)) {
- Log.e(com.example.modifier.TAG, "RCS switch not turned off, retrying...")
- continue
- }
- if (!screenController.toggleRcsSwitch(true)) {
- Log.e(com.example.modifier.TAG, "RCS switch not turned on, retrying...")
- continue
- }
- var resetSuccess = googleMessageStateRepository.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 30.seconds
- ).let { it == RcsConfigureState.READY }
- if (!resetSuccess) {
- screenController.toggleRcsSwitch(false)
- delay(1000)
- screenController.toggleRcsSwitch(true)
- resetSuccess = googleMessageStateRepository.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 1.minutes
- ).let { it == RcsConfigureState.READY }
- }
- Log.i(com.example.modifier.TAG, "waitForRcsState: $resetSuccess")
- appStateRepository.resetRequestedNum()
- if (resetSuccess) {
- delay(3000)
- break
- }
- }
- }
- } else {
- withTimeout(1.hours) {
- while (true) {
- delay(100)
- withContext(Dispatchers.Main) {
- binding.tvLog.text = "Waiting for RCS switch on..."
- }
- googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
- spoofedSimInfoRepository.mock()
- resetAll()
- var switchAppear = googleMessageStateRepository.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 = googleMessageStateRepository.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
- 2.minutes
- )?.let {
- it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
- }
- if (switchAppear != true) {
- Log.e(
- com.example.modifier.TAG,
- "RCS not entered default on state, retrying..."
- )
- continue
- }
- }
- val switchOn = screenController.toggleRcsSwitch(true)
- if (!switchOn) {
- Log.e(com.example.modifier.TAG, "RCS switch not turned on, retrying...")
- continue
- }
- var resetSuccess = googleMessageStateRepository.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 30.seconds
- ).let { it == RcsConfigureState.READY }
- if (!resetSuccess) {
- screenController.toggleRcsSwitch(false)
- delay(1000)
- screenController.toggleRcsSwitch(true)
- resetSuccess = googleMessageStateRepository.waitForRcsState(
- arrayOf(
- RcsConfigureState.READY
- ), 1.minutes
- ).let { it == RcsConfigureState.READY }
- }
- Log.i(com.example.modifier.TAG, "waitForRcsState: $resetSuccess")
- appStateRepository.resetRequestedNum()
- if (resetSuccess) {
- delay(3000)
- break
- }
- }
- }
- }
- }
- private suspend fun requestNumber(
- reset: Boolean = false,
- noBackup: Boolean = false,
- fresh: Boolean = false
- ) {
- val color = ContextCompat.getColorStateList(binding.root.context, R.color.btn_color)
- binding.btnReq.backgroundTintList = color
- if (appPreferences.value.preventRequest) {
- return
- }
- if (appState.value.requesting) {
- return
- }
- appStateRepository.updateRuntimeFlags(requesting = true)
- if (spoofedSimInfo.value.available) {
- backupRepository.backup(
- spoofedSimInfo = spoofedSimInfo.value,
- type = "auto",
- sendCount = appState.value.executedNum,
- fresh = fresh
- )
- } else {
- clearConv();
- }
- appStateRepository.incrementRequestedNum()
- var requestSuccess = false
- var retry = 0
- var needRest = reset
- withTimeoutOrNull(1.hours) {
- while (true) {
- delay(200)
- needRest = needRest || retry > 2 || appState.value.requestedNum > 5
- try {
- val device =
- ktorClient(appPreferences.value.server).get(DeviceApi.Id(id = uniqueId))
- .body<DeviceResponse>()
- if (isClashInstalled(applicationContext)) {
- val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
- if (TextUtils.isEmpty(device.clashProfile)) {
- prefs.edit()
- .remove("clash_profile")
- .apply()
- stopClash()
- } else {
- val oldProfile = prefs.getString("clash_profile", "")
- if (oldProfile != device.clashProfile) {
- prefs.edit()
- .putString("clash_profile", device.clashProfile)
- .apply()
- changeClashProfile(
- device.pinCountry!!, Base64.encodeToString(
- device.clashProfile!!.toByteArray(),
- Base64.DEFAULT
- )
- )
- delay(5000)
- }
- }
- }
- if (requestMode == 2 && !noBackup) {
- val backup = backupItemDao.findBackupForRestore(
- spoofedSimInfo.value.number,
- System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000
- )
- if (backup != null) {
- if (backupRepository.restore(backup)) {
- requestSuccess = true
- break
- } else {
- backupRepository.backup(
- spoofedSimInfo = spoofedSimInfo.value,
- type = "auto",
- sendCount = 0
- )
- continue
- }
- }
- }
- if (needRest && !appPreferences.value.preventReset) {
- reset()
- retry = 0
- needRest = false
- }
- googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
- withContext(Dispatchers.Main) {
- binding.tvLog.text = "Requesting number..."
- }
- val req = RcsNumberRequest(
- deviceId = uniqueId,
- taskId = currentTaskId
- )
- if (!TextUtils.isEmpty(device.pinCountry)) {
- req.country = device.pinCountry
- }
- val response = ktorClient(appPreferences.value.server).put(
- RcsNumberApi()
- ) {
- contentType(ContentType.Application.Json)
- setBody(req)
- timeout {
- requestTimeoutMillis = 60 * 1000
- socketTimeoutMillis = 60 * 1000
- }
- }
- var rcsNumber = response.body<RcsNumberResponse>()
- Log.i(com.example.modifier.TAG, "requestNumber response: $rcsNumber")
- withContext(Dispatchers.Main) {
- binding.tvLog.text = "Requesting success, waiting for logs..."
- }
- spoofedSimInfoRepository.updateSpoofedSimInfo(
- SpoofedSimInfo(
- 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,
- )
- )
- shellRun(CMD_MESSAGING_APP)
- withContext(Dispatchers.Main) {
- binding.tvLog.text = "Waiting for logs..."
- }
- if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
- Log.e(com.example.modifier.TAG, "RCS number expired, retrying...")
- continue
- }
- var sendOtpTimeout = ChronoUnit.SECONDS.between(
- LocalDateTime.now(),
- rcsNumber.expiryTime
- ).seconds
- if (sendOtpTimeout < 60.seconds) {
- Log.e(com.example.modifier.TAG, "OTP timeout too short, retrying...")
- continue
- }
- if (sendOtpTimeout > 2.minutes) {
- sendOtpTimeout = 2.minutes
- }
- if (googleMessageStateRepository.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_OTP),
- sendOtpTimeout
- ) != RcsConfigureState.WAITING_FOR_OTP
- ) {
- if (!screenController.toggleRcsSwitch(true)) {
- needRest = true
- }
- if (RcsConfigureState.REPLAY_REQUEST == googleMessageStateRepository.rcsConfigureState.value) {
- Log.e(
- com.example.modifier.TAG,
- "REPLAY_REQUEST detected, may reset after 3 retry ($retry)"
- )
- retry++
- }
- Log.e(
- com.example.modifier.TAG,
- "RCS not entered waiting for OTP state, retrying..."
- )
- continue
- }
- launch {
- try {
- ktorClient(appPreferences.value.server).post(
- RcsNumberApi.Id.OtpState(
- RcsNumberApi.Id(
- RcsNumberApi(),
- rcsNumber.id
- )
- )
- )
- } catch (e: Exception) {
- Log.e(com.example.modifier.TAG, "Send OtpState Error: ${e.message}", e)
- }
- }
- if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
- Log.e(com.example.modifier.TAG, "RCS number expired, retrying...")
- continue
- }
- withTimeoutOrNull(60.seconds) {
- while (true) {
- try {
- rcsNumber =
- ktorClient(appPreferences.value.server).get(RcsNumberApi.Id(id = rcsNumber.id))
- .body<RcsNumberResponse>()
- Log.i(com.example.modifier.TAG, "wait for otp response: $rcsNumber")
- if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS || rcsNumber.status == RcsNumberResponse.STATUS_EXPIRED) {
- break
- }
- } catch (exception: Exception) {
- Log.e(
- com.example.modifier.TAG,
- "wait for otp Error: ${exception.stackTrace}"
- )
- }
- delay(2.seconds)
- }
- }
- if (rcsNumber.status != RcsNumberResponse.STATUS_SUCCESS) {
- Log.e(com.example.modifier.TAG, "OTP not received, retrying...")
- continue
- }
- val match =
- Regex("Your Messenger verification code is G-(\\d{6})")
- .find(rcsNumber.message!!)
- if (match != null) {
- val otp = match.groupValues[1]
- Log.i(com.example.modifier.TAG, "OTP: $otp")
- val sender = "3538"
- val msg = "Your Messenger verification code is G-$otp"
- val configured = run configuring@{
- repeat(2) {
- Global.sendSmsIntent(sender, msg)
- val state =
- googleMessageStateRepository.waitForRcsState(
- arrayOf(
- RcsConfigureState.CONFIGURED,
- RcsConfigureState.RETRY
- ), 60.seconds
- )
- when (state) {
- RcsConfigureState.CONFIGURED -> {
- return@configuring true
- }
- RcsConfigureState.RETRY -> {
- googleMessageStateRepository.waitForRcsState(
- arrayOf(RcsConfigureState.WAITING_FOR_OTP),
- 60.seconds
- )
- }
- else -> {
- Log.e(
- com.example.modifier.TAG,
- "verifyOtp fail, retrying..."
- )
- }
- }
- }
- false
- }
- if (!configured) {
- Log.e(com.example.modifier.TAG, "RCS not configured, retrying...")
- continue
- } else {
- launch {
- try {
- ktorClient(appPreferences.value.server).post(
- RcsNumberApi.Id.Configured(
- RcsNumberApi.Id(
- RcsNumberApi(),
- rcsNumber.id
- )
- )
- )
- } catch (e: Exception) {
- Log.e(
- com.example.modifier.TAG,
- "Send ConfiguredState Error: ${e.message}",
- e
- )
- }
- }
- requestSuccess = true
- break
- }
- }
- } catch (e: Exception) {
- Log.e(com.example.modifier.TAG, "requestNumberError: ${e.message}", e)
- }
- }
- }
- if (requestSuccess) {
- spoofedSimInfoRepository.updateSpoofedSimInfo(
- spoofedSimInfo = spoofedSimInfo.value.copy(
- available = true
- )
- )
- appStateRepository.resetSuccessNum()
- appStateRepository.resetExecutedNum()
- Log.i(com.example.modifier.TAG, "requestNumber success")
- delay(5000)
- shellRun(PACKAGE_MESSAGING.kill(), "sleep 1", CMD_MESSAGING_APP)
- delay(2000)
- } else {
- Log.e(com.example.modifier.TAG, "requestNumber failed")
- appStateRepository.updateSend(false)
- withContext(Dispatchers.Main) {
- binding.swSend.isChecked = false
- binding.btnReq.backgroundTintList =
- ContextCompat.getColorStateList(binding.root.context, R.color.btn_color_error)
- }
- }
- appStateRepository.updateRuntimeFlags(requesting = false)
- }
- private suspend fun checkRcsConnectivity(): Boolean = run checkRcsConnection@{
- repeat(3) {
- Log.i(com.example.modifier.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(com.example.modifier.TAG, "RCS is connected")
- shellRun(CMD_BACK)
- return@checkRcsConnection true
- } else {
- Log.i(com.example.modifier.TAG, "RCS not connected, retrying...")
- }
- shellRun(CMD_BACK, "sleep ${it * 2}")
- }
- false
- }
- suspend fun checkRcsAvailability(): Boolean {
- appStateRepository.updateRuntimeFlags(checkingConnection = true)
- val availability = run checkAvailability@{
- val rcsConnected = checkRcsConnectivity()
- if (!rcsConnected) {
- return@checkAvailability false
- }
- var config: SysConfigResponse
- val checkRcsAvailabilityNumbers = mutableListOf<String>()
- withTimeoutOrNull(60.seconds) {
- try {
- config = ktorClient(appPreferences.value.server).get(
- SysConfigApi.Id(
- SysConfigApi(),
- "check_availability_numbers"
- )
- )
- .body<SysConfigResponse>()
- Log.i(com.example.modifier.TAG, "sysConfig response: $config")
- checkRcsAvailabilityNumbers.addAll(
- config.value.split(",").map { it.trim() })
- } catch (exception: Exception) {
- Log.e(
- com.example.modifier.TAG,
- "sysConfig Error: ${exception.message}",
- exception
- )
- }
- }
- if (checkRcsAvailabilityNumbers.isEmpty()) {
- Log.e(com.example.modifier.TAG, "checkRcsAvailabilityNumbers is empty")
- return true
- }
- checkRcsAvailabilityNumbers.forEach {
- startActivity(smsIntent(it, ""))
- val s = withTimeoutOrNull(5.seconds) {
- while (true) {
- val traverseResult = TraverseResult()
- screenInspector.traverseNode(traverseResult)
- if (traverseResult.isRcsCapable) {
- return@withTimeoutOrNull true
- } else {
- Log.i(
- com.example.modifier.TAG,
- "checkRcsAvailability: RCS not detected"
- )
- }
- delay(200)
- }
- }
- if (s == true) {
- Log.i(com.example.modifier.TAG, "checkRcsAvailability: $it success")
- delay(1000)
- return@checkAvailability true
- }
- }
- false
- }
- appStateRepository.updateRuntimeFlags(checkingConnection = false)
- return availability
- }
- private suspend fun storeNumbers() {
- repeat(100) {
- requestNumber(reset = true, fresh = it > 0)
- delay(5000)
- }
- }
- }
|