Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
ad8d74f
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Feb 25, 2026
84a6441
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Feb 28, 2026
2d06029
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 2, 2026
99edfa6
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 3, 2026
48a047e
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 3, 2026
a754057
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 3, 2026
7627a75
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 3, 2026
463b95c
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 3, 2026
7daebb6
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 3, 2026
686e433
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 4, 2026
51d888c
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 4, 2026
a48ed4d
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 4, 2026
8724b7e
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 4, 2026
0406497
@W-21148602: Internal Server List does not respect changes made to Se…
JohnsonEricAtSalesforce Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import java.util.Deque;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
* Class to manage login hosts (default and user entered).
Expand Down Expand Up @@ -92,7 +91,14 @@ public LoginServerManager(Context ctx) {
Context.MODE_PRIVATE);
runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE,
Context.MODE_PRIVATE);

// Reset non-custom servers from mobile device management (MDM) and servers XML.
resetNonCustomLoginServers(runtimePrefs);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmathurin, @brandonpage and @bbirman, before I update the tests and move this into ready-for-review I wanted to run the concept of the change past you all.

I chatted briefly with @bbirman to review the iOS behavior since the work item specifically hails that logic as working compared to Android's. Currently, MSDK Android loads the servers.xml only once on install. The work item mentions upgrade, but I didn't see that logic. More, when MDM provides servers they're additive only. When MDM drops a server, the app doesn't reflect that.

What I'm trying here and seems to manually test well is:

  1. Drop both sets of non-custom servers on startup, including non-custom servers from servers.xml and MDM.
  2. Load the current set of non-custom servers from either servers.xml or MDM according to the existing logic, as much as possible.
  3. When MDM is not enabled, preserve the user's custom login servers at the end of the list

I'm doing a little more testing around the behavior of the only show authorized hosts parameter. I want to be sure the MDM flow still respects the user's custom servers.

The question to answer is: Does this seem like a reasonable update to the existing logic to resolve this work item? I can manipulate servers.xml and the MDM data and the app seems to keep the servers list adds, updates or removes non-custom servers just as we'd want. The logic here is older and the methods are not as self-contained as our newer logic, so I wanted to be sure we all got to review any potential side-effects of this before moving further!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For #3 (and pending your authorized hosts testing), on iOS the custom login servers would be preserved if MDM was enabled as long as "onlyShowAuthorizedHosts" is false

@brandonpage brandonpage Feb 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds good to me.

One thought though: Should we only remove MDM servers when network is available? Security wise, it would be bad it:

  1. Put device in airplane mode
  2. Open app. MDM servers are deleted and updated servers cannot be retrieved.
  3. Turn airplane mode off.
  4. LoginServerManager constructor does not run again. MDM server list is not respected.

Is that scenario possible?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks and that thought about network is a great topic. I'll do some research!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a manual look and also asked our agents. RuntimeConfig does cache the policy locally and will give MSDK those values when no network is present ✅

resetNonCustomLoginServers(settings);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A crux change is here in the constructor to (re-)initialize the non-custom login servers from resources server.xml when applicable. Note, this doesn't affect the user's custom login servers.


// Refresh non-custom servers from MDM or servers.xml
initSharedPrefFile();

getSelectedLoginServer();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated getSelectedLoginServer has new logic to detect when the "selected" login server is no longer in the "available" login servers and select the new default login server.

}

Expand Down Expand Up @@ -228,32 +234,48 @@ public void reset() {
}

/**
* Removes a login server from the list.
* Removes a custom login server from the list.
*
* @param server the server to remove
* @param server The server to remove. If the server is not custom, this method does nothing
*/
public void removeServer(LoginServer server) {
removeServer(server, settings, false);
}

