Skip to content

[debugging] Protect managed-only cold activity startup from ANRs - #12786

Draft
davidnguyen-tech wants to merge 2 commits into
dotnet:mainfrom
davidnguyen-tech:davidnguyen-tech-android-managed-launch-protection
Draft

davidnguyen-tech wants to merge 2 commits into
dotnet:mainfrom
davidnguyen-tech:davidnguyen-tech-android-managed-launch-protection

Conversation

@davidnguyen-tech

Copy link
Copy Markdown
Member

Summary

Protect eligible managed-only cold activity launches with a bounded, transient am set-debug-app <package> transaction, without -w or --persistent. Keep the existing Java-debugging opt-in and MSBuild defaults unchanged.

Disabling am start -D avoids Java debugger waiting but does not mark an intentional early managed pause as debugging. AOSP distinguishes DEBUG_ON from DEBUG_WAIT; the former retains ActivityManager's per-process debugging/ANR exemption without waiting for a Java debugger. See ActivityManagerService, ActivityThread, and ProcessErrorStateRecord.

Behavior and safety boundaries

  • Protect only Android 12+ forced cold, non-waiting, non-repeated activity launches targeting the sole device user, with an explicit component in the configured package and confirmed package process.
  • Observe both restored global debug-app state and the matching process's mDebugging=true after attach. PID visibility alone is insufficient; no am start -W or application-code readiness wait is introduced.
  • Serialize activity debugging per device serial within the host process; use independent bounded cleanup after failure/cancellation and preserve primary errors.
  • Preserve legacy launches with explicit unprotected diagnostics for warm/multi-user/implicit/custom-process/unconfirmed-metadata/unsupported initial dump-layout paths. Keep strict ownership/layout errors after mutation. Check cancellation around each fallback.
  • Preserve explicit Java-debugging, no-debug, null-command, broadcast and instrumentation behavior outside this transaction.

Android's debug-app command force-stops a package across all users, which is why protection is restricted to single-user cold launches. There is no atomic compare-and-clear API: unrelated adb clients, same-package concurrent external ownership, disconnection and late remote execution remain platform limitations. An observed different owner is not cleared.

Validation

126 targeted tests pass against the real shared launch implementation and ADB transport using a private TCP fake server. Regressions were demonstrated red before corrections, including real Stopping:/Starting: output, custom-process eligibility, component-only launches, fallback cancellation, and gate diagnostics. Coverage includes PID-before-attach, timeout, cleanup/launch double failure, ownership changes, retries, malformed post-mutation state and pre-arm unsupported layouts.

The original reviewed implementation is commit 663060b22b02f872217f8c602db5671ad64ce03a. Publication merge 06b2860e6fb1f6e9adc71de71aaa7b8a77946b59 preserves that commit and incorporates upstream main 00c4f3d99d4a138fc3c0818369126b18b80457ee, whose sole additional change is nullable annotations in the installer's Platform.cs. The seven-file implementation patch is unchanged. All 126 targeted tests were rerun on the publication merge: 126 passed, 0 failed, 0 skipped.

Built the supported netstandard2.0 product libraries and AndroidSdk netstandard2.0/net10.0 graph; net10.0 tests ran with configured major runtime roll-forward on .NET 11. Fixtures are source-shaped deterministic responses, not device captures. No live device/OEM breakpoint validation or full native SDK build was performed. This draft is not a production runtime sign-off.

davidnguyen-tech and others added 2 commits September 14, 2026 21:45
Use a bounded transient non-wait debug-app transaction for eligible cold activity launches, observe process attach before clearing, and preserve unsupported launch paths with explicit diagnostics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment on lines 31 to +36
public async static Task StartWithDebuggingAsync(this IAndroidDevice device, ExecutionConfiguration configuration, CancellationToken token)
{
if (configuration.RunCommand is AmStartCommand) {
var gate = ManagedActivityLaunch.GetGate (device.ID);
if (!await gate.WaitAsync (TimeSpan.FromSeconds (30), token).ConfigureAwait (false))
throw new TimeoutException (Properties.Resources.ManagedLaunchGateTimeout);

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.

We want to delete the src/Mono.AndroidTools and src/Xamarin.AndroidTools projects.

Could the fix (am set-debug-app <package> call) go in:

It should be behind existing flags that knows they are attempting to debug.

Eventually, we'll unify these two, but I was going to wait until we branch for .NET 11.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants