x1ongzhu hai 1 ano
pai
achega
30917e7181

+ 6 - 2
app/build.gradle

@@ -20,8 +20,9 @@ android {
         applicationId "com.example.modifier"
         minSdk 26
         targetSdk 34
-        versionCode 101
+        versionCode 103
         versionName "1.0.1"
+        archivesBaseName = "modifier-${versionCode}"
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -110,5 +111,8 @@ dependencies {
     implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01"
     implementation 'com.google.android.gms:play-services-code-scanner:16.1.0'
     implementation("com.github.leandroborgesferreira:loading-button-android:2.3.0")
-
+    compileOnly 'de.robv.android.xposed:api:82'
+    implementation 'com.google.code.gson:gson:2.10.1'
+    implementation 'org.apache.commons:commons-lang3:3.14.0'
+    implementation 'commons-io:commons-io:2.16.1'
 }

+ 49 - 8
app/src/main/java/com/example/modifier/Global.kt

@@ -7,11 +7,13 @@ import android.util.Log
 import androidx.core.content.ContextCompat
 import com.example.modifier.http.KtorClient
 import com.example.modifier.model.TelephonyConfig
+import com.example.modifier.ui.shellRun
 import com.example.modifier.utils.RcsHackTool
 import com.google.gson.Gson
 import io.ktor.client.request.head
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.apache.commons.io.FileUtils
@@ -35,6 +37,8 @@ object Global {
     @JvmField
     var telephonyConfig: TelephonyConfig = TelephonyConfig("", "", "", "", "", "", "")
 
+    private const val TAG = "Modifier"
+
     @JvmStatic
     fun load() {
         val context = Utils.getContext()
@@ -337,11 +341,28 @@ object Global {
     }
 
     @JvmStatic
-    fun killPhoneProcess() {
+    suspend fun killPhoneProcess(force: Boolean = false) {
         try {
-            Utils.runAsRoot(
-                "pidof com.android.phone | xargs kill -9",
-            )
+            if (!force) {
+                if (shellRun("getprop phonekilled")["output"]!!.contains("yes")) {
+                    return
+                }
+            }
+            val pid = shellRun("pidof com.android.phone")["output"]!!.trim()
+            if (!Regex("[0-9]+").matches(pid)) {
+                Log.e(TAG, "killPhoneProcess: pid not found")
+                return
+            }
+            Log.i(TAG, "killPhoneProcess: pid=$pid")
+            shellRun("kill -9 $pid", "setprop phonekilled yes")
+            delay(1000)
+
+            val pidNew = shellRun("pidof com.android.phone")["output"]!!.trim()
+            if (pidNew == pid) {
+                Log.e(TAG, "killPhoneProcess: failed to kill phone process")
+            } else {
+                Log.i(TAG, "killPhoneProcess: success, new pid: $pidNew")
+            }
         } catch (e: Exception) {
             e.printStackTrace()
         }
@@ -441,13 +462,33 @@ object Global {
             )
             // convert to Asia/Shanghai
             val dateInZone = date.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
-            Log.i("Modifier", "syncTime: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}")
-            Utils.runAsRoot(
+            Log.i(
+                TAG,
+                "CurrentTime from Baidu: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}"
+            )
+            shellRun(
+                "settings put system time_12_24 24",
+                "settings put global auto_time 0",
+                "settings put global auto_time_zone 0",
                 "setprop persist.sys.timezone Asia/Shanghai",
-                "date '${dateInZone.format(DateTimeFormatter.ofPattern("MMddHHmmyyyy.ss"))}'"
+                "date \"${dateInZone.format(DateTimeFormatter.ofPattern("MMddHHmmyyyy.ss"))}\""
             )
         } catch (e: Exception) {
-            e.printStackTrace()
+            Log.e(TAG, "Error SyncTime", e)
+        }
+    }
+
+    @JvmStatic
+    suspend fun hasRoot(): Boolean {
+        val hasRoot = run checkRoot@{
+            repeat(5) {
+                if (Utils.hasRootAccess()) {
+                    return@checkRoot true
+                }
+                delay(500)
+            }
+            return@checkRoot false
         }
+        return hasRoot
     }
 }

+ 5 - 10
app/src/main/java/com/example/modifier/MainActivity.kt

@@ -31,11 +31,7 @@ class MainActivity : AppCompatActivity() {
         val controller = navHostFragment.navController
         setupWithNavController(mBinding.nav, controller)
         CoroutineScope(Dispatchers.IO).launch {
-            Global.syncTime()
-            if (!Settings.canDrawOverlays(this@MainActivity)) {
-                Utils.enableOverlay()
-            }
-            if (Utils.hasRootAccess()) {
+            if (Global.hasRoot()) {
                 if (!Utils.isAccessibilityEnabled()) {
                     if (!Utils.enableAccessibility()) {
                         val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
@@ -45,11 +41,6 @@ class MainActivity : AppCompatActivity() {
                         finish();
                     }
                 }
-                Utils.runAsRoot(
-                    "settings put global window_animation_scale 1.0",
-                    "settings put global transition_animation_scale 1.0",
-                    "settings put global animator_duration_scale 1.0"
-                )
             } else {
                 withContext(Dispatchers.Main) {
                     MaterialAlertDialogBuilder(this@MainActivity)
@@ -62,6 +53,10 @@ class MainActivity : AppCompatActivity() {
                         .show()
                 }
             }
+            Global.syncTime()
+            if (!Settings.canDrawOverlays(this@MainActivity)) {
+                Utils.enableOverlay()
+            }
         }
     }
 }

+ 66 - 36
app/src/main/java/com/example/modifier/service/ModifierService.kt

@@ -23,6 +23,7 @@ import android.view.accessibility.AccessibilityEvent
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.CompoundButton
 import android.widget.FrameLayout
+import androidx.core.view.ViewCompat
 import androidx.datastore.core.DataStore
 import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.preferencesDataStore
@@ -282,15 +283,16 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
     override fun onCreate() {
         super.onCreate()
         Log.i(TAG, "Starting ModifierService")
-        Log.i(TAG, "phonekilled: ${Utils.runAsRoot("getprop phonekilled")}")
-        if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
-            if (!Utils.runAsRoot("getprop phonekilled").contains("yes")) {
-                Global.killPhoneProcess()
-                Utils.runAsRoot("setprop phonekilled yes")
-            }
-        }
         CoroutineScope(Dispatchers.IO).launch {
-            Global.syncTime()
+            preparing.postValue(true)
+            delay(2.minutes)
+            if (Global.hasRoot()) {
+                Global.syncTime()
+                if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
+                    Global.killPhoneProcess(force = true)
+                }
+            }
+            preparing.postValue(false)
         }
         connect()
     }
@@ -680,7 +682,22 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
         }
         binding.btnInspect.setOnClickListener {
             CoroutineScope(Dispatchers.IO).launch {
-                traverseNode(rootInActiveWindow, TraverseResult())
+                val res = TraverseResult()
+                traverseNode(rootInActiveWindow, res)
+                if (res.rcsSwitch != null) {
+                    Log.i(TAG, "RCS switch isChecked: ${res.rcsSwitch!!.isChecked}")
+                    Log.i(TAG, "Clicking RCS switch")
+                    val rect = Rect()
+                    res.rcsSwitch!!.getBoundsInScreen(rect)
+                    Utils.runAsRoot(
+                        "input tap ${rect.centerX()} ${rect.centerY()}",
+                        "sleep 1",
+                        CMD_BACK, "sleep 1",
+                        CMD_RCS_SETTINGS_ACTIVITY, "sleep 1",
+                    )
+                    traverseNode(rootInActiveWindow, res)
+                    Log.i(TAG, "RCS switch isChecked: ${res.rcsSwitch!!.isChecked}")
+                }
             }
         }
         binding.btnCheck.setOnClickListener {
@@ -759,7 +776,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     rcsConfigureState.postValue(RcsConfigureState.NOT_CONFIGURED)
 
                     withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "requesting number..."
+                        binding.tvLog.text = "Requesting number..."
                     }
 
                     val response = KtorClient.put(
@@ -777,7 +794,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                     Log.i(TAG, "requestNumber response: $rcsNumber")
 
                     withContext(Dispatchers.Main) {
-                        binding.tvLog.text = "requesting success, waiting for logs..."
+                        binding.tvLog.text = "Requesting success, waiting for logs..."
                     }
 
                     Global.save(
@@ -802,16 +819,13 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                                 }
                                 rcsConfigureState.postValue(RcsConfigureState.NOT_CONFIGURED)
                                 resetAll()
-                                val switchAppear = withTimeoutOrNull(2.minutes) {
-                                    while (true) {
-                                        if (rcsConfigureState.value == RcsConfigureState.WAITING_FOR_DEFAULT_ON) {
-                                            break
-                                        }
-                                        delay(1.seconds)
-                                    }
-                                    true
-                                } ?: false
-                                if (!switchAppear) {
+                                val switchAppear = waitForRcsState(
+                                    arrayOf(RcsConfigureState.WAITING_FOR_DEFAULT_ON),
+                                    3.minutes
+                                )?.let {
+                                    it == RcsConfigureState.WAITING_FOR_DEFAULT_ON
+                                }
+                                if (switchAppear != true) {
                                     Log.e(TAG, "RCS not entered default on state, retrying...")
                                     continue
                                 }
@@ -825,26 +839,39 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                                     Log.e(TAG, "RCS switch not found, retrying...")
                                     continue
                                 }
-                                val rect = Rect()
-                                res.rcsSwitch!!.getBoundsInScreen(rect)
-                                Utils.runAsRoot(
-                                    "input tap ${rect.centerX()} ${rect.centerY()}",
-                                    "sleep 1",
-                                    CMD_BACK,
-                                    CMD_RCS_SETTINGS_ACTIVITY,
-                                )
-                                Log.i(TAG, "RCS switch turned on, waiting for state change...")
+                                val switchOn = run turnOnSwitch@{
+                                    repeat(2) {
+                                        if (res.rcsSwitch!!.isChecked) {
+                                            Log.i(TAG, "RCS switch turned on")
+                                            return@turnOnSwitch true
+                                        }
+                                        val rect = Rect()
+                                        res.rcsSwitch!!.getBoundsInScreen(rect)
+                                        Utils.runAsRoot(
+                                            "input tap ${rect.centerX()} ${rect.centerY()}",
+                                            "sleep 1", CMD_BACK, "sleep 1",
+                                            CMD_RCS_SETTINGS_ACTIVITY, "sleep 1",
+                                        )
+                                        traverseNode(rootInActiveWindow, res)
+                                        if (res.rcsSwitch!!.isChecked) {
+                                            Log.i(TAG, "RCS switch turned on")
+                                            return@turnOnSwitch true
+                                        }
+                                    }
+                                    false
+                                }
+                                if (!switchOn) {
+                                    Log.e(TAG, "RCS switch not turned on, retrying...")
+                                    continue
+                                }
                                 val resetSuccess = waitForRcsState(
                                     arrayOf(
-                                        RcsConfigureState.READY,
-                                        RcsConfigureState.RETRY
-                                    ), 2.minutes
+                                        RcsConfigureState.READY
+                                    ), 3.minutes
                                 )
                                 Log.i(TAG, "waitForRcsState: $resetSuccess")
                                 Global.suspend(gms = true, sms = true)
-                                delay(1000)
                                 Global.unsuspend(gms = true, sms = true)
-                                delay(1000)
                                 requestNumberCount = 0
                                 break
                             }
@@ -864,7 +891,7 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         Log.e(TAG, "RCS number expired, retrying...")
                         continue
                     }
