Global.kt 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. package com.example.modifier
  2. import android.annotation.SuppressLint
  3. import android.content.Context
  4. import android.content.Intent
  5. import android.os.Build
  6. import android.util.Log
  7. import androidx.core.content.ContextCompat
  8. import com.example.modifier.data.BackupItem
  9. import com.example.modifier.data.BackupItemDao
  10. import com.example.modifier.http.KtorClient
  11. import com.example.modifier.model.Backup
  12. import com.example.modifier.model.TelephonyConfig
  13. import com.example.modifier.service.ModifierService
  14. import com.example.modifier.ui.shellRun
  15. import com.example.modifier.utils.RcsHackTool
  16. import com.google.gson.Gson
  17. import io.ktor.client.request.head
  18. import kotlinx.coroutines.Dispatchers
  19. import kotlinx.coroutines.coroutineScope
  20. import kotlinx.coroutines.delay
  21. import kotlinx.coroutines.launch
  22. import kotlinx.coroutines.withContext
  23. import org.apache.commons.io.FileUtils
  24. import org.apache.commons.io.IOUtils
  25. import org.apache.commons.lang3.RandomStringUtils
  26. import org.apache.commons.lang3.StringUtils
  27. import java.io.File
  28. import java.io.FileWriter
  29. import java.nio.file.Files
  30. import java.time.ZoneId
  31. import java.time.ZonedDateTime
  32. import java.time.format.DateTimeFormatter
  33. import java.util.Base64
  34. import java.util.Date
  35. import java.util.Locale
  36. object Global {
  37. @JvmField
  38. var serverUrl: String = ""
  39. @JvmField
  40. var name: String? = ""
  41. @JvmField
  42. var telephonyConfig: TelephonyConfig = TelephonyConfig("", "", "", "", "", "", "", "")
  43. private const val TAG = "Modifier"
  44. @JvmStatic
  45. fun load() {
  46. val context = Utils.getContext()
  47. val prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
  48. serverUrl = prefs.getString("server", "http://47.98.225.28") ?: ""
  49. name = prefs.getString("name", Build.DEVICE)
  50. try {
  51. val file = File(ContextCompat.getDataDir(context), "config.json")
  52. if (file.exists()) {
  53. val gson = Gson()
  54. val json = FileUtils.readFileToString(file, "UTF-8")
  55. telephonyConfig = gson.fromJson(json, TelephonyConfig::class.java)
  56. }
  57. } catch (e: Exception) {
  58. e.printStackTrace()
  59. }
  60. }
  61. @JvmStatic
  62. val servers: MutableSet<String>
  63. get() {
  64. val context = Utils.getContext()
  65. val defServers: MutableSet<String> = HashSet()
  66. defServers.add("http://192.168.6.215:3000")
  67. defServers.add("http://192.168.50.135:3000")
  68. defServers.add("http://47.98.225.28")
  69. val prefs =
  70. context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
  71. return HashSet(prefs.getStringSet("servers", defServers))
  72. }
  73. @JvmStatic
  74. fun saveServer(server: String, name: String?) {
  75. serverUrl = server
  76. Global.name = name
  77. val context = Utils.getContext()
  78. val prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
  79. val servers = servers
  80. servers.add(server)
  81. prefs.edit().putStringSet("servers", servers)
  82. .putString("server", server)
  83. .putString("name", name)
  84. .apply()
  85. }
  86. @JvmStatic
  87. fun save(telephonyConfig: TelephonyConfig, suspend: Boolean? = true) {
  88. val context = Utils.getContext()
  89. Global.telephonyConfig.mcc = telephonyConfig.mcc
  90. Global.telephonyConfig.mnc = telephonyConfig.mnc
  91. Global.telephonyConfig.number = telephonyConfig.number
  92. Global.telephonyConfig.country = telephonyConfig.country
  93. Global.telephonyConfig.areaCode = telephonyConfig.areaCode
  94. Global.telephonyConfig.iccid = telephonyConfig.iccid
  95. Global.telephonyConfig.imei = telephonyConfig.imei
  96. Global.telephonyConfig.imsi = telephonyConfig.imsi
  97. try {
  98. if (suspend == true) {
  99. suspend(gms = true, sms = true)
  100. }
  101. val file = File(ContextCompat.getDataDir(context), "config.json")
  102. val gson = Gson()
  103. val json = gson.toJson(telephonyConfig)
  104. try {
  105. val writer = FileWriter(file)
  106. writer.write(json)
  107. writer.close()
  108. } catch (e: Exception) {
  109. e.printStackTrace()
  110. }
  111. Utils.runAsRoot(
  112. "setprop persist.spoof.mcc ${telephonyConfig.mcc}",
  113. "setprop persist.spoof.mnc ${telephonyConfig.mnc}",
  114. "setprop persist.spoof.number ${telephonyConfig.number}",
  115. "setprop persist.spoof.country ${telephonyConfig.country}",
  116. "setprop persist.spoof.iccid ${telephonyConfig.iccid}",
  117. "setprop persist.spoof.imei ${telephonyConfig.imei}",
  118. "setprop persist.spoof.imsi ${telephonyConfig.imsi}",
  119. "cp " + file.path + " /data/data/com.android.phone/rcsConfig.json",
  120. "echo 'copied to phone'",
  121. "chmod 777 /data/data/com.android.phone/rcsConfig.json",
  122. "sleep 1"
  123. )
  124. if (suspend == true) {
  125. unsuspend(gms = true, sms = true)
  126. }
  127. } catch (e: Exception) {
  128. e.printStackTrace()
  129. }
  130. }
  131. fun saveMock() {
  132. val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
  133. it.readText()
  134. }
  135. // get random number
  136. content.split("\n")
  137. .filter { it.isNotBlank() }
  138. .shuffled()
  139. .firstOrNull()
  140. ?.let {
  141. save(
  142. TelephonyConfig(
  143. number = it,
  144. mcc = "310",
  145. mnc = "240",
  146. iccid = genICCID("310", "1"),
  147. "310240" + RandomStringUtils.randomNumeric(9),
  148. Utils.generateIMEI(),
  149. "us",
  150. "1"
  151. )
  152. )
  153. }
  154. }
  155. @JvmStatic
  156. fun clear(gsf: Boolean, gms: Boolean, sms: Boolean) {
  157. try {
  158. suspend(gsf, gms, sms)
  159. val cmds: MutableList<String> = ArrayList()
  160. // suspend
  161. if (gsf) {
  162. cmds.add("pm suspend com.google.android.gsf")
  163. cmds.add("am force-stop com.google.android.gsf")
  164. cmds.add("echo 'gsf suspended'")
  165. }
  166. if (gms) {
  167. cmds.add("pm suspend com.google.android.gms")
  168. cmds.add("am force-stop com.google.android.gms")
  169. cmds.add("echo 'gms suspended'")
  170. }
  171. if (sms) {
  172. cmds.add("pm suspend com.google.android.apps.messaging")
  173. cmds.add("am force-stop com.google.android.apps.messaging")
  174. cmds.add("echo 'sms suspended'")
  175. }
  176. cmds.add("sleep 1")
  177. // clear
  178. if (gsf) {
  179. cmds.add("pm clear com.google.android.gsf")
  180. cmds.add("echo 'cleared gsf'")
  181. }
  182. if (gms) {
  183. cmds.add("pm clear com.google.android.gms")
  184. cmds.add("echo 'cleared gms'")
  185. }
  186. if (sms) {
  187. cmds.add("pm clear com.google.android.apps.messaging")
  188. cmds.add("echo 'cleared sms'")
  189. }
  190. cmds.add("sleep 1")
  191. // unsuspend
  192. if (gsf) {
  193. cmds.add("pm unsuspend com.google.android.gsf")
  194. cmds.add("echo 'gsf unsuspend'")
  195. }
  196. if (gms) {
  197. cmds.add("pm unsuspend com.google.android.gms")
  198. cmds.add("echo 'gms unsuspend'")
  199. }
  200. if (sms) {
  201. cmds.add("pm unsuspend com.google.android.apps.messaging")
  202. cmds.add("echo 'sms unsuspend'")
  203. }
  204. cmds.add("sleep 1")
  205. Utils.runAsRoot(*cmds.toTypedArray<String>())
  206. } catch (e: Exception) {
  207. e.printStackTrace()
  208. }
  209. }
  210. // @JvmStatic
  211. // fun stop(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
  212. // try {
  213. // val cmds: MutableList<String> = ArrayList()
  214. // if (gsf == true) {
  215. // cmds.add("am force-stop com.google.android.gsf")
  216. // cmds.add("echo 'stopped gsf'")
  217. // }
  218. // if (gms == true) {
  219. // cmds.add("am force-stop com.google.android.gms")
  220. // cmds.add("echo 'stopped gms'")
  221. // Thread.sleep(1000)
  222. // }
  223. // if (sms == true) {
  224. // cmds.add("am force-stop com.google.android.apps.messaging")
  225. // cmds.add("echo 'stopped sms'")
  226. // }
  227. // Utils.runAsRoot(*cmds.toTypedArray<String>())
  228. // } catch (e: Exception) {
  229. // e.printStackTrace()
  230. // }
  231. // }
  232. @JvmStatic
  233. fun suspend(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
  234. try {
  235. val cmds: MutableList<String> = ArrayList()
  236. if (gsf == true) {
  237. cmds.add("pm suspend com.google.android.gsf")
  238. cmds.add("am force-stop com.google.android.gsf")
  239. cmds.add("echo 'gsf suspended'")
  240. }
  241. if (gms == true) {
  242. cmds.add("pm suspend com.google.android.gms")
  243. cmds.add("am force-stop com.google.android.gms")
  244. cmds.add("echo 'gms suspended'")
  245. }
  246. if (sms == true) {
  247. cmds.add("pm suspend com.google.android.apps.messaging")
  248. cmds.add("am force-stop com.google.android.apps.messaging")
  249. cmds.add("echo 'sms suspended'")
  250. }
  251. cmds.add("sleep 1")
  252. Utils.runAsRoot(*cmds.toTypedArray<String>())
  253. } catch (e: Exception) {
  254. e.printStackTrace()
  255. }
  256. }
  257. @JvmStatic
  258. fun unsuspend(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
  259. try {
  260. val cmds: MutableList<String> = ArrayList()
  261. if (gsf == true) {
  262. cmds.add("pm unsuspend com.google.android.gsf")
  263. cmds.add("echo 'gsf unsuspend'")
  264. }
  265. if (gms == true) {
  266. cmds.add("pm unsuspend com.google.android.gms")
  267. cmds.add("echo 'gms unsuspend'")
  268. }
  269. if (sms == true) {
  270. cmds.add("pm unsuspend com.google.android.apps.messaging")
  271. cmds.add("echo 'sms unsuspend'")
  272. }
  273. cmds.add("sleep 1")
  274. Utils.runAsRoot(*cmds.toTypedArray<String>())
  275. } catch (e: Exception) {
  276. e.printStackTrace()
  277. }
  278. }
  279. @JvmStatic
  280. fun clearConv() {
  281. val context = Utils.getContext()
  282. if (context.getSharedPreferences("settings", Context.MODE_PRIVATE)
  283. .getBoolean("do_not_clean", false)
  284. ) {
  285. return
  286. }
  287. try {
  288. val dataDir = ContextCompat.getDataDir(context)
  289. val binDir = File(dataDir, "bin")
  290. val dbDir = File(dataDir, "providerDB")
  291. if (!binDir.exists()) {
  292. Utils.copyAssetFolder(context.assets, "bin", binDir.path)
  293. Utils.copyAssetFolder(context.assets, "providerDB", dbDir.path)
  294. }
  295. Log.i("Modifier", "arch: " + Build.SUPPORTED_ABIS.joinToString(", "))
  296. var arch: String? = null
  297. for (supportedAbi in Build.SUPPORTED_ABIS) {
  298. if ("x86" == supportedAbi) {
  299. arch = "x86"
  300. } else if ("x86_64" == supportedAbi) {
  301. arch = "x64"
  302. } else if ("arm64-v8a" == supportedAbi) {
  303. arch = "arm64"
  304. } else if ("armeabi-v7a" == supportedAbi) {
  305. arch = "arm"
  306. }
  307. if (StringUtils.isNoneBlank(arch)) {
  308. suspend(sms = true)
  309. val binPath = File(dataDir, "bin/sqlite3.$arch").path
  310. val providerDBPath = File(dataDir, "providerDB/mmssms.db").path
  311. Log.i("Modifier", "sqlite3 binPath: $binPath")
  312. Utils.runAsRoot(
  313. "chmod +x $binPath",
  314. "$binPath /data/data/com.google.android.apps.messaging/databases/bugle_db \"DELETE FROM conversations;\"",
  315. "$binPath /data/data/com.google.android.apps.messaging/databases/bugle_db \"DELETE FROM messages;\"",
  316. "cp $providerDBPath /data/data/com.android.providers.telephony/databases/mmssms.db",
  317. "chmod 660 /data/data/com.android.providers.telephony/databases/mmssms.db",
  318. "echo ok"
  319. )
  320. unsuspend(sms = true)
  321. break
  322. }
  323. }
  324. } catch (e: Exception) {
  325. e.printStackTrace()
  326. }
  327. }
  328. @JvmStatic
  329. suspend fun resetAll() {
  330. val context = Utils.getContext()
  331. try {
  332. val dataDir = ContextCompat.getDataDir(context)
  333. Utils.copyAssetFolder(context.assets, "providerDB", File(dataDir, "providerDB").path)
  334. val providerDBPath = File(dataDir, "providerDB/mmssms.db").path
  335. shellRun(
  336. "cp $providerDBPath /data/data/com.android.providers.telephony/databases/mmssms.db",
  337. "chmod 660 /data/data/com.android.providers.telephony/databases/mmssms.db",
  338. CMD_SUSPEND_MESSAGING_APP,
  339. CMD_KILL_MESSAGING_APP,
  340. CMD_CLEAR_MESSAGING_APP,
  341. CMD_CLEAR_GSF,
  342. CMD_CLEAR_GMS,
  343. "sleep 1",
  344. CMD_START_PLAY_STORE,
  345. "sleep 1",
  346. CMD_HOME,
  347. "sleep 10",
  348. CMD_CLEAR_GMS,
  349. CMD_RESUME_MESSAGING_APP,
  350. CMD_MESSAGING_APP
  351. )
  352. } catch (e: Exception) {
  353. e.printStackTrace()
  354. }
  355. }
  356. @JvmStatic
  357. suspend fun killPhoneProcess(force: Boolean = false): Boolean {
  358. try {
  359. if (!force) {
  360. if (shellRun("getprop phonekilled")["output"]!!.contains("yes")) {
  361. return true
  362. }
  363. }
  364. run kill@{
  365. repeat(3) {
  366. val pid = shellRun("pidof com.android.phone")["output"]!!.trim()
  367. if (!Regex("[0-9]+").matches(pid)) {
  368. Log.e(TAG, "killPhoneProcess: pid not found")
  369. return true
  370. }
  371. Log.i(TAG, "killPhoneProcess: pid=$pid")
  372. shellRun("kill -9 $pid")
  373. delay(1000)
  374. val pidNew = shellRun("pidof com.android.phone")["output"]!!.trim()
  375. if (pidNew == pid) {
  376. Log.e(TAG, "killPhoneProcess: failed to kill phone process")
  377. } else {
  378. Log.i(TAG, "killPhoneProcess: success, new pid: $pidNew")
  379. shellRun("kill -9 $pid", "setprop phonekilled yes")
  380. return true
  381. }
  382. }
  383. }
  384. } catch (e: Exception) {
  385. Log.e(TAG, "Error Kill Phone", e)
  386. }
  387. return false
  388. }
  389. @JvmStatic
  390. suspend fun sendSmsFrida(sender: String, msg: String) {
  391. val context = Utils.getContext()
  392. try {
  393. val dataDir = ContextCompat.getDataDir(context)
  394. Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)
  395. val binPath = File(dataDir, "bin/frida-inject-16.3.3-android-arm64").path
  396. val pduBase64 =
  397. String(Base64.getEncoder().encode(RcsHackTool.createFakeSms(sender, msg)))
  398. Log.i("Modifier", "pduBase64: $pduBase64")
  399. val script = IOUtils.toString(context.assets.open("scripts/sms.js"), "UTF-8")
  400. .replace("{pduBase64}", pduBase64)
  401. val tmpFile: File
  402. withContext(Dispatchers.IO) {
  403. tmpFile = File.createTempFile("script", ".js")
  404. FileUtils.writeStringToFile(tmpFile, script, "UTF-8")
  405. }
  406. val pid = Utils.runAsRoot("pidof com.android.phone").trim()
  407. if (!Regex("[0-9]+").matches(pid)) {
  408. return
  409. }
  410. Log.i("Modifier", "sendSms: $binPath -p $pid -s $tmpFile")
  411. val p = withContext(Dispatchers.IO) {
  412. Runtime.getRuntime().exec("su -M")
  413. }
  414. p.outputStream.bufferedWriter().use {
  415. it.write("chmod +x $binPath")
  416. it.newLine()
  417. it.flush()
  418. it.write("$binPath -p $pid -s $tmpFile")
  419. it.newLine()
  420. it.flush()
  421. }
  422. coroutineScope {
  423. launch {
  424. p.errorStream.bufferedReader().useLines { lines ->
  425. lines.forEach {
  426. Log.e("Modifier", it)
  427. }
  428. }
  429. }
  430. launch {
  431. p.inputStream
  432. .bufferedReader()
  433. .useLines { lines ->
  434. lines.forEach {
  435. Log.i("Modifier", it)
  436. if (it == "OK") {
  437. p.inputStream.close()
  438. p.errorStream.close()
  439. p.destroy()
  440. }
  441. }
  442. }
  443. }
  444. }
  445. withContext(Dispatchers.IO) {
  446. p.waitFor()
  447. }
  448. } catch (e: Exception) {
  449. e.printStackTrace()
  450. }
  451. }
  452. @JvmStatic
  453. fun sendSmsIntent(sender: String, msg: String) {
  454. val intent = Intent()
  455. intent.setAction("com.example.modifier.sms")
  456. intent.putExtra("sender", sender)
  457. intent.putExtra(
  458. "message",
  459. msg
  460. )
  461. val context = Utils.getContext()
  462. context.sendBroadcast(intent)
  463. }
  464. @JvmStatic
  465. suspend fun syncTime() {
  466. try {
  467. Log.i("Modifier", "syncTime: start")
  468. val response = KtorClient.head("http://www.baidu.com")
  469. val dateHeader = response.headers["Date"]
  470. val date = ZonedDateTime.parse(
  471. dateHeader,
  472. DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
  473. )
  474. // convert to Asia/Shanghai
  475. val dateInZone = date.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
  476. Log.i(
  477. TAG,
  478. "CurrentTime from Baidu: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}"
  479. )
  480. shellRun(
  481. "settings put system time_12_24 24",
  482. "settings put global auto_time 0",
  483. "settings put global auto_time_zone 0",
  484. "setprop persist.sys.timezone Asia/Shanghai",
  485. "date \"${dateInZone.format(DateTimeFormatter.ofPattern("MMddHHmmyyyy.ss"))}\""
  486. )
  487. } catch (e: Exception) {
  488. Log.e(TAG, "Error SyncTime", e)
  489. }
  490. }
  491. @JvmStatic
  492. suspend fun hasRoot(): Boolean {
  493. val hasRoot = run checkRoot@{
  494. repeat(5) {
  495. if (Utils.hasRootAccess()) {
  496. return@checkRoot true
  497. }
  498. delay(500)
  499. }
  500. return@checkRoot false
  501. }
  502. return hasRoot
  503. }
  504. @SuppressLint("DefaultLocale")
  505. @JvmStatic
  506. fun genICCID(mnc: String, areaCode: String): String {
  507. val prefix = String.format("89%02d%s", areaCode.toInt(), mnc)
  508. return prefix + RandomStringUtils.randomNumeric(20 - prefix.length)
  509. }
  510. @JvmStatic
  511. suspend fun rebooted(): Boolean {
  512. if (shellRun("getprop rebooted").contains("yes")) {
  513. return false
  514. }
  515. shellRun("setprop rebooted yes")
  516. return true
  517. }
  518. @SuppressLint("SdCardPath")
  519. @JvmStatic
  520. suspend fun backup(backupItemDao: BackupItemDao, type: String, sendCount: Int): BackupItem {
  521. clearConv()
  522. // ModifierService.instance!!.toggleRcsSwitch(false)
  523. val context = Utils.getContext()
  524. val dest = File(
  525. ContextCompat.getExternalFilesDirs(context, "backup")[0],
  526. System.currentTimeMillis().toString()
  527. )
  528. dest.mkdirs()
  529. val file = File(ContextCompat.getDataDir(context), "config.json")
  530. if (!file.exists()) {
  531. throw Exception("Config file not found")
  532. }
  533. withContext(Dispatchers.IO) {
  534. IOUtils.copy(
  535. Files.newInputStream(file.toPath()),
  536. Files.newOutputStream(File(dest, "config.json").toPath())
  537. )
  538. }
  539. val dataDir = File(dest, "data")
  540. dataDir.mkdirs()
  541. val packages = mutableListOf(
  542. "com.google.android.apps.messaging",
  543. "com.google.android.gms",
  544. "com.google.android.gsf",
  545. )
  546. packages.forEach {
  547. File(dataDir, it).mkdirs()
  548. }
  549. val cmds = mutableListOf<String>()
  550. cmds.addAll(packages.flatMap { mutableListOf("pm suspend $it", "am force-stop $it") })
  551. for (pkg in packages) {
  552. if (!shellRun("ls /data/data/$pkg")["error"]!!.contains("No such file or directory")) {
  553. cmds.add("cp -r /data/data/$pkg $dataDir/$pkg/data")
  554. }
  555. if (!shellRun("ls /data/user_de/0/$pkg")["error"]!!.contains("No such file or directory")) {
  556. cmds.add("cp -r /data/user_de/0/$pkg $dataDir/$pkg/user_de")
  557. }
  558. if (!shellRun("ls /sdcard/Android/data/$pkg")["error"]!!.contains("No such file or directory")) {
  559. cmds.add("cp -r /sdcard/Android/data/$pkg $dataDir/$pkg/external")
  560. }
  561. }
  562. cmds.addAll(packages.reversed().map { "pm unsuspend $it" })
  563. shellRun(*cmds.toTypedArray())
  564. val backup = BackupItem(
  565. createdAt = Date().time,
  566. number = telephonyConfig.number,
  567. code = if (telephonyConfig.areaCode == null) "" else telephonyConfig.areaCode,
  568. country = telephonyConfig.country,
  569. mcc = telephonyConfig.mcc,
  570. mnc = telephonyConfig.mnc,
  571. imei = telephonyConfig.imei,
  572. imsi = telephonyConfig.imsi,
  573. iccid = telephonyConfig.iccid,
  574. path = dest.path,
  575. sendCount = sendCount,
  576. lastUse = Date().time,
  577. type = type
  578. )
  579. backupItemDao.findBackup(telephonyConfig.country, telephonyConfig.number)?.let {
  580. File(it.path).deleteRecursively()
  581. backupItemDao.delete(it)
  582. backup.sendCount += it.sendCount
  583. }
  584. backup.id = backupItemDao.insert(backup).toInt()
  585. return backup
  586. }
  587. @JvmStatic
  588. suspend fun restore(backup: BackupItem) {
  589. save(
  590. TelephonyConfig(
  591. number = backup.number,
  592. mcc = backup.mcc,
  593. mnc = backup.mnc,
  594. country = backup.country,
  595. areaCode = backup.code,
  596. iccid = backup.iccid,
  597. imei = backup.imei,
  598. imsi = backup.imsi,
  599. ), false
  600. )
  601. val packages = mutableListOf(
  602. "com.google.android.apps.messaging",
  603. "com.google.android.gms",
  604. "com.google.android.gsf",
  605. )
  606. val cmds = mutableListOf<String>()
  607. cmds.addAll(packages.flatMap { mutableListOf("pm suspend $it", "am force-stop $it") })
  608. val packageManager = Utils.getContext().packageManager
  609. for (pkg in packages) {
  610. val uid = packageManager.getApplicationInfo(pkg, 0).uid
  611. if (File("${backup.path}/data/$pkg/data").exists()) {
  612. cmds.add("rm -rf /data/data/$pkg/*")
  613. cmds.add("cp -r ${backup.path}/data/$pkg/data/* /data/data/$pkg")
  614. cmds.add("chown -R $uid:$uid /data/data/$pkg")
  615. }
  616. if (File("${backup.path}/data/$pkg/user_de").exists()) {
  617. cmds.add("rm -rf /data/user_de/0/$pkg/*")
  618. cmds.add("cp -r ${backup.path}/data/$pkg/user_de/* /data/user_de/0/$pkg")
  619. cmds.add("chown -R $uid:$uid /data/user_de/0/$pkg")
  620. }
  621. if (File("${backup.path}/data/$pkg/external").exists()) {
  622. cmds.add("rm -rf /sdcard/Android/data/$pkg/*")
  623. cmds.add("cp -r ${backup.path}/data/$pkg/external/* /sdcard/Android/data/$pkg")
  624. cmds.add("chown -R $uid:$uid /sdcard/Android/data/$pkg")
  625. }
  626. }
  627. cmds.addAll(packages.reversed().map { "pm unsuspend $it" })
  628. shellRun(*cmds.toTypedArray())
  629. }
  630. }