Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/google_sign_in/google_sign_in_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,22 +487,26 @@ 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<? super Result<? extends AuthorizeResult>, Unit> callback =
pendingAuthorizationCallback;
pendingAuthorizationCallback = null;
try {
AuthorizationResult authorizationResult =
authorizationClientFactory.create(context).getAuthorizationResultFromIntent(data);
ResultUtilsKt.completeWithValue(
pendingAuthorizationCallback,
callback,
new PlatformAuthorizationResult(
authorizationResult.getAccessToken(),
authorizationResult.getServerAuthCode(),
authorizationResult.getGrantedScopes()));
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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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<OnSuccessListener<AuthorizationResult>> 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<String> scopes = new ArrayList<>(Arrays.asList("scope1", "scope1"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down