2121import androidx .annotation .VisibleForTesting ;
2222import com .google .android .gms .tasks .Continuation ;
2323import com .google .android .gms .tasks .Task ;
24+ import com .google .android .gms .tasks .TaskCompletionSource ;
2425import com .google .android .gms .tasks .Tasks ;
2526import com .google .firebase .FirebaseApp ;
2627import com .google .firebase .FirebaseException ;
3536import com .google .firebase .inject .Provider ;
3637import java .util .ArrayList ;
3738import java .util .List ;
39+ import java .util .concurrent .ExecutorService ;
40+ import java .util .concurrent .Executors ;
3841
3942public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
4043
@@ -46,6 +49,8 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
4649 private final List <AppCheckListener > appCheckListenerList ;
4750 private final StorageHelper storageHelper ;
4851 private final TokenRefreshManager tokenRefreshManager ;
52+ private final ExecutorService backgroundExecutor ;
53+ private final Task <Void > retrieveStoredTokenTask ;
4954 private final Clock clock ;
5055
5156 private AppCheckProviderFactory appCheckProviderFactory ;
@@ -55,6 +60,17 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
5560 public DefaultFirebaseAppCheck (
5661 @ NonNull FirebaseApp firebaseApp ,
5762 @ 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 ) {
5874 checkNotNull (firebaseApp );
5975 checkNotNull (heartBeatController );
6076 this .firebaseApp = firebaseApp ;
@@ -65,8 +81,22 @@ public DefaultFirebaseAppCheck(
6581 new StorageHelper (firebaseApp .getApplicationContext (), firebaseApp .getPersistenceKey ());
6682 this .tokenRefreshManager =
6783 new TokenRefreshManager (firebaseApp .getApplicationContext (), /* firebaseAppCheck= */ this );
84+ this .backgroundExecutor = backgroundExecutor ;
85+ this .retrieveStoredTokenTask = retrieveStoredAppCheckTokenInBackground (backgroundExecutor );
6886 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 ();
70100 }
71101
72102 @ Override
@@ -146,44 +176,50 @@ public void removeAppCheckListener(@NonNull AppCheckListener listener) {
146176 @ NonNull
147177 @ Override
148178 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+ });
175208 }
176209
177210 @ NonNull
178211 @ Override
179212 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+ });
187223 }
188224
189225 /** Fetches an {@link AppCheckToken} via the installed {@link AppCheckProvider}. */
@@ -227,7 +263,7 @@ void setCachedToken(@NonNull AppCheckToken token) {
227263 * well as the in-memory cached {@link AppCheckToken}.
228264 */
229265 private void updateStoredToken (@ NonNull AppCheckToken token ) {
230- storageHelper .saveAppCheckToken (token );
266+ backgroundExecutor . execute (() -> storageHelper .saveAppCheckToken (token ) );
231267 setCachedToken (token );
232268
233269 tokenRefreshManager .maybeScheduleTokenRefresh (token );
0 commit comments