@@ -72,7 +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 ;
75+ private final boolean useFDv2ModeResolution ;
7676 private volatile ConnectionMode currentFDv2Mode ;
7777
7878 // The DataSourceUpdateSinkImpl receives flag updates and status updates from the DataSource.
@@ -150,6 +150,7 @@ public void shutDown() {
150150 connectionInformation = new ConnectionInformationState ();
151151 readStoredConnectionState ();
152152 this .backgroundUpdatingDisabled = ldConfig .isDisableBackgroundPolling ();
153+ this .useFDv2ModeResolution = (dataSourceFactory instanceof FDv2DataSourceBuilder );
153154
154155 connectivityChangeListener = networkAvailable -> handleModeStateChange ();
155156 platformState .addConnectivityChangeListener (connectivityChangeListener );
@@ -190,13 +191,27 @@ private synchronized boolean updateDataSource(
190191 }
191192
192193 DataSource existingDataSource = currentDataSource .get ();
194+ boolean isFDv2ModeSwitch = false ;
193195
194- // FDv2 ModeAware data sources handle all state transitions (including
195- // offline/background) via mode resolution rather than teardown/rebuild.
196- if (!mustReinitializeDataSource && existingDataSource instanceof ModeAware ) {
197- resolveAndSwitchMode ((ModeAware ) existingDataSource );
198- onCompletion .onSuccess (null );
199- return false ;
196+ // FDv2 path: resolve mode and determine if a teardown/rebuild is needed.
197+ if (useFDv2ModeResolution && !mustReinitializeDataSource ) {
198+ ConnectionMode newMode = resolveMode ();
199+ if (newMode == currentFDv2Mode ) {
200+ onCompletion .onSuccess (null );
201+ return false ;
202+ }
203+ // CSFDV2 5.3.8: retain active data source if old and new modes have equivalent config.
204+ FDv2DataSourceBuilder fdv2Builder = (FDv2DataSourceBuilder ) dataSourceFactory ;
205+ ModeDefinition oldDef = fdv2Builder .getModeDefinition (currentFDv2Mode );
206+ ModeDefinition newDef = fdv2Builder .getModeDefinition (newMode );
207+ if (oldDef != null && oldDef == newDef ) {
208+ currentFDv2Mode = newMode ;
209+ onCompletion .onSuccess (null );
210+ return false ;
211+ }
212+ currentFDv2Mode = newMode ;
213+ isFDv2ModeSwitch = true ;
214+ mustReinitializeDataSource = true ;
200215 }
201216
202217 // FDv1 path: check whether the data source needs a full rebuild.
@@ -215,7 +230,12 @@ private synchronized boolean updateDataSource(
215230 boolean shouldStopExistingDataSource = true ,
216231 shouldStartDataSourceIfStopped = false ;
217232
218- if (forceOffline ) {
233+ if (useFDv2ModeResolution ) {
234+ // FDv2 mode resolution already accounts for offline/background states via
235+ // the ModeResolutionTable, so we always rebuild when the mode changed.
236+ shouldStopExistingDataSource = mustReinitializeDataSource ;
237+ shouldStartDataSourceIfStopped = true ;
238+ } else if (forceOffline ) {
219239 logger .debug ("Initialized in offline mode" );
220240 initialized = true ;
221241 dataSourceUpdateSink .setStatus (ConnectionInformation .ConnectionMode .SET_OFFLINE , null );
@@ -249,41 +269,32 @@ private synchronized boolean updateDataSource(
249269 previouslyInBackground .get (),
250270 transactionalDataStore
251271 );
252- DataSource dataSource = dataSourceFactory .build (clientContext );
253- currentDataSource .set (dataSource );
254- previouslyInBackground .set (Boolean .valueOf (inBackground ));
255272
256- if (dataSourceFactory instanceof FDv2DataSourceBuilder ) {
273+ if (useFDv2ModeResolution ) {
257274 FDv2DataSourceBuilder fdv2Builder = (FDv2DataSourceBuilder ) dataSourceFactory ;
258- resolvedModeTable = fdv2Builder . getResolvedModeTable ();
259- currentFDv2Mode = fdv2Builder .getStartingMode ( );
275+ // CONNMODE 2.0.1: mode switches only transition synchronizers, not initializers.
276+ fdv2Builder .setActiveMode ( currentFDv2Mode , ! isFDv2ModeSwitch );
260277 }
261278
279+ DataSource dataSource = dataSourceFactory .build (clientContext );
280+ currentDataSource .set (dataSource );
281+ previouslyInBackground .set (Boolean .valueOf (inBackground ));
282+
262283 dataSource .start (new Callback <Boolean >() {
263284 @ Override
264285 public void onSuccess (Boolean result ) {
265286 initialized = true ;
266- // passing the current connection mode since we don't want to change the mode, just trigger
267- // the logic to update the last connection success.
268287 updateConnectionInfoForSuccess (connectionInformation .getConnectionMode ());
269288 onCompletion .onSuccess (null );
270289 }
271290
272291 @ Override
273292 public void onError (Throwable error ) {
274- // passing the current connection mode since we don't want to change the mode, just trigger
275- // the logic to update the last connection failure.
276293 updateConnectionInfoForError (connectionInformation .getConnectionMode (), error );
277294 onCompletion .onSuccess (null );
278295 }
279296 });
280297
281- // If the app starts in the background, the builder creates the data source with
282- // STREAMING as the starting mode. Perform an initial mode resolution to correct this.
283- if (dataSource instanceof ModeAware ) {
284- resolveAndSwitchMode ((ModeAware ) dataSource );
285- }
286-
287298 return true ;
288299 }
289300
@@ -425,6 +436,13 @@ synchronized boolean startUp(@NonNull Callback<Void> onCompletion) {
425436 return false ;
426437 }
427438 initialized = false ;
439+
440+ if (useFDv2ModeResolution ) {
441+ currentFDv2Mode = resolveMode ();
442+ FDv2DataSourceBuilder fdv2Builder = (FDv2DataSourceBuilder ) dataSourceFactory ;
443+ fdv2Builder .setActiveMode (currentFDv2Mode , true );
444+ }
445+
428446 updateEventProcessor (forcedOffline .get (), platformState .isNetworkAvailable (), platformState .isForeground ());
429447 return updateDataSource (true , onCompletion );
430448 }
@@ -476,34 +494,18 @@ private void handleModeStateChange() {
476494 }
477495
478496 /**
479- * Resolves the current platform state to a ConnectionMode via the ModeResolutionTable,
480- * looks up the ResolvedModeDefinition from the resolved mode table, and calls
481- * switchMode() on the data source if the mode has changed.
497+ * Resolves the current platform state to a {@link ConnectionMode} via the
498+ * {@link ModeResolutionTable}.
482499 */
483- private void resolveAndSwitchMode (@ NonNull ModeAware modeAware ) {
484- Map <ConnectionMode , ResolvedModeDefinition > table = resolvedModeTable ;
485- if (table == null ) {
486- return ;
487- }
500+ private ConnectionMode resolveMode () {
488501 boolean forceOffline = forcedOffline .get ();
489502 boolean networkAvailable = platformState .isNetworkAvailable ();
490503 boolean foreground = platformState .isForeground ();
491504 ModeState state = new ModeState (
492505 foreground && !forceOffline ,
493506 networkAvailable && !forceOffline
494507 );
495- ConnectionMode newMode = ModeResolutionTable .MOBILE .resolve (state );
496- if (newMode == currentFDv2Mode ) {
497- return ;
498- }
499- currentFDv2Mode = newMode ;
500- ResolvedModeDefinition def = table .get (newMode );
501- if (def == null ) {
502- logger .warn ("No resolved definition for mode {}; skipping switchMode" , newMode );
503- return ;
504- }
505- logger .debug ("Switching FDv2 mode to {}" , newMode );
506- modeAware .switchMode (def );
508+ return ModeResolutionTable .MOBILE .resolve (state );
507509 }
508510
509511 synchronized ConnectionInformation getConnectionInformation () {
0 commit comments