21
21
import androidx .annotation .VisibleForTesting ;
22
22
import com .google .android .gms .tasks .Task ;
23
23
import com .google .android .gms .tasks .Tasks ;
24
+ import com .google .firebase .remoteconfig .ConfigUpdate ;
24
25
import com .google .firebase .remoteconfig .ConfigUpdateListener ;
25
26
import com .google .firebase .remoteconfig .FirebaseRemoteConfigClientException ;
26
27
import com .google .firebase .remoteconfig .FirebaseRemoteConfigException ;
30
31
import java .io .InputStream ;
31
32
import java .io .InputStreamReader ;
32
33
import java .net .HttpURLConnection ;
34
+ import java .util .HashSet ;
33
35
import java .util .Random ;
34
36
import java .util .Set ;
35
37
import java .util .concurrent .ScheduledExecutorService ;
@@ -49,18 +51,21 @@ public class ConfigAutoFetch {
49
51
private final HttpURLConnection httpURLConnection ;
50
52
51
53
private final ConfigFetchHandler configFetchHandler ;
54
+ private final ConfigCacheClient activatedCache ;
52
55
private final ConfigUpdateListener retryCallback ;
53
56
private final ScheduledExecutorService scheduledExecutorService ;
54
57
private final Random random ;
55
58
56
59
public ConfigAutoFetch (
57
60
HttpURLConnection httpURLConnection ,
58
61
ConfigFetchHandler configFetchHandler ,
62
+ ConfigCacheClient activatedCache ,
59
63
Set <ConfigUpdateListener > eventListeners ,
60
64
ConfigUpdateListener retryCallback ,
61
65
ScheduledExecutorService scheduledExecutorService ) {
62
66
this .httpURLConnection = httpURLConnection ;
63
67
this .configFetchHandler = configFetchHandler ;
68
+ this .activatedCache = activatedCache ;
64
69
this .eventListeners = eventListeners ;
65
70
this .retryCallback = retryCallback ;
66
71
this .scheduledExecutorService = scheduledExecutorService ;
@@ -73,9 +78,9 @@ private synchronized void propagateErrors(FirebaseRemoteConfigException exceptio
73
78
}
74
79
}
75
80
76
- private synchronized void executeAllListenerCallbacks () {
81
+ private synchronized void executeAllListenerCallbacks (ConfigUpdate configUpdate ) {
77
82
for (ConfigUpdateListener listener : eventListeners ) {
78
- listener .onEvent ( );
83
+ listener .onUpdate ( configUpdate );
79
84
}
80
85
}
81
86
@@ -111,7 +116,8 @@ public void listenForNotifications() {
111
116
}
112
117
}
113
118
114
- retryCallback .onEvent ();
119
+ // TODO: Factor ConfigUpdateListener out of internal retry logic.
120
+ retryCallback .onUpdate (ConfigUpdate .create (new HashSet <>()));
115
121
scheduledExecutorService .shutdownNow ();
116
122
try {
117
123
scheduledExecutorService .awaitTermination (3L , TimeUnit .SECONDS );
@@ -194,35 +200,78 @@ public void run() {
194
200
}
195
201
196
202
@ VisibleForTesting
197
- public synchronized void fetchLatestConfig (int remainingAttempts , long targetVersion ) {
198
- int currentAttempts = remainingAttempts - 1 ;
203
+ public synchronized Task <Void > fetchLatestConfig (int remainingAttempts , long targetVersion ) {
204
+ int remainingAttemptsAfterFetch = remainingAttempts - 1 ;
205
+ int currentAttemptNumber = MAXIMUM_FETCH_ATTEMPTS - remainingAttemptsAfterFetch ;
199
206
200
- // fetchAttemptNumber is calculated by subtracting current attempts from the max number of
201
- // possible attempts.
202
207
Task <ConfigFetchHandler .FetchResponse > fetchTask =
203
208
configFetchHandler .fetchNowWithTypeAndAttemptNumber (
204
- ConfigFetchHandler .FetchType .REALTIME , MAXIMUM_FETCH_ATTEMPTS - currentAttempts );
205
- fetchTask .onSuccessTask (
206
- (fetchResponse ) -> {
207
- long newTemplateVersion = 0 ;
208
- if (fetchResponse .getFetchedConfigs () != null ) {
209
- newTemplateVersion = fetchResponse .getFetchedConfigs ().getTemplateVersionNumber ();
210
- } else if (fetchResponse .getStatus ()
211
- == ConfigFetchHandler .FetchResponse .Status .BACKEND_HAS_NO_UPDATES ) {
212
- newTemplateVersion = targetVersion ;
213
- }
209
+ ConfigFetchHandler .FetchType .REALTIME , currentAttemptNumber );
210
+ Task <ConfigContainer > activatedConfigsTask = activatedCache .get ();
214
211
215
- if (newTemplateVersion >= targetVersion ) {
216
- executeAllListenerCallbacks ();
217
- } else {
218
- Log .d (
219
- TAG ,
220
- "Fetched template version is the same as SDK's current version."
221
- + " Retrying fetch." );
222
- // Continue fetching until template version number if greater then current.
223
- autoFetch (currentAttempts , targetVersion );
224
- }
225
- return Tasks .forResult (null );
226
- });
212
+ return Tasks .whenAllComplete (fetchTask , activatedConfigsTask )
213
+ .continueWithTask (
214
+ scheduledExecutorService ,
215
+ (listOfUnusedCompletedTasks ) -> {
216
+ if (!fetchTask .isSuccessful ()) {
217
+ return Tasks .forException (
218
+ new FirebaseRemoteConfigClientException (
219
+ "Failed to auto-fetch config update." , fetchTask .getException ()));
220
+ }
221
+
222
+ if (!activatedConfigsTask .isSuccessful ()) {
223
+ return Tasks .forException (
224
+ new FirebaseRemoteConfigClientException (
225
+ "Failed to get activated config for auto-fetch" ,
226
+ activatedConfigsTask .getException ()));
227
+ }
228
+
229
+ ConfigFetchHandler .FetchResponse fetchResponse = fetchTask .getResult ();
230
+ ConfigContainer activatedConfigs = activatedConfigsTask .getResult ();
231
+
232
+ if (!fetchResponseIsUpToDate (fetchResponse , targetVersion )) {
233
+ Log .d (
234
+ TAG ,
235
+ "Fetched template version is the same as SDK's current version."
236
+ + " Retrying fetch." );
237
+ // Continue fetching until template version number is greater then current.
238
+ autoFetch (remainingAttemptsAfterFetch , targetVersion );
239
+ return Tasks .forResult (null );
240
+ }
241
+
242
+ if (fetchResponse .getFetchedConfigs () == null ) {
243
+ Log .d (TAG , "The fetch succeeded, but the backend had no updates." );
244
+ return Tasks .forResult (null );
245
+ }
246
+
247
+ // Activate hasn't been called yet, so use an empty container for comparison.
248
+ // See ConfigCacheClient#get() for details on the async operation.
249
+ if (activatedConfigs == null ) {
250
+ activatedConfigs = ConfigContainer .newBuilder ().build ();
251
+ }
252
+
253
+ Set <String > updatedParams =
254
+ activatedConfigs .getChangedParams (fetchResponse .getFetchedConfigs ());
255
+ if (updatedParams .isEmpty ()) {
256
+ Log .d (TAG , "Config was fetched, but no params changed." );
257
+ return Tasks .forResult (null );
258
+ }
259
+
260
+ ConfigUpdate configUpdate = ConfigUpdate .create (updatedParams );
261
+ executeAllListenerCallbacks (configUpdate );
262
+ return Tasks .forResult (null );
263
+ });
264
+ }
265
+
266
+ private static Boolean fetchResponseIsUpToDate (
267
+ ConfigFetchHandler .FetchResponse response , long lastKnownVersion ) {
268
+ // If there's a config, make sure its version is >= the last known version.
269
+ if (response .getFetchedConfigs () != null ) {
270
+ return response .getFetchedConfigs ().getTemplateVersionNumber () >= lastKnownVersion ;
271
+ }
272
+
273
+ // If there isn't a config, return true if the backend had no update.
274
+ // Else, it returned an out of date config.
275
+ return response .getStatus () == ConfigFetchHandler .FetchResponse .Status .BACKEND_HAS_NO_UPDATES ;
227
276
}
228
277
}
0 commit comments