17
17
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .AUTHENTICATION_CANCELED ;
18
18
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .AUTHENTICATION_FAILURE ;
19
19
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .UNKNOWN ;
20
- import static com .google .firebase .appdistribution .impl .TaskUtils .safeSetTaskException ;
21
- import static com .google .firebase .appdistribution .impl .TaskUtils .safeSetTaskResult ;
22
20
23
- import android .annotation .SuppressLint ;
24
21
import android .app .Activity ;
25
22
import android .content .Context ;
26
23
import android .content .Intent ;
27
24
import android .content .pm .ResolveInfo ;
28
25
import android .net .Uri ;
29
- import androidx .annotation .GuardedBy ;
30
26
import androidx .annotation .NonNull ;
31
27
import androidx .annotation .VisibleForTesting ;
32
28
import androidx .browser .customtabs .CustomTabsIntent ;
35
31
import com .google .android .gms .tasks .TaskCompletionSource ;
36
32
import com .google .android .gms .tasks .Tasks ;
37
33
import com .google .firebase .FirebaseApp ;
38
- import com .google .firebase .annotations .concurrent .Blocking ;
34
+ import com .google .firebase .annotations .concurrent .Lightweight ;
39
35
import com .google .firebase .appdistribution .FirebaseAppDistributionException ;
40
36
import com .google .firebase .appdistribution .FirebaseAppDistributionException .Status ;
41
37
import com .google .firebase .inject .Provider ;
@@ -55,28 +51,22 @@ class TesterSignInManager {
55
51
private final SignInStorage signInStorage ;
56
52
private final FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ;
57
53
58
- // TODO: remove synchronized block usage in this class so this does not have to be blocking
59
- @ Blocking private final Executor blockingExecutor ;
54
+ @ Lightweight private final Executor lightweightExecutor ;
55
+ private final TaskCompletionSourceCache < Void > signInTaskCompletionSourceCache ;
60
56
61
- private final Object signInTaskLock = new Object ();
62
-
63
- @ GuardedBy ("signInTaskLock" )
64
57
private boolean hasBeenSentToBrowserForCurrentTask = false ;
65
58
66
- @ GuardedBy ("signInTaskLock" )
67
- private TaskCompletionSource <Void > signInTaskCompletionSource = null ;
68
-
69
59
TesterSignInManager (
70
60
@ NonNull FirebaseApp firebaseApp ,
71
61
@ NonNull Provider <FirebaseInstallationsApi > firebaseInstallationsApiProvider ,
72
62
@ NonNull final SignInStorage signInStorage ,
73
- @ Blocking Executor blockingExecutor ) {
63
+ @ Lightweight Executor lightweightExecutor ) {
74
64
this (
75
65
firebaseApp ,
76
66
firebaseInstallationsApiProvider ,
77
67
signInStorage ,
78
68
FirebaseAppDistributionLifecycleNotifier .getInstance (),
79
- blockingExecutor );
69
+ lightweightExecutor );
80
70
}
81
71
82
72
@ VisibleForTesting
@@ -85,12 +75,13 @@ class TesterSignInManager {
85
75
@ NonNull Provider <FirebaseInstallationsApi > firebaseInstallationsApiProvider ,
86
76
@ NonNull final SignInStorage signInStorage ,
87
77
@ NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ,
88
- @ Blocking Executor blockingExecutor ) {
78
+ @ Lightweight Executor lightweightExecutor ) {
89
79
this .firebaseApp = firebaseApp ;
90
80
this .firebaseInstallationsApiProvider = firebaseInstallationsApiProvider ;
91
81
this .signInStorage = signInStorage ;
92
82
this .lifecycleNotifier = lifecycleNotifier ;
93
- this .blockingExecutor = blockingExecutor ;
83
+ this .lightweightExecutor = lightweightExecutor ;
84
+ this .signInTaskCompletionSourceCache = new TaskCompletionSourceCache <>(lightweightExecutor );
94
85
95
86
lifecycleNotifier .addOnActivityCreatedListener (this ::onActivityCreated );
96
87
lifecycleNotifier .addOnActivityResumedListener (this ::onActivityResumed );
@@ -104,13 +95,11 @@ void onActivityCreated(Activity activity) {
104
95
LogWrapper .v (TAG , "Sign in completed" );
105
96
this .signInStorage
106
97
.setSignInStatus (true )
107
- .addOnSuccessListener (blockingExecutor , unused -> this .setSuccessfulSignInResult ())
98
+ .addOnSuccessListener (
99
+ lightweightExecutor , unused -> signInTaskCompletionSourceCache .setResult (null ))
108
100
.addOnFailureListener (
109
- blockingExecutor ,
110
- e ->
111
- this .setSignInTaskCompletionError (
112
- new FirebaseAppDistributionException (
113
- "Error storing tester sign in state" , UNKNOWN , e )));
101
+ lightweightExecutor ,
102
+ handleTaskFailure ("Error storing tester sign in state" , UNKNOWN ));
114
103
}
115
104
}
116
105
@@ -120,27 +109,26 @@ void onActivityResumed(Activity activity) {
120
109
// SignInResult and InstallActivity are internal to the SDK and should not be treated as
121
110
// reentering the app
122
111
return ;
123
- } else {
124
- // Throw error if app reentered during sign in
125
- synchronized ( signInTaskLock ) {
126
- if (awaitingResultFromBrowser () ) {
127
- LogWrapper . e ( TAG , "App Resumed without sign in flow completing." );
128
- setSignInTaskCompletionError (
112
+ }
113
+
114
+ // Throw error if app reentered during sign in
115
+ if (hasBeenSentToBrowserForCurrentTask ) {
116
+ signInTaskCompletionSourceCache
117
+ . setException (
129
118
new FirebaseAppDistributionException (
130
- ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED ));
131
- }
132
- }
119
+ ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED ))
120
+ .addOnSuccessListener (
121
+ lightweightExecutor ,
122
+ unused -> LogWrapper .e (TAG , "App resumed without sign in flow completing." ));
133
123
}
134
124
}
135
125
136
- // TODO(b/261014422): Use an explicit executor in continuations.
137
- @ SuppressLint ("TaskMainThread" )
138
126
@ NonNull
139
127
public Task <Void > signInTester () {
140
128
return signInStorage
141
129
.getSignInStatus ()
142
130
.onSuccessTask (
143
- blockingExecutor ,
131
+ lightweightExecutor ,
144
132
signedIn -> {
145
133
if (signedIn ) {
146
134
LogWrapper .v (TAG , "Tester is already signed in." );
@@ -150,80 +138,44 @@ public Task<Void> signInTester() {
150
138
});
151
139
}
152
140
153
- // TODO(b/261014422): Use an explicit executor in continuations.
154
- @ SuppressLint ("TaskMainThread" )
155
141
private Task <Void > doSignInTester () {
156
- synchronized (signInTaskLock ) {
157
- if (signInTaskCompletionSource != null
158
- && !signInTaskCompletionSource .getTask ().isComplete ()) {
159
- LogWrapper .v (TAG , "Detected In-Progress sign in task. Returning the same task." );
160
- return signInTaskCompletionSource .getTask ();
161
- }
162
-
163
- signInTaskCompletionSource = new TaskCompletionSource <>();
164
- hasBeenSentToBrowserForCurrentTask = false ;
165
-
166
- firebaseInstallationsApiProvider
167
- .get ()
168
- .getId ()
169
- .addOnFailureListener (
170
- blockingExecutor ,
171
- handleTaskFailure (ErrorMessages .AUTHENTICATION_ERROR , AUTHENTICATION_FAILURE ))
172
- .addOnSuccessListener (
173
- blockingExecutor ,
174
- fid ->
175
- getForegroundActivityAndOpenSignInFlow (fid )
176
- .addOnFailureListener (
177
- blockingExecutor ,
178
- handleTaskFailure (ErrorMessages .UNKNOWN_ERROR , UNKNOWN )));
179
-
180
- return signInTaskCompletionSource .getTask ();
181
- }
142
+ return signInTaskCompletionSourceCache .getOrCreateTaskFromCompletionSource (
143
+ () -> {
144
+ TaskCompletionSource <Void > signInTaskCompletionSource = new TaskCompletionSource <>();
145
+ hasBeenSentToBrowserForCurrentTask = false ;
146
+ firebaseInstallationsApiProvider
147
+ .get ()
148
+ .getId ()
149
+ .addOnFailureListener (
150
+ lightweightExecutor ,
151
+ handleTaskFailure (ErrorMessages .AUTHENTICATION_ERROR , AUTHENTICATION_FAILURE ))
152
+ .addOnSuccessListener (
153
+ lightweightExecutor ,
154
+ fid ->
155
+ getForegroundActivityAndOpenSignInFlow (fid )
156
+ .addOnFailureListener (
157
+ lightweightExecutor ,
158
+ handleTaskFailure (ErrorMessages .UNKNOWN_ERROR , UNKNOWN )));
159
+ return signInTaskCompletionSource ;
160
+ });
182
161
}
183
162
184
163
private Task <Void > getForegroundActivityAndOpenSignInFlow (String fid ) {
185
164
return lifecycleNotifier .consumeForegroundActivity (
186
165
activity -> {
187
- // Launch the intent outside of the synchronized block because we don't need to wait
188
- // for the lock, and we don't want to risk the activity leaving the foreground in
189
- // the meantime.
190
166
openSignInFlowInBrowser (fid , activity );
191
- // This synchronized block is required by the @GuardedBy annotation, but is not
192
- // practically required in this case because the only reads of this variable are on
193
- // the main thread, which this callback is also running on.
194
- synchronized (signInTaskLock ) {
195
- hasBeenSentToBrowserForCurrentTask = true ;
196
- }
167
+ hasBeenSentToBrowserForCurrentTask = true ;
197
168
});
198
169
}
199
170
200
171
private OnFailureListener handleTaskFailure (String message , Status status ) {
201
172
return e -> {
202
173
LogWrapper .e (TAG , message , e );
203
- setSignInTaskCompletionError (new FirebaseAppDistributionException (message , status , e ));
174
+ signInTaskCompletionSourceCache .setException (
175
+ new FirebaseAppDistributionException (message , status , e ));
204
176
};
205
177
}
206
178
207
- private boolean awaitingResultFromBrowser () {
208
- synchronized (signInTaskLock ) {
209
- return signInTaskCompletionSource != null
210
- && !signInTaskCompletionSource .getTask ().isComplete ()
211
- && hasBeenSentToBrowserForCurrentTask ;
212
- }
213
- }
214
-
215
- private void setSignInTaskCompletionError (FirebaseAppDistributionException e ) {
216
- synchronized (signInTaskLock ) {
217
- safeSetTaskException (signInTaskCompletionSource , e );
218
- }
219
- }
220
-
221
- private void setSuccessfulSignInResult () {
222
- synchronized (signInTaskLock ) {
223
- safeSetTaskResult (signInTaskCompletionSource , null );
224
- }
225
- }
226
-
227
179
private static String getApplicationName (Context context ) {
228
180
try {
229
181
return context .getApplicationInfo ().loadLabel (context .getPackageManager ()).toString ();
0 commit comments