21
21
import androidx .annotation .VisibleForTesting ;
22
22
import com .google .android .gms .tasks .Continuation ;
23
23
import com .google .android .gms .tasks .Task ;
24
+ import com .google .android .gms .tasks .TaskCompletionSource ;
24
25
import com .google .android .gms .tasks .Tasks ;
25
26
import com .google .firebase .FirebaseApp ;
26
27
import com .google .firebase .FirebaseException ;
35
36
import com .google .firebase .inject .Provider ;
36
37
import java .util .ArrayList ;
37
38
import java .util .List ;
39
+ import java .util .concurrent .ExecutorService ;
40
+ import java .util .concurrent .Executors ;
38
41
39
42
public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
40
43
@@ -46,6 +49,8 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
46
49
private final List <AppCheckListener > appCheckListenerList ;
47
50
private final StorageHelper storageHelper ;
48
51
private final TokenRefreshManager tokenRefreshManager ;
52
+ private final ExecutorService backgroundExecutor ;
53
+ private final Task <Void > retrieveStoredTokenTask ;
49
54
private final Clock clock ;
50
55
51
56
private AppCheckProviderFactory appCheckProviderFactory ;
@@ -55,6 +60,17 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
55
60
public DefaultFirebaseAppCheck (
56
61
@ NonNull FirebaseApp firebaseApp ,
57
62
@ NonNull Provider <HeartBeatController > heartBeatController ) {
63
+ this (
64
+ checkNotNull (firebaseApp ),
65
+ checkNotNull (heartBeatController ),
66
+ Executors .newCachedThreadPool ());
67
+ }
68
+
69
+ @ VisibleForTesting
70
+ DefaultFirebaseAppCheck (
71
+ @ NonNull FirebaseApp firebaseApp ,
72
+ @ NonNull Provider <HeartBeatController > heartBeatController ,
73
+ @ NonNull ExecutorService backgroundExecutor ) {
58
74
checkNotNull (firebaseApp );
59
75
checkNotNull (heartBeatController );
60
76
this .firebaseApp = firebaseApp ;
@@ -65,8 +81,22 @@ public DefaultFirebaseAppCheck(
65
81
new StorageHelper (firebaseApp .getApplicationContext (), firebaseApp .getPersistenceKey ());
66
82
this .tokenRefreshManager =
67
83
new TokenRefreshManager (firebaseApp .getApplicationContext (), /* firebaseAppCheck= */ this );
84
+ this .backgroundExecutor = backgroundExecutor ;
85
+ this .retrieveStoredTokenTask = retrieveStoredAppCheckTokenInBackground (backgroundExecutor );
68
86
this .clock = new Clock .DefaultClock ();
69
- setCachedToken (storageHelper .retrieveAppCheckToken ());
87
+ }
88
+
89
+ private Task <Void > retrieveStoredAppCheckTokenInBackground (@ NonNull ExecutorService executor ) {
90
+ TaskCompletionSource <Void > taskCompletionSource = new TaskCompletionSource <>();
91
+ executor .execute (
92
+ () -> {
93
+ AppCheckToken token = storageHelper .retrieveAppCheckToken ();
94
+ if (token != null ) {
95
+ setCachedToken (token );
96
+ }
97
+ taskCompletionSource .setResult (null );
98
+ });
99
+ return taskCompletionSource .getTask ();
70
100
}
71
101
72
102
@ Override
@@ -146,44 +176,50 @@ public void removeAppCheckListener(@NonNull AppCheckListener listener) {
146
176
@ NonNull
147
177
@ Override
148
178
public Task <AppCheckTokenResult > getToken (boolean forceRefresh ) {
149
- if (!forceRefresh && hasValidToken ()) {
150
- return Tasks .forResult (DefaultAppCheckTokenResult .constructFromAppCheckToken (cachedToken ));
151
- }
152
- if (appCheckProvider == null ) {
153
- return Tasks .forResult (
154
- DefaultAppCheckTokenResult .constructFromError (
155
- new FirebaseException ("No AppCheckProvider installed." )));
156
- }
157
- // TODO: Cache the in-flight task.
158
- return fetchTokenFromProvider ()
159
- .continueWithTask (
160
- new Continuation <AppCheckToken , Task <AppCheckTokenResult >>() {
161
- @ Override
162
- public Task <AppCheckTokenResult > then (@ NonNull Task <AppCheckToken > task ) {
163
- if (task .isSuccessful ()) {
164
- return Tasks .forResult (
165
- DefaultAppCheckTokenResult .constructFromAppCheckToken (task .getResult ()));
166
- }
167
- // If the token exchange failed, return a dummy token for integrators to attach in
168
- // their headers.
169
- return Tasks .forResult (
170
- DefaultAppCheckTokenResult .constructFromError (
171
- new FirebaseException (
172
- task .getException ().getMessage (), task .getException ())));
173
- }
174
- });
179
+ return retrieveStoredTokenTask .continueWithTask (
180
+ unused -> {
181
+ if (!forceRefresh && hasValidToken ()) {
182
+ return Tasks .forResult (
183
+ DefaultAppCheckTokenResult .constructFromAppCheckToken (cachedToken ));
184
+ }
185
+ if (appCheckProvider == null ) {
186
+ return Tasks .forResult (
187
+ DefaultAppCheckTokenResult .constructFromError (
188
+ new FirebaseException ("No AppCheckProvider installed." )));
189
+ }
190
+ // TODO: Cache the in-flight task.
191
+ return fetchTokenFromProvider ()
192
+ .continueWithTask (
193
+ appCheckTokenTask -> {
194
+ if (appCheckTokenTask .isSuccessful ()) {
195
+ return Tasks .forResult (
196
+ DefaultAppCheckTokenResult .constructFromAppCheckToken (
197
+ appCheckTokenTask .getResult ()));
198
+ }
199
+ // If the token exchange failed, return a dummy token for integrators to attach
200
+ // in their headers.
201
+ return Tasks .forResult (
202
+ DefaultAppCheckTokenResult .constructFromError (
203
+ new FirebaseException (
204
+ appCheckTokenTask .getException ().getMessage (),
205
+ appCheckTokenTask .getException ())));
206
+ });
207
+ });
175
208
}
176
209
177
210
@ NonNull
178
211
@ Override
179
212
public Task <AppCheckToken > getAppCheckToken (boolean forceRefresh ) {
180
- if (!forceRefresh && hasValidToken ()) {
181
- return Tasks .forResult (cachedToken );
182
- }
183
- if (appCheckProvider == null ) {
184
- return Tasks .forException (new FirebaseException ("No AppCheckProvider installed." ));
185
- }
186
- return fetchTokenFromProvider ();
213
+ return retrieveStoredTokenTask .continueWithTask (
214
+ unused -> {
215
+ if (!forceRefresh && hasValidToken ()) {
216
+ return Tasks .forResult (cachedToken );
217
+ }
218
+ if (appCheckProvider == null ) {
219
+ return Tasks .forException (new FirebaseException ("No AppCheckProvider installed." ));
220
+ }
221
+ return fetchTokenFromProvider ();
222
+ });
187
223
}
188
224
189
225
/** Fetches an {@link AppCheckToken} via the installed {@link AppCheckProvider}. */
@@ -227,7 +263,7 @@ void setCachedToken(@NonNull AppCheckToken token) {
227
263
* well as the in-memory cached {@link AppCheckToken}.
228
264
*/
229
265
private void updateStoredToken (@ NonNull AppCheckToken token ) {
230
- storageHelper .saveAppCheckToken (token );
266
+ backgroundExecutor . execute (() -> storageHelper .saveAppCheckToken (token ) );
231
267
setCachedToken (token );
232
268
233
269
tokenRefreshManager .maybeScheduleTokenRefresh (token );
0 commit comments