System.kt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. package com.example.modifier.utils
  2. import android.accessibilityservice.AccessibilityServiceInfo
  3. import android.annotation.SuppressLint
  4. import android.app.AlarmManager
  5. import android.app.PendingIntent
  6. import android.content.Context
  7. import android.content.Intent
  8. import android.content.pm.PackageManager
  9. import android.os.Build
  10. import android.os.SystemClock
  11. import android.provider.Settings
  12. import android.text.TextUtils
  13. import android.util.Log
  14. import android.view.accessibility.AccessibilityManager
  15. import androidx.core.app.ActivityCompat
  16. import androidx.core.content.ContextCompat
  17. import com.example.modifier.BuildConfig
  18. import com.example.modifier.MainActivity
  19. import com.example.modifier.Utils
  20. import com.example.modifier.baseTag
  21. import com.example.modifier.constants.PACKAGE_GMS
  22. import com.example.modifier.extension.kill
  23. import com.example.modifier.http.api.SysConfigApi
  24. import com.example.modifier.http.ktorClient
  25. import com.example.modifier.http.response.SysConfigResponse
  26. import com.example.modifier.service.ModifierService
  27. import io.ktor.client.call.body
  28. import io.ktor.client.plugins.resources.get
  29. import io.ktor.client.request.head
  30. import kotlinx.coroutines.Dispatchers
  31. import kotlinx.coroutines.coroutineScope
  32. import kotlinx.coroutines.delay
  33. import kotlinx.coroutines.launch
  34. import kotlinx.coroutines.withContext
  35. import org.apache.commons.io.FileUtils
  36. import org.json.JSONObject
  37. import java.io.File
  38. import java.net.NetworkInterface
  39. import java.time.ZoneId
  40. import java.time.ZonedDateTime
  41. import java.time.format.DateTimeFormatter
  42. import java.util.Locale
  43. import java.util.UUID
  44. import kotlin.system.exitProcess
  45. const val systemTag = "$baseTag/System"
  46. var ROOT_ACCESS = false
  47. val uniqueId: String
  48. @SuppressLint("HardwareIds")
  49. get() {
  50. var uniqueID = ""
  51. if (uniqueID.isBlank()) {
  52. try {
  53. val context = getContext()!!
  54. uniqueID =
  55. Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
  56. } catch (e: java.lang.Exception) {
  57. e.printStackTrace()
  58. }
  59. }
  60. if (uniqueID.isBlank()) {
  61. uniqueID = UUID.randomUUID().toString()
  62. }
  63. return uniqueID
  64. }
  65. @SuppressLint("PrivateApi")
  66. fun getContext(): Context {
  67. val activityThreadClass = Class.forName("android.app.ActivityThread")
  68. val currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread")
  69. currentActivityThreadMethod.isAccessible = true
  70. val currentActivityThread = currentActivityThreadMethod.invoke(null)
  71. val getApplicationMethod = activityThreadClass.getMethod("getApplication")
  72. getApplicationMethod.isAccessible = true
  73. return getApplicationMethod.invoke(currentActivityThread) as Context
  74. }
  75. suspend fun hasRootAccess(): Boolean {
  76. var rootAccess = false
  77. try {
  78. val p = withContext(Dispatchers.IO) {
  79. ProcessBuilder("su", "-c", "echo imrooted").start()
  80. }
  81. var output = ""
  82. coroutineScope {
  83. launch {
  84. p.inputStream.bufferedReader().useLines { line ->
  85. line.forEach {
  86. output += it + "\n"
  87. }
  88. }
  89. }
  90. launch {
  91. p.errorStream.bufferedReader().useLines { line ->
  92. line.forEach {
  93. Log.e(shellTag, "shellRunErr: $it")
  94. }
  95. }
  96. }
  97. }
  98. withContext(Dispatchers.IO) {
  99. p.waitFor()
  100. }
  101. if (output.contains("imrooted")) {
  102. rootAccess = true
  103. }
  104. } catch (e: Exception) {
  105. e.printStackTrace()
  106. }
  107. return rootAccess
  108. }
  109. fun hasPermission(permission: String): Boolean {
  110. return ActivityCompat.checkSelfPermission(
  111. getContext()!!,
  112. permission
  113. ) == PackageManager.PERMISSION_GRANTED
  114. }
  115. fun isAccessibilityEnabled(): Boolean {
  116. val context = getContext()!!
  117. val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
  118. val enabledServices =
  119. am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK)
  120. for (enabledService in enabledServices) {
  121. Log.i(
  122. systemTag,
  123. "Enabled service: " + enabledService.resolveInfo.serviceInfo.packageName + "/" + enabledService.resolveInfo.serviceInfo.name
  124. )
  125. val enabledServiceInfo = enabledService.resolveInfo.serviceInfo
  126. if (enabledServiceInfo.packageName == context.packageName && enabledServiceInfo.name == ModifierService.NAME) return true
  127. }
  128. return false
  129. }
  130. suspend fun enableAccessibility(): Boolean {
  131. if (isAccessibilityEnabled()) return true
  132. val context = getContext()!!
  133. val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
  134. val enabledServices =
  135. am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK)
  136. val names: MutableList<String?> = ArrayList()
  137. for (enabledService in enabledServices) {
  138. names.add(enabledService.resolveInfo.serviceInfo.packageName + "/" + enabledService.resolveInfo.serviceInfo.name)
  139. }
  140. names.add(context.packageName + "/" + ModifierService.NAME)
  141. try {
  142. shellRun(
  143. "settings put secure enabled_accessibility_services " + TextUtils.join(":", names),
  144. "settings put secure accessibility_enabled 1"
  145. )
  146. return true
  147. } catch (e: java.lang.Exception) {
  148. e.printStackTrace()
  149. }
  150. return false
  151. }
  152. suspend fun enableOverlay() {
  153. try {
  154. shellRun("appops set " + BuildConfig.APPLICATION_ID + " SYSTEM_ALERT_WINDOW allow")
  155. } catch (e: java.lang.Exception) {
  156. e.printStackTrace()
  157. }
  158. }
  159. suspend fun optimize() {
  160. val context = getContext()
  161. val packageManager = context.packageManager
  162. val info = packageManager.getApplicationInfo("com.google.android.gms", 0)
  163. shellRun(
  164. "dumpsys deviceidle whitelist +com.google.android.apps.messaging",
  165. "dumpsys deviceidle whitelist +${BuildConfig.APPLICATION_ID}",
  166. "cmd netpolicy add restrict-background-blacklist ${info.uid}",
  167. "pm grant ${BuildConfig.APPLICATION_ID} android.permission.POST_NOTIFICATIONS",
  168. )
  169. // if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
  170. // shellRun(
  171. // "settings put global window_animation_scale 1",
  172. // "settings put global transition_animation_scale 1",
  173. // "settings put global animator_duration_scale 1"
  174. // )
  175. // }
  176. }
  177. suspend fun syncTime() {
  178. try {
  179. Log.i(systemTag, "syncTime: start")
  180. val response = ktorClient.head("http://www.baidu.com")
  181. val dateHeader = response.headers["Date"]
  182. val date = ZonedDateTime.parse(
  183. dateHeader,
  184. DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
  185. )
  186. val timeMillis = date.toInstant().toEpochMilli()
  187. // convert to Asia/Shanghai
  188. val dateInZone = date.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
  189. Log.i(
  190. systemTag,
  191. "CurrentTime from Baidu: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}"
  192. )
  193. shellRun(
  194. "settings put system time_12_24 24",
  195. "settings put global auto_time 1",
  196. "settings put global auto_time_zone 0",
  197. "service call alarm 3 s16 Asia/Shanghai",
  198. "service call alarm 2 i64 $timeMillis"
  199. )
  200. } catch (e: Exception) {
  201. Log.e(systemTag, "Error SyncTime", e)
  202. }
  203. }
  204. suspend fun setBatteryLevel(level: Int) {
  205. shellRun("dumpsys battery set level $level")
  206. }
  207. suspend fun killPhoneProcess(force: Boolean = false): Boolean {
  208. try {
  209. if (!force) {
  210. if (shellRun("getprop phonekilled").first.contains("yes")) {
  211. return true
  212. }
  213. }
  214. shellRun("kill -9 $(pidof com.android.phone)")
  215. } catch (e: Exception) {
  216. Log.e(systemTag, "Error Kill Phone", e)
  217. }
  218. return false
  219. }
  220. suspend fun currentActivity(): String? {
  221. val out = shellRun("dumpsys activity activities | grep topResumedActivity").first
  222. val activity = Regex("topResumedActivity=ActivityRecord\\{.*/\\.(.*)\\}")
  223. .find(out)?.groups?.get(1)?.value
  224. return activity
  225. }
  226. fun sqlite3path(): String {
  227. val context = getContext()
  228. val dataDir = ContextCompat.getDataDir(context)
  229. val binDir = File(dataDir, "bin")
  230. if (!binDir.exists()) {
  231. Utils.copyAssetFolder(context.assets, "bin", binDir.path)
  232. binDir.listFiles()?.forEach {
  233. it.setExecutable(true)
  234. }
  235. }
  236. Log.i("Modifier", "arch: " + Build.SUPPORTED_ABIS.joinToString(", "))
  237. val file = File(dataDir, "bin/sqlite3.arm")
  238. file.setExecutable(true)
  239. return file.path
  240. }
  241. fun restartSelf() {
  242. val context = getContext()
  243. val mStartActivity = Intent(context, MainActivity::class.java)
  244. val mPendingIntentId = 123456
  245. val mPendingIntent = PendingIntent.getActivity(
  246. context,
  247. mPendingIntentId,
  248. mStartActivity,
  249. PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
  250. )
  251. val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
  252. mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent)
  253. exitProcess(0)
  254. }
  255. fun getIPAddress(): List<String> {
  256. return try {
  257. NetworkInterface.getNetworkInterfaces().toList()
  258. .flatMap { intf ->
  259. intf.inetAddresses.toList()
  260. .map {
  261. it.hostAddress
  262. }
  263. }
  264. .filter {
  265. it.matches(Regex("\\d+\\.\\d+\\.\\d+\\.\\d+")) && !it.startsWith("127.")
  266. }
  267. } catch (e: Exception) {
  268. Log.e(systemTag, "Error getIPAddress", e)
  269. emptyList()
  270. }
  271. }
  272. suspend fun checkPif() {
  273. if (!(Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711"))) {
  274. return
  275. }
  276. try {
  277. val out = shellRun("cat /data/adb/modules/playintegrityfix/module.prop").first
  278. var pifFile = ""
  279. if (out.contains("name=Play Integrity Fix")) {
  280. pifFile = "pif.json"
  281. } else if (out.contains("name=Play Integrity Fork")) {
  282. pifFile = "custom.pif.json"
  283. } else {
  284. Log.e(systemTag, "PIF module not found")
  285. }
  286. val config = ktorClient.get(SysConfigApi.Id(SysConfigApi(), "pif"))
  287. .body<SysConfigResponse>()
  288. if (config.value.isBlank()) {
  289. Log.i(systemTag, "cannot get pif.json from server")
  290. return
  291. }
  292. val newPif = JSONObject(config.value.split("\n")
  293. .joinToString("\n") { line ->
  294. line.replace(Regex("^\\s*//.*"), "")
  295. })
  296. val newFingerPrint = newPif.optString("FINGERPRINT", "")
  297. if (newFingerPrint.isEmpty()) {
  298. Log.i(systemTag, "cannot get fingerprint from pif.json")
  299. return
  300. }
  301. val currentFingerprint = shellRun("cat /data/adb/modules/playintegrityfix/$pifFile").let {
  302. val json = JSONObject(it.first.split("\n")
  303. .joinToString("\n") { line ->
  304. line.replace(Regex("^\\s*//.*"), "")
  305. })
  306. json.optString("FINGERPRINT", "")
  307. }
  308. if (newFingerPrint != currentFingerprint) {
  309. val tmpFile: File
  310. withContext(Dispatchers.IO) {
  311. tmpFile = File.createTempFile("pif", ".json")
  312. FileUtils.writeStringToFile(tmpFile, newPif.toString(), "UTF-8")
  313. shellRun(
  314. "cp ${tmpFile.path} /data/adb/modules/playintegrityfix/$pifFile",
  315. PACKAGE_GMS.kill(),
  316. )
  317. tmpFile.delete()
  318. Log.i(systemTag, "pif.json updated, fingerprint: $newFingerPrint")
  319. delay(5000)
  320. }
  321. } else {
  322. Log.i(systemTag, "pif.json is up to date")
  323. }
  324. } catch (e: Exception) {
  325. Log.e(systemTag, "Error checkPif", e)
  326. }
  327. }