diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index c1d9970169a3..cc9fa4669566 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 7.2.14 + +* Fixes an `IllegalStateException` ("Reply already submitted") crash when an authorization activity + result for `REQUEST_CODE_AUTHORIZE` is delivered more than once (for example after a configuration + change or process death), by clearing the pending callback before resolving it. + ## 7.2.13 * Bumps com.google.android.gms:play-services-auth from 21.5.1 to 21.6.0. diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index aa8b5d1b2172..d21e6fa30704 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -487,11 +487,16 @@ public void revokeAccess( public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (requestCode == REQUEST_CODE_AUTHORIZE) { if (pendingAuthorizationCallback != null) { + // Clear the pending callback before completing it so a re-delivered result (e.g. after a + // configuration change or process death) cannot complete the same reply twice. + Function1, Unit> callback = + pendingAuthorizationCallback; + pendingAuthorizationCallback = null; try { AuthorizationResult authorizationResult = authorizationClientFactory.create(context).getAuthorizationResultFromIntent(data); ResultUtilsKt.completeWithValue( - pendingAuthorizationCallback, + callback, new PlatformAuthorizationResult( authorizationResult.getAccessToken(), authorizationResult.getServerAuthCode(), @@ -499,10 +504,9 @@ public boolean onActivityResult(int requestCode, int resultCode, @Nullable Inten return true; } catch (ApiException e) { ResultUtilsKt.completeWithValue( - pendingAuthorizationCallback, + callback, new AuthorizeFailure(AuthorizeFailureType.API_EXCEPTION, e.getMessage(), null)); } - pendingAuthorizationCallback = null; } else { Log.e("google_sign_in", "Unexpected authorization result callback"); } diff --git a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java index 975f6b92e6d6..40d07fe68294 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java @@ -5,6 +5,7 @@ package io.flutter.plugins.googlesignin; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -992,6 +993,57 @@ public void authorize_returnsPostIntentResult() { assertTrue(callbackCalled[0]); } + // Regression test for https://github.com/flutter/flutter/issues/188062 + // A re-delivered authorization activity result for REQUEST_CODE_AUTHORIZE (e.g. after a + // configuration change or process death) must not resolve the same callback twice, which would + // throw IllegalStateException ("Reply already submitted") on the real Pigeon reply. + @Test + public void authorize_ignoresDuplicateActivityResult() { + final List scopes = new ArrayList<>(Arrays.asList("scope1", "scope1")); + PlatformAuthorizationRequest params = + new PlatformAuthorizationRequest(scopes, null, null, null); + + final String accessToken = "accessToken"; + final String serverAuthCode = "serverAuthCode"; + when(mockAuthorizationClient.authorize(any())).thenReturn(mockAuthorizationTask); + AuthorizationResult successResult = + mockSuccessAuthorizationResult(serverAuthCode, accessToken, scopes); + try { + when(mockAuthorizationClient.getAuthorizationResultFromIntent(any())) + .thenReturn(successResult); + } catch (ApiException e) { + fail(); + } + + plugin.setActivity(mockActivity); + final int[] callbackCount = new int[] {0}; + plugin.authorize( + params, + true, + ResultCompat.asCompatCallback( + reply -> { + callbackCount[0] += 1; + return null; + })); + + @SuppressWarnings("unchecked") + ArgumentCaptor> callbackCaptor = + ArgumentCaptor.forClass(OnSuccessListener.class); + verify(mockAuthorizationTask).addOnSuccessListener(callbackCaptor.capture()); + callbackCaptor.getValue().onSuccess(mockResolutionAuthorizationResult(mockAuthorizationIntent)); + + // The first delivery resolves the authorization and is consumed. + boolean firstHandled = + plugin.onActivityResult(GoogleSignInPlugin.Delegate.REQUEST_CODE_AUTHORIZE, 0, null); + // A duplicate/re-delivered result for the same request code must be ignored, not re-resolved. + boolean secondHandled = + plugin.onActivityResult(GoogleSignInPlugin.Delegate.REQUEST_CODE_AUTHORIZE, 0, null); + + assertTrue(firstHandled); + assertFalse(secondHandled); + assertEquals(1, callbackCount[0]); + } + @Test public void authorize_reportsPendingIntentException() { final List scopes = new ArrayList<>(Arrays.asList("scope1", "scope1")); diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index 533e82b22e54..0bebb400032a 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 7.2.13 +version: 7.2.14 environment: sdk: ^3.12.0