Repository: beeware/briefcase
Pull request #733 (2022-05-08 17:06:06)

Support for Popen start_new_session Flag
## Problem
Currently, all processes invoked by Briefcase are added to the process group of the python interpreter running Briefcase. This can be beneficial since it allows the OS to handle these processes as a single entity.

However, in certain cases, it is necessary for a spawned process to run independently of its parent process. For example, after the Android emulator is started and adb's `logcat` is running, the user is instructed to use CTRL+C to return to the command prompt. Sending `SIGINT` (via CTRL+C) will return the user to the prompt but also kills the process for the emulator (and any other spawned processes for that matter). This happens because the `SIGINT` is propagated to all processes in the group.

## Solution
Add `start_new_session=True` to the relevant `subprocess` calls that need to be treated independently of Briefcase itself.

This is now implemented for invoking the Android emulator to prevent CTRL+C for adb's `logcat` also killing the process for the emulator.

## Implementation
The `Popen` parameter `start_new_session` implements the POSIX syscall `setsid` which ensures a spawned process runs in a new session (and therefore, by definition, a new process group). (The `start_new_session` parameter was added to replace the idiom of passing `preexec_fn=os.setsid` to `Popen` since it has problematic edge cases....and it's being [deprecated](https://bugs.python.org/issue38435).) Since a `console` or controlling terminal is not shared across sessions, the new process will be unaffected by even terminating the originating terminal window.

Windows, though, is a different story. The approach of `subprocess` in the `stdlib` appears to be to expose several underlying process spawn APIs (largely dependent on the underlying platform) instead of an approach that might produce a single API where the underlying implementations aren't so exposed. At any rate, `start_new_session` is not respected on Windows.

To convert `start_new_session=True` for Windows, the two [process creation flags](https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags) below are used:
- `CREATE_NEW_PROCESS_GROUP`: Makes the new process the root process of a new process group. This also disables CTRL+C signal handlers for all processes of the new process group.
- `CREATE_NO_WINDOW`: Creates a new console for the process but does not open a visible window for that console. This flag is used instead of `DETACHED_PROCESS` since the new process can spawn a new console itself (in the absence of one being available) but that console creation will spawn a visible console window. (Related: [Issue 41619](https://bugs.python.org/issue41619))

This configuration conversion for Windows was added to `Subprocess.final_kwargs()`.

## PR Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
- [x] All new features have been tested
- [x] All new features have been documented
- [x] I have read the **CONTRIBUTING.md** file
- [x] I will abide by the code of conduct


codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c354faa) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.27%`.
> The diff coverage is `20.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `92.98% <20.00%> (-7.02%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c354faa) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.27%`.
> The diff coverage is `20.00%`.

> :exclamation: Current head c354faa differs from pull request most recent head 1b09b8b. Consider uploading reports for the commit 1b09b8b to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `92.98% <20.00%> (-7.02%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c354faa) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.27%`.
> The diff coverage is `20.00%`.

> :exclamation: Current head c354faa differs from pull request most recent head c554b68. Consider uploading reports for the commit c554b68 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `92.98% <20.00%> (-7.02%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c554b68) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.05%`.
> The diff coverage is `80.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `98.24% <80.00%> (-1.76%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c554b68) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.05%`.
> The diff coverage is `80.00%`.

> :exclamation: Current head c554b68 differs from pull request most recent head 5535cd7. Consider uploading reports for the commit 5535cd7 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `98.24% <80.00%> (-1.76%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (5535cd7) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.05%`.
> The diff coverage is `80.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `98.24% <80.00%> (-1.76%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (5535cd7) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.05%`.
> The diff coverage is `80.00%`.

> :exclamation: Current head 5535cd7 differs from pull request most recent head 82ddd4f. Consider uploading reports for the commit 82ddd4f to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `98.24% <80.00%> (-1.76%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (5535cd7) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **decrease** coverage by `0.05%`.
> The diff coverage is `80.00%`.

> :exclamation: Current head 5535cd7 differs from pull request most recent head d4e1690. Consider uploading reports for the commit d4e1690 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `98.24% <80.00%> (-1.76%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4e1690) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4e1690) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head d4e1690 differs from pull request most recent head 31b0026. Consider uploading reports for the commit 31b0026 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (31b0026) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (31b0026) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 31b0026 differs from pull request most recent head 3c5cf84. Consider uploading reports for the commit 3c5cf84 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (3c5cf84) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (31b0026) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 31b0026 differs from pull request most recent head 0446840. Consider uploading reports for the commit 0446840 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (0446840) into [main](https://codecov.io/gh/beeware/briefcase/commit/b5ebc6d1c816aada7c1e5c1f605a671a733d1d40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (b5ebc6d) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/commands/dev.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9kZXYucHk=) | `95.23% <0.00%> (-0.15%)` | :arrow_down: |

rmartin16: Sooo....concerning the constants `CREATE_NEW_PROCESS_GROUP` and `CREATE_NO_WINDOW` from `subprocess`....they are actually only available when python is literally on Windows. This is apparently how `subprocess` actually determines whether it's running on Windows or not. I was a little surprised at that... I did consolidate where those constants are specified in the tests and used them as though I would use constants from `subprocess`.
freakboy3742: > Sooo....concerning the constants `CREATE_NEW_PROCESS_GROUP` and `CREATE_NO_WINDOW` from `subprocess`....they are actually only available when python is literally on Windows. 

Well that's... fun :-)

In which case, the approach you've taken here is good (constants in the test classes); the only thing I'd add would be a safety catch test that only runs on Windows, validating that the test constants and the "real" constants are the same.
codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (0446840) into [main](https://codecov.io/gh/beeware/briefcase/commit/c2f57522cf13189b1ca210d66018a6c817f5781f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c2f5752) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 0446840 differs from pull request most recent head 46fc348. Consider uploading reports for the commit 46fc348 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#733](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (46fc348) into [main](https://codecov.io/gh/beeware/briefcase/commit/c2f57522cf13189b1ca210d66018a6c817f5781f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (c2f5752) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/733?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/integrations/android\_sdk.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvYW5kcm9pZF9zZGsucHk=) | `98.85% <ø> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/733/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |


Patch:
diff --git a/changes/733.bugfix.rst b/changes/733.bugfix.rst
new file mode 100644
index 00000000..86c0e2c5
--- /dev/null
+++ b/changes/733.bugfix.rst
@@ -0,0 +1,1 @@
+Using CTRL+C to stop showing Android emulator logs while running the app will no longer cause the emulator to shutdown.
\ No newline at end of file
diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py
index d9afe4ea..716b909f 100644
--- a/src/briefcase/integrations/android_sdk.py
+++ b/src/briefcase/integrations/android_sdk.py
@@ -684,6 +684,7 @@ In future, you can specify this device by running:
                 env=self.env,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
+                start_new_session=True,
             )
 
             # The boot process happens in 2 phases.
diff --git a/src/briefcase/integrations/subprocess.py b/src/briefcase/integrations/subprocess.py
index 290dd4f7..6aec2f01 100644
--- a/src/briefcase/integrations/subprocess.py
+++ b/src/briefcase/integrations/subprocess.py
@@ -62,6 +62,7 @@ class Subprocess:
          * Converting any environment overrides into a full environment
          * Converting the `cwd` into a string
          * Default `text` to True so all outputs are strings
+         * Convert start_new_session=True to creationflags on Windows
         """
         # If `env` has been provided, inject a full copy of the local
         # environment, with the values in `env` overriding the local
@@ -86,6 +87,31 @@ class Subprocess:
         if not ('text' in kwargs or 'universal_newlines' in kwargs):
             kwargs['text'] = True
 
+        # For Windows, convert start_new_session=True to creation flags
+        if self.command.host_os == 'Windows':
+            try:
+                if kwargs.pop('start_new_session') is True:
+                    if 'creationflags' in kwargs:
+                        raise AssertionError(
+                            "Subprocess called with creationflags set and start_new_session=True.\n"
+                            "This will result in CREATE_NEW_PROCESS_GROUP and CREATE_NO_WINDOW being "
+                            "merged in to the creationflags.\n\n"
+                            "Ensure this is desired configuration or don't set start_new_session=True."
+                        )
+                    # CREATE_NEW_PROCESS_GROUP: Makes the new process the root process
+                    #     of a new process group. This also disables CTRL+C signal handlers
+                    #     for all processes of the new process group.
+                    # CREATE_NO_WINDOW: Creates a new console for the process but does not
+                    #     open a visible window for that console. This flag is used instead
+                    #     of DETACHED_PROCESS since the new process can spawn a new console
+                    #     itself (in the absence of one being available) but that console
+                    #     creation will also spawn a visible console window.
+                    new_session_flags = self._subprocess.CREATE_NEW_PROCESS_GROUP | self._subprocess.CREATE_NO_WINDOW
+                    # merge these flags in to any existing flags already provided
+                    kwargs['creationflags'] = kwargs.get('creationflags', 0) | new_session_flags
+            except KeyError:
+                pass
+
         return kwargs
 
     def run(self, args, **kwargs):


Test patch:
diff --git a/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py b/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py
index 6ff1f8e1..7631d60e 100644
--- a/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py
+++ b/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py
@@ -112,6 +112,7 @@ def test_start_emulator(mock_sdk):
         env=mock_sdk.env,
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
+        start_new_session=True,
     )
 
     # There were 5 calls to run
@@ -200,6 +201,7 @@ def test_emulator_fail_to_start(mock_sdk):
         env=mock_sdk.env,
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
+        start_new_session=True,
     )
 
     # There were 2 calls to run, both to get AVD name
@@ -282,6 +284,7 @@ def test_emulator_fail_to_boot(mock_sdk):
         env=mock_sdk.env,
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
+        start_new_session=True,
     )
 
     # There were 4 calls to run before failure
diff --git a/tests/integrations/subprocess/conftest.py b/tests/integrations/subprocess/conftest.py
index f951f873..4e3b283a 100644
--- a/tests/integrations/subprocess/conftest.py
+++ b/tests/integrations/subprocess/conftest.py
@@ -5,6 +5,10 @@ import pytest
 from briefcase.integrations.subprocess import Subprocess
 from briefcase.console import Log
 
+# hardcoded here since subprocess will only include these constants if Python is literally on Windows
+CREATE_NO_WINDOW = 0x8000000
+CREATE_NEW_PROCESS_GROUP = 0x200
+
 
 @pytest.fixture
 def mock_sub():
@@ -27,4 +31,7 @@ def mock_sub():
 
     sub._subprocess.check_output.return_value = "some output line 1\nmore output line 2"
 
+    sub._subprocess.CREATE_NO_WINDOW = CREATE_NO_WINDOW
+    sub._subprocess.CREATE_NEW_PROCESS_GROUP = CREATE_NEW_PROCESS_GROUP
+
     return sub
diff --git a/tests/integrations/subprocess/test_Subprocess__Popen.py b/tests/integrations/subprocess/test_Subprocess__Popen.py
index e621764e..e590ef49 100644
--- a/tests/integrations/subprocess/test_Subprocess__Popen.py
+++ b/tests/integrations/subprocess/test_Subprocess__Popen.py
@@ -3,11 +3,14 @@ import os
 import pytest
 
 from briefcase.console import Log
+from .conftest import CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW
 
 
-def test_call(mock_sub, capsys):
+@pytest.mark.parametrize("platform", ["Linux", "Darwin", "Windows"])
+def test_call(mock_sub, capsys, platform):
     "A simple call will be invoked"
 
+    mock_sub.command.host_os = platform
     mock_sub.Popen(['hello', 'world'])
 
     mock_sub._subprocess.Popen.assert_called_with(['hello', 'world'], text=True)
@@ -39,6 +42,68 @@ def test_call_with_path_arg(mock_sub, capsys, tmp_path):
     assert capsys.readouterr().out == ""
 
 
+@pytest.mark.parametrize(
+    ("platform", "start_new_session", "popen_kwargs"),
+    [
+        ("Linux", None, {}),
+        ("Linux", True, {}),
+        ("Linux", False, {}),
+        ("Darwin", None, {}),
+        ("Darwin", True, {}),
+        ("Darwin", False, {}),
+        ("Windows", None, {}),
+        ("Windows", True, {'creationflags': CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW}),
+        ("Windows", False, {})
+    ]
+)
+def test_call_with_start_new_session(mock_sub, capsys, platform, start_new_session, popen_kwargs):
+    "start_new_session is passed thru on Linux and macOS but converted for Windows"
+
+    mock_sub.command.host_os = platform
+    mock_sub.Popen(['hello', 'world'], start_new_session=start_new_session)
+
+    if platform == "Windows":
+        mock_sub._subprocess.Popen.assert_called_with(
+            ['hello', 'world'],
+            text=True,
+            **popen_kwargs,
+        )
+        assert capsys.readouterr().out == ""
+    else:
+        mock_sub._subprocess.Popen.assert_called_with(
+            ['hello', 'world'],
+            start_new_session=start_new_session,
+            text=True,
+            **popen_kwargs,
+        )
+        assert capsys.readouterr().out == ""
+
+
+@pytest.mark.parametrize(
+    ("creationflags", "final_creationflags"),
+    [
+        (0x1, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | 1),
+        (CREATE_NEW_PROCESS_GROUP, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW),
+        (0, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW),
+    ]
+)
+def test_call_windows_with_start_new_session_and_creationflags(mock_sub, capsys, creationflags, final_creationflags):
+    "creationflags used to simulate start_new_session=True should be merged with any existing flags"
+
+    mock_sub.command.host_os = "Windows"
+
+    # use commented test below when merging creationflags is allowed
+    with pytest.raises(AssertionError, match="Subprocess called with creationflags set"):
+        mock_sub.Popen(['hello', 'world'], start_new_session=True, creationflags=creationflags)
+
+    # mock_sub._subprocess.Popen.assert_called_with(
+    #     ['hello', 'world'],
+    #     creationflags=final_creationflags,
+    #     text=True,
+    # )
+    # assert capsys.readouterr().out == ""
+
+
 def test_debug_call(mock_sub, capsys):
     "If verbosity is turned up, there is output"
     mock_sub.command.logger = Log(verbosity=2)
diff --git a/tests/integrations/subprocess/test_Subprocess__check_output.py b/tests/integrations/subprocess/test_Subprocess__check_output.py
index 5b8c0c27..b5787555 100644
--- a/tests/integrations/subprocess/test_Subprocess__check_output.py
+++ b/tests/integrations/subprocess/test_Subprocess__check_output.py
@@ -4,11 +4,14 @@ from subprocess import CalledProcessError
 import pytest
 
 from briefcase.console import Log
+from .conftest import CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW
 
 
-def test_call(mock_sub, capsys):
+@pytest.mark.parametrize("platform", ["Linux", "Darwin", "Windows"])
+def test_call(mock_sub, capsys, platform):
     "A simple call will be invoked"
 
+    mock_sub.command.host_os = platform
     mock_sub.check_output(['hello', 'world'])
 
     mock_sub._subprocess.check_output.assert_called_with(['hello', 'world'], text=True)
@@ -40,6 +43,68 @@ def test_call_with_path_arg(mock_sub, capsys, tmp_path):
     assert capsys.readouterr().out == ""
 
 
+@pytest.mark.parametrize(
+    ("platform", "start_new_session", "check_output_kwargs"),
+    [
+        ("Linux", None, {}),
+        ("Linux", True, {}),
+        ("Linux", False, {}),
+        ("Darwin", None, {}),
+        ("Darwin", True, {}),
+        ("Darwin", False, {}),
+        ("Windows", None, {}),
+        ("Windows", True, {'creationflags': CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW}),
+        ("Windows", False, {})
+    ]
+)
+def test_call_with_start_new_session(mock_sub, capsys, platform, start_new_session, check_output_kwargs):
+    "start_new_session is passed thru on Linux and macOS but converted for Windows"
+
+    mock_sub.command.host_os = platform
+    mock_sub.check_output(['hello', 'world'], start_new_session=start_new_session)
+
+    if platform == "Windows":
+        mock_sub._subprocess.check_output.assert_called_with(
+            ['hello', 'world'],
+            text=True,
+            **check_output_kwargs,
+        )
+        assert capsys.readouterr().out == ""
+    else:
+        mock_sub._subprocess.check_output.assert_called_with(
+            ['hello', 'world'],
+            start_new_session=start_new_session,
+            text=True,
+            **check_output_kwargs,
+        )
+        assert capsys.readouterr().out == ""
+
+
+@pytest.mark.parametrize(
+    ("creationflags", "final_creationflags"),
+    [
+        (0x1, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | 1),
+        (CREATE_NEW_PROCESS_GROUP, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW),
+        (0, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW),
+    ]
+)
+def test_call_windows_with_start_new_session_and_creationflags(mock_sub, capsys, creationflags, final_creationflags):
+    "creationflags used to simulate start_new_session=True should be merged with any existing flags"
+
+    mock_sub.command.host_os = "Windows"
+
+    # use commented test below when merging creationflags is allowed
+    with pytest.raises(AssertionError, match="Subprocess called with creationflags set"):
+        mock_sub.check_output(['hello', 'world'], start_new_session=True, creationflags=creationflags)
+
+    # mock_sub._subprocess.check_output.assert_called_with(
+    #     ['hello', 'world'],
+    #     creationflags=final_creationflags,
+    #     text=True,
+    # )
+    # assert capsys.readouterr().out == ""
+
+
 def test_debug_call(mock_sub, capsys):
     "If verbosity is turned up, there is output"
     mock_sub.command.logger = Log(verbosity=2)
diff --git a/tests/integrations/subprocess/test_Subprocess__run.py b/tests/integrations/subprocess/test_Subprocess__run.py
index c408be4f..6e5ff0ef 100644
--- a/tests/integrations/subprocess/test_Subprocess__run.py
+++ b/tests/integrations/subprocess/test_Subprocess__run.py
@@ -4,11 +4,14 @@ from subprocess import CalledProcessError
 import pytest
 
 from briefcase.console import Log
+from .conftest import CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW
 
 
-def test_call(mock_sub, capsys):
+@pytest.mark.parametrize("platform", ["Linux", "Darwin", "Windows"])
+def test_call(mock_sub, capsys, platform):
     "A simple call will be invoked"
 
+    mock_sub.command.sys.platform = platform
     mock_sub.run(['hello', 'world'])
 
     mock_sub._subprocess.run.assert_called_with(['hello', 'world'], text=True)
@@ -40,6 +43,68 @@ def test_call_with_path_arg(mock_sub, capsys, tmp_path):
     assert capsys.readouterr().out == ""
 
 
+@pytest.mark.parametrize(
+    ("platform", "start_new_session", "run_kwargs"),
+    [
+        ("Linux", None, {}),
+        ("Linux", True, {}),
+        ("Linux", False, {}),
+        ("Darwin", None, {}),
+        ("Darwin", True, {}),
+        ("Darwin", False, {}),
+        ("Windows", None, {}),
+        ("Windows", True, {'creationflags': CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW}),
+        ("Windows", False, {})
+    ]
+)
+def test_call_with_start_new_session(mock_sub, capsys, platform, start_new_session, run_kwargs):
+    "start_new_session is passed thru on Linux and macOS but converted for Windows"
+
+    mock_sub.command.host_os = platform
+    mock_sub.run(['hello', 'world'], start_new_session=start_new_session)
+
+    if platform == "Windows":
+        mock_sub._subprocess.run.assert_called_with(
+            ['hello', 'world'],
+            text=True,
+            **run_kwargs,
+        )
+        assert capsys.readouterr().out == ""
+    else:
+        mock_sub._subprocess.run.assert_called_with(
+            ['hello', 'world'],
+            start_new_session=start_new_session,
+            text=True,
+            **run_kwargs,
+        )
+        assert capsys.readouterr().out == ""
+
+
+@pytest.mark.parametrize(
+    ("creationflags", "final_creationflags"),
+    [
+        (0x1, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | 1),
+        (CREATE_NEW_PROCESS_GROUP, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW),
+        (0, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW),
+    ]
+)
+def test_call_windows_with_start_new_session_and_creationflags(mock_sub, capsys, creationflags, final_creationflags):
+    "creationflags used to simulate start_new_session=True should be merged with any existing flags"
+
+    mock_sub.command.host_os = "Windows"
+
+    # use commented test below when merging creationflags is allowed
+    with pytest.raises(AssertionError, match="Subprocess called with creationflags set"):
+        mock_sub.run(['hello', 'world'], start_new_session=True, creationflags=creationflags)
+
+    # mock_sub._subprocess.run.assert_called_with(
+    #     ['hello', 'world'],
+    #     creationflags=final_creationflags,
+    #     text=True,
+    # )
+    # assert capsys.readouterr().out == ""
+
+
 def test_debug_call(mock_sub, capsys):
     "If verbosity is turned up, there is output"
     mock_sub.command.logger = Log(verbosity=2)
diff --git a/tests/integrations/subprocess/test_creationflag_constants.py b/tests/integrations/subprocess/test_creationflag_constants.py
new file mode 100644
index 00000000..7eca2de5
--- /dev/null
+++ b/tests/integrations/subprocess/test_creationflag_constants.py
@@ -0,0 +1,12 @@
+import subprocess
+import sys
+
+import pytest
+
+from .conftest import CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW
+
+
+@pytest.mark.skipif(sys.platform != 'win32', reason="requires Windows")
+def test_creationflag_constants():
+    assert subprocess.CREATE_NEW_PROCESS_GROUP == CREATE_NEW_PROCESS_GROUP
+    assert subprocess.CREATE_NO_WINDOW == CREATE_NO_WINDOW