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 c92ef13eeee9..86aa6a310bfe 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.15 + +* 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). + ## 7.2.14 * Uses Google Identity APIs to extract the user ID from the ID token. 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 a8ae80abecdb..9cfa9d3a050f 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 @@ -488,11 +488,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(), @@ -500,10 +505,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 a1f236c4a59b..ebcfb3a5f7c7 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; @@ -990,6 +991,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 5ae58b83217e..ece4f1bc816b 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.14 +version: 7.2.15 environment: sdk: ^3.12.0