-
Notifications
You must be signed in to change notification settings - Fork 617
Use sequential executor in place of synchronized blocks for cached new release #4430
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
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
8d14efe
Remove some synchronized blocks from FirebaseAppDistributionImpl
lfkellogg 218af55
Rename UpdateTaskCache.java to TaskCache.java
lfkellogg 2f398e6
Remove more sync blocks around new release cache
lfkellogg 4c9243e
Some refactoring
lfkellogg 8c8ac7b
Java formatting
lfkellogg 1355cb1
Add tests
lfkellogg 928ff1a
Add javadocs
lfkellogg f618340
Simplify SequentialReference and update tests
lfkellogg 1472f3e
Update registrar
lfkellogg 0b6a977
Fix test and add static imports from TestUtils
lfkellogg e1bd329
Incorporate some feedback
lfkellogg 9ef0e50
Use actual lightweight executor in test
lfkellogg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,14 +25,14 @@ | |
import android.app.Activity; | ||
import android.app.AlertDialog; | ||
import android.content.Context; | ||
import androidx.annotation.GuardedBy; | ||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.annotation.VisibleForTesting; | ||
import com.google.android.gms.tasks.Task; | ||
import com.google.android.gms.tasks.TaskCompletionSource; | ||
import com.google.android.gms.tasks.Tasks; | ||
import com.google.firebase.FirebaseApp; | ||
import com.google.firebase.annotations.concurrent.Lightweight; | ||
import com.google.firebase.appdistribution.AppDistributionRelease; | ||
import com.google.firebase.appdistribution.BinaryType; | ||
import com.google.firebase.appdistribution.FirebaseAppDistribution; | ||
|
@@ -41,6 +41,7 @@ | |
import com.google.firebase.appdistribution.UpdateProgress; | ||
import com.google.firebase.appdistribution.UpdateStatus; | ||
import com.google.firebase.appdistribution.UpdateTask; | ||
import java.util.concurrent.Executor; | ||
|
||
/** | ||
* This class is the "real" implementation of the Firebase App Distribution API which should only be | ||
|
@@ -57,14 +58,10 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution { | |
private final ApkUpdater apkUpdater; | ||
private final AabUpdater aabUpdater; | ||
private final SignInStorage signInStorage; | ||
|
||
private final Object cachedNewReleaseLock = new Object(); | ||
|
||
@GuardedBy("cachedNewReleaseLock") | ||
private AppDistributionReleaseInternal cachedNewRelease; | ||
|
||
private final SequentialReference<AppDistributionReleaseInternal> cachedNewRelease; | ||
private TaskCache<UpdateTask> updateIfNewReleaseAvailableTaskCache = new TaskCache<>(); | ||
private TaskCache<Task<AppDistributionRelease>> checkForNewReleaseTaskCache = new TaskCache<>(); | ||
@Lightweight private Executor lightweightExecutor; | ||
private AlertDialog updateConfirmationDialog; | ||
private AlertDialog signInConfirmationDialog; | ||
@Nullable private Activity dialogHostActivity = null; | ||
|
@@ -83,14 +80,17 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution { | |
@NonNull ApkUpdater apkUpdater, | ||
@NonNull AabUpdater aabUpdater, | ||
@NonNull SignInStorage signInStorage, | ||
@NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier) { | ||
@NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier, | ||
@NonNull @Lightweight Executor lightweightExecutor) { | ||
this.firebaseApp = firebaseApp; | ||
this.testerSignInManager = testerSignInManager; | ||
this.newReleaseFetcher = newReleaseFetcher; | ||
this.apkUpdater = apkUpdater; | ||
this.aabUpdater = aabUpdater; | ||
this.signInStorage = signInStorage; | ||
this.lifecycleNotifier = lifecycleNotifier; | ||
this.cachedNewRelease = new SequentialReference<>(lightweightExecutor); | ||
this.lightweightExecutor = lightweightExecutor; | ||
lifecycleNotifier.addOnActivityDestroyedListener(this::onActivityDestroyed); | ||
lifecycleNotifier.addOnActivityPausedListener(this::onActivityPaused); | ||
lifecycleNotifier.addOnActivityResumedListener(this::onActivityResumed); | ||
|
@@ -110,9 +110,10 @@ public UpdateTask updateIfNewReleaseAvailable() { | |
|
||
lifecycleNotifier | ||
.applyToForegroundActivityTask(this::showSignInConfirmationDialog) | ||
.onSuccessTask(unused -> signInTester()) | ||
.onSuccessTask(unused -> checkForNewRelease()) | ||
.onSuccessTask(lightweightExecutor, unused -> signInTester()) | ||
.onSuccessTask(lightweightExecutor, unused -> checkForNewRelease()) | ||
.continueWithTask( | ||
lightweightExecutor, | ||
task -> { | ||
if (!task.isSuccessful()) { | ||
postProgressToCachedUpdateIfNewReleaseTask( | ||
|
@@ -141,13 +142,16 @@ public UpdateTask updateIfNewReleaseAvailable() { | |
activity -> showUpdateConfirmationDialog(activity, release)); | ||
}) | ||
.onSuccessTask( | ||
lightweightExecutor, | ||
unused -> | ||
updateApp(true) | ||
.addOnProgressListener( | ||
lightweightExecutor, | ||
updateProgress -> | ||
postProgressToCachedUpdateIfNewReleaseTask( | ||
updateTask, updateProgress))) | ||
.addOnFailureListener( | ||
lightweightExecutor, | ||
exception -> setCachedUpdateIfNewReleaseCompletionError(updateTask, exception)); | ||
|
||
return updateTask; | ||
|
@@ -214,44 +218,42 @@ public Task<Void> signInTester() { | |
|
||
@Override | ||
public void signOutTester() { | ||
setCachedNewRelease(null); | ||
signInStorage.setSignInStatus(false); | ||
cachedNewRelease | ||
.set(null) | ||
.addOnSuccessListener(lightweightExecutor, unused -> signInStorage.setSignInStatus(false)); | ||
} | ||
|
||
@Override | ||
@NonNull | ||
// TODO(b/261014422): Use an explicit executor in continuations. | ||
@SuppressLint("TaskMainThread") | ||
public synchronized Task<AppDistributionRelease> checkForNewRelease() { | ||
return checkForNewReleaseTaskCache.getOrCreateTask( | ||
() -> { | ||
if (!isTesterSignedIn()) { | ||
return Tasks.forException( | ||
new FirebaseAppDistributionException( | ||
"Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
} | ||
if (!isTesterSignedIn()) { | ||
return Tasks.forException( | ||
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
} | ||
Comment on lines
+231
to
+234
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also pulled this out of the |
||
|
||
return newReleaseFetcher | ||
.checkForNewRelease() | ||
.onSuccessTask( | ||
appDistributionReleaseInternal -> { | ||
setCachedNewRelease(appDistributionReleaseInternal); | ||
return Tasks.forResult( | ||
ReleaseUtils.convertToAppDistributionRelease( | ||
appDistributionReleaseInternal)); | ||
}) | ||
.addOnFailureListener( | ||
e -> { | ||
if (e instanceof FirebaseAppDistributionException | ||
&& ((FirebaseAppDistributionException) e).getErrorCode() | ||
== AUTHENTICATION_FAILURE) { | ||
// If CheckForNewRelease returns authentication error, the FID is no longer | ||
// valid or does not have access to the latest release. So sign out the tester | ||
// to force FID re-registration | ||
signOutTester(); | ||
} | ||
}); | ||
}); | ||
return checkForNewReleaseTaskCache.getOrCreateTask( | ||
() -> | ||
newReleaseFetcher | ||
.checkForNewRelease() | ||
.onSuccessTask(lightweightExecutor, release -> cachedNewRelease.set(release)) | ||
.onSuccessTask( | ||
lightweightExecutor, | ||
release -> | ||
Tasks.forResult(ReleaseUtils.convertToAppDistributionRelease(release))) | ||
.addOnFailureListener( | ||
lightweightExecutor, | ||
e -> { | ||
if (e instanceof FirebaseAppDistributionException | ||
&& ((FirebaseAppDistributionException) e).getErrorCode() | ||
== AUTHENTICATION_FAILURE) { | ||
// If CheckForNewRelease returns authentication error, the FID is no longer | ||
// valid or does not have access to the latest release. So sign out the | ||
// tester to force FID re-registration | ||
signOutTester(); | ||
} | ||
})); | ||
} | ||
|
||
@Override | ||
|
@@ -265,34 +267,36 @@ public UpdateTask updateApp() { | |
* basic configuration and false for advanced configuration. | ||
*/ | ||
private UpdateTask updateApp(boolean showDownloadInNotificationManager) { | ||
synchronized (cachedNewReleaseLock) { | ||
if (!isTesterSignedIn()) { | ||
UpdateTaskImpl updateTask = new UpdateTaskImpl(); | ||
updateTask.setException( | ||
new FirebaseAppDistributionException( | ||
"Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
return updateTask; | ||
} | ||
if (cachedNewRelease == null) { | ||
LogWrapper.getInstance().v("New release not found."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE)); | ||
} | ||
if (cachedNewRelease.getDownloadUrl() == null) { | ||
LogWrapper.getInstance().v("Download failed to execute."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.DOWNLOAD_URL_NOT_FOUND, | ||
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE)); | ||
} | ||
|
||
if (cachedNewRelease.getBinaryType() == BinaryType.AAB) { | ||
return aabUpdater.updateAab(cachedNewRelease); | ||
} else { | ||
return apkUpdater.updateApk(cachedNewRelease, showDownloadInNotificationManager); | ||
} | ||
if (!isTesterSignedIn()) { | ||
UpdateTaskImpl updateTask = new UpdateTaskImpl(); | ||
updateTask.setException( | ||
new FirebaseAppDistributionException("Tester is not signed in", AUTHENTICATION_FAILURE)); | ||
return updateTask; | ||
} | ||
return TaskUtils.onSuccessUpdateTask( | ||
cachedNewRelease.get(), | ||
lightweightExecutor, | ||
release -> { | ||
if (release == null) { | ||
LogWrapper.getInstance().v("New release not found."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.RELEASE_NOT_FOUND_ERROR, UPDATE_NOT_AVAILABLE)); | ||
} | ||
if (release.getDownloadUrl() == null) { | ||
LogWrapper.getInstance().v("Download failed to execute."); | ||
return getErrorUpdateTask( | ||
new FirebaseAppDistributionException( | ||
ErrorMessages.DOWNLOAD_URL_NOT_FOUND, | ||
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE)); | ||
} | ||
|
||
if (release.getBinaryType() == BinaryType.AAB) { | ||
return aabUpdater.updateAab(release); | ||
} else { | ||
return apkUpdater.updateApk(release, showDownloadInNotificationManager); | ||
} | ||
}); | ||
} | ||
|
||
@VisibleForTesting | ||
|
@@ -313,10 +317,13 @@ void onActivityResumed(Activity activity) { | |
new FirebaseAppDistributionException( | ||
ErrorMessages.HOST_ACTIVITY_INTERRUPTED, HOST_ACTIVITY_INTERRUPTED)); | ||
} else { | ||
synchronized (cachedNewReleaseLock) { | ||
showUpdateConfirmationDialog( | ||
activity, ReleaseUtils.convertToAppDistributionRelease(cachedNewRelease)); | ||
} | ||
cachedNewRelease | ||
.get() | ||
.addOnSuccessListener( | ||
lightweightExecutor, | ||
release -> | ||
showUpdateConfirmationDialog( | ||
activity, ReleaseUtils.convertToAppDistributionRelease(release))); | ||
} | ||
} | ||
} | ||
|
@@ -342,17 +349,8 @@ void onActivityDestroyed(@NonNull Activity activity) { | |
} | ||
|
||
@VisibleForTesting | ||
void setCachedNewRelease(@Nullable AppDistributionReleaseInternal newRelease) { | ||
synchronized (cachedNewReleaseLock) { | ||
cachedNewRelease = newRelease; | ||
} | ||
} | ||
|
||
@VisibleForTesting | ||
AppDistributionReleaseInternal getCachedNewRelease() { | ||
synchronized (cachedNewReleaseLock) { | ||
return cachedNewRelease; | ||
} | ||
SequentialReference<AppDistributionReleaseInternal> getCachedNewRelease() { | ||
return cachedNewRelease; | ||
} | ||
|
||
private Task<Void> showUpdateConfirmationDialog( | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.