BackupRepository.kt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. package com.example.modifier.repo
  2. import android.annotation.SuppressLint
  3. import android.content.Context
  4. import android.util.Log
  5. import androidx.core.content.ContextCompat
  6. import com.example.modifier.baseTag
  7. import com.example.modifier.constants.CMD_MESSAGING_APP
  8. import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY
  9. import com.example.modifier.constants.PACKAGE_GMS
  10. import com.example.modifier.constants.PACKAGE_GSF
  11. import com.example.modifier.constants.PACKAGE_MESSAGING
  12. import com.example.modifier.data.BackupItem
  13. import com.example.modifier.data.BackupItemDao
  14. import com.example.modifier.enums.RcsConfigureState
  15. import com.example.modifier.enums.ReqState
  16. import com.example.modifier.extension.clear
  17. import com.example.modifier.extension.disable
  18. import com.example.modifier.extension.enable
  19. import com.example.modifier.extension.kill
  20. import com.example.modifier.http.api.RcsNumberApi
  21. import com.example.modifier.model.SpoofedSimInfo
  22. import com.example.modifier.service.ModifierService
  23. import com.example.modifier.utils.clearConv
  24. import com.example.modifier.utils.getContext
  25. import com.example.modifier.utils.shellRun
  26. import kotlinx.coroutines.CoroutineScope
  27. import kotlinx.coroutines.Dispatchers
  28. import kotlinx.coroutines.coroutineScope
  29. import kotlinx.coroutines.delay
  30. import kotlinx.coroutines.launch
  31. import kotlinx.coroutines.withContext
  32. import java.io.File
  33. import java.util.Date
  34. import java.util.Optional
  35. import kotlin.coroutines.coroutineContext
  36. import kotlin.time.Duration.Companion.minutes
  37. import kotlin.time.Duration.Companion.seconds
  38. class BackupRepository(
  39. private val context: Context,
  40. private val dao: BackupItemDao,
  41. ) {
  42. companion object {
  43. private const val TAG = "$baseTag/BackupRepository"
  44. }
  45. suspend fun backup(type: String, sendCount: Int, stock: Int = 0): BackupItem {
  46. AppStateRepo.instance.updateRuntimeFlags(reqState = ReqState.BACKUP)
  47. val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
  48. clearConv()
  49. delay(3000)
  50. // ModifierService.instance!!.toggleRcsSwitch(false)
  51. val context = getContext()
  52. val dest = File(
  53. ContextCompat.getExternalFilesDirs(context, "backup")[0],
  54. System.currentTimeMillis().toString()
  55. )
  56. dest.mkdirs()
  57. val packages = mutableListOf(
  58. PACKAGE_MESSAGING,
  59. PACKAGE_GMS,
  60. PACKAGE_GSF
  61. )
  62. packages.forEach {
  63. File(dest, it).mkdirs()
  64. }
  65. val cmds = mutableListOf<String>()
  66. for (pkg in packages) {
  67. if (!shellRun("ls /data/data/$pkg").second
  68. .contains("No such file or directory")
  69. ) {
  70. cmds.add("tar zcpf $dest/$pkg/data.tar.gz -C /data/data $pkg ")
  71. }
  72. if (!shellRun("ls /data/user_de/0/$pkg").second
  73. .contains("No such file or directory")
  74. ) {
  75. cmds.add("tar zcpf $dest/$pkg/data_de.tar.gz -C /data/user_de/0 $pkg ")
  76. }
  77. if (!shellRun("ls /sdcard/Android/data/$pkg").second
  78. .contains("No such file or directory")
  79. ) {
  80. cmds.add("tar zcpf $dest/$pkg/data_ext.tar.gz -C /sdcard/Android/data $pkg ")
  81. }
  82. }
  83. shellRun(*cmds.toTypedArray())
  84. val spoofedSimInfo = spoofedSimInfoRepo.spoofedSimInfo.value
  85. val backup = BackupItem(
  86. createdAt = Date().time,
  87. numberId = spoofedSimInfo.numberId,
  88. number = spoofedSimInfo.number,
  89. code = spoofedSimInfo.areaCode,
  90. country = spoofedSimInfo.country,
  91. mcc = spoofedSimInfo.mcc,
  92. mnc = spoofedSimInfo.mnc,
  93. imei = spoofedSimInfo.imei,
  94. imsi = spoofedSimInfo.imsi,
  95. iccid = spoofedSimInfo.iccid,
  96. path = dest.path,
  97. sendCount = sendCount,
  98. lastUse = Date().time,
  99. type = type,
  100. stock = stock,
  101. carrierId = Optional.ofNullable(spoofedSimInfo.carrierId).orElse("1"),
  102. carrierName = Optional.ofNullable(spoofedSimInfo.carrierName).orElse("T-Mobile"),
  103. retry = 0
  104. )
  105. dao.findBackupForNumber(spoofedSimInfo.country, spoofedSimInfo.number)?.let {
  106. File(it.path).deleteRecursively()
  107. backup.sendCount += it.sendCount
  108. backup.stock = it.stock
  109. dao.delete(it)
  110. }
  111. backup.id = dao.insert(backup).toInt()
  112. return backup
  113. }
  114. @SuppressLint("SdCardPath")
  115. suspend fun restore(backup: BackupItem): Boolean {
  116. val spoofedSimInfoRepo = SpoofedSimInfoRepo.instance
  117. val simInfo = SpoofedSimInfo(
  118. numberId = backup.numberId,
  119. number = backup.number,
  120. mcc = backup.mcc,
  121. mnc = backup.mnc,
  122. country = backup.country,
  123. areaCode = backup.code,
  124. iccid = backup.iccid,
  125. imei = backup.imei,
  126. imsi = backup.imsi,
  127. available = false,
  128. carrierId = backup.carrierId,
  129. carrierName = backup.carrierName
  130. )
  131. spoofedSimInfoRepo.updateSpoofedSimInfo(spoofedSimInfo = simInfo, suspend = false)
  132. val packages = mutableListOf(
  133. PACKAGE_MESSAGING,
  134. PACKAGE_GMS,
  135. PACKAGE_GSF
  136. )
  137. shellRun(PACKAGE_MESSAGING.clear(), "sleep 2", PACKAGE_MESSAGING.disable())
  138. for (pkg in packages) {
  139. for (item in listOf("data.tar.gz", "data_de.tar.gz", "data_ext.tar.gz")) {
  140. val file = File("${backup.path}/$pkg/$item")
  141. if (!file.exists()) {
  142. continue
  143. }
  144. val dest = when (item) {
  145. "data.tar.gz" -> "/data/data"
  146. "data_de.tar.gz" -> "/data/user_de/0"
  147. "data_ext.tar.gz" -> "/sdcard/Android/data"
  148. else -> ""
  149. }
  150. shellRun(
  151. "find $dest/$pkg -type f -delete",
  152. "tar zxf ${file.path} -C $dest --exclude \"**/AndroidPlatformServices.apk\""
  153. )
  154. }
  155. }
  156. val taskRunner = ModifierService.instance?.taskRunner
  157. shellRun(
  158. // "sed -i 's/<boolean name=\"enable_rcs\" value=\"true\" \\/>/<boolean name=\"enable_rcs\" value=\"false\" \\/>/g' /data/data/com.google.android.apps.messaging/shared_prefs/bugle.xml",
  159. PACKAGE_GSF.kill(),
  160. "sleep 1",
  161. PACKAGE_GMS.kill(),
  162. "settings put secure location_mode 0",
  163. // "pm revoke com.google.android.gms android.permission.GET_ACCOUNTS",
  164. // "pm revoke com.google.android.gms android.permission.SYSTEM_ALERT_WINDOW",
  165. // "pm revoke com.google.android.gms android.permission.POST_NOTIFICATIONS",
  166. // "pm revoke com.google.android.gms android.permission.READ_CONTACTS",
  167. // "pm revoke com.google.android.gms android.permission.CAMERA",
  168. // "pm revoke com.google.android.gms android.permission.RECEIVE_MMS",
  169. // "pm revoke com.google.android.gms android.permission.GET_APP_OPS_STATS",
  170. // "pm revoke com.google.android.gms android.permission.PROCESS_OUTGOING_CALLS",
  171. // "pm revoke com.google.android.gms android.permission.BLUETOOTH_CONNECT",
  172. // "pm revoke com.google.android.gms android.permission.BLUETOOTH_SCAN",
  173. // "pm revoke com.google.android.gms android.permission.BLUETOOTH_ADVERTISE",
  174. // "pm revoke com.google.android.gms android.permission.NEARBY_WIFI_DEVICES",
  175. // "pm revoke com.google.android.gms android.permission.UWB_RANGING",
  176. // "pm revoke com.google.android.gms android.permission.READ_CALL_LOG",
  177. // "pm revoke com.google.android.gms android.permission.WRITE_CONTACTS",
  178. // "pm revoke com.google.android.gms android.permission.CALL_PHONE",
  179. // "pm revoke com.google.android.gms android.permission.RECORD_AUDIO",
  180. // "pm revoke com.google.android.gms android.permission.READ_LOGS",
  181. // "pm revoke com.google.android.gms android.permission.READ_MEDIA_AUDIO",
  182. // "pm revoke com.google.android.gms android.permission.READ_MEDIA_IMAGES",
  183. // "pm revoke com.google.android.gms android.permission.READ_MEDIA_VIDEO",
  184. // "pm revoke com.google.android.gms android.permission.ACCESS_MEDIA_LOCATION",
  185. // "pm revoke com.google.android.gms android.permission.ACCESS_BROADCAST_RESPONSE_STATS",
  186. // "pm revoke com.google.android.gms android.permission.WRITE_CALL_LOG",
  187. // "pm revoke com.google.android.gms android.permission.BODY_SENSORS",
  188. // "pm revoke com.google.android.gms android.permission.DUMP",
  189. // "sleep 30",
  190. PACKAGE_MESSAGING.enable(),
  191. "sleep 1",
  192. CMD_MESSAGING_APP,
  193. "sleep 5",
  194. CMD_RCS_SETTINGS_ACTIVITY,
  195. "sleep 5",
  196. PACKAGE_MESSAGING.kill(),
  197. "sleep 1",
  198. CMD_MESSAGING_APP,
  199. "sleep 5",
  200. )
  201. var otp = false
  202. val success = run isSuccess@{
  203. if (backup.stock != 1) {
  204. return@isSuccess true
  205. }
  206. if (taskRunner == null) return@isSuccess false
  207. if (!taskRunner.screenController.toggleRcsSwitch(true)) {
  208. shellRun(
  209. PACKAGE_GMS.kill(),
  210. PACKAGE_MESSAGING.kill(),
  211. "sleep 5",
  212. CMD_MESSAGING_APP
  213. )
  214. delay(5000)
  215. if (!taskRunner.screenController.toggleRcsSwitch(true)) {
  216. return@isSuccess false
  217. }
  218. }
  219. val job = CoroutineScope(coroutineContext).launch {
  220. taskRunner.gmsgStateRepository.rcsConfigureState.collect {
  221. if (it == RcsConfigureState.RETRY) {
  222. shellRun(
  223. PACKAGE_GMS.kill(),
  224. "sleep 5",
  225. PACKAGE_MESSAGING.kill(),
  226. CMD_MESSAGING_APP
  227. )
  228. }
  229. }
  230. }
  231. val state = taskRunner.gmsgStateRepository.waitForRcsState(
  232. arrayOf(RcsConfigureState.CONFIGURED, RcsConfigureState.WAITING_FOR_OTP), 5.minutes
  233. )
  234. job.cancel()
  235. if (state == RcsConfigureState.CONFIGURED) return@isSuccess true
  236. if (state == RcsConfigureState.WAITING_FOR_OTP) otp = true
  237. false
  238. }
  239. if (success) {
  240. Log.i(TAG, "restore: success")
  241. spoofedSimInfoRepo.updateSpoofedSimInfo(
  242. spoofedSimInfo = simInfo.copy(available = true),
  243. suspend = false
  244. )
  245. if (backup.stock == 1) {
  246. backup.stock = 2
  247. dao.update(backup)
  248. RcsNumberApi.updateStockFlag(backup.numberId, 2)
  249. }
  250. return true
  251. } else {
  252. Log.i(TAG, "restore: failed")
  253. if (backup.stock == 1) {
  254. backup.retry++
  255. if (backup.retry >= 3 || otp) {
  256. RcsNumberApi.updateStockFlag(backup.numberId, 3)
  257. if (otp) {
  258. dao.delete(backup)
  259. } else {
  260. backup.stock = 3
  261. dao.update(backup)
  262. }
  263. } else {
  264. dao.update(backup)
  265. }
  266. }
  267. }
  268. return false
  269. }
  270. suspend fun delete(backupItem: BackupItem) {
  271. dao.delete(backupItem)
  272. }
  273. suspend fun findBackupForRestore(number: String): BackupItem? {
  274. return dao.findBackupForRestore(number)
  275. }
  276. suspend fun cleanBackup(start: Long, end: Long) {
  277. dao.findBackupsBetween(start, end).forEach {
  278. runCatching {
  279. File(it.path).deleteRecursively()
  280. }
  281. dao.delete(it)
  282. }
  283. }
  284. }