From f69ea33303d33affc2847361f65c9ba0461e6bd8 Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Mon, 21 Jul 2025 10:11:37 -0400 Subject: [PATCH 1/3] fix: prevent flag change listener from being invoked with null flag key --- .../com/launchdarkly/sdk/android/ContextDataManager.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ContextDataManager.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ContextDataManager.java index f466b38d..48118ee9 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ContextDataManager.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/ContextDataManager.java @@ -314,6 +314,13 @@ private void notifyFlagListeners(Collection updatedFlagKeys) { } final Map> listenersToCall = new HashMap<>(); for (String flagKey: updatedFlagKeys) { + + // TODO: SDK-1040. This conditional is a short term mitigation for a rare issue where a + // null flag key is encountered. + if (flagKey == null) { + continue; + } + Set flagListeners = listeners.get(flagKey); if (flagListeners != null && !flagListeners.isEmpty()) { listenersToCall.put(flagKey, flagListeners); From 72497dc93365ba9610634a9ebf808a21faf6787c Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Mon, 21 Jul 2025 13:03:23 -0400 Subject: [PATCH 2/3] fixing flaky test --- .../sdk/android/LDClientPluginsTest.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java index 2510dbcf..6723bb29 100644 --- a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java +++ b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java @@ -1,6 +1,7 @@ package com.launchdarkly.sdk.android; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import android.app.Application; @@ -99,15 +100,19 @@ public void pluginRegisterCalledForEachClientEnvironment() throws Exception { assertEquals(2, testHook.beforeEvaluationCalls.size()); assertEquals(2, testHook.afterEvaluationCalls.size()); - EnvironmentMetadata environmentMetadata1 = (EnvironmentMetadata) testPlugin.getHooksCalls.get(1).get("environmentMetadata"); - assertEquals(mobileKey, environmentMetadata1.getCredential()); - assertEquals(environmentMetadata1, testPlugin.getHooksCalls.get(1).get("environmentMetadata")); - assertEquals("AndroidClient", environmentMetadata1.getSdkMetadata().getName()); - - assertEquals(LDClient.getForMobileKey("secondaryEnvironment"), testPlugin.registerCalls.get(0).get("client")); - EnvironmentMetadata environmentMetadata2 = (EnvironmentMetadata) testPlugin.registerCalls.get(0).get("environmentMetadata"); - assertEquals(secondaryMobileKey, environmentMetadata2.getCredential()); - assertEquals("AndroidClient", environmentMetadata2.getSdkMetadata().getName()); + for (Map hookCall: testPlugin.getHooksCalls) { + LDClient instance = (LDClient) hookCall.get("client"); + + if (instance.equals(LDClient.get())) { + EnvironmentMetadata environmentMetadata = (EnvironmentMetadata) hookCall.get("environmentMetadata"); + assertEquals(environmentMetadata.getCredential(), mobileKey); + } else if (instance.equals(LDClient.getForMobileKey("secondaryEnvironment"))) { + EnvironmentMetadata environmentMetadata = (EnvironmentMetadata) hookCall.get("environmentMetadata"); + assertEquals(environmentMetadata.getCredential(), secondaryMobileKey); + } else { + fail("Client instance was unexpected."); + } + } logging.assertNoWarningsLogged(); logging.assertNoErrorsLogged(); From 131483e0e5400b4bf9f174fb8a8cff2c7fb224b2 Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Mon, 21 Jul 2025 13:21:08 -0400 Subject: [PATCH 3/3] fixing flaky test --- .../com/launchdarkly/sdk/android/LDClientPluginsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java index 6723bb29..017c55e9 100644 --- a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java +++ b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java @@ -89,7 +89,7 @@ public void pluginRegisterCalledForEachClientEnvironment() throws Exception { .logAdapter(logging.logAdapter); LDConfig config = builder.build(); - try (LDClient ldClient = LDClient.init(application, config, ldContext, 1)) { + try (LDClient ldClient = LDClient.init(application, config, ldContext, 10)) { ldClient.boolVariation("test-flag", false); assertEquals(2, testPlugin.getHooksCalls.size()); assertEquals(2, testPlugin.registerCalls.size()); @@ -100,7 +100,7 @@ public void pluginRegisterCalledForEachClientEnvironment() throws Exception { assertEquals(2, testHook.beforeEvaluationCalls.size()); assertEquals(2, testHook.afterEvaluationCalls.size()); - for (Map hookCall: testPlugin.getHooksCalls) { + for (Map hookCall: testPlugin.registerCalls) { LDClient instance = (LDClient) hookCall.get("client"); if (instance.equals(LDClient.get())) {