27
27
import com .google .firebase .remoteconfig .FirebaseRemoteConfigValue ;
28
28
import com .google .firebase .remoteconfig .RemoteConfigComponent ;
29
29
import java .util .Map ;
30
- import java .util .Random ;
31
30
import java .util .concurrent .ConcurrentHashMap ;
32
31
import java .util .concurrent .Executor ;
33
32
import java .util .concurrent .LinkedBlockingQueue ;
33
+ import java .util .concurrent .ThreadLocalRandom ;
34
34
import java .util .concurrent .ThreadPoolExecutor ;
35
35
import java .util .concurrent .TimeUnit ;
36
36
@@ -50,12 +50,12 @@ public class RemoteConfigManager {
50
50
TimeUnit .HOURS .toMillis (12 );
51
51
private static final long FETCH_NEVER_HAPPENED_TIMESTAMP_MS = 0 ;
52
52
private static final long MIN_APP_START_CONFIG_FETCH_DELAY_MS = 5000 ;
53
- private static final int RANDOM_APP_START_CONFIG_FETCH_DELAY_MS = 25000 ;
53
+ private static final long RANDOM_APP_START_CONFIG_FETCH_DELAY_MS = 25000 ;
54
54
55
55
private final ConcurrentHashMap <String , FirebaseRemoteConfigValue > allRcConfigMap ;
56
56
private final Executor executor ;
57
- private final long appStartTime ;
58
- private final long appStartConfigFetchDelay ;
57
+ private final long appStartTimeInMs ;
58
+ private final long appStartConfigFetchDelayInMs ;
59
59
60
60
private long firebaseRemoteConfigLastFetchTimestampMs = FETCH_NEVER_HAPPENED_TIMESTAMP_MS ;
61
61
@@ -79,20 +79,22 @@ private RemoteConfigManager() {
79
79
executor ,
80
80
firebaseRemoteConfig ,
81
81
MIN_APP_START_CONFIG_FETCH_DELAY_MS
82
- + new Random ().nextInt (RANDOM_APP_START_CONFIG_FETCH_DELAY_MS ));
82
+ + ThreadLocalRandom . current ().nextLong (RANDOM_APP_START_CONFIG_FETCH_DELAY_MS ));
83
83
}
84
84
85
85
@ VisibleForTesting
86
86
RemoteConfigManager (
87
- Executor executor , FirebaseRemoteConfig firebaseRemoteConfig , long fetchDelay ) {
87
+ Executor executor ,
88
+ FirebaseRemoteConfig firebaseRemoteConfig ,
89
+ long appStartConfigFetchDelayInMs ) {
88
90
this .executor = executor ;
89
91
this .firebaseRemoteConfig = firebaseRemoteConfig ;
90
92
this .allRcConfigMap =
91
93
firebaseRemoteConfig == null
92
94
? new ConcurrentHashMap <>()
93
95
: new ConcurrentHashMap <>(firebaseRemoteConfig .getAll ());
94
- this .appStartTime = getCurrentSystemTimeMillis ();
95
- this .appStartConfigFetchDelay = fetchDelay ;
96
+ this .appStartTimeInMs = getCurrentSystemTimeMillis ();
97
+ this .appStartConfigFetchDelayInMs = appStartConfigFetchDelayInMs ;
96
98
}
97
99
98
100
/** Gets the singleton instance. */
@@ -313,7 +315,7 @@ private void triggerRemoteConfigFetchIfNecessary() {
313
315
if (allRcConfigMap .isEmpty ()) { // Initial fetch.
314
316
syncConfigValues (firebaseRemoteConfig .getAll ());
315
317
}
316
- if (passesRandomAppStartDelay () && shouldFetchAndActivateRemoteConfigValues ()) {
318
+ if (shouldFetchAndActivateRemoteConfigValues ()) {
317
319
triggerFirebaseRemoteConfigFetchAndActivateOnSuccessfulFetch ();
318
320
}
319
321
}
@@ -360,22 +362,34 @@ public boolean isFirebaseRemoteConfigAvailable() {
360
362
return firebaseRemoteConfig != null ;
361
363
}
362
364
365
+ /** Returns true if a RC fetch should be made, false otherwise. */
366
+ private boolean shouldFetchAndActivateRemoteConfigValues () {
367
+ long currentTimeInMs = getCurrentSystemTimeMillis ();
368
+ return hasAppStartConfigFetchDelayElapsed (currentTimeInMs )
369
+ && hasLastFetchBecomeStale (currentTimeInMs );
370
+ }
371
+
372
+ /**
373
+ * Delay fetch by some random time since app start. This is to prevent b/187985523.
374
+ *
375
+ * @return true if the random delay has elapsed, false otherwise
376
+ */
377
+ private boolean hasAppStartConfigFetchDelayElapsed (long currentTimeInMs ) {
378
+ return (currentTimeInMs - appStartTimeInMs ) >= appStartConfigFetchDelayInMs ;
379
+ }
380
+
363
381
// We want to fetch once when the app starts and every 12 hours after that.
364
382
// The reason we maintain our own timestamps and do not use FRC's is because FRC only updates
365
383
// the last successful fetch timestamp AFTER successfully fetching - which might mean that
366
384
// we could potentially fire off multiple fetch requests. This protects against that because
367
385
// we update the timestamp before a successful fetch and reset it back if the fetch was
368
386
// unsuccessful, making sure that a fetch is triggered again.
369
387
// TODO(b/132369190): This shouldn't be needed once the feature is implemented in FRC.
370
- private boolean shouldFetchAndActivateRemoteConfigValues ( ) {
371
- return (getCurrentSystemTimeMillis () - firebaseRemoteConfigLastFetchTimestampMs )
388
+ private boolean hasLastFetchBecomeStale ( long currentTimeInMs ) {
389
+ return (currentTimeInMs - firebaseRemoteConfigLastFetchTimestampMs )
372
390
> TIME_AFTER_WHICH_A_FETCH_IS_CONSIDERED_STALE_MS ;
373
391
}
374
392
375
- private boolean passesRandomAppStartDelay () {
376
- return (getCurrentSystemTimeMillis () - appStartTime ) >= appStartConfigFetchDelay ;
377
- }
378
-
379
393
/** Gets the version code of the Android app. */
380
394
@ VisibleForTesting
381
395
public static int getVersionCode (Context context ) {
0 commit comments