Skip to content

Move continuations off main thread for App Check SDKs. #4453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 15, 2022
1 change: 1 addition & 0 deletions appcheck/firebase-appcheck-debug/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased
* [changed] Migrated [app_check] SDKs to use standard Firebase executors. (#4431, #4449)
* [changed] Integrated the [app_check] Debug SDK with Firebase Components. (#4436)
* [changed] Moved Task continuations off the main thread. (#4453)

# 16.1.0
* [unchanged] Updated to accommodate the release of the updated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.annotations.concurrent.Lightweight;
import com.google.firebase.appcheck.debug.internal.DebugAppCheckProvider;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentRegistrar;
Expand All @@ -40,6 +41,7 @@ public class FirebaseAppCheckDebugRegistrar implements ComponentRegistrar {

@Override
public List<Component<?>> getComponents() {
Qualified<Executor> liteExecutor = Qualified.qualified(Lightweight.class, Executor.class);
Qualified<Executor> backgroundExecutor = Qualified.qualified(Background.class, Executor.class);
Qualified<Executor> blockingExecutor = Qualified.qualified(Blocking.class, Executor.class);

Expand All @@ -48,13 +50,15 @@ public List<Component<?>> getComponents() {
.name(LIBRARY_NAME)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.optionalProvider(InternalDebugSecretProvider.class))
.add(Dependency.required(liteExecutor))
.add(Dependency.required(backgroundExecutor))
.add(Dependency.required(blockingExecutor))
.factory(
(container) ->
new DebugAppCheckProvider(
container.get(FirebaseApp.class),
container.getProvider(InternalDebugSecretProvider.class),
container.get(liteExecutor),
container.get(backgroundExecutor),
container.get(blockingExecutor)))
.build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import static com.google.android.gms.common.internal.Preconditions.checkNotNull;

import android.annotation.SuppressLint;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
Expand All @@ -26,6 +25,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.annotations.concurrent.Lightweight;
import com.google.firebase.appcheck.AppCheckProvider;
import com.google.firebase.appcheck.AppCheckToken;
import com.google.firebase.appcheck.debug.InternalDebugSecretProvider;
Expand All @@ -42,17 +42,20 @@ public class DebugAppCheckProvider implements AppCheckProvider {
private static final String UTF_8 = "UTF-8";

private final NetworkClient networkClient;
private final Executor liteExecutor;
private final Executor blockingExecutor;
private final RetryManager retryManager;
private final Task<String> debugSecretTask;

public DebugAppCheckProvider(
@NonNull FirebaseApp firebaseApp,
@NonNull Provider<InternalDebugSecretProvider> debugSecretProvider,
@Lightweight Executor liteExecutor,
@Background Executor backgroundExecutor,
@Blocking Executor blockingExecutor) {
checkNotNull(firebaseApp);
this.networkClient = new NetworkClient(firebaseApp);
this.liteExecutor = liteExecutor;
this.blockingExecutor = blockingExecutor;
this.retryManager = new RetryManager();

Expand All @@ -70,9 +73,11 @@ public DebugAppCheckProvider(
DebugAppCheckProvider(
@NonNull String debugSecret,
@NonNull NetworkClient networkClient,
@NonNull Executor liteExecutor,
@NonNull Executor blockingExecutor,
@NonNull RetryManager retryManager) {
this.networkClient = networkClient;
this.liteExecutor = liteExecutor;
this.blockingExecutor = blockingExecutor;
this.retryManager = retryManager;
this.debugSecretTask = Tasks.forResult(debugSecret);
Expand Down Expand Up @@ -102,15 +107,14 @@ static Task<String> determineDebugSecret(
return taskCompletionSource.getTask();
}

// TODO(b/261013814): Use an explicit executor in continuations.
@SuppressLint("TaskMainThread")
@NonNull
@Override
public Task<AppCheckToken> getToken() {
return debugSecretTask
.continueWithTask(
task -> {
ExchangeDebugTokenRequest request = new ExchangeDebugTokenRequest(task.getResult());
.onSuccessTask(
liteExecutor,
debugSecret -> {
ExchangeDebugTokenRequest request = new ExchangeDebugTokenRequest(debugSecret);
return Tasks.call(
blockingExecutor,
() ->
Expand All @@ -119,14 +123,9 @@ public Task<AppCheckToken> getToken() {
NetworkClient.DEBUG,
retryManager));
})
.continueWithTask(
task -> {
if (task.isSuccessful()) {
return Tasks.forResult(
DefaultAppCheckToken.constructFromAppCheckTokenResponse(task.getResult()));
}
// TODO: Surface more error details.
return Tasks.forException(task.getException());
});
.onSuccessTask(
liteExecutor,
response ->
Tasks.forResult(DefaultAppCheckToken.constructFromAppCheckTokenResponse(response)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.annotations.concurrent.Lightweight;
import com.google.firebase.components.Component;
import com.google.firebase.components.Dependency;
import com.google.firebase.components.Qualified;
Expand All @@ -42,6 +43,7 @@ public void testGetComponents() {
.containsExactly(
Dependency.required(FirebaseApp.class),
Dependency.optionalProvider(InternalDebugSecretProvider.class),
Dependency.required(Qualified.qualified(Lightweight.class, Executor.class)),
Dependency.required(Qualified.qualified(Background.class, Executor.class)),
Dependency.required(Qualified.qualified(Blocking.class, Executor.class)));
assertThat(appCheckDebugComponent.isLazy()).isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ public class DebugAppCheckProviderTest {
private StorageHelper storageHelper;
private SharedPreferences sharedPreferences;
// TODO(b/258273630): Use TestOnlyExecutors instead of MoreExecutors.directExecutor().
private Executor executor = MoreExecutors.directExecutor();
private Executor liteExecutor = MoreExecutors.directExecutor();
private Executor backgroundExecutor = MoreExecutors.directExecutor();
private Executor blockingExecutor = MoreExecutors.directExecutor();

@Before
public void setup() {
Expand Down Expand Up @@ -103,7 +105,11 @@ public void testPublicConstructor_nullFirebaseApp_expectThrows() {
NullPointerException.class,
() -> {
new DebugAppCheckProvider(
null, null, TestOnlyExecutors.background(), TestOnlyExecutors.blocking());
null,
null,
TestOnlyExecutors.lite(),
TestOnlyExecutors.background(),
TestOnlyExecutors.blocking());
});
}

Expand All @@ -113,7 +119,7 @@ public void testDetermineDebugSecret_noStoredSecret_createsNewSecret() {
assertThat(storageHelper.retrieveDebugSecret()).isNull();

Task<String> debugSecretTask =
DebugAppCheckProvider.determineDebugSecret(mockFirebaseApp, executor);
DebugAppCheckProvider.determineDebugSecret(mockFirebaseApp, backgroundExecutor);
assertThat(storageHelper.retrieveDebugSecret()).isNotNull();
assertThat(storageHelper.retrieveDebugSecret()).isEqualTo(debugSecretTask.getResult());
}
Expand All @@ -123,7 +129,7 @@ public void testDetermineDebugSecret_storedSecret_usesExistingSecret() {
storageHelper.saveDebugSecret(DEBUG_SECRET);

Task<String> debugSecretTask =
DebugAppCheckProvider.determineDebugSecret(mockFirebaseApp, executor);
DebugAppCheckProvider.determineDebugSecret(mockFirebaseApp, backgroundExecutor);
assertThat(debugSecretTask.getResult()).isEqualTo(DEBUG_SECRET);
assertThat(storageHelper.retrieveDebugSecret()).isEqualTo(DEBUG_SECRET);
}
Expand All @@ -137,7 +143,8 @@ public void exchangeDebugToken_onSuccess_setsTaskResult() throws Exception {
when(mockAppCheckTokenResponse.getTimeToLive()).thenReturn(TIME_TO_LIVE);

DebugAppCheckProvider provider =
new DebugAppCheckProvider(DEBUG_SECRET, mockNetworkClient, executor, mockRetryManager);
new DebugAppCheckProvider(
DEBUG_SECRET, mockNetworkClient, liteExecutor, blockingExecutor, mockRetryManager);
Task<AppCheckToken> task = provider.getToken();

verify(mockNetworkClient)
Expand All @@ -155,7 +162,8 @@ public void exchangeDebugToken_onFailure_setsTaskException() throws Exception {
.thenThrow(new IOException());

DebugAppCheckProvider provider =
new DebugAppCheckProvider(DEBUG_SECRET, mockNetworkClient, executor, mockRetryManager);
new DebugAppCheckProvider(
DEBUG_SECRET, mockNetworkClient, liteExecutor, blockingExecutor, mockRetryManager);
Task<AppCheckToken> task = provider.getToken();

verify(mockNetworkClient)
Expand Down
1 change: 1 addition & 0 deletions appcheck/firebase-appcheck-playintegrity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased
* [changed] Migrated [app_check] SDKs to use standard Firebase executors. (#4431, #4449)
* [changed] Integrated the [app_check] Play Integrity SDK with Firebase Components. (#4436)
* [changed] Moved Task continuations off the main thread. (#4453)

# 16.1.0
* [unchanged] Updated to accommodate the release of the updated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.android.gms.common.annotation.KeepForSdk;
import com.google.firebase.FirebaseApp;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.annotations.concurrent.Lightweight;
import com.google.firebase.appcheck.playintegrity.internal.PlayIntegrityAppCheckProvider;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentRegistrar;
Expand All @@ -39,17 +40,21 @@ public class FirebaseAppCheckPlayIntegrityRegistrar implements ComponentRegistra

@Override
public List<Component<?>> getComponents() {
Qualified<Executor> liteExecutor = Qualified.qualified(Lightweight.class, Executor.class);
Qualified<Executor> blockingExecutor = Qualified.qualified(Blocking.class, Executor.class);

return Arrays.asList(
Component.builder(PlayIntegrityAppCheckProvider.class)
.name(LIBRARY_NAME)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.required(liteExecutor))
.add(Dependency.required(blockingExecutor))
.factory(
(container) ->
new PlayIntegrityAppCheckProvider(
container.get(FirebaseApp.class), container.get(blockingExecutor)))
container.get(FirebaseApp.class),
container.get(liteExecutor),
container.get(blockingExecutor)))
.build(),
LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

package com.google.firebase.appcheck.playintegrity.internal;

import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.google.android.gms.tasks.Task;
Expand All @@ -25,6 +24,7 @@
import com.google.android.play.core.integrity.IntegrityTokenResponse;
import com.google.firebase.FirebaseApp;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.annotations.concurrent.Lightweight;
import com.google.firebase.appcheck.AppCheckProvider;
import com.google.firebase.appcheck.AppCheckToken;
import com.google.firebase.appcheck.internal.DefaultAppCheckToken;
Expand All @@ -39,15 +39,19 @@ public class PlayIntegrityAppCheckProvider implements AppCheckProvider {
private final String projectNumber;
private final IntegrityManager integrityManager;
private final NetworkClient networkClient;
private final Executor liteExecutor;
private final Executor blockingExecutor;
private final RetryManager retryManager;

public PlayIntegrityAppCheckProvider(
@NonNull FirebaseApp firebaseApp, @Blocking Executor blockingExecutor) {
@NonNull FirebaseApp firebaseApp,
@Lightweight Executor liteExecutor,
@Blocking Executor blockingExecutor) {
this(
firebaseApp.getOptions().getGcmSenderId(),
IntegrityManagerFactory.create(firebaseApp.getApplicationContext()),
new NetworkClient(firebaseApp),
liteExecutor,
blockingExecutor,
new RetryManager());
}
Expand All @@ -57,22 +61,23 @@ public PlayIntegrityAppCheckProvider(
@NonNull String projectNumber,
@NonNull IntegrityManager integrityManager,
@NonNull NetworkClient networkClient,
@NonNull Executor liteExecutor,
@NonNull Executor blockingExecutor,
@NonNull RetryManager retryManager) {
this.projectNumber = projectNumber;
this.integrityManager = integrityManager;
this.networkClient = networkClient;
this.liteExecutor = liteExecutor;
this.blockingExecutor = blockingExecutor;
this.retryManager = retryManager;
}

// TODO(b/261013814): Use an explicit executor in continuations.
@SuppressLint("TaskMainThread")
@NonNull
@Override
public Task<AppCheckToken> getToken() {
return getPlayIntegrityAttestation()
.onSuccessTask(
liteExecutor,
integrityTokenResponse -> {
ExchangePlayIntegrityTokenRequest request =
new ExchangePlayIntegrityTokenRequest(integrityTokenResponse.token());
Expand All @@ -85,32 +90,30 @@ public Task<AppCheckToken> getToken() {
retryManager));
})
.onSuccessTask(
appCheckTokenResponse -> {
return Tasks.forResult(
DefaultAppCheckToken.constructFromAppCheckTokenResponse(appCheckTokenResponse));
});
liteExecutor,
appCheckTokenResponse ->
Tasks.forResult(
DefaultAppCheckToken.constructFromAppCheckTokenResponse(
appCheckTokenResponse)));
}

// TODO(b/261013814): Use an explicit executor in continuations.
@SuppressLint("TaskMainThread")
@NonNull
private Task<IntegrityTokenResponse> getPlayIntegrityAttestation() {
GeneratePlayIntegrityChallengeRequest generateChallengeRequest =
new GeneratePlayIntegrityChallengeRequest();
Task<GeneratePlayIntegrityChallengeResponse> generateChallengeTask =
Tasks.call(
return Tasks.call(
blockingExecutor,
() ->
GeneratePlayIntegrityChallengeResponse.fromJsonString(
networkClient.generatePlayIntegrityChallenge(
generateChallengeRequest.toJsonString().getBytes(UTF_8), retryManager)));
return generateChallengeTask.onSuccessTask(
generatePlayIntegrityChallengeResponse -> {
return integrityManager.requestIntegrityToken(
IntegrityTokenRequest.builder()
.setCloudProjectNumber(Long.parseLong(projectNumber))
.setNonce(generatePlayIntegrityChallengeResponse.getChallenge())
.build());
});
generateChallengeRequest.toJsonString().getBytes(UTF_8), retryManager)))
.onSuccessTask(
liteExecutor,
generatePlayIntegrityChallengeResponse ->
integrityManager.requestIntegrityToken(
IntegrityTokenRequest.builder()
.setCloudProjectNumber(Long.parseLong(projectNumber))
.setNonce(generatePlayIntegrityChallengeResponse.getChallenge())
.build()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.firebase.FirebaseApp;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.annotations.concurrent.Lightweight;
import com.google.firebase.components.Component;
import com.google.firebase.components.Dependency;
import com.google.firebase.components.Qualified;
Expand All @@ -40,6 +41,7 @@ public void testGetComponents() {
assertThat(appCheckPlayIntegrityComponent.getDependencies())
.containsExactly(
Dependency.required(FirebaseApp.class),
Dependency.required(Qualified.qualified(Lightweight.class, Executor.class)),
Dependency.required(Qualified.qualified(Blocking.class, Executor.class)));
assertThat(appCheckPlayIntegrityComponent.isLazy()).isTrue();
}
Expand Down
Loading