Skip to content

[Microsoft.Android.Run] Await app shutdown on Ctrl+C - #12318

Open
jonathanpeppers wants to merge 3 commits into
dotnet:mainfrom
jonathanpeppers:jonathanpeppers-fix-run-cancellation
Open

[Microsoft.Android.Run] Await app shutdown on Ctrl+C#12318
jonathanpeppers wants to merge 3 commits into
dotnet:mainfrom
jonathanpeppers:jonathanpeppers-fix-run-cancellation

Conversation

@jonathanpeppers

Copy link
Copy Markdown
Member

Pressing Ctrl+C during dotnet run could let Microsoft.Android.Run exit before its fire-and-forget adb shell am force-stop completed, leaving the application running on the device.

Move shutdown into the awaited run lifecycle. The Ctrl+C handler now only signals cancellation, while final cleanup waits for force-stop before returning the standard SIGINT exit code. The device integration test now requires the app to be stopped immediately when dotnet run exits instead of polling around the race.

Fixes #11264

  • Useful description of why the change is necessary.
  • Links to issues fixed
  • Unit tests: strengthened DotNetRunCtrlC; Microsoft.Android.Run builds with 0 warnings and errors.

jonathanpeppers and others added 2 commits August 7, 2026 14:38
Ensure Ctrl+C waits for adb force-stop before the run tool exits, while preserving the standard cancellation exit code. Strengthen the device test to require shutdown completion without polling.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace duplicated magic values with a documented constant.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 7, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a race where Microsoft.Android.Run could exit on Ctrl+C before adb shell am force-stop completed, leaving the app running on the device. It moves shutdown into the awaited run lifecycle and tightens the device integration test to require the app to be stopped when dotnet run exits.

Changes:

  • Await StopAppAsync() during the run lifecycle shutdown instead of fire-and-forget from the Ctrl+C handler.
  • Standardize SIGINT exit code handling via a CtrlCExitCode constant and improve StopAppAsync() error reporting.
  • Strengthen DotNetRunCtrlC device integration test to fail if cancellation is reported as an error and to require immediate app shutdown.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Microsoft.Android.Run/Program.cs Moves Ctrl+C shutdown work into the awaited lifecycle and adjusts stop-app behavior/exit code handling.
tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs Updates Ctrl+C test expectations to validate no cancellation error and immediate device app termination.

Comment thread src/Microsoft.Android.Run/Program.cs Outdated
Track Ctrl+C independently from the cancellation token source because instrumentation also cancels that source during normal cleanup.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80fed9de-25fd-4f10-9c83-145a24798a3c
@jonathanpeppers

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes — 0 errors, 1 warning, 1 suggestion.

The cleanup is now correctly awaited before exit, and the immediate device-state assertion is stronger. The awaited adb force-stop still needs a bounded shutdown path so an unresponsive adb cannot make Ctrl+C hang indefinitely; the regression test should also lock in exit code 130.

CI is still in progress; all completed checks currently pass.

Generated by Android PR Reviewer for #12318 · gpt56 · 160.9 AIC · ⌖ 10.1 AIC · ⊞ 25.3K
Comment /review to run again

var userArg = string.IsNullOrEmpty (deviceUserId) ? "" : $" --user {deviceUserId}";
await AdbHelper.RunAsync (adbPath, adbTarget, $"shell am force-stop{userArg} {package}", CancellationToken.None, verbose);
try {
var (exitCode, _, error) = await AdbHelper.RunAsync (adbPath, adbTarget, $"shell am force-stop{userArg} {package}", CancellationToken.None, verbose);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 ⚠️ Async pattern — This newly awaited cleanup has no cancellation or timeout. ProcessUtils.StartProcess waits for the process-exit event indefinitely, and OnCancelKeyPress keeps setting e.Cancel = true; if adb shell am force-stop hangs, dotnet run can no longer be stopped with Ctrl+C (including a second press). Please bound this cleanup with a timeout, or make a subsequent Ctrl+C terminate/cancel the in-flight cleanup.

Rule: Cancellation and timeout propagation

string outputText = output.ToString ();
Assert.IsTrue (outputText.Contains ("Stopping application..."),
$"Output should contain 'Stopping application...' from Microsoft.Android.Run's Ctrl+C handler");
Assert.IsFalse (outputText.Contains ("Error: The operation was canceled."),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 💡 Testing — The production change promises exit code 130, but this test only checks output and process termination; it would pass if cancellation returned 0 or 1. Please assert process.ExitCode == 130 after WaitForExit so the behavior introduced with CtrlCExitCode is covered.

Rule: Verify observable regression behavior

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.

dotnet run Ctrl+C does not stop the app on the device

2 participants