From 456b36143fc546d1db3e25beb280773886b5f640 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 12:05:05 +0200 Subject: [PATCH 01/11] Target mobile hooks for MDM --- netbird | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbird b/netbird index 07e54501..db2c9b6f 160000 --- a/netbird +++ b/netbird @@ -1 +1 @@ -Subproject commit 07e5450117dd0451aaeefc18729a822115587e69 +Subproject commit db2c9b6f49ac88343f546c14476c47e2a3fe7268 From de54bf452fe23daea5409e4fa7ea2aff433596ab Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 11:06:04 +0200 Subject: [PATCH 02/11] Declare APP_RESTRICTIONS reading in manifest --- app/src/main/AndroidManifest.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 897d5fdf..3f737a8f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,13 @@ android:theme="@style/Theme.NetBird" tools:targetApi="31"> + + + Date: Mon, 15 Jun 2026 11:07:23 +0200 Subject: [PATCH 03/11] Manifest restrictions for split tunnel --- app/src/main/res/values/arrays.xml | 13 +++ app/src/main/res/values/strings.xml | 54 +++++++++ app/src/main/res/xml/app_restrictions.xml | 135 ++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/xml/app_restrictions.xml diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 00000000..6ddd6604 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,13 @@ + + + + + Allow only listed apps (everything else bypasses) + Disallow listed apps (everything else routes) + + + + allow + disallow + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5fb4474b..fead681a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,4 +148,58 @@ Switched to profile \'%s\' Logged out from profile \'%s\' Profile \'%s\' removed successfully + + + Management URL + URL of the NetBird management server. Format https://host[:port]. + + Pre-shared key + WireGuard pre-shared key used as an additional symmetric secret. Secret value. + + Disable auto-connect + When enabled, the tunnel does not auto-connect at app start. + + Disable client routes + When enabled, this client does not consume routes advertised by routing peers. + + Disable server routes + When enabled, this client does not act as a routing peer for other clients. + + Block inbound + When enabled, the client blocks all inbound peer traffic on the WireGuard interface. + + Allow server SSH + When enabled, this client accepts incoming SSH sessions via NetBird SSH. + + Enable Rosenpass + Enables Rosenpass post-quantum key exchange on WireGuard tunnels. + + Rosenpass permissive + When enabled, falls back to plain WireGuard if a peer does not support Rosenpass. + + WireGuard port + UDP port for the local WireGuard interface. Allowed range 1-65535. + + Split tunnel mode + Choose allow (only listed apps route through NetBird) or disallow (listed apps bypass NetBird). + + Split tunnel apps + Comma-separated list of package names used by the selected split-tunnel mode. + + Disable update settings + When enabled, blocks every configuration change from the UI and CLI. + + Disable profiles + When enabled, the client cannot list, create, switch or remove NetBird connection profiles. + + Disable networks + When enabled, the client UI cannot list, select or deselect NetBird networks. + + Disable advanced view + When enabled, the new UI hides the advanced-view section. + + Disable metrics collection + When enabled, the client does not collect or report local usage metrics. diff --git a/app/src/main/res/xml/app_restrictions.xml b/app/src/main/res/xml/app_restrictions.xml new file mode 100644 index 00000000..08462ff6 --- /dev/null +++ b/app/src/main/res/xml/app_restrictions.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8469ab42a8e0de4c63f19c6d74ddd57a0ce3a45d Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 11:10:08 +0200 Subject: [PATCH 04/11] Java wiring --- .../io/netbird/client/MDMPolicyFetcher.java | 69 +++++++++++++++++++ .../java/io/netbird/client/MyApplication.java | 7 ++ .../io/netbird/client/tool/EngineRunner.java | 12 ++++ .../io/netbird/client/tool/VPNService.java | 34 +++++++++ 4 files changed, 122 insertions(+) create mode 100644 app/src/main/java/io/netbird/client/MDMPolicyFetcher.java diff --git a/app/src/main/java/io/netbird/client/MDMPolicyFetcher.java b/app/src/main/java/io/netbird/client/MDMPolicyFetcher.java new file mode 100644 index 00000000..d06e4227 --- /dev/null +++ b/app/src/main/java/io/netbird/client/MDMPolicyFetcher.java @@ -0,0 +1,69 @@ +package io.netbird.client; + +import android.content.Context; +import android.content.RestrictionsManager; +import android.os.Bundle; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import io.netbird.gomobile.android.PolicyFetcher; + +/** + * MDMPolicyFetcher reads the current Android managed-config snapshot from + * RestrictionsManager and returns it as a JSON-encoded string to the Go + * layer. Registered once at app start via Android.setMobilePolicyFetcher; + * the Go side invokes fetchJSON() on every LoadPolicy call so the response + * is always fresh. + * + * Returns an empty string when no managed config is set — the daemon side + * treats that as the "no MDM source present" sentinel. + */ +public class MDMPolicyFetcher implements PolicyFetcher { + private static final String TAG = "MDMPolicyFetcher"; + + private final Context context; + + public MDMPolicyFetcher(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public String fetchJSON() { + RestrictionsManager rm = (RestrictionsManager) context.getSystemService(Context.RESTRICTIONS_SERVICE); + if (rm == null) { + return ""; + } + Bundle restrictions = rm.getApplicationRestrictions(); + if (restrictions == null || restrictions.isEmpty()) { + return ""; + } + try { + return bundleToJSON(restrictions).toString(); + } catch (JSONException e) { + Log.w(TAG, "Failed to serialize managed restrictions to JSON: " + e); + return ""; + } + } + + private static JSONObject bundleToJSON(Bundle bundle) throws JSONException { + JSONObject obj = new JSONObject(); + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + if (value instanceof Bundle) { + obj.put(key, bundleToJSON((Bundle) value)); + } else if (value instanceof Object[]) { + JSONArray arr = new JSONArray(); + for (Object item : (Object[]) value) { + arr.put(item); + } + obj.put(key, arr); + } else { + obj.put(key, value); + } + } + return obj; + } +} diff --git a/app/src/main/java/io/netbird/client/MyApplication.java b/app/src/main/java/io/netbird/client/MyApplication.java index ea2493c1..99ba8b7e 100644 --- a/app/src/main/java/io/netbird/client/MyApplication.java +++ b/app/src/main/java/io/netbird/client/MyApplication.java @@ -5,6 +5,8 @@ import androidx.appcompat.app.AppCompatDelegate; +import io.netbird.gomobile.android.Android; + public class MyApplication extends Application { @Override @@ -14,5 +16,10 @@ public void onCreate() { SharedPreferences prefs = getSharedPreferences("settings", MODE_PRIVATE); int themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); AppCompatDelegate.setDefaultNightMode(themeMode); + + // Register the MDM policy fetcher exactly once for the process + // lifetime. The Go side invokes fetchJSON() on every LoadPolicy + // call so the returned snapshot is always fresh — no caching here. + Android.setMobilePolicyFetcher(new MDMPolicyFetcher(this)); } } \ No newline at end of file diff --git a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java index d4069b51..62a86063 100644 --- a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java +++ b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java @@ -245,6 +245,18 @@ public synchronized void stop() { goClient.stop(); } + /** + * Invoked by the platform broadcast receiver when the OS reports a + * managed-config change (Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED). + * Delegates to the Go layer; on the Go side this stops the current + * engine context so the outer loop (runClient) wakes up and re-invokes + * Run with a freshly resolved Config that includes the latest MDM + * overlay. + */ + public synchronized void onMDMPolicyChanged() { + goClient.onMDMPolicyChanged(); + } + public PeerInfoArray peersInfo() { return goClient.peersList(); } diff --git a/tool/src/main/java/io/netbird/client/tool/VPNService.java b/tool/src/main/java/io/netbird/client/tool/VPNService.java index 83ab6124..bd5afe77 100644 --- a/tool/src/main/java/io/netbird/client/tool/VPNService.java +++ b/tool/src/main/java/io/netbird/client/tool/VPNService.java @@ -39,6 +39,7 @@ public class VPNService extends android.net.VpnService { private ConcreteNetworkAvailabilityListener networkAvailabilityListener; private EngineRestarter engineRestarter; private android.content.BroadcastReceiver stopEngineReceiver; + private android.content.BroadcastReceiver mdmPolicyChangedReceiver; @Override public void onCreate() { @@ -99,6 +100,32 @@ public void onReceive(Context context, Intent intent) { filter, Context.RECEIVER_NOT_EXPORTED ); + + // Listen for MDM managed-config changes. The OS broadcasts this + // intent whenever a Device Owner / Profile Owner pushes new + // application restrictions; the receiver only signals the engine + // to restart with a freshly resolved Config — the actual values + // are read on demand by MDMPolicyFetcher.fetchJSON during the + // next Run. + mdmPolicyChangedReceiver = new android.content.BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED.equals(intent.getAction())) { + Log.d(LOGTAG, "Received MDM policy change broadcast"); + if (engineRunner != null) { + engineRunner.onMDMPolicyChanged(); + } + } + } + }; + android.content.IntentFilter mdmFilter = new android.content.IntentFilter( + Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); + androidx.core.content.ContextCompat.registerReceiver( + this, + mdmPolicyChangedReceiver, + mdmFilter, + Context.RECEIVER_NOT_EXPORTED + ); } @Override @@ -147,6 +174,13 @@ public void onDestroy() { Log.w(LOGTAG, "Receiver not registered", e); } } + if (mdmPolicyChangedReceiver != null) { + try { + unregisterReceiver(mdmPolicyChangedReceiver); + } catch (IllegalArgumentException e) { + Log.w(LOGTAG, "MDM receiver not registered", e); + } + } networkAvailabilityListener.unsubscribe(); networkChangeDetector.unsubscribe(); From f4d48a63db1a3069ff4c849f225390d1fdb9fbb3 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 11:44:12 +0200 Subject: [PATCH 05/11] Blocks editing of UI for params that are administrated by MDM --- .../client/ui/advanced/AdvancedFragment.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java b/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java index 212fed94..a6b481fc 100644 --- a/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java +++ b/app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java @@ -1,9 +1,13 @@ package io.netbird.client.ui.advanced; import android.content.Context; +import android.content.RestrictionsManager; import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.view.View; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -292,11 +296,79 @@ private void initializeEngineConfigSwitches() { binding.switchDisableIpv6.toggle(); }); + applyMDMLocks(); + } catch (Exception e) { Log.e(LOGTAG, "Failed to initialize engine config switches", e); } } + /** + * Lock and align every UI control whose corresponding key is currently + * MDM-enforced. The list of managed keys + their enforced values is + * read directly from RestrictionsManager — the same OS-native source + * the Go layer uses (via MDMPolicyFetcher). No round-trip to Go is + * needed, and the two sides cannot diverge. + * + * For each managed key: + * - the switch is forced to the MDM value (overrides the user's + * on-disk preference); + * - the switch + its surrounding clickable layout are disabled so + * the user cannot toggle them. + */ + private void applyMDMLocks() { + Context ctx = getContext(); + if (ctx == null) { + return; + } + RestrictionsManager rm = (RestrictionsManager) ctx.getSystemService(Context.RESTRICTIONS_SERVICE); + if (rm == null) { + return; + } + android.os.Bundle restrictions = rm.getApplicationRestrictions(); + if (restrictions == null || restrictions.isEmpty()) { + return; + } + + lockSwitchIfManaged(restrictions, "rosenpassEnabled", binding.switchRosenpass, binding.layoutRosenpas); + lockSwitchIfManaged(restrictions, "rosenpassPermissive", binding.switchRosenpassPermissive, binding.layoutRosenpassPermissive); + lockSwitchIfManaged(restrictions, "allowServerSSH", binding.switchAllowSsh, binding.layoutAllowSsh); + lockSwitchIfManaged(restrictions, "blockInbound", binding.switchBlockInbound, binding.layoutBlockInbound); + lockSwitchIfManaged(restrictions, "disableClientRoutes", binding.switchDisableClientRoutes, binding.layoutDisableClientRoutes); + lockSwitchIfManaged(restrictions, "disableServerRoutes", binding.switchDisableServerRoutes, binding.layoutDisableServerRoutes); + + // PreSharedKey is a string, not a bool; lock the field if managed. + if (restrictions.containsKey("preSharedKey")) { + EditText psk = binding.presharedKey; + psk.setEnabled(false); + // Show the redaction sentinel so the actual MDM value is never + // leaked into the UI — matches the daemon-side behavior of + // GetConfig. + psk.setText(hiddenKey); + binding.btnSave.setEnabled(false); + } + } + + /** + * Helper: if `key` is present in the OS-pushed restrictions, force the + * switch to its enforced bool value and disable the switch and its + * parent layout. The parent layout must be disabled too, otherwise + * the TV-remote "tap layout to toggle switch" path remains active. + */ + private void lockSwitchIfManaged(android.os.Bundle restrictions, String key, + CompoundButton switchCtrl, View parentLayout) { + if (switchCtrl == null || !restrictions.containsKey(key)) { + return; + } + boolean value = restrictions.getBoolean(key); + switchCtrl.setChecked(value); + switchCtrl.setEnabled(false); + if (parentLayout != null) { + parentLayout.setEnabled(false); + parentLayout.setClickable(false); + } + } + @Override public void onDestroyView() { super.onDestroyView(); From f41075dd39c13cee1556c6a56908810ae3cb2b3f Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 12:01:49 +0200 Subject: [PATCH 06/11] Fixes MDM hot reload not converging to restarted engine --- .../io/netbird/client/tool/EngineRestarter.java | 16 ++++++++++++++++ .../java/io/netbird/client/tool/VPNService.java | 9 +++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tool/src/main/java/io/netbird/client/tool/EngineRestarter.java b/tool/src/main/java/io/netbird/client/tool/EngineRestarter.java index ecebc474..28c5fe03 100644 --- a/tool/src/main/java/io/netbird/client/tool/EngineRestarter.java +++ b/tool/src/main/java/io/netbird/client/tool/EngineRestarter.java @@ -308,6 +308,22 @@ public void onNetworkTypeChanged() { } } + /** + * Triggers the same stop + restart sequence used by the network-change + * path, but without the debounce delay. Used by the MDM policy-change + * broadcast receiver: when an admin pushes a new managed config the + * engine must restart immediately so the new values take effect on the + * next Run (which re-reads MDM via MDMPolicyFetcher). + */ + public void requestRestartNow() { + Log.d(LOGTAG, "explicit restart requested (no debounce)"); + synchronized (restartLock) { + restartScheduled = true; + handler.removeCallbacks(restartRunnable); + handler.post(restartRunnable); + } + } + /** * Cancels any pending debounced restart. Called whenever an external * actor (typically a user-driven Connect/Disconnect) takes over the diff --git a/tool/src/main/java/io/netbird/client/tool/VPNService.java b/tool/src/main/java/io/netbird/client/tool/VPNService.java index bd5afe77..f938fcae 100644 --- a/tool/src/main/java/io/netbird/client/tool/VPNService.java +++ b/tool/src/main/java/io/netbird/client/tool/VPNService.java @@ -112,8 +112,13 @@ public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { if (Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED.equals(intent.getAction())) { Log.d(LOGTAG, "Received MDM policy change broadcast"); - if (engineRunner != null) { - engineRunner.onMDMPolicyChanged(); + // Route through EngineRestarter so the existing restart + // machinery (state-listener gating, UI suppression + // window, runWithoutAuth on stop) handles the stop + + // re-run cleanly. A bare engineRunner.stop() leaves the + // engine stopped with no one to relaunch it. + if (engineRestarter != null) { + engineRestarter.requestRestartNow(); } } } From f9919b577150492b13cf9c4b2103fc4463c34bf4 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 13:02:52 +0200 Subject: [PATCH 07/11] Updates to removed code --- netbird | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbird b/netbird index db2c9b6f..bec26d5a 160000 --- a/netbird +++ b/netbird @@ -1 +1 @@ -Subproject commit db2c9b6f49ac88343f546c14476c47e2a3fe7268 +Subproject commit bec26d5a14e7ac6f1d97ff533bf5275577bb30db From 0b62139083cbc0a5cf7c993124dd4dd37a0b9376 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 13:03:02 +0200 Subject: [PATCH 08/11] Removes unused code --- .../java/io/netbird/client/tool/EngineRunner.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java index 62a86063..d4069b51 100644 --- a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java +++ b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java @@ -245,18 +245,6 @@ public synchronized void stop() { goClient.stop(); } - /** - * Invoked by the platform broadcast receiver when the OS reports a - * managed-config change (Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED). - * Delegates to the Go layer; on the Go side this stops the current - * engine context so the outer loop (runClient) wakes up and re-invokes - * Run with a freshly resolved Config that includes the latest MDM - * overlay. - */ - public synchronized void onMDMPolicyChanged() { - goClient.onMDMPolicyChanged(); - } - public PeerInfoArray peersInfo() { return goClient.peersList(); } From 7f64edc43719275ab171ca88b3ee9039f48edd38 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 17:49:32 +0200 Subject: [PATCH 09/11] Align to updated fetchers setters --- .../java/io/netbird/client/MyApplication.java | 10 ++++------ .../io/netbird/client/tool/EngineRunner.java | 6 ++++++ .../client/tool}/MDMPolicyFetcher.java | 19 +++++++++++-------- 3 files changed, 21 insertions(+), 14 deletions(-) rename {app/src/main/java/io/netbird/client => tool/src/main/java/io/netbird/client/tool}/MDMPolicyFetcher.java (79%) diff --git a/app/src/main/java/io/netbird/client/MyApplication.java b/app/src/main/java/io/netbird/client/MyApplication.java index 99ba8b7e..5d10ae2d 100644 --- a/app/src/main/java/io/netbird/client/MyApplication.java +++ b/app/src/main/java/io/netbird/client/MyApplication.java @@ -5,8 +5,6 @@ import androidx.appcompat.app.AppCompatDelegate; -import io.netbird.gomobile.android.Android; - public class MyApplication extends Application { @Override @@ -17,9 +15,9 @@ public void onCreate() { int themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); AppCompatDelegate.setDefaultNightMode(themeMode); - // Register the MDM policy fetcher exactly once for the process - // lifetime. The Go side invokes fetchJSON() on every LoadPolicy - // call so the returned snapshot is always fresh — no caching here. - Android.setMobilePolicyFetcher(new MDMPolicyFetcher(this)); + // NOTE: the MDM policy fetcher is registered on the goClient + // instance inside EngineRunner — see EngineRunner constructor. + // Process-wide registration was removed when the Go side moved + // to per-Client DI for the Loader. } } \ No newline at end of file diff --git a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java index d4069b51..da309ffb 100644 --- a/tool/src/main/java/io/netbird/client/tool/EngineRunner.java +++ b/tool/src/main/java/io/netbird/client/tool/EngineRunner.java @@ -48,6 +48,12 @@ public EngineRunner(Context context, NetworkChangeListener networkChangeListener iFaceDiscover, networkChangeListener); + // Per-Client MDM policy fetcher (DI on the goClient side). + // The Go layer holds the *mdm.Loader on this Client instance; + // every Run/RunWithoutLogin call overlays the latest MDM + // policy on top of the freshly resolved Config. + goClient.setMDMPolicyFetcher(new MDMPolicyFetcher(context)); + updateLogLevel(isTraceLogEnabled, isDebuggable); } diff --git a/app/src/main/java/io/netbird/client/MDMPolicyFetcher.java b/tool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java similarity index 79% rename from app/src/main/java/io/netbird/client/MDMPolicyFetcher.java rename to tool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java index d06e4227..09fc8536 100644 --- a/app/src/main/java/io/netbird/client/MDMPolicyFetcher.java +++ b/tool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java @@ -1,4 +1,4 @@ -package io.netbird.client; +package io.netbird.client.tool; import android.content.Context; import android.content.RestrictionsManager; @@ -12,14 +12,17 @@ import io.netbird.gomobile.android.PolicyFetcher; /** - * MDMPolicyFetcher reads the current Android managed-config snapshot from - * RestrictionsManager and returns it as a JSON-encoded string to the Go - * layer. Registered once at app start via Android.setMobilePolicyFetcher; - * the Go side invokes fetchJSON() on every LoadPolicy call so the response - * is always fresh. + * MDMPolicyFetcher reads the current Android managed-config snapshot + * from RestrictionsManager and returns it as a JSON-encoded string to + * the Go layer. Registered on the goClient via setMDMPolicyFetcher + * inside EngineRunner; the Go side invokes fetchJSON() on every + * Loader.Load call so the response is always fresh. * - * Returns an empty string when no managed config is set — the daemon side - * treats that as the "no MDM source present" sentinel. + * Returns an empty string when no managed config is set — the daemon + * side treats that as the "no MDM source present" sentinel. + * + * Lives in the tool package so the network-extension target can + * instantiate it without depending on the app package. */ public class MDMPolicyFetcher implements PolicyFetcher { private static final String TAG = "MDMPolicyFetcher"; From 8442624088103f763304decdaadd7ec1e50614a8 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 15 Jun 2026 17:51:23 +0200 Subject: [PATCH 10/11] Align repo --- netbird | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbird b/netbird index bec26d5a..03408938 160000 --- a/netbird +++ b/netbird @@ -1 +1 @@ -Subproject commit bec26d5a14e7ac6f1d97ff533bf5275577bb30db +Subproject commit 034089385468346d1a3765b99d97c9ea314388ca From cee5fa9e9c3f9eca5fb9783fa506b233239aeb63 Mon Sep 17 00:00:00 2001 From: riccardom Date: Tue, 16 Jun 2026 11:00:26 +0200 Subject: [PATCH 11/11] Aligns to invoking apply MDM config on all paths around --- netbird | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbird b/netbird index 03408938..b2c57328 160000 --- a/netbird +++ b/netbird @@ -1 +1 @@ -Subproject commit 034089385468346d1a3765b99d97c9ea314388ca +Subproject commit b2c5732847ae5c22ab8f526204d9f121df72869e