Mdm integration#198
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds end-to-end Android MDM managed-configuration support. A new ChangesAndroid MDM Managed-Configuration Support
Sequence Diagram(s)sequenceDiagram
participant AndroidOS
participant VPNService
participant EngineRestarter
participant EngineRunner
participant MDMPolicyFetcher
participant RestrictionsManager
AndroidOS->>VPNService: ACTION_APPLICATION_RESTRICTIONS_CHANGED
VPNService->>EngineRestarter: requestRestartNow()
EngineRestarter->>EngineRunner: restartRunnable (immediate)
EngineRunner->>MDMPolicyFetcher: fetchJSON()
MDMPolicyFetcher->>RestrictionsManager: getApplicationRestrictions()
RestrictionsManager-->>MDMPolicyFetcher: Bundle
MDMPolicyFetcher-->>EngineRunner: JSON string
EngineRunner->>EngineRunner: apply MDM policy overlay and run
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
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: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java`:
- Around line 358-370: In the lockSwitchIfManaged method, reorder the operations
to call setEnabled(false) before setChecked(value) so that the
OnCheckedChangeListener does not fire during MDM lock application. Additionally,
add an early-exit guard at the beginning of each OnCheckedChangeListener
implementation (in initializeEngineConfigSwitches and the Rosenpass listeners)
that checks if the switch/control is enabled and returns immediately if it is
disabled, preventing unnecessary preference commits and state manipulations.
- Around line 10-12: Remove the duplicate import statement for android.view.View
in the AdvancedFragment.java file. The import android.view.View appears twice
consecutively in the import block and only one instance should be retained.
Delete the second occurrence of the duplicate import android.view.View
statement, keeping the first one.
In `@tool/src/main/java/io/netbird/client/tool/EngineRestarter.java`:
- Around line 318-325: The requestRestartNow() method can drop pending restart
requests when a restart is already in progress because it immediately posts the
runnable, which then clears the restartScheduled flag in restartEngine() and
exits early without replaying the request. Instead of directly posting the
runnable, check if a restart is currently in progress using isRestartInProgress,
and if so, queue the request rather than posting it immediately. Add a queue
data structure to track pending restart requests, and modify restartEngine() to
drain and process any queued requests when the current restart cycle completes
(regardless of success, error, or timeout). This ensures that MDM policy changes
are not dropped and converge deterministically by replaying queued requests
after each restart cycle finishes.
🪄 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: 17b0a625-d9d1-424d-a04d-c05ce89c4860
📒 Files selected for processing (11)
app/src/main/AndroidManifest.xmlapp/src/main/java/io/netbird/client/MDMPolicyFetcher.javaapp/src/main/java/io/netbird/client/MyApplication.javaapp/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.javaapp/src/main/res/values/arrays.xmlapp/src/main/res/values/strings.xmlapp/src/main/res/xml/app_restrictions.xmlnetbirdtool/src/main/java/io/netbird/client/tool/EngineRestarter.javatool/src/main/java/io/netbird/client/tool/EngineRunner.javatool/src/main/java/io/netbird/client/tool/VPNService.java
| import android.view.View; | ||
| import android.view.LayoutInflater; | ||
| import android.view.View; |
There was a problem hiding this comment.
Duplicate import of android.view.View.
Line 10 and line 12 both import android.view.View.
🧹 Proposed fix
import android.widget.CompoundButton;
import android.widget.EditText;
-import android.view.View;
import android.view.LayoutInflater;
import android.view.View;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import android.view.View; | |
| import android.view.LayoutInflater; | |
| import android.view.View; | |
| import android.view.LayoutInflater; | |
| import android.view.View; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java` around
lines 10 - 12, Remove the duplicate import statement for android.view.View in
the AdvancedFragment.java file. The import android.view.View appears twice
consecutively in the import block and only one instance should be retained.
Delete the second occurrence of the duplicate import android.view.View
statement, keeping the first one.
| 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
Listener side-effects fire during MDM lock application.
setChecked(value) at line 364 triggers the OnCheckedChangeListener before setEnabled(false) runs at line 365. Every MDM-locked switch will execute its listener, causing:
- Unnecessary
goPreferences.commit()calls for each locked switch - For the Rosenpass switch, the listener also manipulates the permissive switch's enabled/checked state (lines 131-134), potentially conflicting with MDM enforcement of
rosenpassPermissive
Fix by disabling the switch before setting its value, then add guards in listeners:
🔧 Proposed fix for lockSwitchIfManaged
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.setEnabled(false);
switchCtrl.setChecked(value);
- switchCtrl.setEnabled(false);
if (parentLayout != null) {
parentLayout.setEnabled(false);
parentLayout.setClickable(false);
}
}Then add an early-exit guard to each listener (example for one switch):
binding.switchDisableClientRoutes.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (!buttonView.isEnabled()) return; // Skip writes when MDM-locked
try {
goPreferences.setDisableClientRoutes(isChecked);
goPreferences.commit();
} catch (Exception e) {
Log.e(LOGTAG, "Failed to set disable client routes", e);
}
});Apply the same guard to all listeners in initializeEngineConfigSwitches() and the Rosenpass listeners.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java` around
lines 358 - 370, In the lockSwitchIfManaged method, reorder the operations to
call setEnabled(false) before setChecked(value) so that the
OnCheckedChangeListener does not fire during MDM lock application. Additionally,
add an early-exit guard at the beginning of each OnCheckedChangeListener
implementation (in initializeEngineConfigSwitches and the Rosenpass listeners)
that checks if the switch/control is enabled and returns immediately if it is
disabled, preventing unnecessary preference commits and state manipulations.
| public void requestRestartNow() { | ||
| Log.d(LOGTAG, "explicit restart requested (no debounce)"); | ||
| synchronized (restartLock) { | ||
| restartScheduled = true; | ||
| handler.removeCallbacks(restartRunnable); | ||
| handler.post(restartRunnable); | ||
| } | ||
| } |
There was a problem hiding this comment.
Immediate MDM restart requests can be dropped during an in-flight restart.
If requestRestartNow() is called while a restart is already in progress, the posted runnable can execute, clear the scheduled flag in restartEngine(), then exit early on isRestartInProgress. That drops the newer policy-change request instead of replaying it after the current cycle.
Queue the pending request and drain it when the current restart completes (success/error/timeout) so MDM changes converge deterministically.
Suggested direction
+// keep a pending-restart intent if a request arrives mid-cycle
+private boolean restartRequestedDuringInFlight = false;
public void requestRestartNow() {
Log.d(LOGTAG, "explicit restart requested (no debounce)");
synchronized (restartLock) {
- restartScheduled = true;
- handler.removeCallbacks(restartRunnable);
- handler.post(restartRunnable);
+ restartScheduled = true;
+ if (isRestartInProgress) {
+ restartRequestedDuringInFlight = true;
+ return;
+ }
+ handler.removeCallbacks(restartRunnable);
+ handler.post(restartRunnable);
}
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tool/src/main/java/io/netbird/client/tool/EngineRestarter.java` around lines
318 - 325, The requestRestartNow() method can drop pending restart requests when
a restart is already in progress because it immediately posts the runnable,
which then clears the restartScheduled flag in restartEngine() and exits early
without replaying the request. Instead of directly posting the runnable, check
if a restart is currently in progress using isRestartInProgress, and if so,
queue the request rather than posting it immediately. Add a queue data structure
to track pending restart requests, and modify restartEngine() to drain and
process any queued requests when the current restart cycle completes (regardless
of success, error, or timeout). This ensures that MDM policy changes are not
dropped and converge deterministically by replaying queued requests after each
restart cycle finishes.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@netbird`:
- Line 1: The netbird submodule is pinned to a non-existent commit
(bec26d5a14e7ac6f1d97ff533bf5275577bb30db) in the remote repository, preventing
the netbird.aar artifact generation which is required for MDMPolicyFetcher.java
to access the PolicyFetcher interface. Update the submodule pointer in the
.gitmodules file and the submodule reference to point to a valid, existing
commit in the netbird repository. Verify the commit exists by checking the
remote repository at https://github.com/netbirdio/netbird.git before updating.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java (1)
60-64:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRecursively normalize array elements before emitting JSON.
bundleToJSON()recurses for directBundlevalues, but array elements are appended raw. If an array contains structured values, the JSON payload diverges from the expected Android→Go contract.Suggested fix
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); + if (item instanceof Bundle) { + arr.put(bundleToJSON((Bundle) item)); + } else { + arr.put(item); + } } obj.put(key, arr); } else { obj.put(key, value); } } return obj; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java` around lines 60 - 64, In the `bundleToJSON()` method where Object arrays are handled, the array elements are being appended directly without recursive normalization. For each item in the array, check if it is a Bundle instance and recursively call `bundleToJSON()` on it (just as is done for direct Bundle values), or append the raw item if it is not a Bundle. This ensures that structured values nested within arrays are properly converted to JSON according to the Android→Go contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java`:
- Around line 60-64: In the `bundleToJSON()` method where Object arrays are
handled, the array elements are being appended directly without recursive
normalization. For each item in the array, check if it is a Bundle instance and
recursively call `bundleToJSON()` on it (just as is done for direct Bundle
values), or append the raw item if it is not a Bundle. This ensures that
structured values nested within arrays are properly converted to JSON according
to the Android→Go contract.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4725d539-9354-472b-8893-c588310d5ce9
📒 Files selected for processing (4)
app/src/main/java/io/netbird/client/MyApplication.javanetbirdtool/src/main/java/io/netbird/client/tool/EngineRunner.javatool/src/main/java/io/netbird/client/tool/MDMPolicyFetcher.java
✅ Files skipped from review due to trivial changes (1)
- app/src/main/java/io/netbird/client/MyApplication.java
Adds Android MDM integration
Summary by CodeRabbit