From 4406a5586978d4fd58649b0ec3174e3f19c41ae5 Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 17:19:46 +0100 Subject: [PATCH 1/9] Adds retry logic to attestation. --- .../stripe/attestation/AttestationError.kt | 77 +++++++++++ .../IntegrityStandardRequestManager.kt | 123 ++++++++++++++---- .../IntegrityStandardRequestManagerTest.kt | 6 +- 3 files changed, 177 insertions(+), 29 deletions(-) create mode 100644 stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt new file mode 100644 index 00000000000..dbdb398676a --- /dev/null +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -0,0 +1,77 @@ +package com.stripe.attestation; + +import com.google.android.play.core.integrity.StandardIntegrityException + +class AttestationError( + val errorType: ErrorType, + message: String, + cause: Throwable? = null +) : Throwable(message, cause) { + + /** + * + */ + enum class ErrorType( + val isRetriable: Boolean + ) { + API_NOT_AVAILABLE(isRetriable = false), + APP_NOT_INSTALLED(isRetriable = false), + APP_UID_MISMATCH(isRetriable = false), + CANNOT_BIND_TO_SERVICE(isRetriable = true), + CLIENT_TRANSIENT_ERROR(isRetriable = true), + CLOUD_PROJECT_NUMBER_IS_INVALID(isRetriable = false), + GOOGLE_SERVER_UNAVAILABLE(isRetriable = true), + INTEGRITY_TOKEN_PROVIDER_INVALID(isRetriable = false), + INTERNAL_ERROR(isRetriable = true), + NO_ERROR(isRetriable = true), + NETWORK_ERROR(isRetriable = true), + PLAY_SERVICES_NOT_FOUND(isRetriable = false), + PLAY_SERVICES_VERSION_OUTDATED(isRetriable = false), + PLAY_STORE_NOT_FOUND(isRetriable = true), + PLAY_STORE_VERSION_OUTDATED(isRetriable = false), + REQUEST_HASH_TOO_LONG(isRetriable = false), + TOO_MANY_REQUESTS(isRetriable = true), + MAX_RETRIES_EXCEEDED(isRetriable = false), + UNKNOWN(isRetriable = false) + } + + companion object { + fun fromException(exception: Throwable): AttestationError { + return if (exception is StandardIntegrityException) { + // see https://developer.android.com/google/play/integrity/error-codes#retryable_error_codes + val errorType = when (exception.errorCode) { + -1 -> ErrorType.API_NOT_AVAILABLE + -5 -> ErrorType.APP_NOT_INSTALLED + -7 -> ErrorType.APP_UID_MISMATCH + -9 -> ErrorType.CANNOT_BIND_TO_SERVICE + -18 -> ErrorType.CLIENT_TRANSIENT_ERROR + -16 -> ErrorType.CLOUD_PROJECT_NUMBER_IS_INVALID + -12 -> ErrorType.GOOGLE_SERVER_UNAVAILABLE + -19 -> ErrorType.INTEGRITY_TOKEN_PROVIDER_INVALID + -100 -> ErrorType.INTERNAL_ERROR + -3 -> ErrorType.NETWORK_ERROR + 0 -> ErrorType.NO_ERROR + -6 -> ErrorType.PLAY_SERVICES_NOT_FOUND + -15 -> ErrorType.PLAY_SERVICES_VERSION_OUTDATED + -2 -> ErrorType.PLAY_STORE_NOT_FOUND + -14 -> ErrorType.PLAY_STORE_VERSION_OUTDATED + -17 -> ErrorType.REQUEST_HASH_TOO_LONG + -8 -> ErrorType.TOO_MANY_REQUESTS + else -> ErrorType.UNKNOWN + } + AttestationError( + errorType = errorType, + message = exception.message ?: "Integrity error occurred", + cause = exception + ) + } else { + // Handle non-standard exceptions as unknown errors + AttestationError( + errorType = ErrorType.UNKNOWN, + message = "An unknown error occurred", + cause = exception + ) + } + } + } +} \ No newline at end of file diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt index 415eb4b8952..76231cb3a4f 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt @@ -6,6 +6,7 @@ import com.google.android.play.core.integrity.StandardIntegrityManager import com.google.android.play.core.integrity.StandardIntegrityManager.PrepareIntegrityTokenRequest import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenProvider import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenRequest +import kotlinx.coroutines.delay @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) interface IntegrityRequestManager { @@ -13,9 +14,15 @@ interface IntegrityRequestManager { * Prepare the integrity token. This warms up the integrity token generation, it's recommended * to call it as soon as possible if you know you will need an integrity token. * + * @param maxRetries The number of times to retry the request (using exponential backoff). + * Increase this value if calls to this method are non-blocking. + * See https://developer.android.com/google/play/integrity/error-codes#retry-logic + * * Needs to be called before calling [requestToken]. */ - suspend fun prepare(): Result + suspend fun prepare( + maxRetries: Int = 1, + ): Result /** * Requests an Integrity token. @@ -23,10 +30,16 @@ interface IntegrityRequestManager { * @param requestIdentifier A string to be hashed to generate a request identifier. * Can be null. Provide a value that identifies the API request * to protect it from tampering attacks. + * @param maxRetries The number of times to retry the request (using exponential backoff). + * Increase this value if calls to this method are non-blocking. + * See https://developer.android.com/google/play/integrity/error-codes#retry-logic * * [Docs](https://developer.android.com/google/play/integrity/standard#protect-requests) */ - suspend fun requestToken(requestIdentifier: String? = null): Result + suspend fun requestToken( + requestIdentifier: String? = null, + maxRetries: Int = 1, + ): Result } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -39,39 +52,95 @@ class IntegrityStandardRequestManager( private val standardIntegrityManager: StandardIntegrityManager by lazy { factory.create() } private var integrityTokenProvider: StandardIntegrityTokenProvider? = null - override suspend fun prepare(): Result = runCatching { - val finishedTask: Task = standardIntegrityManager - .prepareIntegrityToken( - PrepareIntegrityTokenRequest.builder() - .setCloudProjectNumber(cloudProjectNumber) - .build() - ).awaitTask() + override suspend fun prepare( + retries: Int, + ): Result = exponentialBackoff(maxRetries = retries) { + runCatching { + val finishedTask = standardIntegrityManager + .prepareIntegrityToken( + PrepareIntegrityTokenRequest.builder() + .setCloudProjectNumber(cloudProjectNumber) + .build() + ).awaitTask() - finishedTask.toResult() - .onSuccess { integrityTokenProvider = it } - .onFailure { error -> logError("Integrity: Failed to prepare integrity token", error) } - .getOrThrow() + finishedTask.toResult() + .onSuccess { integrityTokenProvider = it } + .getOrThrow() + } + .map {} + .recoverCatching { + logError("Integrity - Failed to prepare integrity token", it) + throw AttestationError.fromException(it) + } } override suspend fun requestToken( requestIdentifier: String?, - ): Result = request(requestIdentifier) + maxRetries: Int, + ): Result = request(requestIdentifier, maxRetries) private suspend fun request( requestHash: String?, - ): Result = runCatching { - val finishedTask = requireNotNull( - value = integrityTokenProvider, - lazyMessage = { "Integrity token provider is not initialized. Call prepare() first." } - ).request( - StandardIntegrityTokenRequest.builder() - .setRequestHash(requestHash) - .build() - ).awaitTask() + maxRetries: Int, + ): Result = exponentialBackoff( + maxRetries = maxRetries, + ) { + runCatching { + val finishedTask = requireNotNull( + value = integrityTokenProvider, + lazyMessage = { "Integrity token provider is not initialized. Call prepare() first." } + ).request( + StandardIntegrityTokenRequest.builder() + .setRequestHash(requestHash) + .build() + ).awaitTask() + + finishedTask.toResult().getOrThrow() + } + .map { it.token() } + .recoverCatching { + logError("Integrity - Failed to prepare integrity token", it) + throw AttestationError.fromException(it) + } + } + + suspend fun exponentialBackoff( + maxRetries: Int = MAX_RETRIES, + initialDelay: Long = INITIAL_DELAY, + block: suspend () -> Result + ): Result { + var currentDelay = initialDelay + repeat(maxRetries) { attempt -> + val result = block() + + if (result.isSuccess) { + return result + } + + val exception = result.exceptionOrNull() + + // Retry only if the error is retriable + if (exception is AttestationError && exception.errorType.isRetriable) { + logError("Retrying due to retriable error on attempt $attempt", exception) + delay(currentDelay) + currentDelay = (currentDelay * MULTIPLIER).toLong() + } else { + return result + } + } + return Result.failure( + AttestationError( + errorType = AttestationError.ErrorType.MAX_RETRIES_EXCEEDED, + message = "Failed after $maxRetries attempts, giving up.", + cause = null + ) + ) + } - finishedTask.toResult() - .mapCatching { it.token() } - .onFailure { error -> logError("Integrity - Failed to request integrity token", error) } - .getOrThrow() + companion object { + // Constants for the retry mechanism + private const val INITIAL_DELAY = 2000L // Start with 2 seconds + private const val MAX_RETRIES = 2 + private const val MULTIPLIER = 2.0 } } diff --git a/stripe-attestation/src/test/java/com/stripe/attestation/IntegrityStandardRequestManagerTest.kt b/stripe-attestation/src/test/java/com/stripe/attestation/IntegrityStandardRequestManagerTest.kt index 6bde3f5089c..08439e6dbcb 100644 --- a/stripe-attestation/src/test/java/com/stripe/attestation/IntegrityStandardRequestManagerTest.kt +++ b/stripe-attestation/src/test/java/com/stripe/attestation/IntegrityStandardRequestManagerTest.kt @@ -26,7 +26,7 @@ class IntegrityStandardRequestManagerTest { } @Test - fun `prepare - success`() = runTest { + fun `prepare - success returns successful result`() = runTest { val tokenProvider = FakeStandardIntegrityTokenProvider(Tasks.forResult(FakeStandardIntegrityToken())) val integrityStandardRequestManager = buildRequestManager( prepareTask = Tasks.forResult(tokenProvider), @@ -38,7 +38,7 @@ class IntegrityStandardRequestManagerTest { } @Test - fun `prepare - failure`() = runTest { + fun `prepare - failure on prepare task returns Attestation error`() = runTest { val integrityStandardRequestManager = buildRequestManager( prepareTask = Tasks.forException(Exception("Failed to build token provider")), ) @@ -46,6 +46,7 @@ class IntegrityStandardRequestManagerTest { val result = integrityStandardRequestManager.prepare() assert(result.isFailure) + assert(result.exceptionOrNull() is AttestationError) } @Test @@ -72,6 +73,7 @@ class IntegrityStandardRequestManagerTest { val result = integrityStandardRequestManager.requestToken("requestIdentifier") assert(result.isFailure) + assert(result.exceptionOrNull() is AttestationError) } @After From ceb2fdfccffbf645ee81757186bc18e834007fab Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 17:20:18 +0100 Subject: [PATCH 2/9] Removes comment. --- .../src/main/java/com/stripe/attestation/AttestationError.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index dbdb398676a..6157a02e09f 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -8,9 +8,6 @@ class AttestationError( cause: Throwable? = null ) : Throwable(message, cause) { - /** - * - */ enum class ErrorType( val isRetriable: Boolean ) { From 207c5c27ba735e94ab295531356da4cc9ecb0191 Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 17:44:54 +0100 Subject: [PATCH 3/9] removes dependency. --- .../src/main/java/com/stripe/attestation/AttestationError.kt | 2 +- .../com/stripe/attestation/IntegrityStandardRequestManager.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index 6157a02e09f..8513c27b282 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -6,7 +6,7 @@ class AttestationError( val errorType: ErrorType, message: String, cause: Throwable? = null -) : Throwable(message, cause) { +) : Exception(message, cause) { enum class ErrorType( val isRetriable: Boolean diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt index 76231cb3a4f..47963e2f0f1 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt @@ -1,7 +1,6 @@ package com.stripe.attestation import androidx.annotation.RestrictTo -import com.google.android.gms.tasks.Task import com.google.android.play.core.integrity.StandardIntegrityManager import com.google.android.play.core.integrity.StandardIntegrityManager.PrepareIntegrityTokenRequest import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenProvider From fad7326b8770ec67bacf9785db059726367e8bd7 Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 18:14:57 +0100 Subject: [PATCH 4/9] Updates codes. --- .../stripe/attestation/AttestationError.kt | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index 8513c27b282..3f50ebf7a1a 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -1,6 +1,8 @@ package com.stripe.attestation; import com.google.android.play.core.integrity.StandardIntegrityException +import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode +import com.stripe.attestation.AttestationError.ErrorType.* class AttestationError( val errorType: ErrorType, @@ -37,24 +39,24 @@ class AttestationError( return if (exception is StandardIntegrityException) { // see https://developer.android.com/google/play/integrity/error-codes#retryable_error_codes val errorType = when (exception.errorCode) { - -1 -> ErrorType.API_NOT_AVAILABLE - -5 -> ErrorType.APP_NOT_INSTALLED - -7 -> ErrorType.APP_UID_MISMATCH - -9 -> ErrorType.CANNOT_BIND_TO_SERVICE - -18 -> ErrorType.CLIENT_TRANSIENT_ERROR - -16 -> ErrorType.CLOUD_PROJECT_NUMBER_IS_INVALID - -12 -> ErrorType.GOOGLE_SERVER_UNAVAILABLE - -19 -> ErrorType.INTEGRITY_TOKEN_PROVIDER_INVALID - -100 -> ErrorType.INTERNAL_ERROR - -3 -> ErrorType.NETWORK_ERROR - 0 -> ErrorType.NO_ERROR - -6 -> ErrorType.PLAY_SERVICES_NOT_FOUND - -15 -> ErrorType.PLAY_SERVICES_VERSION_OUTDATED - -2 -> ErrorType.PLAY_STORE_NOT_FOUND - -14 -> ErrorType.PLAY_STORE_VERSION_OUTDATED - -17 -> ErrorType.REQUEST_HASH_TOO_LONG - -8 -> ErrorType.TOO_MANY_REQUESTS - else -> ErrorType.UNKNOWN + StandardIntegrityErrorCode.API_NOT_AVAILABLE -> API_NOT_AVAILABLE + StandardIntegrityErrorCode.APP_NOT_INSTALLED -> APP_NOT_INSTALLED + StandardIntegrityErrorCode.APP_UID_MISMATCH -> APP_UID_MISMATCH + StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE -> CANNOT_BIND_TO_SERVICE + StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR -> CLIENT_TRANSIENT_ERROR + StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID -> CLOUD_PROJECT_NUMBER_IS_INVALID + StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE -> GOOGLE_SERVER_UNAVAILABLE + StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID -> INTEGRITY_TOKEN_PROVIDER_INVALID + StandardIntegrityErrorCode.INTERNAL_ERROR -> INTERNAL_ERROR + StandardIntegrityErrorCode.NETWORK_ERROR -> NETWORK_ERROR + StandardIntegrityErrorCode.NO_ERROR -> NO_ERROR + StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND -> PLAY_SERVICES_NOT_FOUND + StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED -> PLAY_SERVICES_VERSION_OUTDATED + StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND -> PLAY_STORE_NOT_FOUND + StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED -> PLAY_STORE_VERSION_OUTDATED + StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG -> REQUEST_HASH_TOO_LONG + StandardIntegrityErrorCode.TOO_MANY_REQUESTS -> TOO_MANY_REQUESTS + else -> UNKNOWN } AttestationError( errorType = errorType, @@ -64,7 +66,7 @@ class AttestationError( } else { // Handle non-standard exceptions as unknown errors AttestationError( - errorType = ErrorType.UNKNOWN, + errorType = UNKNOWN, message = "An unknown error occurred", cause = exception ) From 2231d0095551413746ffd307368c2a733752739d Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 18:30:43 +0100 Subject: [PATCH 5/9] Updates retry variable names. --- .../attestation/IntegrityStandardRequestManager.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt index 47963e2f0f1..007a79a84dd 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt @@ -20,7 +20,7 @@ interface IntegrityRequestManager { * Needs to be called before calling [requestToken]. */ suspend fun prepare( - maxRetries: Int = 1, + maxRetries: Int = 0, ): Result /** @@ -37,7 +37,7 @@ interface IntegrityRequestManager { */ suspend fun requestToken( requestIdentifier: String? = null, - maxRetries: Int = 1, + maxRetries: Int = 0, ): Result } @@ -104,12 +104,13 @@ class IntegrityStandardRequestManager( } suspend fun exponentialBackoff( - maxRetries: Int = MAX_RETRIES, + maxRetries: Int, initialDelay: Long = INITIAL_DELAY, block: suspend () -> Result ): Result { var currentDelay = initialDelay - repeat(maxRetries) { attempt -> + val totalTries = maxRetries + 1 + repeat(totalTries) { attempt -> val result = block() if (result.isSuccess) { @@ -139,7 +140,6 @@ class IntegrityStandardRequestManager( companion object { // Constants for the retry mechanism private const val INITIAL_DELAY = 2000L // Start with 2 seconds - private const val MAX_RETRIES = 2 private const val MULTIPLIER = 2.0 } } From 39893dbfc35bff2d5efc6067cb1e3b3b1807c994 Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 18:33:20 +0100 Subject: [PATCH 6/9] Updates retry variable names. --- .../src/main/java/com/stripe/attestation/AttestationError.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index 3f50ebf7a1a..09784ebeda4 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -1,4 +1,4 @@ -package com.stripe.attestation; +package com.stripe.attestation import com.google.android.play.core.integrity.StandardIntegrityException import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode @@ -73,4 +73,4 @@ class AttestationError( } } } -} \ No newline at end of file +} From 6212cc63241db42758ab539b8fdd1a7f279bb5e1 Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 20:30:57 +0100 Subject: [PATCH 7/9] Cleans up mapping logic. --- .../stripe/attestation/AttestationError.kt | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index 09784ebeda4..6828d4becf3 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -1,9 +1,11 @@ package com.stripe.attestation +import androidx.annotation.RestrictTo import com.google.android.play.core.integrity.StandardIntegrityException import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode import com.stripe.attestation.AttestationError.ErrorType.* +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AttestationError( val errorType: ErrorType, message: String, @@ -36,41 +38,39 @@ class AttestationError( companion object { fun fromException(exception: Throwable): AttestationError { - return if (exception is StandardIntegrityException) { - // see https://developer.android.com/google/play/integrity/error-codes#retryable_error_codes - val errorType = when (exception.errorCode) { - StandardIntegrityErrorCode.API_NOT_AVAILABLE -> API_NOT_AVAILABLE - StandardIntegrityErrorCode.APP_NOT_INSTALLED -> APP_NOT_INSTALLED - StandardIntegrityErrorCode.APP_UID_MISMATCH -> APP_UID_MISMATCH - StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE -> CANNOT_BIND_TO_SERVICE - StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR -> CLIENT_TRANSIENT_ERROR - StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID -> CLOUD_PROJECT_NUMBER_IS_INVALID - StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE -> GOOGLE_SERVER_UNAVAILABLE - StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID -> INTEGRITY_TOKEN_PROVIDER_INVALID - StandardIntegrityErrorCode.INTERNAL_ERROR -> INTERNAL_ERROR - StandardIntegrityErrorCode.NETWORK_ERROR -> NETWORK_ERROR - StandardIntegrityErrorCode.NO_ERROR -> NO_ERROR - StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND -> PLAY_SERVICES_NOT_FOUND - StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED -> PLAY_SERVICES_VERSION_OUTDATED - StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND -> PLAY_STORE_NOT_FOUND - StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED -> PLAY_STORE_VERSION_OUTDATED - StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG -> REQUEST_HASH_TOO_LONG - StandardIntegrityErrorCode.TOO_MANY_REQUESTS -> TOO_MANY_REQUESTS - else -> UNKNOWN - } - AttestationError( - errorType = errorType, + return when (exception) { + is StandardIntegrityException -> AttestationError( + errorType = mapErrorCodeToErrorType(exception.errorCode), message = exception.message ?: "Integrity error occurred", cause = exception ) - } else { - // Handle non-standard exceptions as unknown errors - AttestationError( + else -> AttestationError( errorType = UNKNOWN, message = "An unknown error occurred", cause = exception ) } } + + private fun mapErrorCodeToErrorType(errorCode: Int): ErrorType = when (errorCode) { + StandardIntegrityErrorCode.API_NOT_AVAILABLE -> API_NOT_AVAILABLE + StandardIntegrityErrorCode.APP_NOT_INSTALLED -> APP_NOT_INSTALLED + StandardIntegrityErrorCode.APP_UID_MISMATCH -> APP_UID_MISMATCH + StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE -> CANNOT_BIND_TO_SERVICE + StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR -> CLIENT_TRANSIENT_ERROR + StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID -> CLOUD_PROJECT_NUMBER_IS_INVALID + StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE -> GOOGLE_SERVER_UNAVAILABLE + StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID -> INTEGRITY_TOKEN_PROVIDER_INVALID + StandardIntegrityErrorCode.INTERNAL_ERROR -> INTERNAL_ERROR + StandardIntegrityErrorCode.NETWORK_ERROR -> NETWORK_ERROR + StandardIntegrityErrorCode.NO_ERROR -> NO_ERROR + StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND -> PLAY_SERVICES_NOT_FOUND + StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED -> PLAY_SERVICES_VERSION_OUTDATED + StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND -> PLAY_STORE_NOT_FOUND + StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED -> PLAY_STORE_VERSION_OUTDATED + StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG -> REQUEST_HASH_TOO_LONG + StandardIntegrityErrorCode.TOO_MANY_REQUESTS -> TOO_MANY_REQUESTS + else -> UNKNOWN + } } } From 4d9473a38f398a6d1bbe3cf5690044dcbe6ae053 Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 20:35:15 +0100 Subject: [PATCH 8/9] ktlint fixes. --- .../stripe/attestation/AttestationError.kt | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index 6828d4becf3..fbb0a0fa9fb 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -3,7 +3,6 @@ package com.stripe.attestation import androidx.annotation.RestrictTo import com.google.android.play.core.integrity.StandardIntegrityException import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode -import com.stripe.attestation.AttestationError.ErrorType.* @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AttestationError( @@ -45,7 +44,7 @@ class AttestationError( cause = exception ) else -> AttestationError( - errorType = UNKNOWN, + errorType = ErrorType.UNKNOWN, message = "An unknown error occurred", cause = exception ) @@ -53,24 +52,24 @@ class AttestationError( } private fun mapErrorCodeToErrorType(errorCode: Int): ErrorType = when (errorCode) { - StandardIntegrityErrorCode.API_NOT_AVAILABLE -> API_NOT_AVAILABLE - StandardIntegrityErrorCode.APP_NOT_INSTALLED -> APP_NOT_INSTALLED - StandardIntegrityErrorCode.APP_UID_MISMATCH -> APP_UID_MISMATCH - StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE -> CANNOT_BIND_TO_SERVICE - StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR -> CLIENT_TRANSIENT_ERROR - StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID -> CLOUD_PROJECT_NUMBER_IS_INVALID - StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE -> GOOGLE_SERVER_UNAVAILABLE - StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID -> INTEGRITY_TOKEN_PROVIDER_INVALID - StandardIntegrityErrorCode.INTERNAL_ERROR -> INTERNAL_ERROR - StandardIntegrityErrorCode.NETWORK_ERROR -> NETWORK_ERROR - StandardIntegrityErrorCode.NO_ERROR -> NO_ERROR - StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND -> PLAY_SERVICES_NOT_FOUND - StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED -> PLAY_SERVICES_VERSION_OUTDATED - StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND -> PLAY_STORE_NOT_FOUND - StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED -> PLAY_STORE_VERSION_OUTDATED - StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG -> REQUEST_HASH_TOO_LONG - StandardIntegrityErrorCode.TOO_MANY_REQUESTS -> TOO_MANY_REQUESTS - else -> UNKNOWN + StandardIntegrityErrorCode.API_NOT_AVAILABLE -> ErrorType.API_NOT_AVAILABLE + StandardIntegrityErrorCode.APP_NOT_INSTALLED -> ErrorType.APP_NOT_INSTALLED + StandardIntegrityErrorCode.APP_UID_MISMATCH -> ErrorType.APP_UID_MISMATCH + StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE -> ErrorType.CANNOT_BIND_TO_SERVICE + StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR -> ErrorType.CLIENT_TRANSIENT_ERROR + StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID -> ErrorType.CLOUD_PROJECT_NUMBER_IS_INVALID + StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE -> ErrorType.GOOGLE_SERVER_UNAVAILABLE + StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID -> ErrorType.INTEGRITY_TOKEN_PROVIDER_INVALID + StandardIntegrityErrorCode.INTERNAL_ERROR -> ErrorType.INTERNAL_ERROR + StandardIntegrityErrorCode.NETWORK_ERROR -> ErrorType.NETWORK_ERROR + StandardIntegrityErrorCode.NO_ERROR -> ErrorType.NO_ERROR + StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND -> ErrorType.PLAY_SERVICES_NOT_FOUND + StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED -> ErrorType.PLAY_SERVICES_VERSION_OUTDATED + StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND -> ErrorType.PLAY_STORE_NOT_FOUND + StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED -> ErrorType.PLAY_STORE_VERSION_OUTDATED + StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG -> ErrorType.REQUEST_HASH_TOO_LONG + StandardIntegrityErrorCode.TOO_MANY_REQUESTS -> ErrorType.TOO_MANY_REQUESTS + else -> ErrorType.UNKNOWN } } } From 6ca7f0a911b51337727682093aac0d17b149fb2b Mon Sep 17 00:00:00 2001 From: Carlos Munoz Date: Fri, 20 Dec 2024 21:46:47 +0100 Subject: [PATCH 9/9] ktlint fixes. --- .../stripe/attestation/AttestationError.kt | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt index fbb0a0fa9fb..e34cb4b8ae5 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/AttestationError.kt @@ -36,40 +36,37 @@ class AttestationError( } companion object { - fun fromException(exception: Throwable): AttestationError { - return when (exception) { - is StandardIntegrityException -> AttestationError( - errorType = mapErrorCodeToErrorType(exception.errorCode), - message = exception.message ?: "Integrity error occurred", - cause = exception - ) - else -> AttestationError( - errorType = ErrorType.UNKNOWN, - message = "An unknown error occurred", - cause = exception - ) - } + fun fromException(exception: Throwable): AttestationError = when (exception) { + is StandardIntegrityException -> AttestationError( + errorType = errorCodeToErrorTypeMap[exception.errorCode] ?: ErrorType.UNKNOWN, + message = exception.message ?: "Integrity error occurred", + cause = exception + ) + else -> AttestationError( + errorType = ErrorType.UNKNOWN, + message = "An unknown error occurred", + cause = exception + ) } - private fun mapErrorCodeToErrorType(errorCode: Int): ErrorType = when (errorCode) { - StandardIntegrityErrorCode.API_NOT_AVAILABLE -> ErrorType.API_NOT_AVAILABLE - StandardIntegrityErrorCode.APP_NOT_INSTALLED -> ErrorType.APP_NOT_INSTALLED - StandardIntegrityErrorCode.APP_UID_MISMATCH -> ErrorType.APP_UID_MISMATCH - StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE -> ErrorType.CANNOT_BIND_TO_SERVICE - StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR -> ErrorType.CLIENT_TRANSIENT_ERROR - StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID -> ErrorType.CLOUD_PROJECT_NUMBER_IS_INVALID - StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE -> ErrorType.GOOGLE_SERVER_UNAVAILABLE - StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID -> ErrorType.INTEGRITY_TOKEN_PROVIDER_INVALID - StandardIntegrityErrorCode.INTERNAL_ERROR -> ErrorType.INTERNAL_ERROR - StandardIntegrityErrorCode.NETWORK_ERROR -> ErrorType.NETWORK_ERROR - StandardIntegrityErrorCode.NO_ERROR -> ErrorType.NO_ERROR - StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND -> ErrorType.PLAY_SERVICES_NOT_FOUND - StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED -> ErrorType.PLAY_SERVICES_VERSION_OUTDATED - StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND -> ErrorType.PLAY_STORE_NOT_FOUND - StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED -> ErrorType.PLAY_STORE_VERSION_OUTDATED - StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG -> ErrorType.REQUEST_HASH_TOO_LONG - StandardIntegrityErrorCode.TOO_MANY_REQUESTS -> ErrorType.TOO_MANY_REQUESTS - else -> ErrorType.UNKNOWN - } + private val errorCodeToErrorTypeMap = mapOf( + StandardIntegrityErrorCode.API_NOT_AVAILABLE to ErrorType.API_NOT_AVAILABLE, + StandardIntegrityErrorCode.APP_NOT_INSTALLED to ErrorType.APP_NOT_INSTALLED, + StandardIntegrityErrorCode.APP_UID_MISMATCH to ErrorType.APP_UID_MISMATCH, + StandardIntegrityErrorCode.CANNOT_BIND_TO_SERVICE to ErrorType.CANNOT_BIND_TO_SERVICE, + StandardIntegrityErrorCode.CLIENT_TRANSIENT_ERROR to ErrorType.CLIENT_TRANSIENT_ERROR, + StandardIntegrityErrorCode.CLOUD_PROJECT_NUMBER_IS_INVALID to ErrorType.CLOUD_PROJECT_NUMBER_IS_INVALID, + StandardIntegrityErrorCode.GOOGLE_SERVER_UNAVAILABLE to ErrorType.GOOGLE_SERVER_UNAVAILABLE, + StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID to ErrorType.INTEGRITY_TOKEN_PROVIDER_INVALID, + StandardIntegrityErrorCode.INTERNAL_ERROR to ErrorType.INTERNAL_ERROR, + StandardIntegrityErrorCode.NETWORK_ERROR to ErrorType.NETWORK_ERROR, + StandardIntegrityErrorCode.NO_ERROR to ErrorType.NO_ERROR, + StandardIntegrityErrorCode.PLAY_SERVICES_NOT_FOUND to ErrorType.PLAY_SERVICES_NOT_FOUND, + StandardIntegrityErrorCode.PLAY_SERVICES_VERSION_OUTDATED to ErrorType.PLAY_SERVICES_VERSION_OUTDATED, + StandardIntegrityErrorCode.PLAY_STORE_NOT_FOUND to ErrorType.PLAY_STORE_NOT_FOUND, + StandardIntegrityErrorCode.PLAY_STORE_VERSION_OUTDATED to ErrorType.PLAY_STORE_VERSION_OUTDATED, + StandardIntegrityErrorCode.REQUEST_HASH_TOO_LONG to ErrorType.REQUEST_HASH_TOO_LONG, + StandardIntegrityErrorCode.TOO_MANY_REQUESTS to ErrorType.TOO_MANY_REQUESTS + ) } }