Add Android debug bundle support with Troubleshoot UI#163
Conversation
Pass context.getCacheDir() through AndroidPlatformFiles so the Go debug bundle generator can create temporary zip files in a writable directory instead of /data/local/tmp/.
Move trace log toggle and log sharing from Advanced to a new Troubleshoot fragment accessible via the drawer menu. Replace the old logcat share with debug bundle generation and upload that copies the upload key to clipboard. Add anonymize toggle. Works with or without a running engine.
|
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 24 minutes and 35 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 ignored due to path filters (20)
📒 Files selected for processing (7)
📝 WalkthroughWalkthroughA new "Troubleshoot" feature is introduced with a dedicated fragment, relocating trace-log configuration from Advanced settings. A Changes
Sequence DiagramsequenceDiagram
participant User as User
participant Fragment as TroubleshootFragment
participant Service as ServiceAccessor
participant VPN as VPNService.MyLocalBinder
participant Engine as EngineRunner
participant GoClient as goClient (C/Go)
participant Clipboard as ClipboardManager
User->>Fragment: Clicks debug-bundle button
Fragment->>Fragment: Disable button, start async task
Fragment->>Service: debugBundle(anonymize)
Service->>VPN: debugBundle(anonymize)
VPN->>Engine: debugBundle(anonymize)
Engine->>Engine: Fetch profile paths & cacheDir
Engine->>Engine: Create AndroidPlatformFiles
Engine->>GoClient: debugBundle(platformFiles, anonymize)
GoClient-->>Engine: Return debug bundle (String)
Engine-->>VPN: Return result
VPN-->>Service: Return result
Service-->>Fragment: Return result
Fragment->>Fragment: Re-enable button
Fragment->>Clipboard: Copy bundle to clipboard
Fragment->>Fragment: Show success toast
Note over User: Debug bundle copied & ready
alt Exception occurs
Service-->>Fragment: Throw Exception
Fragment->>Fragment: Re-enable button
Fragment->>Fragment: Log error
Fragment->>Fragment: Show error toast (exception message)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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)
tool/src/main/java/io/netbird/client/tool/EngineRunner.java (1)
94-95: ExtractAndroidPlatformFilescreation into one helper to avoid drift.The same construction logic is duplicated in two places; a helper reduces future mismatch risk when path inputs evolve.
♻️ Small refactor
- var platformFiles = new AndroidPlatformFiles(configurationFilePath, stateFilePath, context.getCacheDir().getAbsolutePath()); + var platformFiles = createPlatformFiles(configurationFilePath, stateFilePath); ... - String cacheDir = context.getCacheDir().getAbsolutePath(); - var platformFiles = new AndroidPlatformFiles(configPath, statePath, cacheDir); + var platformFiles = createPlatformFiles(configPath, statePath); + +private AndroidPlatformFiles createPlatformFiles(String configPath, String statePath) { + return new AndroidPlatformFiles(configPath, statePath, context.getCacheDir().getAbsolutePath()); +}Also applies to: 218-221
🤖 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 94 - 95, Extract the duplicated AndroidPlatformFiles construction into a single helper method (e.g., createAndroidPlatformFiles) that accepts configurationFilePath, stateFilePath and the cache directory path (use context.getCacheDir().getAbsolutePath()) and returns a new AndroidPlatformFiles instance; replace both direct instantiations (the one using "new AndroidPlatformFiles(configurationFilePath, stateFilePath, context.getCacheDir().getAbsolutePath())" and the other occurrence) with calls to this helper to centralize construction and avoid future drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/io/netbird/client/MainActivity.java`:
- Around line 418-421: The code performs a check-then-use on the field mBinder
causing a potential race; capture a local snapshot (e.g., BinderType binder =
mBinder) before the null check and then use that local variable when calling
debugBundle(anonymize) in MainActivity to avoid mBinder changing between the
null check and the call; if the local snapshot is null, throw the same
Exception("VPN service not connected") so behavior is unchanged.
In
`@app/src/main/java/io/netbird/client/ui/troubleshoot/TroubleshootFragment.java`:
- Around line 69-87: The background thread callbacks access fragment fields
(binding/buttonDebugBundle) after async work which can be nullified in
onDestroyView; wrap all UI updates inside activity.runOnUiThread with a
null-check for binding (or capture a local reference to binding) and verify the
fragment is still added (isAdded()) before touching binding/buttonDebugBundle or
showing Toasts; apply these guards in both the success path (where debugBundle
key is copied to clipboard) and the exception path so no UI is accessed after
onDestroyView.
In `@app/src/main/res/layout/fragment_troubleshoot.xml`:
- Around line 76-80: The anonymize SwitchMaterial
(android:id="@+id/switch_anonymize") currently defaults to unchecked in
fragment_troubleshoot.xml causing debug bundles to include sensitive data;
update the Switch XML to set android:checked="true" so anonymization is enabled
by default, and also ensure any initialization code that later reads/writes this
control (the code that binds or reads switch_anonymize) respects and persists
this default state to preferences if applicable (i.e., add or verify a
preference key handling for switch_anonymize similar to the trace log switch).
In `@netbird`:
- Line 1: The netbird submodule currently points at a non-existent SHA
(a35ecf9aa8d8867df5f13a98840e152aa2cda0b4) so CI will fail; fix it by checking
out the netbird submodule remote, fetch the expected upstream branch, identify a
valid commit on that branch, update the netbird submodule pointer to that
reachable commit, stage the changed gitlink in the parent repo and commit/push
the update so .gitmodules/index reference now point to the valid commit on the
upstream branch.
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java`:
- Around line 217-223: debugBundle currently calls goClient.debugBundle(...)
without the error-handling and synchronization used by other control methods;
wrap the call in a synchronized block on the same lock used elsewhere
(protecting goClient) and surround the call with try-catch(Exception e) that
logs the error and calls notifyError(e) to propagate failures to listeners,
keeping the creation of AndroidPlatformFiles and use of profileManager unchanged
and mirroring the pattern used in selectRoute/deselectRoute/renewTun.
---
Nitpick comments:
In `@tool/src/main/java/io/netbird/client/tool/EngineRunner.java`:
- Around line 94-95: Extract the duplicated AndroidPlatformFiles construction
into a single helper method (e.g., createAndroidPlatformFiles) that accepts
configurationFilePath, stateFilePath and the cache directory path (use
context.getCacheDir().getAbsolutePath()) and returns a new AndroidPlatformFiles
instance; replace both direct instantiations (the one using "new
AndroidPlatformFiles(configurationFilePath, stateFilePath,
context.getCacheDir().getAbsolutePath())" and the other occurrence) with calls
to this helper to centralize construction and avoid future drift.
🪄 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: 772db875-6634-4729-a33d-4e0a4cc71206
📒 Files selected for processing (14)
app/src/main/java/io/netbird/client/MainActivity.javaapp/src/main/java/io/netbird/client/ServiceAccessor.javaapp/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.javaapp/src/main/java/io/netbird/client/ui/troubleshoot/TroubleshootFragment.javaapp/src/main/res/drawable/ic_menu_troubleshoot.xmlapp/src/main/res/layout/fragment_advanced.xmlapp/src/main/res/layout/fragment_troubleshoot.xmlapp/src/main/res/menu/activity_main_drawer.xmlapp/src/main/res/navigation/mobile_navigation.xmlapp/src/main/res/values/strings.xmlnetbirdtool/src/main/java/io/netbird/client/tool/AndroidPlatformFiles.javatool/src/main/java/io/netbird/client/tool/EngineRunner.javatool/src/main/java/io/netbird/client/tool/VPNService.java
💤 Files with no reviewable changes (1)
- app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java
| if (mBinder == null) { | ||
| throw new Exception("VPN service not connected"); | ||
| } | ||
| return mBinder.debugBundle(anonymize); |
There was a problem hiding this comment.
Use a local binder snapshot to avoid a check-then-use race.
mBinder is read twice; it can change between the null-check and call, especially since this path is triggered off the UI thread. Capture once, then use that reference.
💡 Suggested fix
`@Override`
public String debugBundle(boolean anonymize) throws Exception {
- if (mBinder == null) {
+ VPNService.MyLocalBinder binder = mBinder;
+ if (binder == null) {
throw new Exception("VPN service not connected");
}
- return mBinder.debugBundle(anonymize);
+ return binder.debugBundle(anonymize);
}📝 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.
| if (mBinder == null) { | |
| throw new Exception("VPN service not connected"); | |
| } | |
| return mBinder.debugBundle(anonymize); | |
| VPNService.MyLocalBinder binder = mBinder; | |
| if (binder == null) { | |
| throw new Exception("VPN service not connected"); | |
| } | |
| return binder.debugBundle(anonymize); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/io/netbird/client/MainActivity.java` around lines 418 -
421, The code performs a check-then-use on the field mBinder causing a potential
race; capture a local snapshot (e.g., BinderType binder = mBinder) before the
null check and then use that local variable when calling debugBundle(anonymize)
in MainActivity to avoid mBinder changing between the null check and the call;
if the local snapshot is null, throw the same Exception("VPN service not
connected") so behavior is unchanged.
Remove density-specific PNG icons and replace with vector drawables using Material Design outlined style for consistent appearance.
Wrap goClient call with try-catch to log errors, matching the pattern used by selectRoute and deselectRoute.
Check binding and isAdded() before accessing UI in background thread callbacks to prevent NPE if the fragment is destroyed while the debug bundle upload is in progress.
Add Android debug bundle support with Troubleshoot UI
Summary by CodeRabbit
New Features
Refactor