x1ongzhu 1 yıl önce
ebeveyn
işleme
25dd566647

+ 1 - 355
app/src/main/java/com/example/modifier/Global.kt

@@ -18,7 +18,7 @@ import com.example.modifier.data.BackupItem
 import com.example.modifier.data.BackupItemDao
 import com.example.modifier.extension.disable
 import com.example.modifier.extension.enable
-import com.example.modifier.model.SimInfo
+import com.example.modifier.model.SpoofedSimInfo
 import com.example.modifier.serializer.Json
 import com.example.modifier.utils.clearConv
 import com.example.modifier.utils.hasPermission
@@ -41,201 +41,6 @@ import kotlin.system.exitProcess
 
 
 object Global {
-    @JvmField
-    var simInfo: SimInfo = SimInfo(
-        number = "",
-        mcc = "",
-        mnc = "",
-        iccid = "",
-        imsi = "",
-        imei = "",
-        country = "",
-        areaCode = "",
-        available = false,
-        carrierId = "1",
-        carrierName = "T-Mobile"
-    )
-
-    @JvmStatic
-    fun load() {
-        val context = Utils.getContext()
-        try {
-            val file = File(ContextCompat.getDataDir(context), "config.json")
-            if (file.exists()) {
-                val gson = Gson()
-                val json = FileUtils.readFileToString(file, "UTF-8")
-                simInfo = gson.fromJson(json, SimInfo::class.java)
-            }
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
-    @JvmStatic
-    fun save() {
-        val context = Utils.getContext()
-        val file = File(ContextCompat.getDataDir(context), "config.json")
-        FileUtils.write(file, Json.encodeToString(simInfo), "UTF-8")
-    }
-
-    @SuppressLint("MissingPermission")
-    @JvmStatic
-    suspend fun save(simInfo: SimInfo, suspend: Boolean? = true) {
-        Global.simInfo.mcc = simInfo.mcc
-        Global.simInfo.mnc = simInfo.mnc
-        Global.simInfo.number = simInfo.number
-        Global.simInfo.country = simInfo.country
-        Global.simInfo.areaCode = simInfo.areaCode
-        Global.simInfo.iccid = simInfo.iccid
-        Global.simInfo.imei = simInfo.imei
-        Global.simInfo.imsi = simInfo.imsi
-        Global.simInfo.available = simInfo.available
-        Global.simInfo.carrierId = simInfo.carrierId
-        Global.simInfo.carrierName = simInfo.carrierName
-
-        try {
-            if (suspend == true) {
-                suspendPackage(PACKAGE_GMS, PACKAGE_MESSAGING)
-            }
-            save()
-
-            shellRun(
-                "setprop persist.spoof.mcc ${simInfo.mcc}",
-                "setprop persist.spoof.mnc ${simInfo.mnc}",
-                "setprop persist.spoof.number ${simInfo.number}",
-                "setprop persist.spoof.country ${simInfo.country}",
-                "setprop persist.spoof.iccid ${simInfo.iccid}",
-                "setprop persist.spoof.imei ${simInfo.imei}",
-                "setprop persist.spoof.imsi ${simInfo.imsi}",
-                "setprop persist.spoof.carrier.id ${
-                    simInfo.carrierId.replace(
-                        "^\\W*\$".toRegex(),
-                        "''"
-                    )
-                }",
-                "setprop persist.spoof.carrier.name ${
-                    simInfo.carrierName.replace(
-                        "^\\W*\$".toRegex(),
-                        "''"
-                    )
-                }",
-            )
-
-            val context = Utils.getContext()
-            val subscriptionManager: SubscriptionManager =
-                context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
-
-            if (hasPermission(Manifest.permission.READ_PHONE_STATE)) {
-                val simCount = subscriptionManager.activeSubscriptionInfoCountMax
-                for (i in 0 until simCount) {
-                    val info = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
-                    if (info != null) {
-                        val mcc = info.mccString
-                        val mnc = info.mncString
-                        Log.i(com.example.modifier.TAG, "mccmnc spoofed: $mcc$mnc")
-                    }
-                }
-            }
-            if (suspend == true) {
-                resumePackage(PACKAGE_GMS, PACKAGE_MESSAGING)
-            }
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-    }
-
-    private fun isOldVersion(): Boolean {
-        val info =
-            Utils.getContext().packageManager.getPackageInfo("com.google.android.apps.messaging", 0)
-        var oldVersion = false
-        if (info != null) {
-            if (info.versionCode < 170545910) {
-                oldVersion = true
-            }
-        }
-        return oldVersion
-    }
-
-    suspend fun saveMock() {
-        if (isOldVersion()) {
-            val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
-                it.readText()
-            }
-            // get random number
-            content.split("\n")
-                .filter { it.isNotBlank() }
-                .shuffled()
-                .firstOrNull()
-                ?.let {
-                    save(
-                        SimInfo(
-                            number = it,
-                            mcc = "310",
-                            mnc = "150",
-                            iccid = genICCID("310", "1"),
-                            "310150" + RandomStringUtils.randomNumeric(9),
-                            Utils.generateIMEI(),
-                            "us",
-                            "1",
-                            available = false,
-                            carrierId = "1779",
-                            carrierName = "Cricket Wireless"
-                        )
-                    )
-                }
-        } else {
-            val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
-                it.readText()
-            }
-            // get random number
-            content.split("\n")
-                .filter { it.isNotBlank() }
-                .shuffled()
-                .firstOrNull()
-                ?.let {
-                    save(
-                        SimInfo(
-                            number = it,
-                            mcc = "310",
-                            mnc = "240",
-                            iccid = genICCID("310", "1"),
-                            "310240" + RandomStringUtils.randomNumeric(9),
-                            Utils.generateIMEI(),
-                            "us",
-                            "1",
-                            available = false,
-                            "1",
-                            "T-Mobile"
-                        )
-                    )
-                }
-        }
-    }
-
-//    @JvmStatic
-//    fun stop(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
-//        try {
-//            val cmds: MutableList<String> = ArrayList()
-//            if (gsf == true) {
-//                cmds.add(PACKAGE_GSF.kill())
-//                cmds.add("echo 'stopped gsf'")
-//            }
-//            if (gms == true) {
-//                cmds.add(PACKAGE_GMS.kill())
-//                cmds.add("echo 'stopped gms'")
-//                Thread.sleep(1000)
-//            }
-//            if (sms == true) {
-//                cmds.add(PACKAGE_MESSAGING.kill())
-//                cmds.add("echo 'stopped sms'")
-//            }
-//            Utils.runAsRoot(*cmds.toTypedArray<String>())
-//        } catch (e: Exception) {
-//            e.printStackTrace()
-//        }
-//    }
-
-
 
     fun sqlite3path(): String {
         val context = Utils.getContext()
@@ -255,7 +60,6 @@ object Global {
         return file.path
     }
 
-
     @JvmStatic
     fun sendSmsIntent(sender: String, msg: String) {
         val intent = Intent()
@@ -269,165 +73,7 @@ object Global {
         context.sendBroadcast(intent)
     }
 
-    @SuppressLint("DefaultLocale")
-    @JvmStatic
-    fun genICCID(mnc: String, areaCode: String): String {
-        val prefix = String.format("89%02d%s", areaCode.toInt(), mnc)
-        return prefix + RandomStringUtils.randomNumeric(20 - prefix.length)
-    }
-
-    @SuppressLint("SdCardPath")
-    @JvmStatic
-    suspend fun backup(
-        backupItemDao: BackupItemDao,
-        type: String,
-        sendCount: Int,
-        fresh: Boolean = false
-    ): BackupItem {
-        clearConv()
-        delay(3000)
-//        ModifierService.instance!!.toggleRcsSwitch(false)
-        val context = Utils.getContext()
-        val dest = File(
-            ContextCompat.getExternalFilesDirs(context, "backup")[0],
-            System.currentTimeMillis().toString()
-        )
-        dest.mkdirs()
-        val file = File(ContextCompat.getDataDir(context), "config.json")
-        if (!file.exists()) {
-            throw Exception("Config file not found")
-        }
-        withContext(Dispatchers.IO) {
-            IOUtils.copy(
-                Files.newInputStream(file.toPath()),
-                Files.newOutputStream(File(dest, "config.json").toPath())
-            )
-        }
-
-        val packages = mutableListOf(
-            PACKAGE_MESSAGING,
-            PACKAGE_GMS,
-            PACKAGE_GSF
-        )
-        packages.forEach {
-            File(dest, it).mkdirs()
-        }
-
-        val cmds = mutableListOf<String>()
-
-        for (pkg in packages) {
-            if (!shellRun("ls /data/data/$pkg").component2()
-                    .contains("No such file or directory")
-            ) {
-                cmds.add("tar zcpf $dest/$pkg/data.tar.gz -C /data/data $pkg ")
-            }
-            if (!shellRun("ls /data/user_de/0/$pkg").component2()
-                    .contains("No such file or directory")
-            ) {
-                cmds.add("tar zcpf $dest/$pkg/data_de.tar.gz -C /data/user_de/0 $pkg ")
-            }
-            if (!shellRun("ls /sdcard/Android/data/$pkg").component2()
-                    .contains("No such file or directory")
-            ) {
-                cmds.add("tar zcpf $dest/$pkg/data_ext.tar.gz -C /sdcard/Android/data $pkg ")
-            }
-        }
 
-        shellRun(*cmds.toTypedArray())
-
-        val backup = BackupItem(
-            createdAt = Date().time,
-            number = simInfo.number,
-            code = simInfo.areaCode,
-            country = simInfo.country,
-            mcc = simInfo.mcc,
-            mnc = simInfo.mnc,
-            imei = simInfo.imei,
-            imsi = simInfo.imsi,
-            iccid = simInfo.iccid,
-            path = dest.path,
-            sendCount = sendCount,
-            lastUse = Date().time,
-            type = type,
-            fresh = fresh,
-            carrierId = Optional.ofNullable(simInfo.carrierId).orElse("1"),
-            carrierName = Optional.ofNullable(simInfo.carrierName).orElse("T-Mobile")
-        )
-
-        backupItemDao.findBackupForNumber(simInfo.country, simInfo.number)?.let {
-            File(it.path).deleteRecursively()
-            backupItemDao.delete(it)
-            backup.sendCount += it.sendCount
-            backup.fresh = false
-        }
-
-        backup.id = backupItemDao.insert(backup).toInt()
-        return backup
-    }
-
-    @SuppressLint("SdCardPath")
-    @JvmStatic
-    suspend fun restore(backup: BackupItem): Boolean {
-        save(
-            SimInfo(
-                number = backup.number,
-                mcc = backup.mcc,
-                mnc = backup.mnc,
-                country = backup.country,
-                areaCode = backup.code,
-                iccid = backup.iccid,
-                imei = backup.imei,
-                imsi = backup.imsi,
-                available = true,
-                carrierId = backup.carrierId,
-                carrierName = backup.carrierName
-            ), false
-        )
-        val packages = mutableListOf(
-            PACKAGE_MESSAGING,
-            PACKAGE_GMS,
-            PACKAGE_GSF
-        )
-
-        shellRun(
-            PACKAGE_MESSAGING.disable(),
-            PACKAGE_GMS.disable(),
-            PACKAGE_GSF.disable()
-        )
-        for (pkg in packages) {
-            for (item in listOf("data.tar.gz", "data_de.tar.gz", "data_ext.tar.gz")) {
-                val file = File("${backup.path}/$pkg/$item")
-                if (!file.exists()) {
-                    continue
-                }
-                val dest = when (item) {
-                    "data.tar.gz" -> "/data/data"
-                    "data_de.tar.gz" -> "/data/user_de/0"
-                    "data_ext.tar.gz" -> "/sdcard/Android/data"
-                    else -> ""
-                }
-                shellRun(
-                    "find $dest/$pkg -type f -delete",
-                    "tar zxf ${file.path} -C $dest --exclude \"**/AndroidPlatformServices.apk\""
-                )
-            }
-        }
-        shellRun(
-//            "pm clear com.google.android.gsf",
-            PACKAGE_GSF.enable(),
-            "sleep 1",
-//            "pm clear com.google.android.gms",
-            PACKAGE_GMS.enable(),
-//            "sleep 30",
-            PACKAGE_MESSAGING.enable(),
-            "sleep 3",
-            CMD_MESSAGING_APP,
-            "sleep 5",
-            "am start -a android.intent.action.SENDTO -d sms:+18583199738 --es sms_body \"Test\" --ez exit_on_sent false"
-        )
-
-        return true
-    }
 
     @JvmStatic
     fun restartModifier() {

+ 0 - 74
app/src/main/java/com/example/modifier/Utils.java

@@ -42,81 +42,7 @@ public class Utils {
         return null;
     }
 
-    public static String generateIMEI() {
-        int pos;
-        int[] str = Arrays.stream(("35684610" + RandomStringUtils.randomNumeric(7)).split("")).mapToInt(Integer::parseInt).toArray();
-        int sum = 0;
-        int final_digit;
-        int t;
-        int len_offset;
-        int len = 15;
-        String imei = "";
 
-        len_offset = (len + 1) % 2;
-        for (pos = 0; pos < len - 1; pos++) {
-            if ((pos + len_offset) % 2 != 0) {
-                t = str[pos] * 2;
-                if (t > 9) {
-                    t -= 9;
-                }
-                sum += t;
-            } else {
-                sum += str[pos];
-            }
-        }
-
-        final_digit = (10 - (sum % 10)) % 10;
-        str[len - 1] = final_digit;
-
-        for (int d : str) {
-            imei += String.valueOf(d);
-        }
-
-        return imei;
-    }
-
-    public static String generateIMEI1() {
-        int pos;
-        int[] str = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-        int sum = 0;
-        int final_digit;
-        int t;
-        int len_offset;
-        int len = 15;
-        String imei = "";
-
-        String[] rbi = new String[]{"01", "10", "30", "33", "35", "44", "45", "49", "50", "51", "52", "53", "54", "86", "91", "98", "99"};
-        String[] arr = rbi[(int) Math.floor(Math.random() * rbi.length)].split("");
-        str[0] = Integer.parseInt(arr[0]);
-        str[1] = Integer.parseInt(arr[1]);
-        pos = 2;
-
-        while (pos < len - 1) {
-            str[pos++] = (int) (Math.floor(Math.random() * 10) % 10);
-        }
-
-        len_offset = (len + 1) % 2;
-        for (pos = 0; pos < len - 1; pos++) {
-            if ((pos + len_offset) % 2 != 0) {
-                t = str[pos] * 2;
-                if (t > 9) {
-                    t -= 9;
-                }
-                sum += t;
-            } else {
-                sum += str[pos];
-            }
-        }
-
-        final_digit = (10 - (sum % 10)) % 10;
-        str[len - 1] = final_digit;
-
-        for (int d : str) {
-            imei += String.valueOf(d);
-        }
-
-        return imei;
-    }
 
     public static void makeLoadingButton(Context context, MaterialButton button) {
         CircularProgressIndicatorSpec spec = new CircularProgressIndicatorSpec(context, null, 0,

+ 4 - 3
app/src/main/java/com/example/modifier/adapter/BackupAdapter.kt

@@ -17,6 +17,7 @@ import com.example.modifier.adapter.BackupAdapter.BackupViewHolder
 import com.example.modifier.data.BackupItem
 import com.example.modifier.data.BackupItemDao
 import com.example.modifier.databinding.ItemBackupBinding
+import com.example.modifier.repo.BackupRepository
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -30,7 +31,7 @@ import java.util.Date
 class BackupAdapter(
     private val context: Context,
     private val backups: MutableList<BackupItem>,
-    private val backupItemDao: BackupItemDao
+    private val backupRepository: BackupRepository
 ) :
     RecyclerView.Adapter<BackupViewHolder>() {
 
@@ -65,7 +66,7 @@ class BackupAdapter(
                     Utils.makeLoadingButton(context, holder.binding.btnRestore)
                     CoroutineScope(Dispatchers.IO).launch {
                         try {
-                            Global.restore(backup)
+                            backupRepository.restore(backup)
                         } catch (e: Exception) {
                             e.printStackTrace()
                         }
@@ -105,7 +106,7 @@ class BackupAdapter(
                         withContext(Dispatchers.Main) {
                             notifyItemRemoved(position)
                         }
-                        backupItemDao.delete(backup)
+                        backupRepository.delete(backup)
                     }
 
                 }

+ 0 - 9
app/src/main/java/com/example/modifier/data/MessageLogData.kt

@@ -1,9 +0,0 @@
-package com.example.modifier.data
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-
-class MessageLogData : MutableLiveData<String>() {
-
-
-}

+ 0 - 6
app/src/main/java/com/example/modifier/http/KtorClient.kt

@@ -1,8 +1,5 @@
 package com.example.modifier.http
 
-import android.util.Log
-import com.example.modifier.Utils
-import com.example.modifier.data.AppPreferencesRepository
 import com.example.modifier.http.response.ErrorResponse
 import io.ktor.client.HttpClient
 import io.ktor.client.call.body
@@ -23,9 +20,6 @@ import io.ktor.client.plugins.resources.Resources
 import io.ktor.client.utils.unwrapCancellationException
 import io.ktor.serialization.kotlinx.json.json
 import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.last
-import kotlinx.coroutines.runBlocking
 import kotlinx.serialization.ExperimentalSerializationApi
 import kotlinx.serialization.json.Json
 

+ 1 - 1
app/src/main/java/com/example/modifier/model/SimInfo.kt → app/src/main/java/com/example/modifier/model/SpoofedSimInfo.kt

@@ -3,7 +3,7 @@ package com.example.modifier.model
 import kotlinx.serialization.Serializable
 
 @Serializable
-data class SimInfo(
+data class SpoofedSimInfo(
     var number: String,
     var mcc: String,
     var mnc: String,

+ 9 - 5
app/src/main/java/com/example/modifier/data/AppPreferencesRepository.kt → app/src/main/java/com/example/modifier/repo/AppPreferencesRepository.kt

@@ -1,4 +1,4 @@
-package com.example.modifier.data
+package com.example.modifier.repo
 
 import android.annotation.SuppressLint
 import android.content.Context
@@ -9,6 +9,7 @@ import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.datastore.preferences.preferencesDataStore
 import com.example.modifier.BuildConfig
+import com.example.modifier.data.AppPreferences
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
@@ -16,9 +17,7 @@ import kotlin.coroutines.coroutineContext
 
 val Context.appPreferencesDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.appPreferences")
 
-class AppPreferencesRepository(
-    private val context: Context
-) {
+class AppPreferencesRepository(private val context: Context) {
 
     private object PreferencesKeys {
         val SERVER = stringPreferencesKey("server")
@@ -39,7 +38,12 @@ class AppPreferencesRepository(
                 preferences[PreferencesKeys.ID] = id
             }
         }
-        val name = it[PreferencesKeys.NAME] ?: Build.DEVICE
+        val name =
+            it[PreferencesKeys.NAME] ?: context.getSharedPreferences(
+                BuildConfig.APPLICATION_ID,
+                Context.MODE_PRIVATE
+            )
+                .getString("name", Build.DEVICE)!!
         val preventClean = it[PreferencesKeys.PREVENT_CLEAN] ?: false
         val preventRequest = it[PreferencesKeys.PREVENT_REQUEST] ?: false
         val preventReset = it[PreferencesKeys.PREVENT_RESET] ?: false

+ 9 - 3
app/src/main/java/com/example/modifier/data/AppStateRepository.kt → app/src/main/java/com/example/modifier/repo/AppStateRepository.kt

@@ -1,4 +1,4 @@
-package com.example.modifier.data
+package com.example.modifier.repo
 
 import android.content.Context
 import androidx.datastore.preferences.core.booleanPreferencesKey
@@ -8,6 +8,8 @@ import androidx.datastore.preferences.preferencesDataStore
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.asFlow
 import com.example.modifier.BuildConfig
+import com.example.modifier.data.AppRuntimeFlags
+import com.example.modifier.data.AppState
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
@@ -30,11 +32,15 @@ class AppStateRepository(
 
     private val appStateFlow =
         context.appStateDataStore.data.combine(appRuntimeFlags.asFlow()) { preferences, runtimeFlags ->
+            val sPrefs =
+                context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
             AppState(
                 send = preferences[PreferencesKeys.SEND] ?: false,
                 executedNum = preferences[PreferencesKeys.EXECUTED_NUM] ?: 0,
-                successNum = preferences[PreferencesKeys.SUCCESS_NUM] ?: 0,
-                requestedNum = preferences[PreferencesKeys.REQUESTED_NUM] ?: 0,
+                successNum = preferences[PreferencesKeys.SUCCESS_NUM]
+                    ?: sPrefs.getInt("sendCount", 0),
+                requestedNum = preferences[PreferencesKeys.REQUESTED_NUM]
+                    ?: sPrefs.getInt("requestNumberCount", 0),
                 running = runtimeFlags.running,
                 requesting = runtimeFlags.requesting,
                 preparing = runtimeFlags.preparing,

+ 185 - 0
app/src/main/java/com/example/modifier/repo/BackupRepository.kt

@@ -0,0 +1,185 @@
+package com.example.modifier.repo
+
+import android.content.Context
+import androidx.core.content.ContextCompat
+import com.example.modifier.Utils
+import com.example.modifier.constants.CMD_MESSAGING_APP
+import com.example.modifier.constants.PACKAGE_GMS
+import com.example.modifier.constants.PACKAGE_GSF
+import com.example.modifier.constants.PACKAGE_MESSAGING
+import com.example.modifier.data.BackupItem
+import com.example.modifier.data.BackupItemDao
+import com.example.modifier.extension.disable
+import com.example.modifier.extension.enable
+import com.example.modifier.model.SpoofedSimInfo
+import com.example.modifier.utils.clearConv
+import com.example.modifier.utils.shellRun
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.withContext
+import org.apache.commons.io.IOUtils
+import java.io.File
+import java.nio.file.Files
+import java.util.Date
+import java.util.Optional
+
+class BackupRepository(
+    private val context: Context,
+    private val dao: BackupItemDao,
+    private val spoofedSimInfoRepository: SpoofedSimInfoRepository
+) {
+
+    suspend fun backup(
+        spoofedSimInfo: SpoofedSimInfo,
+        type: String,
+        sendCount: Int,
+        fresh: Boolean = false
+    ): BackupItem {
+        clearConv()
+        delay(3000)
+//        ModifierService.instance!!.toggleRcsSwitch(false)
+        val context = Utils.getContext()
+        val dest = File(
+            ContextCompat.getExternalFilesDirs(context, "backup")[0],
+            System.currentTimeMillis().toString()
+        )
+        dest.mkdirs()
+        val file = File(ContextCompat.getDataDir(context), "config.json")
+        if (!file.exists()) {
+            throw Exception("Config file not found")
+        }
+        withContext(Dispatchers.IO) {
+            IOUtils.copy(
+                Files.newInputStream(file.toPath()),
+                Files.newOutputStream(File(dest, "config.json").toPath())
+            )
+        }
+
+        val packages = mutableListOf(
+            PACKAGE_MESSAGING,
+            PACKAGE_GMS,
+            PACKAGE_GSF
+        )
+        packages.forEach {
+            File(dest, it).mkdirs()
+        }
+
+        val cmds = mutableListOf<String>()
+
+        for (pkg in packages) {
+            if (!shellRun("ls /data/data/$pkg").second
+                    .contains("No such file or directory")
+            ) {
+                cmds.add("tar zcpf $dest/$pkg/data.tar.gz -C /data/data $pkg ")
+            }
+            if (!shellRun("ls /data/user_de/0/$pkg").second
+                    .contains("No such file or directory")
+            ) {
+                cmds.add("tar zcpf $dest/$pkg/data_de.tar.gz -C /data/user_de/0 $pkg ")
+            }
+            if (!shellRun("ls /sdcard/Android/data/$pkg").second
+                    .contains("No such file or directory")
+            ) {
+                cmds.add("tar zcpf $dest/$pkg/data_ext.tar.gz -C /sdcard/Android/data $pkg ")
+            }
+        }
+
+        shellRun(*cmds.toTypedArray())
+
+        val backup = BackupItem(
+            createdAt = Date().time,
+            number = spoofedSimInfo.number,
+            code = spoofedSimInfo.areaCode,
+            country = spoofedSimInfo.country,
+            mcc = spoofedSimInfo.mcc,
+            mnc = spoofedSimInfo.mnc,
+            imei = spoofedSimInfo.imei,
+            imsi = spoofedSimInfo.imsi,
+            iccid = spoofedSimInfo.iccid,
+            path = dest.path,
+            sendCount = sendCount,
+            lastUse = Date().time,
+            type = type,
+            fresh = fresh,
+            carrierId = Optional.ofNullable(spoofedSimInfo.carrierId).orElse("1"),
+            carrierName = Optional.ofNullable(spoofedSimInfo.carrierName).orElse("T-Mobile")
+        )
+
+        dao.findBackupForNumber(spoofedSimInfo.country, spoofedSimInfo.number)?.let {
+            File(it.path).deleteRecursively()
+            dao.delete(it)
+            backup.sendCount += it.sendCount
+            backup.fresh = false
+        }
+
+        backup.id = dao.insert(backup).toInt()
+        return backup
+    }
+
+    suspend fun restore(backup: BackupItem): Boolean {
+        spoofedSimInfoRepository.updateSpoofedSimInfo(
+            SpoofedSimInfo(
+                number = backup.number,
+                mcc = backup.mcc,
+                mnc = backup.mnc,
+                country = backup.country,
+                areaCode = backup.code,
+                iccid = backup.iccid,
+                imei = backup.imei,
+                imsi = backup.imsi,
+                available = true,
+                carrierId = backup.carrierId,
+                carrierName = backup.carrierName
+            ),
+            false
+        )
+        val packages = mutableListOf(
+            PACKAGE_MESSAGING,
+            PACKAGE_GMS,
+            PACKAGE_GSF
+        )
+
+        shellRun(
+            PACKAGE_MESSAGING.disable(),
+            PACKAGE_GMS.disable(),
+            PACKAGE_GSF.disable()
+        )
+        for (pkg in packages) {
+            for (item in listOf("data.tar.gz", "data_de.tar.gz", "data_ext.tar.gz")) {
+                val file = File("${backup.path}/$pkg/$item")
+                if (!file.exists()) {
+                    continue
+                }
+                val dest = when (item) {
+                    "data.tar.gz" -> "/data/data"
+                    "data_de.tar.gz" -> "/data/user_de/0"
+                    "data_ext.tar.gz" -> "/sdcard/Android/data"
+                    else -> ""
+                }
+                shellRun(
+                    "find $dest/$pkg -type f -delete",
+                    "tar zxf ${file.path} -C $dest --exclude \"**/AndroidPlatformServices.apk\""
+                )
+            }
+        }
+        shellRun(
+//            "pm clear com.google.android.gsf",
+            PACKAGE_GSF.enable(),
+            "sleep 1",
+//            "pm clear com.google.android.gms",
+            PACKAGE_GMS.enable(),
+//            "sleep 30",
+            PACKAGE_MESSAGING.enable(),
+            "sleep 3",
+            CMD_MESSAGING_APP,
+            "sleep 5",
+            "am start -a android.intent.action.SENDTO -d sms:+18583199738 --es sms_body \"Test\" --ez exit_on_sent false"
+        )
+
+        return true
+    }
+
+    suspend fun delete(backupItem: BackupItem) {
+        dao.delete(backupItem)
+    }
+}

+ 1 - 1
app/src/main/java/com/example/modifier/data/GoogleMessageStateRepository.kt → app/src/main/java/com/example/modifier/repo/GoogleMessageStateRepository.kt

@@ -1,4 +1,4 @@
-package com.example.modifier.data
+package com.example.modifier.repo
 
 import android.content.Context
 import android.os.Handler

+ 200 - 0
app/src/main/java/com/example/modifier/repo/SpoofedSimInfoRepository.kt

@@ -0,0 +1,200 @@
+package com.example.modifier.repo
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.content.Context
+import android.telephony.SubscriptionManager
+import android.util.Log
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.example.modifier.BuildConfig
+import com.example.modifier.Utils
+import com.example.modifier.constants.PACKAGE_GMS
+import com.example.modifier.constants.PACKAGE_MESSAGING
+import com.example.modifier.model.SpoofedSimInfo
+import com.example.modifier.utils.genICCID
+import com.example.modifier.utils.genIMEI
+import com.example.modifier.utils.genIMSI
+import com.example.modifier.utils.hasPermission
+import com.example.modifier.utils.isOldVersion
+import com.example.modifier.utils.resumePackage
+import com.example.modifier.utils.shellRun
+import com.example.modifier.utils.suspendPackage
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlin.coroutines.coroutineContext
+
+val Context.simInfoDataStore by preferencesDataStore(name = "${BuildConfig.APPLICATION_ID}.simInfo")
+
+class SpoofedSimInfoRepository(private val context: Context) {
+
+    private object PreferencesKeys {
+        val NUMBER = stringPreferencesKey("number")
+        val MCC = stringPreferencesKey("mcc")
+        val MNC = stringPreferencesKey("mnc")
+        val ICCID = stringPreferencesKey("iccid")
+        val IMSI = stringPreferencesKey("imsi")
+        val IMEI = stringPreferencesKey("imei")
+        val COUNTRY = stringPreferencesKey("country")
+        val AREA_CODE = stringPreferencesKey("area_code")
+        val AVAILABLE = booleanPreferencesKey("available")
+        val CARRIER_ID = stringPreferencesKey("carrier_id")
+        val CARRIER_NAME = stringPreferencesKey("carrier_name")
+    }
+
+    val simInfoFlow = context.simInfoDataStore.data.map {
+        val number = it[PreferencesKeys.NUMBER] ?: ""
+        val mcc = it[PreferencesKeys.MCC] ?: ""
+        val mnc = it[PreferencesKeys.MNC] ?: ""
+        val iccid = it[PreferencesKeys.ICCID] ?: ""
+        val imsi = it[PreferencesKeys.IMSI] ?: ""
+        val imei = it[PreferencesKeys.IMEI] ?: ""
+        val country = it[PreferencesKeys.COUNTRY] ?: ""
+        val areaCode = it[PreferencesKeys.AREA_CODE] ?: ""
+        val available = it[PreferencesKeys.AVAILABLE] ?: false
+        val carrierId = it[PreferencesKeys.CARRIER_ID] ?: ""
+        val carrierName = it[PreferencesKeys.CARRIER_NAME] ?: ""
+        SpoofedSimInfo(
+            number = number,
+            mcc = mcc,
+            mnc = mnc,
+            iccid = iccid,
+            imsi = imsi,
+            imei = imei,
+            country = country,
+            areaCode = areaCode,
+            available = available,
+            carrierId = carrierId,
+            carrierName = carrierName
+        )
+    }
+
+    suspend fun stateFlow() = simInfoFlow.stateIn(CoroutineScope(coroutineContext))
+
+    suspend fun value() {
+    }
+
+    @SuppressLint("MissingPermission")
+    suspend fun updateSpoofedSimInfo(spoofedSimInfo: SpoofedSimInfo, suspend: Boolean? = true) {
+        context.simInfoDataStore.edit {
+            it[PreferencesKeys.NUMBER] = spoofedSimInfo.number
+            it[PreferencesKeys.MCC] = spoofedSimInfo.mcc
+            it[PreferencesKeys.MNC] = spoofedSimInfo.mnc
+            it[PreferencesKeys.ICCID] = spoofedSimInfo.iccid
+            it[PreferencesKeys.IMSI] = spoofedSimInfo.imsi
+            it[PreferencesKeys.IMEI] = spoofedSimInfo.imei
+            it[PreferencesKeys.COUNTRY] = spoofedSimInfo.country
+            it[PreferencesKeys.AREA_CODE] = spoofedSimInfo.areaCode
+            it[PreferencesKeys.AVAILABLE] = spoofedSimInfo.available
+            it[PreferencesKeys.CARRIER_ID] = spoofedSimInfo.carrierId
+            it[PreferencesKeys.CARRIER_NAME] = spoofedSimInfo.carrierName
+        }
+        try {
+            if (suspend == true) {
+                suspendPackage(PACKAGE_GMS, PACKAGE_MESSAGING)
+            }
+
+            shellRun(
+                "setprop persist.spoof.number ${spoofedSimInfo.number}",
+                "setprop persist.spoof.mcc ${spoofedSimInfo.mcc}",
+                "setprop persist.spoof.mnc ${spoofedSimInfo.mnc}",
+                "setprop persist.spoof.iccid ${spoofedSimInfo.iccid}",
+                "setprop persist.spoof.imsi ${spoofedSimInfo.imsi}",
+                "setprop persist.spoof.imei ${spoofedSimInfo.imei}",
+                "setprop persist.spoof.country ${spoofedSimInfo.country}",
+                "setprop persist.spoof.carrier.id ${
+                    spoofedSimInfo.carrierId.replace(
+                        "^\\W*\$".toRegex(),
+                        "''"
+                    )
+                }",
+                "setprop persist.spoof.carrier.name ${
+                    spoofedSimInfo.carrierName.replace(
+                        "^\\W*\$".toRegex(),
+                        "''"
+                    )
+                }",
+            )
+
+            val context = Utils.getContext()
+            val subscriptionManager: SubscriptionManager =
+                context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
+
+            if (hasPermission(Manifest.permission.READ_PHONE_STATE)) {
+                val simCount = subscriptionManager.activeSubscriptionInfoCountMax
+                for (i in 0 until simCount) {
+                    val info = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
+                    if (info != null) {
+                        val mcc = info.mccString
+                        val mnc = info.mncString
+                        Log.i(com.example.modifier.TAG, "mccmnc spoofed: $mcc$mnc")
+                    }
+                }
+            }
+            if (suspend == true) {
+                resumePackage(PACKAGE_GMS, PACKAGE_MESSAGING)
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    suspend fun mock() {
+        if (isOldVersion(context)) {
+            val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
+                it.readText()
+            }
+            // get random number
+            content.split("\n")
+                .filter { it.isNotBlank() }
+                .shuffled()
+                .firstOrNull()
+                ?.let {
+                    updateSpoofedSimInfo(
+                        SpoofedSimInfo(
+                            number = it,
+                            mcc = "310",
+                            mnc = "150",
+                            iccid = genICCID("310", "1"),
+                            imsi = genIMSI("310150"),
+                            imei = genIMEI(),
+                            country = "us",
+                            areaCode = "1",
+                            available = false,
+                            carrierId = "1779",
+                            carrierName = "Cricket Wireless"
+                        )
+                    )
+                }
+        } else {
+            val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
+                it.readText()
+            }
+            // get random number
+            content.split("\n")
+                .filter { it.isNotBlank() }
+                .shuffled()
+                .firstOrNull()
+                ?.let {
+                    updateSpoofedSimInfo(
+                        SpoofedSimInfo(
+                            number = it,
+                            mcc = "310",
+                            mnc = "240",
+                            iccid = genICCID("310", "1"),
+                            imsi = genIMSI("310240"),
+                            imei = genIMEI(),
+                            country = "us",
+                            areaCode = "1",
+                            available = false,
+                            carrierId = "1",
+                            carrierName = "T-Mobile"
+                        )
+                    )
+                }
+        }
+    }
+}

+ 53 - 39
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -1,12 +1,10 @@
 package com.example.modifier.service
 
 import android.accessibilityservice.AccessibilityService
-import android.accessibilityservice.AccessibilityServiceInfo
 import android.annotation.SuppressLint
 import android.content.ComponentName
 import android.content.Context
 import android.graphics.PixelFormat
-import android.graphics.Rect
 import android.os.Build
 import android.os.Handler
 import android.os.Looper
@@ -29,7 +27,6 @@ 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.backup
 import com.example.modifier.Global.restartModifier
 import com.example.modifier.R
 import com.example.modifier.TraverseResult
@@ -42,11 +39,11 @@ 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.data.AppPreferencesRepository
+import com.example.modifier.repo.AppPreferencesRepository
 import com.example.modifier.data.AppState
-import com.example.modifier.data.AppStateRepository
+import com.example.modifier.repo.AppStateRepository
 import com.example.modifier.data.BackupItemDao
-import com.example.modifier.data.GoogleMessageStateRepository
+import com.example.modifier.repo.GoogleMessageStateRepository
 import com.example.modifier.databinding.FloatingWindowBinding
 import com.example.modifier.enums.RcsConfigureState
 import com.example.modifier.enums.RcsConnectionStatus
@@ -60,14 +57,19 @@ 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.SimInfo
+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
@@ -116,7 +118,6 @@ import org.json.JSONObject
 import java.io.File
 import java.time.LocalDateTime
 import java.time.temporal.ChronoUnit
-import java.util.Optional
 import java.util.Timer
 import java.util.TimerTask
 import java.util.concurrent.atomic.AtomicReference
@@ -145,10 +146,10 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     private var currentTaskId = 0
     private var lastSend = 0L
 
-    private val backupItemDao: BackupItemDao by lazy {
+    private val backupItemDao by lazy {
         AppDatabase.getDatabase(this).itemDao()
     }
-    private val appPreferencesRepository: AppPreferencesRepository by lazy {
+    private val appPreferencesRepository by lazy {
         AppPreferencesRepository(this)
     }
     private lateinit var appPreferences: StateFlow<AppPreferences>
@@ -156,17 +157,24 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         AppStateRepository(this)
     }
     private lateinit var appState: StateFlow<AppState>
-    private val googleMessageStateRepository: GoogleMessageStateRepository by lazy {
+    private val googleMessageStateRepository by lazy {
         GoogleMessageStateRepository(this)
     }
     private var requestMode = 1;
     private var currentActivity = ""
-    private val screenInspector: ScreenInspector by lazy {
+    private val screenInspector by lazy {
         ScreenInspector(this)
     }
-    private val screenController: ScreenController by lazy {
+    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 {
@@ -208,6 +216,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         CoroutineScope(Dispatchers.IO).launch {
             appState = appStateRepository.getAppState()
             appPreferences = appPreferencesRepository.getAppPreferences()
+            spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
             appStateRepository.updateRuntimeFlags(preparing = true)
             val hasRoot = run checkRoot@{
                 repeat(30) {
@@ -745,7 +754,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                 val dataObj = JSONObject()
                 dataObj.put("canSend", appState.value.send)
                 dataObj.put("busy", appState.value.busy)
-                dataObj.put("currentCountry", Global.simInfo.country)
+                dataObj.put("currentCountry", spoofedSimInfo.value.country)
                 data.put("data", dataObj)
                 mSocket.emit("message", data)
             } catch (e: JSONException) {
@@ -763,7 +772,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         binding.tvLog.text = "Waiting for RCS switch on..."
                     }
                     googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
-                    Global.saveMock()
+                    spoofedSimInfoRepository.mock()
                     resetAll()
                     var switchAppear = googleMessageStateRepository.waitForRcsState(
                         arrayOf(RcsConfigureState.WAITING_FOR_TOS),
@@ -829,7 +838,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         binding.tvLog.text = "Waiting for RCS switch on..."
                     }
                     googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
-                    Global.saveMock()
+                    spoofedSimInfoRepository.mock()
                     resetAll()
                     var switchAppear = googleMessageStateRepository.waitForRcsState(
                         arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
@@ -903,9 +912,9 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
         appStateRepository.updateRuntimeFlags(requesting = true)
 
-        if (Global.simInfo.available == true) {
-            backup(
-                backupItemDao = backupItemDao,
+        if (spoofedSimInfo.value.available) {
+            backupRepository.backup(
+                spoofedSimInfo = spoofedSimInfo.value,
                 type = "auto",
                 sendCount = appState.value.executedNum,
                 fresh = fresh
@@ -953,15 +962,19 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
 
                     if (requestMode == 2 && !noBackup) {
                         val backup = backupItemDao.findBackupForRestore(
-                            Global.simInfo.number,
+                            spoofedSimInfo.value.number,
                             System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000
                         )
                         if (backup != null) {
-                            if (Global.restore(backup)) {
+                            if (backupRepository.restore(backup)) {
                                 requestSuccess = true
                                 break
                             } else {
-                                backup(backupItemDao, "auto", 0)
+                                backupRepository.backup(
+                                    spoofedSimInfo = spoofedSimInfo.value,
+                                    type = "auto",
+                                    sendCount = 0
+                                )
                                 continue
                             }
                         }
@@ -1002,21 +1015,19 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         binding.tvLog.text = "Requesting success, waiting for logs..."
                     }
 
-                    Global.save(
-                        SimInfo(
-                            rcsNumber.number,
-                            rcsNumber.mcc,
-                            rcsNumber.mnc,
-                            Global.genICCID(rcsNumber.mnc, rcsNumber.areaCode),
-                            rcsNumber.mcc + rcsNumber.mnc + RandomStringUtils.randomNumeric(
-                                15 - rcsNumber.mcc.length - rcsNumber.mnc.length
-                            ),
-                            Utils.generateIMEI(),
-                            rcsNumber.country,
-                            rcsNumber.areaCode,
-                            false,
-                            rcsNumber.carrierId,
-                            rcsNumber.carrierName,
+                    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,
                         )
                     )
 
@@ -1180,8 +1191,11 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
             }
         }
         if (requestSuccess) {
-            Global.simInfo.available = true
-            Global.save()
+            spoofedSimInfoRepository.updateSpoofedSimInfo(
+                spoofedSimInfo = spoofedSimInfo.value.copy(
+                    available = true
+                )
+            )
             appStateRepository.resetSuccessNum()
             appStateRepository.resetExecutedNum()
             Log.i(com.example.modifier.TAG, "requestNumber success")

+ 4 - 9
app/src/main/java/com/example/modifier/service/ScreenController.kt

@@ -6,6 +6,7 @@ import android.view.accessibility.AccessibilityNodeInfo
 import com.example.modifier.TraverseResult
 import com.example.modifier.constants.CMD_BACK
 import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY
+import com.example.modifier.utils.currentActivity
 import com.example.modifier.utils.shellRun
 import kotlinx.coroutines.delay
 
@@ -38,9 +39,7 @@ class ScreenController(val context: AccessibilityService, val inspector: ScreenI
                             btn.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                             delay(1000)
                         }
-                        while (shellRun("dumpsys activity activities | grep topResumedActivity")
-                                .first.contains("RcsSettingsActivity")
-                        ) {
+                        while (currentActivity()?.contains("RcsSettingsActivity") == true) {
                             shellRun(CMD_BACK)
                             delay(500)
                         }
@@ -52,9 +51,7 @@ class ScreenController(val context: AccessibilityService, val inspector: ScreenI
                         context.rootInActiveWindow.findAccessibilityNodeInfosByViewId("android:id/button1")
                             .firstOrNull()?.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                         delay(1000)
-                        while (shellRun("dumpsys activity activities | grep topResumedActivity")
-                                .first.contains("RcsSettingsActivity")
-                        ) {
+                        while (currentActivity()?.contains("RcsSettingsActivity") == true) {
                             shellRun(CMD_BACK)
                             delay(500)
                         }
@@ -69,9 +66,7 @@ class ScreenController(val context: AccessibilityService, val inspector: ScreenI
             }
             false
         }
-        while (shellRun("dumpsys activity activities | grep topResumedActivity")
-                .first.contains("RcsSettingsActivity")
-        ) {
+        while (currentActivity()?.contains("RcsSettingsActivity") == true) {
             shellRun(CMD_BACK)
             delay(500)
         }

+ 17 - 5
app/src/main/java/com/example/modifier/ui/backup/BackupFragment.kt

@@ -10,15 +10,17 @@ import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.recyclerview.widget.LinearLayoutManager
-import com.example.modifier.Global
 import com.example.modifier.R
 import com.example.modifier.Utils
 import com.example.modifier.adapter.BackupAdapter
 import com.example.modifier.data.AppDatabase
 import com.example.modifier.data.BackupItem
-import com.example.modifier.data.BackupItemDao
 import com.example.modifier.databinding.FragmentBackupBinding
+import com.example.modifier.model.SpoofedSimInfo
+import com.example.modifier.repo.BackupRepository
+import com.example.modifier.repo.SpoofedSimInfoRepository
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
@@ -26,9 +28,16 @@ class BackupFragment : Fragment() {
     private lateinit var binding: FragmentBackupBinding
     private val list = mutableListOf<BackupItem>()
     private lateinit var adapter: BackupAdapter
-    private val backupItemDao: BackupItemDao by lazy {
+    private val spoofedSimInfoRepository by lazy {
+        SpoofedSimInfoRepository(requireContext())
+    }
+    private lateinit var spoofedSimInfo: StateFlow<SpoofedSimInfo>
+    private val backupItemDao by lazy {
         AppDatabase.getDatabase(requireContext()).itemDao()
     }
+    private val backupRepository by lazy {
+        BackupRepository(requireContext(), backupItemDao, spoofedSimInfoRepository)
+    }
 
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
@@ -37,15 +46,18 @@ class BackupFragment : Fragment() {
         if (this::binding.isInitialized) {
             return binding.root
         }
+        lifecycleScope.launch {
+            spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
+        }
         binding = FragmentBackupBinding.inflate(inflater, container, false)
-        adapter = BackupAdapter(requireContext(), list, backupItemDao)
+        adapter = BackupAdapter(requireContext(), list, backupRepository)
         binding.rvBackup.adapter = adapter
         binding.rvBackup.layoutManager = LinearLayoutManager(requireContext())
         binding.fabBackup.setOnClickListener {
             Utils.makeLoadingButton(requireContext(), binding.fabBackup)
             lifecycleScope.launch {
                 withContext(Dispatchers.IO) {
-                    val backup = Global.backup(backupItemDao, "manual", 0)
+                    val backup = backupRepository.backup(spoofedSimInfo.value, "manual", 0)
                     list.add(0, backup)
                 }
 

+ 44 - 50
app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt

@@ -22,19 +22,21 @@ import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import com.example.modifier.Global
-import com.example.modifier.Global.save
 import com.example.modifier.R
 import com.example.modifier.Utils
 import com.example.modifier.constants.servers
 import com.example.modifier.data.AppPreferences
-import com.example.modifier.data.AppPreferencesRepository
+import com.example.modifier.repo.AppPreferencesRepository
 import com.example.modifier.databinding.FragmentSettingsBinding
 import com.example.modifier.http.ktorClient
 import com.example.modifier.http.api.RcsNumberApi
 import com.example.modifier.http.request.RcsNumberRequest
 import com.example.modifier.http.response.RcsNumberResponse
-import com.example.modifier.model.SimInfo
+import com.example.modifier.model.SpoofedSimInfo
+import com.example.modifier.repo.SpoofedSimInfoRepository
 import com.example.modifier.service.ModifierService.Companion.instance
+import com.example.modifier.utils.genICCID
+import com.example.modifier.utils.genIMEI
 import com.example.modifier.utils.uniqueId
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import io.ktor.client.call.body
@@ -48,6 +50,10 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.single
+import kotlinx.coroutines.flow.subscribe
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.apache.commons.lang3.RandomStringUtils
@@ -57,7 +63,6 @@ import java.util.Objects
 import java.util.Optional
 
 
-
 @SuppressLint("SetTextI18n", "MissingPermission", "HardwareIds", "NewApi")
 class SettingsFragment : Fragment() {
 
@@ -67,6 +72,10 @@ class SettingsFragment : Fragment() {
         AppPreferencesRepository(requireContext())
     }
     private lateinit var appPreferences: StateFlow<AppPreferences>
+    private val spoofedSimInfoRepository by lazy {
+        SpoofedSimInfoRepository(requireContext())
+    }
+    private lateinit var spoofedSimInfo: StateFlow<SpoofedSimInfo>
 
     init {
         Log.i("SettingsFragment", "SettingsFragment")
@@ -76,6 +85,7 @@ class SettingsFragment : Fragment() {
         super.onCreate(savedInstanceState)
         CoroutineScope(Dispatchers.IO).launch {
             appPreferences = appPreferencesRepository.getAppPreferences()
+            spoofedSimInfo = spoofedSimInfoRepository.stateFlow()
         }
         requestPermissionLauncher =
             registerForActivityResult(
@@ -92,14 +102,6 @@ class SettingsFragment : Fragment() {
             }
     }
 
-    override fun onResume() {
-        super.onResume()
-        lifecycleScope.launch {
-            loadConfigs()
-        }
-
-    }
-
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
@@ -114,7 +116,7 @@ class SettingsFragment : Fragment() {
                 return@setEndIconOnClickListener
             }
             binding.etIccid.setText(
-                Global.genICCID(
+                genICCID(
                     binding.etMnc.text.toString(),
                     binding.etAreaCode.text.toString()
                 )
@@ -134,7 +136,7 @@ class SettingsFragment : Fragment() {
             binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length))
         }
         binding.tlImei.setEndIconOnClickListener {
-            binding.etImei.setText(Utils.generateIMEI())
+            binding.etImei.setText(genIMEI())
         }
         binding.btnSave.setOnClickListener {
             onSave()
@@ -203,12 +205,12 @@ class SettingsFragment : Fragment() {
                     val mnc = res.mnc
                     val country = res.country
                     val areaCode = res.areaCode
-                    val iccid = Global.genICCID(mnc, areaCode)
+                    val iccid = genICCID(mnc, areaCode)
                     val imsi =
                         mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
-                    val imei = Utils.generateIMEI()
-                    save(
-                        SimInfo(
+                    val imei = genIMEI()
+                    spoofedSimInfoRepository.updateSpoofedSimInfo(
+                        SpoofedSimInfo(
                             number,
                             mcc,
                             mnc,
@@ -222,8 +224,6 @@ class SettingsFragment : Fragment() {
                             res.carrierName
                         )
                     )
-
-                    loadConfigs()
                 }
                 binding.btnRequest.icon = null
                 binding.btnRequest.isEnabled = true
@@ -319,37 +319,31 @@ class SettingsFragment : Fragment() {
 
         }
         lifecycleScope.launch {
-            loadConfigs()
-            appPreferencesRepository.getAppPreferences().collect {
-                binding.etServer.setText(it.server)
-                binding.etDeviceLabel.setText(it.name)
-                binding.switchClean.isChecked = it.preventClean
-                binding.switchRequest.isChecked = it.preventRequest
-                binding.swReset.isChecked = it.preventReset
+            launch {
+                appPreferencesRepository.getAppPreferences().collect {
+                    binding.etServer.setText(it.server)
+                    binding.etDeviceLabel.setText(it.name)
+                    binding.switchClean.isChecked = it.preventClean
+                    binding.switchRequest.isChecked = it.preventRequest
+                    binding.swReset.isChecked = it.preventReset
+                }
             }
-        }
-
-        return binding.root
-    }
-
-    private suspend fun loadConfigs() {
-        withContext(Dispatchers.IO) {
-            Global.load()
-            withContext(Dispatchers.Main) {
-                binding.etServer.setSimpleItems(servers)
-                val telephonyConfig = Global.simInfo
-                binding.etNumber.setText(telephonyConfig.number)
-                binding.etMcc.setText(telephonyConfig.mcc)
-                binding.etMnc.setText(telephonyConfig.mnc)
-                binding.etIccid.setText(telephonyConfig.iccid)
-                binding.etImsi.setText(telephonyConfig.imsi)
-                binding.etImei.setText(telephonyConfig.imei)
-                binding.etCountry.setText(telephonyConfig.country)
-                binding.etAreaCode.setText(telephonyConfig.areaCode)
-                binding.etCarrierId.setText(telephonyConfig.carrierId)
-                binding.etCarrierName.setText(telephonyConfig.carrierName)
+            launch {
+                spoofedSimInfo.collect {
+                    binding.etNumber.setText(it.number)
+                    binding.etMcc.setText(it.mcc)
+                    binding.etMnc.setText(it.mnc)
+                    binding.etIccid.setText(it.iccid)
+                    binding.etImsi.setText(it.imsi)
+                    binding.etImei.setText(it.imei)
+                    binding.etCountry.setText(it.country)
+                    binding.etAreaCode.setText(it.areaCode)
+                    binding.etCarrierId.setText(it.carrierId)
+                    binding.etCarrierName.setText(it.carrierName)
+                }
             }
         }
+        return binding.root
     }
 
     private fun onSave() {
@@ -357,8 +351,8 @@ class SettingsFragment : Fragment() {
         binding.btnSave.isEnabled = false
         lifecycleScope.launch {
             withContext(Dispatchers.IO) {
-                save(
-                    SimInfo(
+                spoofedSimInfoRepository.updateSpoofedSimInfo(
+                    SpoofedSimInfo(
                         binding.etNumber.text.toString(),
                         binding.etMcc.text.toString(),
                         binding.etMnc.text.toString(),

+ 1 - 1
app/src/main/java/com/example/modifier/ui/utils/UtilsFragment.kt

@@ -18,7 +18,7 @@ import com.example.modifier.constants.PACKAGE_GMS
 import com.example.modifier.constants.PACKAGE_GSF
 import com.example.modifier.constants.PACKAGE_MESSAGING
 import com.example.modifier.data.AppPreferences
-import com.example.modifier.data.AppPreferencesRepository
+import com.example.modifier.repo.AppPreferencesRepository
 import com.example.modifier.databinding.DialogUpdateBinding
 import com.example.modifier.databinding.FragmentUtilsBinding
 import com.example.modifier.extension.kill

+ 113 - 0
app/src/main/java/com/example/modifier/utils/SimInfo.kt

@@ -0,0 +1,113 @@
+package com.example.modifier.utils
+
+import org.apache.commons.lang3.RandomStringUtils
+import java.util.Arrays
+import java.util.Locale
+import kotlin.math.floor
+
+fun genIMEI(): String {
+    val str = Arrays.stream(
+        ("35684610" + RandomStringUtils.randomNumeric(7)).split("".toRegex())
+            .dropLastWhile { it.isEmpty() }
+            .toTypedArray()).mapToInt { s: String -> s.toInt() }.toArray()
+    var sum = 0
+    var t: Int
+    val len_offset: Int
+    val len = 15
+    var imei = ""
+
+    len_offset = (len + 1) % 2
+    var pos = 0
+    while (pos < len - 1) {
+        if ((pos + len_offset) % 2 != 0) {
+            t = str[pos] * 2
+            if (t > 9) {
+                t -= 9
+            }
+            sum += t
+        } else {
+            sum += str[pos]
+        }
+        pos++
+    }
+
+    val final_digit = (10 - (sum % 10)) % 10
+    str[len - 1] = final_digit
+
+    for (d in str) {
+        imei += d.toString()
+    }
+
+    return imei
+}
+
+fun generateIMEI1(): String {
+    val str = intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+    var sum = 0
+    var t: Int
+    val len_offset: Int
+    val len = 15
+    var imei = ""
+
+    val rbi = arrayOf(
+        "01",
+        "10",
+        "30",
+        "33",
+        "35",
+        "44",
+        "45",
+        "49",
+        "50",
+        "51",
+        "52",
+        "53",
+        "54",
+        "86",
+        "91",
+        "98",
+        "99"
+    )
+    val arr = rbi[floor(Math.random() * rbi.size)
+        .toInt()].split("".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+    str[0] = arr[0].toInt()
+    str[1] = arr[1].toInt()
+    var pos = 2
+
+    while (pos < len - 1) {
+        str[pos++] = (floor(Math.random() * 10) % 10).toInt()
+    }
+
+    len_offset = (len + 1) % 2
+    pos = 0
+    while (pos < len - 1) {
+        if ((pos + len_offset) % 2 != 0) {
+            t = str[pos] * 2
+            if (t > 9) {
+                t -= 9
+            }
+            sum += t
+        } else {
+            sum += str[pos]
+        }
+        pos++
+    }
+
+    val final_digit = (10 - (sum % 10)) % 10
+    str[len - 1] = final_digit
+
+    for (d in str) {
+        imei += d.toString()
+    }
+
+    return imei
+}
+
+fun genICCID(mnc: String, areaCode: String): String {
+    val prefix = String.format(Locale.US, "89%02d%s", areaCode.toInt(), mnc)
+    return prefix + RandomStringUtils.randomNumeric(20 - prefix.length)
+}
+
+fun genIMSI(mccmnc: String): String {
+    return "310240" + RandomStringUtils.randomNumeric(15 - mccmnc.length)
+}

+ 15 - 5
app/src/main/java/com/example/modifier/utils/System.kt

@@ -176,7 +176,7 @@ suspend fun syncTime() {
 }
 
 suspend fun isRebooted(): Boolean {
-    if (shellRun("getprop rebooted").component1().contains("yes")) {
+    if (shellRun("getprop rebooted").first.contains("yes")) {
         return false
     }
     shellRun("setprop rebooted yes")
@@ -190,13 +190,13 @@ suspend fun setBatteryLevel(level: Int) {
 suspend fun killPhoneProcess(force: Boolean = false): Boolean {
     try {
         if (!force) {
-            if (shellRun("getprop phonekilled").component1().contains("yes")) {
+            if (shellRun("getprop phonekilled").first.contains("yes")) {
                 return true
             }
         }
         run kill@{
             repeat(3) {
-                val pid = shellRun("pidof com.android.phone").component1().trim()
+                val pid = shellRun("pidof com.android.phone").first.trim()
                 if (!Regex("[0-9]+").matches(pid)) {
                     Log.e(com.example.modifier.TAG, "killPhoneProcess: pid not found")
                     return true
@@ -205,9 +205,12 @@ suspend fun killPhoneProcess(force: Boolean = false): Boolean {
                 shellRun("kill -9 $pid")
                 delay(1000)
 
-                val pidNew = shellRun("pidof com.android.phone").component1().trim()
+                val pidNew = shellRun("pidof com.android.phone").first.trim()
                 if (pidNew == pid) {
-                    Log.e(com.example.modifier.TAG, "killPhoneProcess: failed to kill phone process")
+                    Log.e(
+                        com.example.modifier.TAG,
+                        "killPhoneProcess: failed to kill phone process"
+                    )
                 } else {
                     Log.i(com.example.modifier.TAG, "killPhoneProcess: success, new pid: $pidNew")
                     shellRun("kill -9 $pid", "setprop phonekilled yes")
@@ -219,4 +222,11 @@ suspend fun killPhoneProcess(force: Boolean = false): Boolean {
         Log.e(com.example.modifier.TAG, "Error Kill Phone", e)
     }
     return false
+}
+
+suspend fun currentActivity(): String? {
+    val out = shellRun("dumpsys activity activities | grep topResumedActivity").first
+    val activity = Regex("topResumedActivity=ActivityRecord\\{.*/\\.(\\S*)\\}")
+        .find(out)?.groups?.get(1)?.value
+    return activity
 }

+ 5 - 5
app/src/test/java/com/example/modifier/ExampleUnitTest.kt

@@ -25,10 +25,10 @@ import java.time.format.DateTimeFormatter
 class ExampleUnitTest {
     @Test
     fun test1() {
-        val result = Regex("Your Messenger verification code is G-(\\d{6})")
-            .find("RM0 Your Messenger verification code is G-865069")
-        println(result?.groupValues?.get(1))
+        println(
+            Regex("topResumedActivity=ActivityRecord\\{.*/\\.(\\S*)\\}")
+                .find("    topResumedActivity=ActivityRecord{bebbd0d u0 com.google.android.apps.messaging/.ui.appsettings.RcsSettingsActivity} t2447}\n")
+                ?.groups?.get(1)?.value
+        )
     }
-
-
 }