Skip to content

Commit 450b652

Browse files
committed
fix(telemetry): correct L3/L4 layout and remove B2 (MDM) from Android selection
- L3 is now FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY (welcome.salesforce.com via WD flag) - L4 is now FEATURE_LOGIN_SERVER_MY_DOMAIN (host ending in .my.salesforce.com) - L5 remains the custom/fallthrough bucket - My Domain detection now uses `.my.salesforce.com` host suffix instead of isPoolServer guard - B2 (FEATURE_BROWSER_LOGIN_MDM) stays defined in Features.java for future use but is never selected on Android — MDM on Android forces cert auth (different code path that never sets completedViaBrowserTab); isMdmForcedBrowserLogin() is kept with an explanatory comment pending a real signal - Android B-marker priority is now: B3 (admin) > B4 (force flag) > B1 (server auth config) - Tests updated accordingly (B2 test expects B1 fallthrough; L3/L4 tests use new mapping) Part of W-23240736
1 parent 38126ea commit 450b652

3 files changed

Lines changed: 90 additions & 61 deletions

File tree

libs/SalesforceSDK/src/com/salesforce/androidsdk/app/Features.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public class Features {
5757
// "Which login server type" — registered per-user on every auth-flow completion
5858
public static final String FEATURE_LOGIN_SERVER_PRODUCTION = "L1";
5959
public static final String FEATURE_LOGIN_SERVER_SANDBOX = "L2";
60-
public static final String FEATURE_LOGIN_SERVER_MY_DOMAIN = "L3";
61-
public static final String FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY = "L4";
60+
public static final String FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY = "L3";
61+
public static final String FEATURE_LOGIN_SERVER_MY_DOMAIN = "L4";
6262
public static final String FEATURE_LOGIN_SERVER_CUSTOM = "L5";
6363
}

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginActivity.kt

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -643,42 +643,47 @@ open class LoginActivity : FragmentActivity() {
643643
*/
644644
@VisibleForTesting
645645
internal fun selectLMarker(usedWelcomeDiscovery: Boolean, loginServerUrl: String): String = when {
646-
usedWelcomeDiscovery -> FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY
647-
LoginServerManager.PRODUCTION_LOGIN_URL == loginServerUrl -> FEATURE_LOGIN_SERVER_PRODUCTION
648-
LoginServerManager.SANDBOX_LOGIN_URL == loginServerUrl -> FEATURE_LOGIN_SERVER_SANDBOX
649-
!LoginServerManager.isPoolServer(loginServerUrl) &&
650-
loginServerUrl != LoginServerManager.WELCOME_LOGIN_URL -> FEATURE_LOGIN_SERVER_MY_DOMAIN
651-
else -> FEATURE_LOGIN_SERVER_CUSTOM
646+
usedWelcomeDiscovery -> FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY // L3
647+
LoginServerManager.PRODUCTION_LOGIN_URL == loginServerUrl -> FEATURE_LOGIN_SERVER_PRODUCTION // L1
648+
LoginServerManager.SANDBOX_LOGIN_URL == loginServerUrl -> FEATURE_LOGIN_SERVER_SANDBOX // L2
649+
loginServerUrl.toUri().host?.endsWith(".my.salesforce.com") == true -> FEATURE_LOGIN_SERVER_MY_DOMAIN // L4
650+
else -> FEATURE_LOGIN_SERVER_CUSTOM // L5
652651
}
653652

654653
/**
655654
* Selects the B-marker (browser login reason) for telemetry.
656-
* Returns exactly one of B1–B4 if browser login was used, or null if it was not.
657-
* Priority: B3 (admin) > B2 (MDM) > B4 (force flag) > B1 (server auth config)
655+
* Returns exactly one of B1, B3, or B4 if browser login was used, or null if it was not.
656+
* Priority: B3 (admin) > B4 (force flag) > B1 (server auth config)
657+
*
658+
* Note: B2 (MDM) is defined in [Features] but is never selected on Android. On Android,
659+
* MDM forces cert auth via a different code path that never sets [completedViaBrowserTab].
658660
*
659661
* @param completedViaBrowserTab Whether login completed via browser Custom Tab
660662
* @param completedViaAdminCustomTab Whether login completed via the "Login for Admin" Custom Tab
661-
* @param isMdmForced Whether MDM policy forced browser login
663+
* @param isMdmForced Unused on Android — reserved for future use (always pass false)
662664
* @param forceAdvancedAuth Whether the [SalesforceSDKManager.forceAdvancedAuthentication] flag is set
663665
* @return The B-marker feature code to register, or null if browser login was not used
664666
*/
665667
@VisibleForTesting
666668
internal fun selectBMarker(
667669
completedViaBrowserTab: Boolean,
668670
completedViaAdminCustomTab: Boolean,
669-
isMdmForced: Boolean,
671+
@Suppress("UNUSED_PARAMETER") isMdmForced: Boolean,
670672
forceAdvancedAuth: Boolean,
671673
): String? = when {
672674
!completedViaBrowserTab -> null
673675
completedViaAdminCustomTab -> FEATURE_BROWSER_LOGIN_FOR_ADMIN // B3
674-
isMdmForced -> FEATURE_BROWSER_LOGIN_MDM // B2
675676
forceAdvancedAuth -> FEATURE_BROWSER_LOGIN_FORCE_FLAG // B4
676677
else -> FEATURE_BROWSER_LOGIN_SERVER_AUTH_CONFIG // B1 fallthrough
677678
}
678679

679680
/**
680-
* Returns true when MDM policy has forced browser login.
681-
* MDM registers the global [Features.FEATURE_MDM] flag via [RuntimeConfig].
681+
* Reserved for future use — currently unused on Android.
682+
*
683+
* On Android, MDM forces cert-auth (a different code path that never sets
684+
* [completedViaBrowserTab]), so [Features.FEATURE_BROWSER_LOGIN_MDM] (B2) is never
685+
* selected. This helper is retained so the call site in [onAuthFlowSuccess] remains
686+
* forward-compatible once a real Android MDM-browser-login signal is identified.
682687
*/
683688
private fun isMdmForcedBrowserLogin(): Boolean =
684689
SalesforceSDKManager.getInstance().isGlobalFeatureRegistered(

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/app/BrowserLoginTelemetryTest.kt

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
3030
import androidx.test.filters.SmallTest
3131
import com.salesforce.androidsdk.app.Features.FEATURE_BROWSER_LOGIN_FOR_ADMIN
3232
import com.salesforce.androidsdk.app.Features.FEATURE_BROWSER_LOGIN_FORCE_FLAG
33-
import com.salesforce.androidsdk.app.Features.FEATURE_BROWSER_LOGIN_MDM
3433
import com.salesforce.androidsdk.app.Features.FEATURE_BROWSER_LOGIN_SERVER_AUTH_CONFIG
3534
import com.salesforce.androidsdk.app.Features.FEATURE_LOGIN_SERVER_CUSTOM
3635
import com.salesforce.androidsdk.app.Features.FEATURE_LOGIN_SERVER_MY_DOMAIN
@@ -56,6 +55,19 @@ import org.junit.runner.RunWith
5655
* which are `internal` helpers extracted for testability and annotated `@VisibleForTesting`.
5756
* We call them via a relaxed mockk and `callOriginal()` so the real logic executes without
5857
* needing a running Android Activity context.
58+
*
59+
* L-marker mapping:
60+
* L1 = Production server
61+
* L2 = Sandbox server
62+
* L3 = welcome.salesforce.com (Welcome Discovery flow)
63+
* L4 = My Domain (host ending in .my.salesforce.com)
64+
* L5 = Everything else (custom)
65+
*
66+
* B-marker mapping (Android):
67+
* B1 = Server auth config (fallthrough)
68+
* B2 = MDM — defined but never selected on Android (MDM forces cert auth, a different path)
69+
* B3 = Admin Custom Tab
70+
* B4 = Force-advanced-auth flag
5971
*/
6072
@RunWith(AndroidJUnit4::class)
6173
@SmallTest
@@ -88,7 +100,7 @@ class BrowserLoginTelemetryTest {
88100
fun test_givenBrowserLoginViaServerAuthConfig_whenSelectBMarker_thenB1Returned() {
89101
every { activity.selectBMarker(any(), any(), any(), any()) } answers { callOriginal() }
90102

91-
// B1: browser tab, not admin, not MDM, not force-flag
103+
// B1: browser tab, not admin, not force-flag (MDM ignored on Android)
92104
val result = activity.selectBMarker(
93105
completedViaBrowserTab = true,
94106
completedViaAdminCustomTab = false,
@@ -100,18 +112,20 @@ class BrowserLoginTelemetryTest {
100112
}
101113

102114
@Test
103-
fun test_givenBrowserLoginViaMDM_whenSelectBMarker_thenB2Returned() {
115+
fun test_givenBrowserLoginViaMDM_whenSelectBMarker_thenB1ReturnedNotB2() {
104116
every { activity.selectBMarker(any(), any(), any(), any()) } answers { callOriginal() }
105117

106-
// B2: browser tab, not admin, MDM forced
118+
// On Android, MDM forces cert auth (different code path — completedViaBrowserTab never
119+
// becomes true in that flow). When isMdmForced=true is passed, it is ignored and B1
120+
// is returned as the fallthrough because no real Android MDM-browser-login signal exists.
107121
val result = activity.selectBMarker(
108122
completedViaBrowserTab = true,
109123
completedViaAdminCustomTab = false,
110124
isMdmForced = true,
111125
forceAdvancedAuth = false,
112126
)
113-
assertEquals("MDM-forced browser login should yield B2",
114-
FEATURE_BROWSER_LOGIN_MDM, result)
127+
assertEquals("MDM flag is ignored on Android; should fall through to B1",
128+
FEATURE_BROWSER_LOGIN_SERVER_AUTH_CONFIG, result)
115129
}
116130

117131
@Test
@@ -133,7 +147,7 @@ class BrowserLoginTelemetryTest {
133147
fun test_givenBrowserLoginViaForceFlag_whenSelectBMarker_thenB4Returned() {
134148
every { activity.selectBMarker(any(), any(), any(), any()) } answers { callOriginal() }
135149

136-
// B4: browser tab, not admin, not MDM, force flag ON
150+
// B4: browser tab, not admin, force flag ON
137151
val result = activity.selectBMarker(
138152
completedViaBrowserTab = true,
139153
completedViaAdminCustomTab = false,
@@ -145,33 +159,33 @@ class BrowserLoginTelemetryTest {
145159
}
146160

147161
@Test
148-
fun test_givenAdminTabAndMdmAndForceFlag_whenSelectBMarker_thenB3Wins() {
162+
fun test_givenAdminTabAndForceFlag_whenSelectBMarker_thenB3Wins() {
149163
every { activity.selectBMarker(any(), any(), any(), any()) } answers { callOriginal() }
150164

151-
// Priority: B3 > B2 > B4 > B1
165+
// Priority on Android: B3 > B4 > B1
152166
val result = activity.selectBMarker(
153167
completedViaBrowserTab = true,
154168
completedViaAdminCustomTab = true,
155-
isMdmForced = true,
169+
isMdmForced = false,
156170
forceAdvancedAuth = true,
157171
)
158172
assertEquals("Admin tab should take highest priority (B3)",
159173
FEATURE_BROWSER_LOGIN_FOR_ADMIN, result)
160174
}
161175

162176
@Test
163-
fun test_givenMdmAndForceFlag_whenSelectBMarker_thenB2Wins() {
177+
fun test_givenForceFlagAndMdm_whenSelectBMarker_thenB4Wins() {
164178
every { activity.selectBMarker(any(), any(), any(), any()) } answers { callOriginal() }
165179

166-
// Priority: B2 > B4
180+
// On Android MDM is ignored; force flag wins over B1 fallthrough
167181
val result = activity.selectBMarker(
168182
completedViaBrowserTab = true,
169183
completedViaAdminCustomTab = false,
170184
isMdmForced = true,
171185
forceAdvancedAuth = true,
172186
)
173-
assertEquals("MDM should take priority over force flag (B2 > B4)",
174-
FEATURE_BROWSER_LOGIN_MDM, result)
187+
assertEquals("Force flag should win over ignored MDM signal (B4)",
188+
FEATURE_BROWSER_LOGIN_FORCE_FLAG, result)
175189
}
176190

177191
// endregion
@@ -202,49 +216,62 @@ class BrowserLoginTelemetryTest {
202216
}
203217

204218
@Test
205-
fun test_givenMyDomainServer_whenSelectLMarker_thenL3Returned() {
219+
fun test_givenWelcomeDiscovery_whenSelectLMarker_thenL3Returned() {
206220
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
207221

208-
// My Domain is a non-pool, non-WD URL
222+
// L3: Welcome Discovery flow — WD flag takes precedence regardless of the resolved URL
209223
val result = activity.selectLMarker(
210-
usedWelcomeDiscovery = false,
224+
usedWelcomeDiscovery = true,
211225
loginServerUrl = "https://myorg.my.salesforce.com",
212226
)
213-
assertEquals("My Domain login server should yield L3",
214-
FEATURE_LOGIN_SERVER_MY_DOMAIN, result)
227+
assertEquals("Welcome Discovery should yield L3",
228+
FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY, result)
215229
}
216230

217231
@Test
218-
fun test_givenWelcomeDiscovery_whenSelectLMarker_thenL4Returned() {
232+
fun test_givenWelcomeDiscoveryWithProductionUrl_whenSelectLMarker_thenL3Returned() {
219233
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
220234

221-
// WD flag takes precedence regardless of the resolved server URL
235+
// WD flag wins even when the resolved URL is production
222236
val result = activity.selectLMarker(
223237
usedWelcomeDiscovery = true,
224-
loginServerUrl = "https://myorg.my.salesforce.com",
238+
loginServerUrl = LoginServerManager.PRODUCTION_LOGIN_URL,
225239
)
226-
assertEquals("Welcome Discovery should yield L4",
240+
assertEquals("WD flag should override production URL and yield L3",
227241
FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY, result)
228242
}
229243

230244
@Test
231-
fun test_givenWelcomeDiscoveryWithProductionUrl_whenSelectLMarker_thenL4Returned() {
245+
fun test_givenMyDomainServer_whenSelectLMarker_thenL4Returned() {
232246
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
233247

234-
// WD flag wins even when the resolved URL is production
248+
// L4: host ends with .my.salesforce.com
235249
val result = activity.selectLMarker(
236-
usedWelcomeDiscovery = true,
237-
loginServerUrl = LoginServerManager.PRODUCTION_LOGIN_URL,
250+
usedWelcomeDiscovery = false,
251+
loginServerUrl = "https://myorg.my.salesforce.com",
238252
)
239-
assertEquals("WD flag should override production URL and yield L4",
240-
FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY, result)
253+
assertEquals("My Domain login server should yield L4",
254+
FEATURE_LOGIN_SERVER_MY_DOMAIN, result)
255+
}
256+
257+
@Test
258+
fun test_givenMyDomainSandboxServer_whenSelectLMarker_thenL4Returned() {
259+
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
260+
261+
// Sandbox My Domain also ends with .my.salesforce.com (L4, not L2, because URL != sandbox constant)
262+
val result = activity.selectLMarker(
263+
usedWelcomeDiscovery = false,
264+
loginServerUrl = "https://myorg.sandbox.my.salesforce.com",
265+
)
266+
assertEquals("My Domain sandbox server should yield L4",
267+
FEATURE_LOGIN_SERVER_MY_DOMAIN, result)
241268
}
242269

243270
@Test
244271
fun test_givenWelcomeLoginUrl_whenSelectLMarker_thenL5Returned() {
245272
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
246273

247-
// The WD URL itself is a pool server, so when usedWelcomeDiscovery=false it falls to L5.
274+
// The WD URL itself: when usedWelcomeDiscovery=false it falls to L5 (custom)
248275
val result = activity.selectLMarker(
249276
usedWelcomeDiscovery = false,
250277
loginServerUrl = LoginServerManager.WELCOME_LOGIN_URL,
@@ -253,15 +280,27 @@ class BrowserLoginTelemetryTest {
253280
FEATURE_LOGIN_SERVER_CUSTOM, result)
254281
}
255282

283+
@Test
284+
fun test_givenCustomServer_whenSelectLMarker_thenL5Returned() {
285+
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
286+
287+
val result = activity.selectLMarker(
288+
usedWelcomeDiscovery = false,
289+
loginServerUrl = "https://custom.example.com",
290+
)
291+
assertEquals("Custom server should yield L5",
292+
FEATURE_LOGIN_SERVER_CUSTOM, result)
293+
}
294+
256295
@Test
257296
fun test_givenExactlyOneL_whenProductionServer_thenOnlyL1Selected() {
258297
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
259298

260299
val allLMarkers = listOf(
261300
FEATURE_LOGIN_SERVER_PRODUCTION,
262301
FEATURE_LOGIN_SERVER_SANDBOX,
263-
FEATURE_LOGIN_SERVER_MY_DOMAIN,
264302
FEATURE_LOGIN_SERVER_WELCOME_DISCOVERY,
303+
FEATURE_LOGIN_SERVER_MY_DOMAIN,
265304
FEATURE_LOGIN_SERVER_CUSTOM,
266305
)
267306
val selected = activity.selectLMarker(
@@ -273,20 +312,5 @@ class BrowserLoginTelemetryTest {
273312
assertEquals("The selected marker should be L1", FEATURE_LOGIN_SERVER_PRODUCTION, selected)
274313
}
275314

276-
@Test
277-
fun test_givenMyDomainWithTrailingSpace_whenSelectLMarker_thenL3Returned() {
278-
every { activity.selectLMarker(any(), any()) } answers { callOriginal() }
279-
280-
// selectLMarker trims the URL, so trailing spaces should not affect the result.
281-
// Note: the trimming is done in onAuthFlowSuccess before calling selectLMarker,
282-
// but selectLMarker still receives pre-trimmed input here.
283-
val result = activity.selectLMarker(
284-
usedWelcomeDiscovery = false,
285-
loginServerUrl = "https://myorg.my.salesforce.com",
286-
)
287-
assertEquals("My Domain URL should yield L3",
288-
FEATURE_LOGIN_SERVER_MY_DOMAIN, result)
289-
}
290-
291315
// endregion
292316
}

0 commit comments

Comments
 (0)