-                    val sendOtpTimeout = ChronoUnit.SECONDS.between(
+                    var sendOtpTimeout = ChronoUnit.SECONDS.between(
                         LocalDateTime.now(),
                         rcsNumber.expiryTime
                     ).seconds
@@ -872,6 +899,9 @@ class ModifierService : AccessibilityService(), Emitter.Listener {
                         Log.e(TAG, "OTP timeout too short, retrying...")
                         continue
                     }
+                    if (sendOtpTimeout > 5.minutes) {
+                        sendOtpTimeout = 5.minutes
+                    }
                     if (waitForRcsState(
                             arrayOf(RcsConfigureState.WAITING_FOR_OTP),
                             sendOtpTimeout

+ 25 - 15
app/src/main/java/com/example/modifier/ui/Helpers.kt

@@ -1,23 +1,17 @@
 package com.example.modifier.ui
 
+import android.util.Log
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
-suspend fun runAsRoot(vararg commands: String) {
+private const val TAG = "Modifier"
+suspend fun shellRun(vararg commands: String): Map<String, String> {
+    var output = ""
+    var error = ""
     withContext(Dispatchers.IO) {
         val p = ProcessBuilder("su", "-M").start()
-        p.inputStream.bufferedReader().useLines {
-            it.forEach {
-
-            }
-        }
-
-        p.errorStream.bufferedReader().useLines {
-            it.forEach {
-
-            }
-        }
-
         p.outputStream.bufferedWriter().use { writer ->
             commands.forEach { command ->
                 writer.write(command)
@@ -25,8 +19,24 @@ suspend fun runAsRoot(vararg commands: String) {
                 writer.flush()
             }
         }
-
+        coroutineScope {
+            launch {
+                p.inputStream.bufferedReader().useLines {
+                    it.forEach {
+                        output += it + "\n"
+                    }
+                }
+            }
+            launch {
+                p.errorStream.bufferedReader().useLines {
+                    it.forEach {
+                        error += it + "\n"
+                    }
+                }
+            }
+        }
         p.waitFor()
-
     }
+    Log.i(TAG, "shellRun: \t${commands.joinToString("\n\t\t\t")}\noutput=$output\nerror=$error")
+    return mapOf("output" to output, "error" to error)
 }

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

@@ -194,6 +194,38 @@ class UtilsFragment : Fragment() {
             Toast.makeText(context, "Frida stopped", Toast.LENGTH_SHORT).show()
         }
 
+        binding.btnKillPhone.setOnClickListener {
+            binding.btnKillPhone.isEnabled = false
+            Utils.makeLoadingButton(context, binding.btnKillPhone)
+            CoroutineScope(Dispatchers.IO).launch {
+                Global.killPhoneProcess(force = true)
+                withContext(Dispatchers.Main) {
+                    binding.btnKillPhone.setIconResource(R.drawable.ic_done)
+                    binding.btnKillPhone.text = "OK"
+                    delay(1500L)
+                    binding.btnKillPhone.isEnabled = true
+                    binding.btnKillPhone.icon = null
+                    binding.btnKillPhone.text = "Kill Phone"
+                }
+            }
+        }
+
+        binding.btnSyncTime.setOnClickListener {
+            binding.btnSyncTime.isEnabled = false
+            Utils.makeLoadingButton(context, binding.btnSyncTime)
+            CoroutineScope(Dispatchers.IO).launch {
+                Global.syncTime()
+                withContext(Dispatchers.Main) {
+                    binding.btnSyncTime.setIconResource(R.drawable.ic_done)
+                    binding.btnSyncTime.text = "OK"
+                    delay(1500L)
+                    binding.btnSyncTime.isEnabled = true
+                    binding.btnSyncTime.icon = null
+                    binding.btnSyncTime.text = "Sync Time"
+                }
+            }
+        }
+
         binding.btnUpdateModifier.setOnClickListener {
             CoroutineScope(Dispatchers.IO).launch {
                 try {

+ 23 - 2
app/src/main/res/layout/fragment_utils.xml

@@ -206,11 +206,11 @@
 
 
                         <LinearLayout
-
                             android:layout_width="match_parent"
                             android:layout_height="wrap_content"
                             android:layout_marginTop="16dp"
-                            android:orientation="horizontal">
+                            android:orientation="horizontal"
+                            android:visibility="gone">
 
                             <com.google.android.material.button.MaterialButton
                                 android:id="@+id/btn_start_frida"
@@ -225,6 +225,27 @@
                                 android:layout_marginLeft="8dp"
                                 android:text="Stop Frida" />
                         </LinearLayout>
+
+                        <LinearLayout
+
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="16dp"
+                            android:orientation="horizontal">
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_kill_phone"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="Kill Phone" />
+
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_sync_time"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginLeft="8dp"
+                                android:text="Sync Time" />
+                        </LinearLayout>
                     </LinearLayout>
                 </com.google.android.material.card.MaterialCardView>
 

+ 1 - 0
settings.gradle

@@ -17,6 +17,7 @@ dependencyResolutionManagement {
         google()
         mavenCentral()
         maven { url 'https://jitpack.io' }
+        maven { url 'https://api.xposed.info/' }
     }
 }