feat: add MDM managed app configuration support#170
feat: add MDM managed app configuration support#170dbrieck wants to merge 3 commits intonetbirdio:mainfrom
Conversation
Integrate Android Enterprise managed configurations to allow MDM solutions (Intune, Google Workspace, etc.) to push NetBird configuration to managed devices. - Add app_restrictions.xml schema defining 7 MDM configuration keys - Add ManagedConfigReader to read RestrictionsManager values - Add ManagedConfigReceiver for runtime MDM config change events - Register restrictions metadata and receiver in AndroidManifest.xml - Apply MDM config and auto-login with setup key in EngineRunner Related to netbirdio/netbird#1918
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 47 minutes and 17 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAndroid Enterprise Managed Configuration support added: new app_restrictions XML, ManagedConfigReader, and ManagedConfigReceiver. EngineRunner now attempts to apply MDM config (and optionally silent setup-key login) during startup; receiver applies changes on ACTION_APPLICATION_RESTRICTIONS_CHANGED broadcasts. Changes
Sequence Diagram(s)sequenceDiagram
participant System as System
participant Receiver as ManagedConfigReceiver
participant Reader as ManagedConfigReader
participant RM as RestrictionsManager
participant Engine as EngineRunner
participant Profile as ProfileManagerWrapper
participant Config as ManagedConfig
System->>Receiver: APPLICATION_RESTRICTIONS_CHANGED
Receiver->>Reader: read(context)
Reader->>RM: getApplicationRestrictions()
RM-->>Reader: restrictions bundle
Reader-->>Receiver: ManagedConfig (or null)
alt ManagedConfig present
Receiver->>Engine: synchronize on MDM_CONFIG_LOCK
Receiver->>Profile: getActiveConfigPath()
Receiver->>Config: apply(configPath)
Config-->>Receiver: success / error
else No config
Receiver-->>Receiver: log and exit
end
note right of Engine: On startup EngineRunner also calls Reader.read() under MDM_CONFIG_LOCK and may perform setup-key login
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
app/src/main/res/xml/app_restrictions.xml (1)
12-59: Consider using string resources forandroid:titleandandroid:description.These values are shown to MDM admins in consoles like Intune/Workspace ONE when configuring the app. Hardcoding them here prevents localization and makes it inconsistent with the rest of the app's string handling. Moving them to
res/values/strings.xml(and translations) would improve the admin experience in non‑English locales.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/res/xml/app_restrictions.xml` around lines 12 - 59, The XML currently hardcodes android:title and android:description for the restrictions (keys: managementUrl, setupKey, adminUrl, preSharedKey, rosenpassEnabled, rosenpassPermissive, disableAutoConnect); move each title and description into res/values/strings.xml (add descriptive resource names like title_management_url, desc_management_url, title_setup_key, desc_setup_key, etc.), replace the literal text in app_restrictions.xml with the corresponding `@string/` resource references, and add translations as needed; keep the android:key and android:defaultValue entries unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java`:
- Around line 107-120: The setup-key login is currently attempted on every
EngineRunner.runClient() because it only checks mdmConfig.hasSetupKey(); change
this to first detect whether the engine/device is already enrolled (e.g., check
the active profile for an existing peer key or a device registration flag) and
only call
io.netbird.gomobile.android.Android.newAuth(...).loginWithSetupKeySync(...) when
not enrolled. In practice modify the block referencing mdmConfig.hasSetupKey()
in EngineRunner to add a precondition like "if mdmConfig.hasSetupKey() &&
!isEnrolled()" (or an equivalent check against the active profile/peer key)
before invoking loginWithSetupKeySync so successful prior enrollments skip the
remote round-trip and log noise.
- Around line 97-124: There is a race between EngineRunner.runClient()'s call to
mdmConfig.apply(configurationFilePath) and ManagedConfigReceiver.onReceive()
mutating the same config; serialize these operations by introducing a shared
lock (e.g., a static final Object MDM_CONFIG_LOCK) used by both
EngineRunner.runClient() and ManagedConfigReceiver.onReceive() around any call
to mdmConfig.apply(configPath), and in runClient() check whether the engine is
already running (or a flag like engineStarted) and skip the apply when the
engine is active to avoid mid-run file writes; update references to
mdmConfig.apply(configurationFilePath), ManagedConfigReceiver.onReceive(), and
the engine start calls (goClient.runSync/goClient.runAsync) to use the
lock/flag.
- Around line 109-114: The MDM setup-key enrollment is calling
Android.newAuth(configurationFilePath, "") which passes an empty server address;
update EngineRunner to use Preferences.defaultServer() instead (i.e., call
Android.newAuth with Preferences.defaultServer()) so behavior matches
ChangeServerFragmentViewModel, or add a brief comment next to the
Android.newAuth call documenting why an empty string is intentional and what the
Go SDK fallback behavior is; locate the call in EngineRunner where
io.netbird.gomobile.android.Auth auth is created and adjust the second argument
accordingly while leaving the subsequent
auth.loginWithSetupKeySync(mdmConfig.getSetupKey(), DeviceName.getDeviceName())
unchanged.
In `@tool/src/main/java/io/netbird/client/tool/ManagedConfigReceiver.java`:
- Around line 26-47: onReceive currently does blocking IPC/JNI/file I/O
(ManagedConfigReader.read, new ProfileManagerWrapper(...),
ProfileManagerWrapper.getActiveConfigPath, ManagedConfig.apply) on the main
thread which risks ANRs; change the receiver to call goAsync() and perform those
calls off the main thread (or submit them to a shared single-threaded EXECUTOR
used by EngineRunner) and ensure you call PendingResult.finish() when done so
work runs asynchronously and no long/blocking operations occur in onReceive.
---
Nitpick comments:
In `@app/src/main/res/xml/app_restrictions.xml`:
- Around line 12-59: The XML currently hardcodes android:title and
android:description for the restrictions (keys: managementUrl, setupKey,
adminUrl, preSharedKey, rosenpassEnabled, rosenpassPermissive,
disableAutoConnect); move each title and description into res/values/strings.xml
(add descriptive resource names like title_management_url, desc_management_url,
title_setup_key, desc_setup_key, etc.), replace the literal text in
app_restrictions.xml with the corresponding `@string/` resource references, and
add translations as needed; keep the android:key and android:defaultValue
entries unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7b57820b-55a1-4dc5-bb31-36316eb9346b
📒 Files selected for processing (5)
app/src/main/AndroidManifest.xmlapp/src/main/res/xml/app_restrictions.xmltool/src/main/java/io/netbird/client/tool/EngineRunner.javatool/src/main/java/io/netbird/client/tool/ManagedConfigReader.javatool/src/main/java/io/netbird/client/tool/ManagedConfigReceiver.java
| // Apply MDM managed configuration before starting the engine. | ||
| // MDM values override user-set preferences on every launch. | ||
| try { | ||
| io.netbird.gomobile.android.ManagedConfig mdmConfig = ManagedConfigReader.read(context); | ||
| if (mdmConfig != null && mdmConfig.hasConfig()) { | ||
| mdmConfig.apply(configurationFilePath); | ||
| Log.i(LOGTAG, "MDM managed configuration applied"); | ||
|
|
||
| // If MDM provides a setup key and the engine needs login, | ||
| // perform silent registration with the setup key | ||
| if (mdmConfig.hasSetupKey()) { | ||
| try { | ||
| io.netbird.gomobile.android.Auth auth = | ||
| io.netbird.gomobile.android.Android.newAuth(configurationFilePath, ""); | ||
| if (auth != null) { | ||
| auth.loginWithSetupKeySync(mdmConfig.getSetupKey(), DeviceName.getDeviceName()); | ||
| Log.i(LOGTAG, "MDM: silent setup key registration completed"); | ||
| } | ||
| } catch (Exception e) { | ||
| // Setup key login may fail if already registered or key expired. | ||
| // This is not fatal — continue with normal flow. | ||
| Log.w(LOGTAG, "MDM: setup key login skipped or failed: " + e.getMessage()); | ||
| } | ||
| } | ||
| } | ||
| } catch (Exception e) { | ||
| Log.e(LOGTAG, "Failed to apply MDM config, continuing with existing config", e); | ||
| } |
There was a problem hiding this comment.
Race between runClient() MDM apply and ManagedConfigReceiver.onReceive().
ManagedConfigReceiver.onReceive() (main thread) also calls config.apply(configPath) against the same configuration file used here. There is no synchronization between the two paths, so:
- If a restriction change is broadcast while
runClient()is betweenmdmConfig.apply(configurationFilePath)(line 102) andgoClient.run*(...)(lines 129/131), the config file the Go client ultimately reads may be a partially-updated mix of both writers. - On a hot config change mid-session, the receiver mutates the file the running engine is actively using, with no coordination.
Consider serializing MDM application through a shared lock (or a single-threaded queue/handler) used by both EngineRunner.runClient() and ManagedConfigReceiver.onReceive(), and skipping the apply in runClient() when the engine is already running. A simple fix:
🔒 Suggested synchronization
class EngineRunner {
+ // Shared lock guarding any mutation of the active profile's managed-config state.
+ static final Object MDM_CONFIG_LOCK = new Object();
@@
- try {
- io.netbird.gomobile.android.ManagedConfig mdmConfig = ManagedConfigReader.read(context);
- if (mdmConfig != null && mdmConfig.hasConfig()) {
- mdmConfig.apply(configurationFilePath);
+ try {
+ io.netbird.gomobile.android.ManagedConfig mdmConfig = ManagedConfigReader.read(context);
+ if (mdmConfig != null && mdmConfig.hasConfig()) {
+ synchronized (MDM_CONFIG_LOCK) {
+ mdmConfig.apply(configurationFilePath);
+ }And use the same MDM_CONFIG_LOCK around config.apply(configPath) in ManagedConfigReceiver.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java` around lines 97
- 124, There is a race between EngineRunner.runClient()'s call to
mdmConfig.apply(configurationFilePath) and ManagedConfigReceiver.onReceive()
mutating the same config; serialize these operations by introducing a shared
lock (e.g., a static final Object MDM_CONFIG_LOCK) used by both
EngineRunner.runClient() and ManagedConfigReceiver.onReceive() around any call
to mdmConfig.apply(configPath), and in runClient() check whether the engine is
already running (or a flag like engineStarted) and skip the apply when the
engine is active to avoid mid-run file writes; update references to
mdmConfig.apply(configurationFilePath), ManagedConfigReceiver.onReceive(), and
the engine start calls (goClient.runSync/goClient.runAsync) to use the
lock/flag.
| if (mdmConfig.hasSetupKey()) { | ||
| try { | ||
| io.netbird.gomobile.android.Auth auth = | ||
| io.netbird.gomobile.android.Android.newAuth(configurationFilePath, ""); | ||
| if (auth != null) { | ||
| auth.loginWithSetupKeySync(mdmConfig.getSetupKey(), DeviceName.getDeviceName()); | ||
| Log.i(LOGTAG, "MDM: silent setup key registration completed"); | ||
| } | ||
| } catch (Exception e) { | ||
| // Setup key login may fail if already registered or key expired. | ||
| // This is not fatal — continue with normal flow. | ||
| Log.w(LOGTAG, "MDM: setup key login skipped or failed: " + e.getMessage()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Setup-key login is re-attempted on every engine start.
Because MDM config is applied on each runClient() call and mdmConfig.hasSetupKey() will stay true as long as the admin leaves the key configured, loginWithSetupKeySync will be invoked every time the VPN service starts. The failure path is caught, but this generates a round-trip to the management server (and log noise) on every launch even for already-enrolled devices.
Consider gating the login attempt on "engine is not yet enrolled" state (e.g., check device registration / presence of peer key in the active profile) rather than solely on hasSetupKey().
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java` around lines 107
- 120, The setup-key login is currently attempted on every
EngineRunner.runClient() because it only checks mdmConfig.hasSetupKey(); change
this to first detect whether the engine/device is already enrolled (e.g., check
the active profile for an existing peer key or a device registration flag) and
only call
io.netbird.gomobile.android.Android.newAuth(...).loginWithSetupKeySync(...) when
not enrolled. In practice modify the block referencing mdmConfig.hasSetupKey()
in EngineRunner to add a precondition like "if mdmConfig.hasSetupKey() &&
!isEnrolled()" (or an equivalent check against the active profile/peer key)
before invoking loginWithSetupKeySync so successful prior enrollments skip the
remote round-trip and log noise.
| io.netbird.gomobile.android.Auth auth = | ||
| io.netbird.gomobile.android.Android.newAuth(configurationFilePath, ""); | ||
| if (auth != null) { | ||
| auth.loginWithSetupKeySync(mdmConfig.getSetupKey(), DeviceName.getDeviceName()); | ||
| Log.i(LOGTAG, "MDM: silent setup key registration completed"); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Try to locate the generated binding signature or any other call sites of newAuth.
rg -nP -C3 '\bnewAuth\s*\('
rg -nP --type=go -C3 '\bfunc\s+NewAuth\b|\bAuth\s+struct\b' || trueRepository: netbirdio/android-client
Length of output: 2016
🏁 Script executed:
#!/bin/bash
# Check mdmConfig to see if it contains server address
rg -nP -C5 'mdmConfig' tool/src/main/java/io/netbird/client/tool/EngineRunner.java | head -100
# Look for any server address constants or defaults
rg -nP 'managementServer|serverAddress|baseURL|apiUrl' app/src/main/java/ --max-count 20
# Check if there are other Android.newAuth calls
rg -nP 'newAuth' tool/src/main/java/ app/src/main/java/ | wc -l
rg -nP 'newAuth' --context 5 tool/src/main/java/ app/src/main/java/Repository: netbirdio/android-client
Length of output: 7692
🏁 Script executed:
#!/bin/bash
# Check if mdmConfig has any server address field
rg -nP 'class ManagedConfig|interface ManagedConfig' -A 20
# Look for Preferences.defaultServer()
rg -nP 'defaultServer|LOGTAG' app/src/main/java/io/netbird/client/ui/ tool/src/main/java/ --max-count 15
# Search for ManagedConfigReader and see what it reads
rg -nP 'class ManagedConfigReader' -A 30
# Check if there's any documentation about the second parameter
rg -nP 'newAuth.*comment|newAuth.*//|newAuth.*\*' -B 2 -A 2Repository: netbirdio/android-client
Length of output: 17522
🏁 Script executed:
#!/bin/bash
# Check what Preferences.defaultServer() returns
rg -nP 'public static String defaultServer' -A 5 app/src/main/java/io/netbird/client/tool/Preferences.java
# Look for any comments about empty string in MDM flow
rg -nP 'MDM|setup.?key' -B 2 -A 2 tool/src/main/java/io/netbird/client/tool/EngineRunner.java
# Check if ManagedConfig has any server-related fields/methods
rg -nP 'getManagementServer|serverAddress|server' tool/src/main/java/io/netbird/client/tool/ --type=java
# Look for any explanation of the second parameter in comments/docs
rg -nP '//.*newAuth|/\*.*newAuth' -B 2 -A 2Repository: netbirdio/android-client
Length of output: 2345
🏁 Script executed:
#!/bin/bash
# Find Preferences.java
fd -type f -name 'Preferences.java'
# Search for defaultServer method anywhere
rg -nP 'defaultServer' -B 2 -A 5Repository: netbirdio/android-client
Length of output: 3484
🏁 Script executed:
#!/bin/bash
# Look for git history of this line
git log -p --follow -S 'newAuth(configurationFilePath, "")' -- tool/src/main/java/io/netbird/client/tool/EngineRunner.java 2>/dev/null | head -60 || echo "No git history found"
# Look for any TODOs or FIXMEs around the MDM code
rg -nP 'TODO|FIXME|XXX|HACK' -B 2 -A 2 tool/src/main/java/io/netbird/client/tool/EngineRunner.java
# Check git log summary for this file
git log --oneline tool/src/main/java/io/netbird/client/tool/EngineRunner.java 2>/dev/null | head -20 || trueRepository: netbirdio/android-client
Length of output: 124
🏁 Script executed:
#!/bin/bash
# Check the specific commit that added this code
git show 045d922 --stat | head -40
# Also show the full diff of that commit for EngineRunner.java
git show 045d922 -- tool/src/main/java/io/netbird/client/tool/EngineRunner.java | head -150Repository: netbirdio/android-client
Length of output: 9100
Use Preferences.defaultServer() for the management server address in MDM setup-key enrollment.
Passing an empty string here is inconsistent with ChangeServerFragmentViewModel, which explicitly uses Preferences.defaultServer() ("https://api.netbird.io") when enrolling with a setup key. Either pass the default server address directly:
io.netbird.gomobile.android.Auth auth =
io.netbird.gomobile.android.Android.newAuth(configurationFilePath, Preferences.defaultServer());Or document in code why the empty string is intentional and what the Go SDK does with it (e.g., whether it has a built-in fallback).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java` around lines 109
- 114, The MDM setup-key enrollment is calling
Android.newAuth(configurationFilePath, "") which passes an empty server address;
update EngineRunner to use Preferences.defaultServer() instead (i.e., call
Android.newAuth with Preferences.defaultServer()) so behavior matches
ChangeServerFragmentViewModel, or add a brief comment next to the
Android.newAuth call documenting why an empty string is intentional and what the
Go SDK fallback behavior is; locate the call in EngineRunner where
io.netbird.gomobile.android.Auth auth is created and adjust the second argument
accordingly while leaving the subsequent
auth.loginWithSetupKeySync(mdmConfig.getSetupKey(), DeviceName.getDeviceName())
unchanged.
|
Thank you, I'll review these automated code review comments. |
- EngineRunner: add MDM_CONFIG_LOCK to serialize config apply operations - EngineRunner: pass management URL (not empty string) to newAuth - ManagedConfigReceiver: use goAsync() + executor to avoid main-thread ANR - ManagedConfigReceiver: use shared MDM_CONFIG_LOCK for concurrency safety - app_restrictions.xml: use @string/ resource references for localization - strings.xml: add MDM restriction title/description string resources
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
tool/src/main/java/io/netbird/client/tool/EngineRunner.java (1)
116-133:⚠️ Potential issue | 🟡 MinorSilent setup-key login still runs on every engine start.
With the lock fix in place, this block now runs on every
runClient()invocation as long as the admin leavessetupKeyin the MDM payload. For already-enrolled devices this triggers an unnecessary round-trip to the management server on each VPN start and produces log noise. Consider gating on a "not yet enrolled" signal (presence of a peer key in the active profile / device registration flag) rather than solely onmdmConfig.hasSetupKey().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java` around lines 116 - 133, The silent setup-key login runs on every runClient() because it only checks mdmConfig.hasSetupKey(); change EngineRunner to first verify the device is not already enrolled before calling io.netbird.gomobile.android.Android.newAuth(...) and auth.loginWithSetupKeySync(...). Specifically, gate the block by inspecting the active profile/registration state (e.g., check for a peer key or a device registration flag on the current profile returned by the engine/profile APIs) and only attempt setup-key login when that check indicates the device is unregistered; keep the existing try/catch/logging around auth.loginWithSetupKeySync and DeviceName.getDeviceName().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java`:
- Around line 118-125: EngineRunner is passing mdmConfig.getSetupKey() as the
second arg to io.netbird.gomobile.android.Android.newAuth(...) which expects the
management server URL; change the call in EngineRunner to pass the management
URL instead (e.g., mdmConfig.getManagementURL()), and if that getter is
null/empty fall back to Preferences.defaultServer() or rely on the
configurationFilePath-only behavior; update the Android.newAuth(...) invocation
accordingly so it receives the correct server URL rather than the setup key, and
leave the subsequent auth.loginWithSetupKeySync(...) call unchanged.
---
Duplicate comments:
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java`:
- Around line 116-133: The silent setup-key login runs on every runClient()
because it only checks mdmConfig.hasSetupKey(); change EngineRunner to first
verify the device is not already enrolled before calling
io.netbird.gomobile.android.Android.newAuth(...) and
auth.loginWithSetupKeySync(...). Specifically, gate the block by inspecting the
active profile/registration state (e.g., check for a peer key or a device
registration flag on the current profile returned by the engine/profile APIs)
and only attempt setup-key login when that check indicates the device is
unregistered; keep the existing try/catch/logging around
auth.loginWithSetupKeySync and DeviceName.getDeviceName().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 209f6e5f-93fa-4663-b2af-2536a0da5217
📒 Files selected for processing (4)
app/src/main/res/values/strings.xmlapp/src/main/res/xml/app_restrictions.xmltool/src/main/java/io/netbird/client/tool/EngineRunner.javatool/src/main/java/io/netbird/client/tool/ManagedConfigReceiver.java
✅ Files skipped from review due to trivial changes (1)
- app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/main/res/xml/app_restrictions.xml
- tool/src/main/java/io/netbird/client/tool/ManagedConfigReceiver.java
Use mdmConfig.getManagementURL() so NewAuth connects to the correct MDM-specified server instead of using the setup key as the URL.
|
Hello @dbrieck! |
Summary
Add Android Enterprise managed configurations support, allowing MDM solutions (Intune, Google Workspace, etc.) to push NetBird configuration to managed devices for zero-touch deployment.
Related to netbirdio/netbird#1918
Changes
app/src/main/res/xml/app_restrictions.xml— Android Enterprise managed configuration schema defining 7 keys:managementUrl,setupKey,adminUrl,preSharedKey,rosenpassEnabled,rosenpassPermissive,disableAutoConnectapp/src/main/AndroidManifest.xml— RegisteredAPP_RESTRICTIONSmetadata andManagedConfigReceiverbroadcast receivertool/.../ManagedConfigReader.java— Reads MDM config fromRestrictionsManager.getApplicationRestrictions()into Go SDKManagedConfigtool/.../ManagedConfigReceiver.java—BroadcastReceiverforACTION_APPLICATION_RESTRICTIONS_CHANGEDto handle runtime MDM updatestool/.../EngineRunner.java— Applies MDM config and auto-login with setup key before VPN engine startHow It Works
ManagedConfigReaderreads fromRestrictionsManagerManagedConfigReceiverDependencies
Summary by CodeRabbit