|
|
@@ -13,18 +13,16 @@ import com.example.modifier.constants.CMD_MESSAGING_APP
|
|
|
import com.example.modifier.constants.CMD_RCS_SETTINGS_ACTIVITY
|
|
|
import com.example.modifier.constants.PACKAGE_GMS
|
|
|
import com.example.modifier.constants.PACKAGE_MESSAGING
|
|
|
-import com.example.modifier.data.AppState
|
|
|
import com.example.modifier.enums.RcsConfigureState
|
|
|
import com.example.modifier.enums.RcsConnectionStatus
|
|
|
import com.example.modifier.enums.RequestNumberState
|
|
|
+import com.example.modifier.exception.RequestNumberException
|
|
|
+import com.example.modifier.exception.RequestNumberException.Companion.ErrorCode
|
|
|
import com.example.modifier.extension.kill
|
|
|
import com.example.modifier.http.api.DeviceApi
|
|
|
import com.example.modifier.http.api.RcsNumberApi
|
|
|
import com.example.modifier.http.api.SysConfigApi
|
|
|
import com.example.modifier.http.ktorClient
|
|
|
-import com.example.modifier.http.request.RcsNumberRequest
|
|
|
-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.RunScriptAction
|
|
|
@@ -48,28 +46,19 @@ import com.example.modifier.utils.isOldVersion
|
|
|
import com.example.modifier.utils.resetAll
|
|
|
import com.example.modifier.utils.shellRun
|
|
|
import com.example.modifier.utils.smsIntent
|
|
|
-import com.example.modifier.utils.spoofSmsIntent
|
|
|
+import com.example.modifier.utils.injectOTP
|
|
|
import io.ktor.client.HttpClient
|
|
|
import io.ktor.client.call.body
|
|
|
import io.ktor.client.engine.okhttp.OkHttp
|
|
|
import io.ktor.client.plugins.HttpResponseValidator
|
|
|
import io.ktor.client.plugins.ServerResponseException
|
|
|
import io.ktor.client.plugins.resources.get
|
|
|
-import io.ktor.client.plugins.resources.post
|
|
|
-import io.ktor.client.plugins.resources.put
|
|
|
-import io.ktor.client.plugins.timeout
|
|
|
import io.ktor.client.request.prepareGet
|
|
|
-import io.ktor.client.request.setBody
|
|
|
-import io.ktor.http.ContentType
|
|
|
-import io.ktor.http.contentType
|
|
|
import io.ktor.utils.io.ByteReadChannel
|
|
|
import io.ktor.utils.io.core.isEmpty
|
|
|
import io.ktor.utils.io.core.readBytes
|
|
|
-import kotlinx.coroutines.CoroutineScope
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
import kotlinx.coroutines.delay
|
|
|
-import kotlinx.coroutines.flow.StateFlow
|
|
|
-import kotlinx.coroutines.launch
|
|
|
import kotlinx.coroutines.withContext
|
|
|
import kotlinx.coroutines.withTimeout
|
|
|
import kotlinx.coroutines.withTimeoutOrNull
|
|
|
@@ -363,6 +352,112 @@ class TaskRunner(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ suspend fun requestNumberAtomic() {
|
|
|
+ appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.REQUEST)
|
|
|
+ googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
|
|
|
+ val device = DeviceApi.getDevice(appPrefsRepo.appPrefs.value.id)
|
|
|
+ val rcsNumber = RcsNumberApi.getRcsNumber(
|
|
|
+ appPrefsRepo.appPrefs.value.id,
|
|
|
+ currentTaskId,
|
|
|
+ device.pinCountry
|
|
|
+ )
|
|
|
+ Log.i(tag, "requestNumber response: $rcsNumber")
|
|
|
+
|
|
|
+ appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.OTP_1)
|
|
|
+
|
|
|
+ spoofedSimInfoRepo.updateSpoofedSimInfo(
|
|
|
+ SpoofedSimInfo(
|
|
|
+ numberId = rcsNumber.id,
|
|
|
+ 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,
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ shellRun(CMD_MESSAGING_APP)
|
|
|
+
|
|
|
+ if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_NUMBER_EXPIRED)
|
|
|
+ }
|
|
|
+ var sendOtpTimeout = ChronoUnit.SECONDS.between(
|
|
|
+ LocalDateTime.now(),
|
|
|
+ rcsNumber.expiryTime
|
|
|
+ ).seconds
|
|
|
+ if (sendOtpTimeout < 5.seconds) {
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_TIMEOUT_TOO_SHORT)
|
|
|
+ }
|
|
|
+ if (sendOtpTimeout > 2.minutes) {
|
|
|
+ sendOtpTimeout = 2.minutes
|
|
|
+ }
|
|
|
+ if (googleMessageStateRepository.waitForRcsState(
|
|
|
+ arrayOf(RcsConfigureState.WAITING_FOR_OTP),
|
|
|
+ sendOtpTimeout
|
|
|
+ ) != RcsConfigureState.WAITING_FOR_OTP
|
|
|
+ ) {
|
|
|
+ if (!screenController.toggleRcsSwitch(true)) {
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_RCS_TOGGLED_OFF)
|
|
|
+ }
|
|
|
+ if (RcsConfigureState.REPLAY_REQUEST == googleMessageStateRepository.rcsConfigureState.value) {
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_REPLAY_RETRY)
|
|
|
+ }
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_OTP_NOT_SENT)
|
|
|
+ }
|
|
|
+
|
|
|
+ RcsNumberApi.notifyOtpState(rcsNumber.id)
|
|
|
+
|
|
|
+ if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_NUMBER_EXPIRED)
|
|
|
+ }
|
|
|
+
|
|
|
+ appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.OTP_2)
|
|
|
+ val otp = RcsNumberApi.waitForOtp(rcsNumber.id)
|
|
|
+ ?: throw RequestNumberException(ErrorCode.CODE_OTP_NOT_RECEIVED)
|
|
|
+
|
|
|
+ appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.CONFIG)
|
|
|
+
|
|
|
+ val configured = run configuring@{
|
|
|
+ repeat(2) {
|
|
|
+ injectOTP(otp)
|
|
|
+ val state =
|
|
|
+ googleMessageStateRepository.waitForRcsState(
|
|
|
+ arrayOf(
|
|
|
+ RcsConfigureState.CONFIGURED,
|
|
|
+ RcsConfigureState.RETRY
|
|
|
+ ), 60.seconds
|
|
|
+ )
|
|
|
+ when (state) {
|
|
|
+ RcsConfigureState.CONFIGURED -> {
|
|
|
+ return@configuring true
|
|
|
+ }
|
|
|
+
|
|
|
+ RcsConfigureState.RETRY -> {
|
|
|
+ googleMessageStateRepository.waitForRcsState(
|
|
|
+ arrayOf(RcsConfigureState.WAITING_FOR_OTP),
|
|
|
+ 60.seconds
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ else -> {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ false
|
|
|
+ }
|
|
|
+ if (!configured) {
|
|
|
+ throw RequestNumberException(ErrorCode.CODE_OTP_VERIFY_FAILED)
|
|
|
+ } else {
|
|
|
+ RcsNumberApi.notifyConfigured(rcsNumber.id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
suspend fun requestNumber(
|
|
|
reset: Boolean = false,
|
|
|
noBackup: Boolean = false,
|
|
|
@@ -389,42 +484,12 @@ class TaskRunner(
|
|
|
}
|
|
|
|
|
|
appStateRepo.incrementRequestedNum()
|
|
|
- var requestSuccess = false
|
|
|
- var retry = 0
|
|
|
var needRest = reset
|
|
|
- withTimeoutOrNull(2.hours) {
|
|
|
+ var requestSuccess = withTimeoutOrNull(2.hours) {
|
|
|
while (true) {
|
|
|
delay(200)
|
|
|
- needRest = needRest || retry > 2 || appStateRepo.appState.value.requestedNum > 5
|
|
|
+ needRest = needRest || appStateRepo.appState.value.requestedNum > 5
|
|
|
try {
|
|
|
- val device =
|
|
|
- ktorClient.get(DeviceApi.Id(id = appPrefsRepo.appPrefs.value.id))
|
|
|
- .body<DeviceResponse>()
|
|
|
- if (isClashInstalled(context)) {
|
|
|
-
|
|
|
- val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
|
|
- if (TextUtils.isEmpty(device.clashProfile)) {
|
|
|
- prefs.edit()
|
|
|
- .remove("clash_profile")
|
|
|
- .apply()
|
|
|
- //stopClash()
|
|
|
- } else {
|
|
|
- val oldProfile = prefs.getString("clash_profile", "")
|
|
|
- if (oldProfile != device.clashProfile) {
|
|
|
- prefs.edit()
|
|
|
- .putString("clash_profile", device.clashProfile)
|
|
|
- .apply()
|
|
|
- changeClashProfile(
|
|
|
- device.pinCountry!!, Base64.encodeToString(
|
|
|
- device.clashProfile!!.toByteArray(),
|
|
|
- Base64.DEFAULT
|
|
|
- )
|
|
|
- )
|
|
|
- delay(5000)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
if (requestMode == 2 && !noBackup) {
|
|
|
val backup = backupRepository.findBackupForRestore(
|
|
|
spoofedSimInfoRepo.spoofedSimInfo.value.number,
|
|
|
@@ -432,8 +497,7 @@ class TaskRunner(
|
|
|
)
|
|
|
if (backup != null) {
|
|
|
if (backupRepository.restore(backup)) {
|
|
|
- requestSuccess = true
|
|
|
- break
|
|
|
+ return@withTimeoutOrNull true
|
|
|
} else {
|
|
|
appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.BACKUP)
|
|
|
backupRepository.backup(
|
|
|
@@ -448,196 +512,17 @@ class TaskRunner(
|
|
|
|
|
|
if (needRest && !appPrefsRepo.appPrefs.value.preventReset) {
|
|
|
reset()
|
|
|
- retry = 0
|
|
|
needRest = false
|
|
|
}
|
|
|
|
|
|
- googleMessageStateRepository.updateRcsState(RcsConfigureState.NOT_CONFIGURED)
|
|
|
- appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.REQUEST)
|
|
|
-
|
|
|
- val req = RcsNumberRequest(
|
|
|
- deviceId = appPrefsRepo.appPrefs.value.id,
|
|
|
- taskId = currentTaskId
|
|
|
- )
|
|
|
- if (!TextUtils.isEmpty(device.pinCountry)) {
|
|
|
- req.country = device.pinCountry
|
|
|
- }
|
|
|
- val response = ktorClient.put(
|
|
|
- RcsNumberApi()
|
|
|
- ) {
|
|
|
- contentType(ContentType.Application.Json)
|
|
|
- setBody(req)
|
|
|
- timeout {
|
|
|
- requestTimeoutMillis = 60 * 1000
|
|
|
- socketTimeoutMillis = 60 * 1000
|
|
|
- }
|
|
|
- }
|
|
|
- var rcsNumber = response.body<RcsNumberResponse>()
|
|
|
- Log.i(tag, "requestNumber response: $rcsNumber")
|
|
|
-
|
|
|
- appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.OTP_1)
|
|
|
-
|
|
|
- spoofedSimInfoRepo.updateSpoofedSimInfo(
|
|
|
- SpoofedSimInfo(
|
|
|
- numberId = rcsNumber.id,
|
|
|
- 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,
|
|
|
- )
|
|
|
- )
|
|
|
-
|
|
|
- shellRun(CMD_MESSAGING_APP)
|
|
|
-
|
|
|
- if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
|
|
|
- Log.e(tag, "RCS number expired, retrying...")
|
|
|
- continue
|
|
|
- }
|
|
|
- var sendOtpTimeout = ChronoUnit.SECONDS.between(
|
|
|
- LocalDateTime.now(),
|
|
|
- rcsNumber.expiryTime
|
|
|
- ).seconds
|
|
|
- if (sendOtpTimeout < 60.seconds) {
|
|
|
- Log.e(tag, "OTP timeout too short, retrying...")
|
|
|
- continue
|
|
|
- }
|
|
|
- if (sendOtpTimeout > 2.minutes) {
|
|
|
- sendOtpTimeout = 2.minutes
|
|
|
- }
|
|
|
- if (googleMessageStateRepository.waitForRcsState(
|
|
|
- arrayOf(RcsConfigureState.WAITING_FOR_OTP),
|
|
|
- sendOtpTimeout
|
|
|
- ) != RcsConfigureState.WAITING_FOR_OTP
|
|
|
- ) {
|
|
|
- if (!screenController.toggleRcsSwitch(true)) {
|
|
|
- needRest = true
|
|
|
- }
|
|
|
- if (RcsConfigureState.REPLAY_REQUEST == googleMessageStateRepository.rcsConfigureState.value) {
|
|
|
- Log.e(tag, "REPLAY_REQUEST detected, may reset after 3 retry ($retry)")
|
|
|
- retry++
|
|
|
- }
|
|
|
- Log.e(tag, "RCS not entered waiting for OTP state, retrying...")
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- launch {
|
|
|
- try {
|
|
|
- ktorClient.post(
|
|
|
- RcsNumberApi.Id.OtpState(
|
|
|
- RcsNumberApi.Id(
|
|
|
- RcsNumberApi(),
|
|
|
- rcsNumber.id
|
|
|
- )
|
|
|
- )
|
|
|
- )
|
|
|
- } catch (e: Exception) {
|
|
|
- Log.e(tag, "Send OtpState Error: ${e.message}", e)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (rcsNumber.expiryTime.isBefore(LocalDateTime.now())) {
|
|
|
- Log.e(tag, "RCS number expired, retrying...")
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.OTP_2)
|
|
|
- withTimeoutOrNull(60.seconds) {
|
|
|
- while (true) {
|
|
|
- try {
|
|
|
- rcsNumber =
|
|
|
- ktorClient.get(RcsNumberApi.Id(id = rcsNumber.id))
|
|
|
- .body<RcsNumberResponse>()
|
|
|
- Log.i(tag, "wait for otp response: $rcsNumber")
|
|
|
- if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS || rcsNumber.status == RcsNumberResponse.STATUS_EXPIRED) {
|
|
|
- break
|
|
|
- }
|
|
|
- } catch (exception: Exception) {
|
|
|
- Log.e(tag, "wait for otp Error: ${exception.stackTrace}")
|
|
|
- }
|
|
|
- delay(2.seconds)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (rcsNumber.status != RcsNumberResponse.STATUS_SUCCESS) {
|
|
|
- Log.e(tag, "OTP not received, retrying...")
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- val match =
|
|
|
- Regex("Your Messenger verification code is G-(\\d{6})")
|
|
|
- .find(rcsNumber.message!!)
|
|
|
- if (match != null) {
|
|
|
- appStateRepo.updateRuntimeFlags(requestNumberState = RequestNumberState.CONFIG)
|
|
|
-
|
|
|
- val otp = match.groupValues[1]
|
|
|
- Log.i(tag, "OTP: $otp")
|
|
|
- val sender = "3538"
|
|
|
- val msg = "Your Messenger verification code is G-$otp"
|
|
|
-
|
|
|
- val configured = run configuring@{
|
|
|
- repeat(2) {
|
|
|
- spoofSmsIntent(sender, msg)
|
|
|
- val state =
|
|
|
- googleMessageStateRepository.waitForRcsState(
|
|
|
- arrayOf(
|
|
|
- RcsConfigureState.CONFIGURED,
|
|
|
- RcsConfigureState.RETRY
|
|
|
- ), 60.seconds
|
|
|
- )
|
|
|
- when (state) {
|
|
|
- RcsConfigureState.CONFIGURED -> {
|
|
|
- return@configuring true
|
|
|
- }
|
|
|
-
|
|
|
- RcsConfigureState.RETRY -> {
|
|
|
- googleMessageStateRepository.waitForRcsState(
|
|
|
- arrayOf(RcsConfigureState.WAITING_FOR_OTP),
|
|
|
- 60.seconds
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- else -> {
|
|
|
- Log.e(tag, "verifyOtp fail, retrying...")
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- false
|
|
|
- }
|
|
|
- if (!configured) {
|
|
|
- Log.e(tag, "RCS not configured, retrying...")
|
|
|
- continue
|
|
|
- } else {
|
|
|
- launch {
|
|
|
- try {
|
|
|
- ktorClient.post(
|
|
|
- RcsNumberApi.Id.Configured(
|
|
|
- RcsNumberApi.Id(
|
|
|
- RcsNumberApi(),
|
|
|
- rcsNumber.id
|
|
|
- )
|
|
|
- )
|
|
|
- )
|
|
|
- } catch (e: Exception) {
|
|
|
- Log.e(
|
|
|
- tag,
|
|
|
- "Send ConfiguredState Error: ${e.message}",
|
|
|
- e
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- requestSuccess = true
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
+ requestNumberAtomic()
|
|
|
+ return@withTimeoutOrNull true
|
|
|
} catch (e: Exception) {
|
|
|
- Log.e(tag, "requestNumberError: ${e.message}", e)
|
|
|
+ if (e is RequestNumberException) {
|
|
|
+ Log.e(tag, "requestNumberError: ${e.message}")
|
|
|
+ } else {
|
|
|
+ Log.e(tag, "requestNumberError: ${e.message}", e)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|