/**
* Removes a login server from the list.
*
* @param server The server to remove
* @param sharedPreferences The shared preferences to remove the server from
* @param allowNonCustomRemoval Boolean true allows the removal of non-custom login servers and
* false does not
*/
private void removeServer(
final LoginServer server,
final SharedPreferences sharedPreferences,
final boolean allowNonCustomRemoval
) {
List<LoginServer> servers = getLoginServers();
int index = servers.indexOf(server);

if (server.isCustom && index != -1) {
int numServers = settings.getInt(NUMBER_OF_ENTRIES, 0);
Deque<LoginServer> stack = new ArrayDeque<>(servers.subList(index+1, numServers));
if (allowNonCustomRemoval || server.isCustom && index != -1) {
int numServers = servers.size();
Deque<LoginServer> stack = new ArrayDeque<>(servers.subList(index + 1, numServers));

final Editor edit = settings.edit();
final Editor edit = sharedPreferences.edit();
edit.remove(String.format(Locale.US, SERVER_NAME, index))
.remove(String.format(Locale.US, SERVER_URL, index))
.remove(String.format(Locale.US, IS_CUSTOM, index));
.remove(String.format(Locale.US, SERVER_URL, index))
.remove(String.format(Locale.US, IS_CUSTOM, index));

// Re-index servers after the one removed from the list.
for (int i = (index + 1); i < numServers; i++) {
LoginServer reIndexServer = stack.pop();
edit.remove(String.format(Locale.US, SERVER_NAME, i))
.remove(String.format(Locale.US, SERVER_URL, i))
.remove(String.format(Locale.US, IS_CUSTOM, i))
.putString(String.format(Locale.US, SERVER_NAME, i-1), reIndexServer.name)
.putString(String.format(Locale.US, SERVER_URL, i-1), reIndexServer.url)
.putBoolean(String.format(Locale.US, IS_CUSTOM, i-1), reIndexServer.isCustom);
.remove(String.format(Locale.US, SERVER_URL, i))
.remove(String.format(Locale.US, IS_CUSTOM, i))
.putString(String.format(Locale.US, SERVER_NAME, i - 1), reIndexServer.name)
.putString(String.format(Locale.US, SERVER_URL, i - 1), reIndexServer.url)
.putBoolean(String.format(Locale.US, IS_CUSTOM, i - 1), reIndexServer.isCustom);
}

edit.putInt(NUMBER_OF_ENTRIES, --numServers).apply();
Expand Down Expand Up @@ -467,16 +489,17 @@ private List<LoginServer> getLoginServersFromXML() {
* first time a user is upgrading to a newer version of the Mobile SDK.
*/
private void initSharedPrefFile() {
final Map<String, ?> values = settings.getAll();
if (values != null && !values.isEmpty()) {
return;
}
final List<LoginServer> loginServersFromXml = getLoginServersFromXML();

List<LoginServer> servers = getLoginServers();
if (servers == null || servers.isEmpty()) {
servers = getLoginServersFromXML();
servers = loginServersFromXml;
if (servers == null || servers.isEmpty()) {
servers = getLegacyLoginServers();
}
} else {
loginServersFromXml.addAll(servers);
servers = loginServersFromXml;
}
int numServers = servers.size();
final Editor edit = settings.edit();
Expand Down Expand Up @@ -538,6 +561,25 @@ private List<LoginServer> getLoginServersFromPreferences(SharedPreferences prefs
return (!allServers.isEmpty() ? allServers : null);
}

/**
* Resets the list of non-custom login servers in the provided shared preferences.
*
* @param sharedPreferences The shared preferences
*/
private void resetNonCustomLoginServers(
final SharedPreferences sharedPreferences
) {
final List<LoginServer> loginServersFromPreferences = getLoginServersFromPreferences(sharedPreferences);
if (loginServersFromPreferences != null) {
for (int i = 0; i < loginServersFromPreferences.size(); i++) {
final LoginServer loginServer = loginServersFromPreferences.get(i);
if (!loginServer.isCustom) {
removeServer(loginServer, sharedPreferences, true);
}
}
}
}

/**
* Class to encapsulate a login server name, URL, index and type (custom or not).
*/
Expand Down