25
25
import android .app .Activity ;
26
26
import android .app .AlertDialog ;
27
27
import android .content .Context ;
28
- import androidx .annotation .GuardedBy ;
29
28
import androidx .annotation .NonNull ;
30
29
import androidx .annotation .Nullable ;
31
30
import androidx .annotation .VisibleForTesting ;
32
31
import com .google .android .gms .tasks .Task ;
33
32
import com .google .android .gms .tasks .TaskCompletionSource ;
34
33
import com .google .android .gms .tasks .Tasks ;
35
34
import com .google .firebase .FirebaseApp ;
35
+ import com .google .firebase .annotations .concurrent .Lightweight ;
36
36
import com .google .firebase .appdistribution .AppDistributionRelease ;
37
37
import com .google .firebase .appdistribution .BinaryType ;
38
38
import com .google .firebase .appdistribution .FirebaseAppDistribution ;
41
41
import com .google .firebase .appdistribution .UpdateProgress ;
42
42
import com .google .firebase .appdistribution .UpdateStatus ;
43
43
import com .google .firebase .appdistribution .UpdateTask ;
44
+ import java .util .concurrent .Executor ;
44
45
45
46
/**
46
47
* This class is the "real" implementation of the Firebase App Distribution API which should only be
@@ -57,14 +58,10 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
57
58
private final ApkUpdater apkUpdater ;
58
59
private final AabUpdater aabUpdater ;
59
60
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 ;
66
62
private TaskCache <UpdateTask > updateIfNewReleaseAvailableTaskCache = new TaskCache <>();
67
63
private TaskCache <Task <AppDistributionRelease >> checkForNewReleaseTaskCache = new TaskCache <>();
64
+ @ Lightweight private Executor lightweightExecutor ;
68
65
private AlertDialog updateConfirmationDialog ;
69
66
private AlertDialog signInConfirmationDialog ;
70
67
@ Nullable private Activity dialogHostActivity = null ;
@@ -83,14 +80,17 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
83
80
@ NonNull ApkUpdater apkUpdater ,
84
81
@ NonNull AabUpdater aabUpdater ,
85
82
@ NonNull SignInStorage signInStorage ,
86
- @ NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ) {
83
+ @ NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ,
84
+ @ NonNull @ Lightweight Executor lightweightExecutor ) {
87
85
this .firebaseApp = firebaseApp ;
88
86
this .testerSignInManager = testerSignInManager ;
89
87
this .newReleaseFetcher = newReleaseFetcher ;
90
88
this .apkUpdater = apkUpdater ;
91
89
this .aabUpdater = aabUpdater ;
92
90
this .signInStorage = signInStorage ;
93
91
this .lifecycleNotifier = lifecycleNotifier ;
92
+ this .cachedNewRelease = new SequentialReference <>(lightweightExecutor );
93
+ this .lightweightExecutor = lightweightExecutor ;
94
94
lifecycleNotifier .addOnActivityDestroyedListener (this ::onActivityDestroyed );
95
95
lifecycleNotifier .addOnActivityPausedListener (this ::onActivityPaused );
96
96
lifecycleNotifier .addOnActivityResumedListener (this ::onActivityResumed );
@@ -110,9 +110,10 @@ public UpdateTask updateIfNewReleaseAvailable() {
110
110
111
111
lifecycleNotifier
112
112
.applyToForegroundActivityTask (this ::showSignInConfirmationDialog )
113
- .onSuccessTask (unused -> signInTester ())
114
- .onSuccessTask (unused -> checkForNewRelease ())
113
+ .onSuccessTask (lightweightExecutor , unused -> signInTester ())
114
+ .onSuccessTask (lightweightExecutor , unused -> checkForNewRelease ())
115
115
.continueWithTask (
116
+ lightweightExecutor ,
116
117
task -> {
117
118
if (!task .isSuccessful ()) {
118
119
postProgressToCachedUpdateIfNewReleaseTask (
@@ -141,13 +142,16 @@ public UpdateTask updateIfNewReleaseAvailable() {
141
142
activity -> showUpdateConfirmationDialog (activity , release ));
142
143
})
143
144
.onSuccessTask (
145
+ lightweightExecutor ,
144
146
unused ->
145
147
updateApp (true )
146
148
.addOnProgressListener (
149
+ lightweightExecutor ,
147
150
updateProgress ->
148
151
postProgressToCachedUpdateIfNewReleaseTask (
149
152
updateTask , updateProgress )))
150
153
.addOnFailureListener (
154
+ lightweightExecutor ,
151
155
exception -> setCachedUpdateIfNewReleaseCompletionError (updateTask , exception ));
152
156
153
157
return updateTask ;
@@ -214,44 +218,42 @@ public Task<Void> signInTester() {
214
218
215
219
@ Override
216
220
public void signOutTester () {
217
- setCachedNewRelease (null );
218
- signInStorage .setSignInStatus (false );
221
+ cachedNewRelease
222
+ .set (null )
223
+ .addOnSuccessListener (lightweightExecutor , unused -> signInStorage .setSignInStatus (false ));
219
224
}
220
225
221
226
@ Override
222
227
@ NonNull
223
228
// TODO(b/261014422): Use an explicit executor in continuations.
224
229
@ SuppressLint ("TaskMainThread" )
225
230
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
+ }
233
235
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
+ }) );
255
257
}
256
258
257
259
@ Override
@@ -265,34 +267,36 @@ public UpdateTask updateApp() {
265
267
* basic configuration and false for advanced configuration.
266
268
*/
267
269
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 ;
295
275
}
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
+ });
296
300
}
297
301
298
302
@ VisibleForTesting
@@ -313,10 +317,13 @@ void onActivityResumed(Activity activity) {
313
317
new FirebaseAppDistributionException (
314
318
ErrorMessages .HOST_ACTIVITY_INTERRUPTED , HOST_ACTIVITY_INTERRUPTED ));
315
319
} 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 )));
320
327
}
321
328
}
322
329
}
@@ -342,17 +349,8 @@ void onActivityDestroyed(@NonNull Activity activity) {
342
349
}
343
350
344
351
@ 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 ;
356
354
}
357
355
358
356
private Task <Void > showUpdateConfirmationDialog (
0 commit comments