diff --git a/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaService.kt b/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaService.kt index 726a7295cc..3e5c2825c2 100644 --- a/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaService.kt +++ b/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaService.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import com.google.android.gms.common.Feature +import com.google.android.gms.common.api.ApiException import com.google.android.gms.common.api.CommonStatusCodes import com.google.android.gms.common.api.Status import com.google.android.gms.common.internal.ConnectionInfo @@ -128,11 +129,12 @@ class RecaptchaServiceImpl( } } catch (e: Exception) { Log.w(TAG, e) + val status = (e as? ApiException)?.status ?: Status.INTERNAL_ERROR try { if (params.version == LEGACY_VERSION) { - callback.onHandle(Status.INTERNAL_ERROR, null) + callback.onHandle(status, null) } else { - callback.onResults(Status.INTERNAL_ERROR, InitResults()) + callback.onResults(status, InitResults()) } } catch (e: Exception) { // Ignored @@ -157,11 +159,12 @@ class RecaptchaServiceImpl( } } catch (e: Exception) { Log.w(TAG, e) + val status = (e as? ApiException)?.status ?: Status.INTERNAL_ERROR try { if (params.version == LEGACY_VERSION) { - callback.onData(Status.INTERNAL_ERROR, null) + callback.onData(status, null) } else { - callback.onResults(Status.INTERNAL_ERROR, ExecuteResults()) + callback.onResults(status, ExecuteResults()) } } catch (e: Exception) { // Ignored diff --git a/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaWebImpl.kt b/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaWebImpl.kt index aab27bc901..802142bcd4 100644 --- a/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaWebImpl.kt +++ b/play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaWebImpl.kt @@ -25,8 +25,11 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.webkit.WebViewClientCompat +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.common.api.Status import com.google.android.gms.recaptcha.RecaptchaHandle import com.google.android.gms.recaptcha.RecaptchaResultData +import com.google.android.gms.recaptcha.RecaptchaStatusCodes import com.google.android.gms.recaptcha.internal.ExecuteParams import com.google.android.gms.recaptcha.internal.InitParams import com.google.android.gms.tasks.Task @@ -131,20 +134,28 @@ class RecaptchaWebImpl(private val context: Context, private val packageName: St additionalArgs[key] = params.action.additionalArgs.getString(key)!! } val request = RecaptchaExecuteRequest(token = lastRequestToken, action = params.action.toString(), additionalArgs = additionalArgs).encode().toBase64(Base64.URL_SAFE, Base64.NO_WRAP) - val token = suspendCoroutine { continuation -> - executeFinished.set(false) - executeContinuation = continuation - eval("recaptcha.m.Main.execute(\"${request}\")") - lifecycleScope.launch { - delay(10000) - if (!executeFinished.getAndSet(true)) { - try { - continuation.resumeWithException(RuntimeException("Timeout reached")) - } catch (_: Exception) {} + try { + val token = suspendCoroutine { continuation -> + executeFinished.set(false) + executeContinuation = continuation + eval("recaptcha.m.Main.execute(\"${request}\")") + lifecycleScope.launch { + delay(10000) + if (!executeFinished.getAndSet(true)) { + try { + continuation.resumeWithException(RuntimeException("Timeout reached")) + } catch (_: Exception) {} + } } } + return RecaptchaResultData(token) + } catch (e: Exception) { + Log.w(TAG, "Exception during Recaptcha execute", e) + webView?.stopLoading() + webView?.loadUrl("about:blank") + webView = null + throw ApiException(Status(RecaptchaStatusCodes.RECAPTCHA_FEATURE_OFF, "Recaptcha execution failed: ${e.message}")) } - return RecaptchaResultData(token) } override suspend fun close(handle: RecaptchaHandle): Boolean { diff --git a/vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt b/vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt index d24283b978..6fa8449ac2 100644 --- a/vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt +++ b/vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt @@ -166,18 +166,23 @@ private class IntegrityServiceImpl(private val context: Context, override val li } Log.d(TAG, "requestIntegrityToken authToken: $authToken") - val droidGuardData = withContext(Dispatchers.IO) { - val droidGuardResultsRequest = DroidGuardResultsRequest() - droidGuardResultsRequest.bundle.putString("thirdPartyCallerAppPackageName", packageName) - Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, data: $data)") - val droidGuardToken = DroidGuard.getClient(context).getResults(INTEGRITY_FLOW_NAME, data, droidGuardResultsRequest).await() - Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, droidGuardToken: $droidGuardToken)") - Base64.decode(droidGuardToken, Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE).toByteString() + val droidGuardData = try { + withContext(Dispatchers.IO) { + val droidGuardResultsRequest = DroidGuardResultsRequest() + droidGuardResultsRequest.bundle.putString("thirdPartyCallerAppPackageName", packageName) + Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, data: $data)") + val droidGuardToken = DroidGuard.getClient(context).getResults(INTEGRITY_FLOW_NAME, data, droidGuardResultsRequest).await() + Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, droidGuardToken: $droidGuardToken)") + Base64.decode(droidGuardToken, Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE).toByteString() + } + } catch (e: Exception) { + Log.e(TAG, "DroidGuard execution failed", e) + throw StandardIntegrityException(IntegrityErrorCode.CANNOT_BIND_TO_SERVICE, "DroidGuard binding or execution failed: ${e.message}") } if (droidGuardData.utf8().startsWith(INTEGRITY_PREFIX_ERROR)) { - Log.w(TAG, "droidGuardData: ${droidGuardData.utf8()}") - throw StandardIntegrityException(IntegrityErrorCode.NETWORK_ERROR, "DroidGuard failed.") + Log.w(TAG, "droidGuardData error: ${droidGuardData.utf8()}") + throw StandardIntegrityException(IntegrityErrorCode.CANNOT_BIND_TO_SERVICE, "DroidGuard returned error: ${droidGuardData.utf8()}") } val integrityRequest = IntegrityRequest( @@ -203,9 +208,9 @@ private class IntegrityServiceImpl(private val context: Context, override val li val integrityToken = integrityResponse.contentWrapper?.content?.token if (integrityToken.isNullOrEmpty()) { if (integrityResponse.integrityResponseError?.error != null) { - throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, integrityResponse.integrityResponseError.error) + throw StandardIntegrityException(IntegrityErrorCode.API_NOT_AVAILABLE, integrityResponse.integrityResponseError.error) } - throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "No token in response.") + throw StandardIntegrityException(IntegrityErrorCode.API_NOT_AVAILABLE, "No token in response.") } Log.d(TAG, "requestIntegrityToken integrityToken: $integrityToken") @@ -214,7 +219,8 @@ private class IntegrityServiceImpl(private val context: Context, override val li }.onFailure { Log.w(TAG, "requestIntegrityToken has exception: ", it) integrityData?.updateAppIntegrityContent(context, System.currentTimeMillis(), "Integrity check failed: ${it.message}") - callback.onError(integrityData?.packageName, IntegrityErrorCode.INTERNAL_ERROR, it.message ?: "Exception") + val errorCode = (it as? StandardIntegrityException)?.code ?: IntegrityErrorCode.INTERNAL_ERROR + callback.onError(integrityData?.packageName ?: packageName, errorCode, it.message ?: "Exception") } } }