Skip to content

Commit 98ea42c

Browse files
authored
Merge 9ef0e50 into 45c2d7c
2 parents 45c2d7c + 9ef0e50 commit 98ea42c

File tree

9 files changed

+333
-189
lines changed

9 files changed

+333
-189
lines changed

firebase-appdistribution/firebase-appdistribution.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ dependencies {
4949
implementation project(':firebase-installations-interop')
5050
implementation project(':firebase-common')
5151
testImplementation project(path: ':firebase-appdistribution')
52+
testImplementation project(':integ-testing')
5253
runtimeOnly project(':firebase-installations')
5354

5455
testImplementation 'junit:junit:4.13.2'

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/AppDistributionReleaseInternal.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
/**
2323
* This class represents the AppDistributionRelease object returned by the App Distribution backend.
24+
*
25+
* <p>TODO(lkellogg): Combine this with AppDistributionReleaseImpl
2426
*/
2527
@AutoValue
2628
abstract class AppDistributionReleaseInternal {
@@ -65,6 +67,9 @@ static Builder builder() {
6567
@Nullable
6668
abstract String getDownloadUrl();
6769

70+
@NonNull
71+
abstract Builder toBuilder();
72+
6873
/** Builder for {@link AppDistributionReleaseInternal}. */
6974
@AutoValue.Builder
7075
abstract static class Builder {

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/FirebaseAppDistributionImpl.java

Lines changed: 80 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525
import android.app.Activity;
2626
import android.app.AlertDialog;
2727
import android.content.Context;
28-
import androidx.annotation.GuardedBy;
2928
import androidx.annotation.NonNull;
3029
import androidx.annotation.Nullable;
3130
import androidx.annotation.VisibleForTesting;
3231
import com.google.android.gms.tasks.Task;
3332
import com.google.android.gms.tasks.TaskCompletionSource;
3433
import com.google.android.gms.tasks.Tasks;
3534
import com.google.firebase.FirebaseApp;
35+
import com.google.firebase.annotations.concurrent.Lightweight;
3636
import com.google.firebase.appdistribution.AppDistributionRelease;
3737
import com.google.firebase.appdistribution.BinaryType;
3838
import com.google.firebase.appdistribution.FirebaseAppDistribution;
@@ -41,6 +41,7 @@
4141
import com.google.firebase.appdistribution.UpdateProgress;
4242
import com.google.firebase.appdistribution.UpdateStatus;
4343
import com.google.firebase.appdistribution.UpdateTask;
44+
import java.util.concurrent.Executor;
4445

4546
/**
4647
* This class is the "real" implementation of the Firebase App Distribution API which should only be
@@ -57,14 +58,10 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
5758
private final ApkUpdater apkUpdater;
5859
private final AabUpdater aabUpdater;
5960
private final SignInStorage signInStorage;
60-
61-
private final Object cachedNewReleaseLock = new Object();
62-
63-
@GuardedBy("cachedNewReleaseLock")
64-
private AppDistributionReleaseInternal cachedNewRelease;
65-
61+
private final SequentialReference<AppDistributionReleaseInternal> cachedNewRelease;
6662
private TaskCache<UpdateTask> updateIfNewReleaseAvailableTaskCache = new TaskCache<>();
6763
private TaskCache<Task<AppDistributionRelease>> checkForNewReleaseTaskCache = new TaskCache<>();
64+
@Lightweight private Executor lightweightExecutor;
6865
private AlertDialog updateConfirmationDialog;
6966
private AlertDialog signInConfirmationDialog;
7067
@Nullable private Activity dialogHostActivity = null;
@@ -83,14 +80,17 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
8380
@NonNull ApkUpdater apkUpdater,
8481
@NonNull AabUpdater aabUpdater,
8582
@NonNull SignInStorage signInStorage,
86-
@NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier) {
83+
@NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier,
84+
@NonNull @Lightweight Executor lightweightExecutor) {
8785
this.firebaseApp = firebaseApp;
8886
this.testerSignInManager = testerSignInManager;
8987
this.newReleaseFetcher = newReleaseFetcher;
9088
this.apkUpdater = apkUpdater;
9189
this.aabUpdater = aabUpdater;
9290
this.signInStorage = signInStorage;
9391
this.lifecycleNotifier = lifecycleNotifier;
92+
this.cachedNewRelease = new SequentialReference<>(lightweightExecutor);
93+
this.lightweightExecutor = lightweightExecutor;
9494
lifecycleNotifier.addOnActivityDestroyedListener(this::onActivityDestroyed);
9595
lifecycleNotifier.addOnActivityPausedListener(this::onActivityPaused);
9696
lifecycleNotifier.addOnActivityResumedListener(this::onActivityResumed);
@@ -110,9 +110,10 @@ public UpdateTask updateIfNewReleaseAvailable() {
110110

111111
lifecycleNotifier
112112
.applyToForegroundActivityTask(this::showSignInConfirmationDialog)
113-
.onSuccessTask(unused -> signInTester())
114-
.onSuccessTask(unused -> checkForNewRelease())
113+
.onSuccessTask(lightweightExecutor, unused -> signInTester())
114+
.onSuccessTask(lightweightExecutor, unused -> checkForNewRelease())
115115
.continueWithTask(
116+
lightweightExecutor,
116117
task -> {
117118
if (!task.isSuccessful()) {
118119
postProgressToCachedUpdateIfNewReleaseTask(
@@ -141,13 +142,16 @@ public UpdateTask updateIfNewReleaseAvailable() {
141142
activity -> showUpdateConfirmationDialog(activity, release));
142143
})
143144
.onSuccessTask(
145+
lightweightExecutor,
144146
unused ->
145147
updateApp(true)
146148
.addOnProgressListener(
149+
lightweightExecutor,
147150
updateProgress ->
148151
postProgressToCachedUpdateIfNewReleaseTask(
149152
updateTask, updateProgress)))
150153
.addOnFailureListener(
154+
lightweightExecutor,
151155
exception -> setCachedUpdateIfNewReleaseCompletionError(updateTask, exception));
152156

153157
return updateTask;
@@ -214,44 +218,42 @@ public Task<Void> signInTester() {
214218

215219
@Override
216220
public void signOutTester() {
217-
setCachedNewRelease(null);
218-
signInStorage.setSignInStatus(false);
221+
cachedNewRelease
222+
.set(null)
223+
.addOnSuccessListener(lightweightExecutor, unused -> signInStorage.setSignInStatus(false));
219224
}
220225

221226
@Override
222227
@NonNull
223228
// TODO(b/261014422): Use an explicit executor in continuations.
224229
@SuppressLint("TaskMainThread")
225230
public synchronized Task<AppDistributionRelease> checkForNewRelease() {
226-
return checkForNewReleaseTaskCache.getOrCreateTask(
227-
() -> {
228-
if (!isTesterSignedIn()) {
229-
return Tasks.forException(
230-
new FirebaseAppDistributionException(
231-
"Tester is not signed in", AUTHENTICATION_FAILURE));
232-
}
231+
if (!isTesterSignedIn()) {
232+
return Tasks.forException(
233+
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE));
234+
}
233235

234-
return newReleaseFetcher
235-
.checkForNewRelease()
236-
.onSuccessTask(
237-
appDistributionReleaseInternal -> {
238-
setCachedNewRelease(appDistributionReleaseInternal);
239-
return Tasks.forResult(
240-
ReleaseUtils.convertToAppDistributionRelease(
241-
appDistributionReleaseInternal));
242-
})
243-
.addOnFailureListener(
244-
e -> {
245-
if (e instanceof FirebaseAppDistributionException
246-
&& ((FirebaseAppDistributionException) e).getErrorCode()
247-
== AUTHENTICATION_FAILURE) {
248-
// If CheckForNewRelease returns authentication error, the FID is no longer
249-
// valid or does not have access to the latest release. So sign out the tester
250-
// to force FID re-registration
251-
signOutTester();
252-
}
253-
});
254-
});
236+
return checkForNewReleaseTaskCache.getOrCreateTask(
237+
() ->
238+
newReleaseFetcher
239+
.checkForNewRelease()
240+
.onSuccessTask(lightweightExecutor, release -> cachedNewRelease.set(release))
241+
.onSuccessTask(
242+
lightweightExecutor,
243+
release ->
244+
Tasks.forResult(ReleaseUtils.convertToAppDistributionRelease(release)))
245+
.addOnFailureListener(
246+
lightweightExecutor,
247+
e -> {
248+
if (e instanceof FirebaseAppDistributionException
249+
&& ((FirebaseAppDistributionException) e).getErrorCode()
250+
== AUTHENTICATION_FAILURE) {
251+
// If CheckForNewRelease returns authentication error, the FID is no longer
252+
// valid or does not have access to the latest release. So sign out the
253+
// tester to force FID re-registration
254+
signOutTester();
255+
}
256+
}));
255257
}
256258

257259
@Override
@@ -265,34 +267,36 @@ public UpdateTask updateApp() {
265267
* basic configuration and false for advanced configuration.
266268
*/
267269
private UpdateTask updateApp(boolean showDownloadInNotificationManager) {
268-
synchronized (cachedNewReleaseLock) {
269-
if (!isTesterSignedIn()) {
270-
UpdateTaskImpl updateTask = new UpdateTaskImpl();
271-
updateTask.setException(
272-
new FirebaseAppDistributionException(
273-
"Tester is not signed in", AUTHENTICATION_FAILURE));
274-
return updateTask;
275-
}
276-
if (cachedNewRelease == null) {
277-
LogWrapper.getInstance().v("New release not found.");
278-
return getErrorUpdateTask(
279-
new FirebaseAppDistributionException(
280-
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE));
281-
}
282-
if (cachedNewRelease.getDownloadUrl() == null) {
283-
LogWrapper.getInstance().v("Download failed to execute.");
284-
return getErrorUpdateTask(
285-
new FirebaseAppDistributionException(
286-
ErrorMessages.DOWNLOAD_URL_NOT_FOUND,
287-
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
288-
}
289-
290-
if (cachedNewRelease.getBinaryType() == BinaryType.AAB) {
291-
return aabUpdater.updateAab(cachedNewRelease);
292-
} else {
293-
return apkUpdater.updateApk(cachedNewRelease, showDownloadInNotificationManager);
294-
}
270+
if (!isTesterSignedIn()) {
271+
UpdateTaskImpl updateTask = new UpdateTaskImpl();
272+
updateTask.setException(
273+
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE));
274+
return updateTask;
295275
}
276+
return TaskUtils.onSuccessUpdateTask(
277+
cachedNewRelease.get(),
278+
lightweightExecutor,
279+
release -> {
280+
if (release == null) {
281+
LogWrapper.getInstance().v("New release not found.");
282+
return getErrorUpdateTask(
283+
new FirebaseAppDistributionException(
284+
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE));
285+
}
286+
if (release.getDownloadUrl() == null) {
287+
LogWrapper.getInstance().v("Download failed to execute.");
288+
return getErrorUpdateTask(
289+
new FirebaseAppDistributionException(
290+
ErrorMessages.DOWNLOAD_URL_NOT_FOUND,
291+
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
292+
}
293+
294+
if (release.getBinaryType() == BinaryType.AAB) {
295+
return aabUpdater.updateAab(release);
296+
} else {
297+
return apkUpdater.updateApk(release, showDownloadInNotificationManager);
298+
}
299+
});
296300
}
297301

298302
@VisibleForTesting
@@ -313,10 +317,13 @@ void onActivityResumed(Activity activity) {
313317
new FirebaseAppDistributionException(
314318
ErrorMessages.HOST_ACTIVITY_INTERRUPTED, HOST_ACTIVITY_INTERRUPTED));
315319
} else {
316-
synchronized (cachedNewReleaseLock) {
317-
showUpdateConfirmationDialog(
318-
activity, ReleaseUtils.convertToAppDistributionRelease(cachedNewRelease));
319-
}
320+
cachedNewRelease
321+
.get()
322+
.addOnSuccessListener(
323+
lightweightExecutor,
324+
release ->
325+
showUpdateConfirmationDialog(
326+
activity, ReleaseUtils.convertToAppDistributionRelease(release)));
320327
}
321328
}
322329
}
@@ -342,17 +349,8 @@ void onActivityDestroyed(@NonNull Activity activity) {
342349
}
343350

344351
@VisibleForTesting
345-
void setCachedNewRelease(@Nullable AppDistributionReleaseInternal newRelease) {
346-
synchronized (cachedNewReleaseLock) {
347-
cachedNewRelease = newRelease;
348-
}
349-
}
350-
351-
@VisibleForTesting
352-
AppDistributionReleaseInternal getCachedNewRelease() {
353-
synchronized (cachedNewReleaseLock) {
354-
return cachedNewRelease;
355-
}
352+
SequentialReference<AppDistributionReleaseInternal> getCachedNewRelease() {
353+
return cachedNewRelease;
356354
}
357355

358356
private Task<Void> showUpdateConfirmationDialog(

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/FirebaseAppDistributionRegistrar.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import androidx.annotation.NonNull;
2121
import com.google.firebase.FirebaseApp;
2222
import com.google.firebase.annotations.concurrent.Blocking;
23+
import com.google.firebase.annotations.concurrent.Lightweight;
2324
import com.google.firebase.appdistribution.FirebaseAppDistribution;
2425
import com.google.firebase.components.Component;
2526
import com.google.firebase.components.ComponentContainer;
@@ -47,13 +48,16 @@ public class FirebaseAppDistributionRegistrar implements ComponentRegistrar {
4748
@Override
4849
public @NonNull List<Component<?>> getComponents() {
4950
Qualified<Executor> blockingExecutor = Qualified.qualified(Blocking.class, Executor.class);
51+
Qualified<Executor> lightweightExecutor =
52+
Qualified.qualified(Lightweight.class, Executor.class);
5053
return Arrays.asList(
5154
Component.builder(FirebaseAppDistribution.class)
5255
.name(LIBRARY_NAME)
5356
.add(Dependency.required(FirebaseApp.class))
5457
.add(Dependency.requiredProvider(FirebaseInstallationsApi.class))
5558
.add(Dependency.required(blockingExecutor))
56-
.factory(c -> buildFirebaseAppDistribution(c, c.get(blockingExecutor)))
59+
.add(Dependency.required(lightweightExecutor))
60+
.factory(c -> buildFirebaseAppDistribution(c, blockingExecutor, lightweightExecutor))
5761
// construct FirebaseAppDistribution instance on startup so we can register for
5862
// activity lifecycle callbacks before the API is called
5963
.alwaysEager()
@@ -62,8 +66,12 @@ public class FirebaseAppDistributionRegistrar implements ComponentRegistrar {
6266
}
6367

6468
private FirebaseAppDistribution buildFirebaseAppDistribution(
65-
ComponentContainer container, Executor blockingExecutor) {
69+
ComponentContainer container,
70+
Qualified<Executor> blockingExecutorType,
71+
Qualified<Executor> lightweightExecutorType) {
6672
FirebaseApp firebaseApp = container.get(FirebaseApp.class);
73+
Executor blockingExecutor = container.get(blockingExecutorType);
74+
Executor lightweightExecutor = container.get(lightweightExecutorType);
6775
Context context = firebaseApp.getApplicationContext();
6876
Provider<FirebaseInstallationsApi> firebaseInstallationsApiProvider =
6977
container.getProvider(FirebaseInstallationsApi.class);
@@ -86,7 +94,8 @@ private FirebaseAppDistribution buildFirebaseAppDistribution(
8694
new ApkUpdater(firebaseApp, new ApkInstaller(), blockingExecutor),
8795
new AabUpdater(blockingExecutor),
8896
signInStorage,
89-
lifecycleNotifier);
97+
lifecycleNotifier,
98+
lightweightExecutor);
9099

91100
if (context instanceof Application) {
92101
Application firebaseApplication = (Application) context;

0 commit comments

Comments
 (0)