@@ -53,11 +53,11 @@ class ConnectivityManager {
5353 private final ClientContext baseClientContext ;
5454 private final PlatformState platformState ;
5555 private final ComponentConfigurer <DataSource > dataSourceFactory ;
56- private final TransactionalDataStore transactionalDataStore ;
5756 private final DataSourceUpdateSink dataSourceUpdateSink ;
5857 private final ConnectionInformationState connectionInformation ;
5958 private final PersistentDataStoreWrapper .PerEnvironmentData environmentStore ;
6059 private final EventProcessor eventProcessor ;
60+ private final TransactionalDataStore transactionalDataStore ;
6161 private final PlatformState .ForegroundChangeListener foregroundListener ;
6262 private final PlatformState .ConnectivityChangeListener connectivityChangeListener ;
6363 private final TaskExecutor taskExecutor ;
@@ -72,6 +72,7 @@ class ConnectivityManager {
7272 private final AtomicReference <Boolean > previouslyInBackground = new AtomicReference <>();
7373 private final LDLogger logger ;
7474 private volatile boolean initialized = false ;
75+ private volatile Map <ConnectionMode , ResolvedModeDefinition > resolvedModeTable ;
7576 private volatile ConnectionMode currentFDv2Mode ;
7677
7778 // The DataSourceUpdateSinkImpl receives flag updates and status updates from the DataSource.
@@ -134,11 +135,11 @@ public void shutDown() {
134135 ) {
135136 this .baseClientContext = clientContext ;
136137 this .dataSourceFactory = dataSourceFactory ;
137- this .transactionalDataStore = contextDataManager ;
138138 this .dataSourceUpdateSink = new DataSourceUpdateSinkImpl (contextDataManager );
139139 this .platformState = ClientContextImpl .get (clientContext ).getPlatformState ();
140140 this .eventProcessor = eventProcessor ;
141141 this .environmentStore = environmentStore ;
142+ this .transactionalDataStore = contextDataManager ;
142143 this .taskExecutor = ClientContextImpl .get (clientContext ).getTaskExecutor ();
143144 this .logger = clientContext .getBaseLogger ();
144145
@@ -153,7 +154,7 @@ public void shutDown() {
153154 connectivityChangeListener = networkAvailable -> {
154155 DataSource dataSource = currentDataSource .get ();
155156 if (dataSource instanceof ModeAware ) {
156- eventProcessor .setOffline (forcedOffline . get () || !networkAvailable );
157+ eventProcessor .setOffline (!networkAvailable );
157158 resolveAndSwitchMode ((ModeAware ) dataSource );
158159 } else {
159160 updateDataSource (false , LDUtil .noOpCallback ());
@@ -244,15 +245,21 @@ private synchronized boolean updateDataSource(
244245 ClientContext clientContext = ClientContextImpl .forDataSource (
245246 baseClientContext ,
246247 dataSourceUpdateSink ,
247- transactionalDataStore ,
248248 context ,
249249 inBackground ,
250- previouslyInBackground .get ()
250+ previouslyInBackground .get (),
251+ transactionalDataStore
251252 );
252253 DataSource dataSource = dataSourceFactory .build (clientContext );
253254 currentDataSource .set (dataSource );
254255 previouslyInBackground .set (Boolean .valueOf (inBackground ));
255256
257+ if (dataSourceFactory instanceof FDv2DataSourceBuilder ) {
258+ FDv2DataSourceBuilder fdv2Builder = (FDv2DataSourceBuilder ) dataSourceFactory ;
259+ resolvedModeTable = fdv2Builder .getResolvedModeTable ();
260+ currentFDv2Mode = fdv2Builder .getStartingMode ();
261+ }
262+
256263 dataSource .start (new Callback <Boolean >() {
257264 @ Override
258265 public void onSuccess (Boolean result ) {
@@ -272,10 +279,9 @@ public void onError(Throwable error) {
272279 }
273280 });
274281
275- // Resolve the initial mode after start() so that switchMode() can safely replace
276- // the source manager without conflicting with the start() task submission .
282+ // If the app starts in the background, the builder creates the data source with
283+ // STREAMING as the starting mode. Perform an initial mode resolution to correct this .
277284 if (dataSource instanceof ModeAware ) {
278- currentFDv2Mode = ConnectionMode .STREAMING ;
279285 resolveAndSwitchMode ((ModeAware ) dataSource );
280286 }
281287
@@ -423,31 +429,6 @@ synchronized boolean startUp(@NonNull Callback<Void> onCompletion) {
423429 return updateDataSource (true , onCompletion );
424430 }
425431
426- /**
427- * Resolves the current platform state to a {@link ConnectionMode} using the mode resolution
428- * table, and calls {@link ModeAware#switchMode} if the resolved mode differs from the current
429- * mode. This replaces the legacy teardown/rebuild cycle for FDv2 data sources.
430- */
431- private void resolveAndSwitchMode (ModeAware modeAware ) {
432- ConnectionMode resolvedMode ;
433- if (forcedOffline .get ()) {
434- resolvedMode = ConnectionMode .OFFLINE ;
435- } else {
436- ModeState state = new ModeState (
437- platformState .isForeground (),
438- platformState .isNetworkAvailable ()
439- );
440- resolvedMode = ModeResolutionTable .MOBILE .resolve (state );
441- }
442-
443- ConnectionMode previousMode = currentFDv2Mode ;
444- if (previousMode != resolvedMode ) {
445- logger .debug ("Switching FDv2 data source mode: {} -> {}" , previousMode , resolvedMode );
446- currentFDv2Mode = resolvedMode ;
447- modeAware .switchMode (resolvedMode );
448- }
449- }
450-
451432 /**
452433 * Permanently stops data updating for the current client instance. We call this if the client
453434 * is being closed, or if we receive an error that indicates the mobile key is invalid.
@@ -481,6 +462,37 @@ boolean isForcedOffline() {
481462 return forcedOffline .get ();
482463 }
483464
465+ /**
466+ * Resolves the current platform state to a ConnectionMode via the ModeResolutionTable,
467+ * looks up the ResolvedModeDefinition from the resolved mode table, and calls
468+ * switchMode() on the data source if the mode has changed.
469+ */
470+ private void resolveAndSwitchMode (@ NonNull ModeAware modeAware ) {
471+ Map <ConnectionMode , ResolvedModeDefinition > table = resolvedModeTable ;
472+ if (table == null ) {
473+ return ;
474+ }
475+ boolean forceOffline = forcedOffline .get ();
476+ boolean networkAvailable = platformState .isNetworkAvailable ();
477+ boolean foreground = platformState .isForeground ();
478+ ModeState state = new ModeState (
479+ foreground && !forceOffline ,
480+ networkAvailable && !forceOffline
481+ );
482+ ConnectionMode newMode = ModeResolutionTable .MOBILE .resolve (state );
483+ if (newMode == currentFDv2Mode ) {
484+ return ;
485+ }
486+ currentFDv2Mode = newMode ;
487+ ResolvedModeDefinition def = table .get (newMode );
488+ if (def == null ) {
489+ logger .warn ("No resolved definition for mode {}; skipping switchMode" , newMode );
490+ return ;
491+ }
492+ logger .debug ("Switching FDv2 mode to {}" , newMode );
493+ modeAware .switchMode (def );
494+ }
495+
484496 synchronized ConnectionInformation getConnectionInformation () {
485497 return connectionInformation ;
486498 }
0 commit comments