diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/newtransfer/TransferSendManager.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/newtransfer/TransferSendManager.kt index edef6f222..68feaf306 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/newtransfer/TransferSendManager.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/newtransfer/TransferSendManager.kt @@ -29,7 +29,7 @@ import com.infomaniak.swisstransfer.ui.screen.newtransfer.ImportFilesViewModel.A import com.infomaniak.swisstransfer.ui.screen.newtransfer.ImportFilesViewModel.SendActionResult import com.infomaniak.swisstransfer.workers.UploadWorker import dagger.hilt.android.scopes.ViewModelScoped -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -53,58 +53,98 @@ class TransferSendManager @Inject constructor( private val _integrityCheckResult = MutableStateFlow(AppIntegrityResult.Idle) val integrityCheckResult = _integrityCheckResult.asStateFlow() - //region App Integrity suspend fun sendTransfer(newUploadSession: NewUploadSession) { _integrityCheckResult.value = AppIntegrityResult.Ongoing - coroutineScope { - runCatching { - appIntegrityManager.getChallenge( - onSuccess = { requestAppIntegrityToken(appIntegrityManager, newUploadSession) }, - onFailure = ::setFailedIntegrityResult, - ) - }.onFailure { exception -> - SentryLog.e(TAG, "Failed to start the upload", exception) + + withIntegrityToken( + onSuccess = { attestationToken -> sendTransfer(newUploadSession, attestationToken) }, + onRefused = { _integrityCheckResult.value = AppIntegrityResult.Fail }, + onFailure = { exception -> + if (exception !is CancellationException) { + SentryLog.e(TAG, "Integrity token received an exception", exception) + } else { + SentryLog.i(TAG, "Integrity token received an exception", exception) + } _sendActionResult.update { SendActionResult.Failure } } + ) + } + + private suspend fun sendTransfer(newUploadSession: NewUploadSession, attestationToken: String) { + _integrityCheckResult.value = AppIntegrityResult.Success + _sendActionResult.update { SendActionResult.Pending } + + runCatching { + val uuid = uploadManager.createAndGetUpload(newUploadSession).uuid + uploadManager.initUploadSession( + attestationHeaderName = AppIntegrityManager.ATTESTATION_TOKEN_HEADER, + attestationToken = attestationToken, + )!! // TODO: Handle ContainerErrorsException here + uploadWorkerScheduler.scheduleWork(uuid) + _sendActionResult.update { + val totalSize = importationFilesManager.importedFiles.value.sumOf { it.fileSize } + SendActionResult.Success(totalSize) + } + }.onFailure { exception -> + SentryLog.e(TAG, "Failed to start the upload", exception) + _sendActionResult.update { SendActionResult.Failure } } } - private fun CoroutineScope.requestAppIntegrityToken( - appIntegrityManager: AppIntegrityManager, - newUploadSession: NewUploadSession, + //region App Integrity + private suspend inline fun withIntegrityToken( + onSuccess: (attestationToken: String) -> Unit, + onRefused: () -> Unit = {}, + onFailure: (exception: Throwable) -> Unit = {}, ) { - appIntegrityManager.requestClassicIntegrityVerdictToken( - onSuccess = { token -> - SentryLog.i(APP_INTEGRITY_MANAGER_TAG, "request for app integrity token successful $token") - getApiIntegrityVerdict(appIntegrityManager, token, newUploadSession) - }, - onFailure = ::setFailedIntegrityResult, - ) + runCatching { + var attestationToken: String? = null + + coroutineScope { + appIntegrityManager.getChallenge( + onSuccess = { launch { attestationToken = requestAppIntegrityToken(appIntegrityManager) } }, + onFailure = {}, + ) + } + + attestationToken?.let(onSuccess) ?: onRefused() + }.onFailure { + onFailure.invoke(it) + } } - private fun CoroutineScope.getApiIntegrityVerdict( - appIntegrityManager: AppIntegrityManager, - appIntegrityToken: String, - newUploadSession: NewUploadSession, - ) { - launch { - appIntegrityManager.getApiIntegrityVerdict( - integrityToken = appIntegrityToken, - packageName = BuildConfig.APPLICATION_ID, - targetUrl = sharedApiUrlCreator.createUploadContainerUrl, - onSuccess = { attestationToken -> - SentryLog.i(APP_INTEGRITY_MANAGER_TAG, "Api verdict check") - Log.i(APP_INTEGRITY_MANAGER_TAG, "getApiIntegrityVerdict: $attestationToken") - _integrityCheckResult.value = AppIntegrityResult.Success - sendTransfer(attestationToken, newUploadSession) + private suspend fun requestAppIntegrityToken(appIntegrityManager: AppIntegrityManager): String? { + var attestationToken: String? = null + + coroutineScope { + appIntegrityManager.requestClassicIntegrityVerdictToken( + onSuccess = { token -> + SentryLog.i(APP_INTEGRITY_MANAGER_TAG, "request for app integrity token successful") + launch { attestationToken = getApiIntegrityVerdict(appIntegrityManager, token) } }, - onFailure = ::setFailedIntegrityResult, + onFailure = {}, ) } + + return attestationToken } - private fun setFailedIntegrityResult() { - _integrityCheckResult.value = AppIntegrityResult.Fail + private suspend fun getApiIntegrityVerdict(appIntegrityManager: AppIntegrityManager, appIntegrityToken: String): String? { + var token: String? = null + + appIntegrityManager.getApiIntegrityVerdict( + integrityToken = appIntegrityToken, + packageName = BuildConfig.APPLICATION_ID, + targetUrl = sharedApiUrlCreator.createUploadContainerUrl, + onSuccess = { attestationToken -> + SentryLog.i(APP_INTEGRITY_MANAGER_TAG, "Api verdict check") + Log.i(APP_INTEGRITY_MANAGER_TAG, "getApiIntegrityVerdict: $attestationToken") + token = attestationToken + }, + onFailure = {}, + ) + + return token } fun resetIntegrityCheckResult() { @@ -116,27 +156,6 @@ class TransferSendManager @Inject constructor( _sendActionResult.value = SendActionResult.NotStarted } - private fun CoroutineScope.sendTransfer(attestationToken: String, newUploadSession: NewUploadSession) { - _sendActionResult.update { SendActionResult.Pending } - launch { - runCatching { - val uuid = uploadManager.createAndGetUpload(newUploadSession).uuid - uploadManager.initUploadSession( - attestationHeaderName = AppIntegrityManager.ATTESTATION_TOKEN_HEADER, - attestationToken = attestationToken, - )!! // TODO: Handle ContainerErrorsException here - uploadWorkerScheduler.scheduleWork(uuid) - _sendActionResult.update { - val totalSize = importationFilesManager.importedFiles.value.sumOf { it.fileSize } - SendActionResult.Success(totalSize) - } - }.onFailure { exception -> - SentryLog.e(TAG, "Failed to start the upload", exception) - _sendActionResult.update { SendActionResult.Failure } - } - } - } - companion object { private val TAG = TransferSendManager::class.java.simpleName }