Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Documentation/docs-mobile/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@
href: messages/xa1037.md
- name: XA1038
href: messages/xa1038.md
- name: XA1042
href: messages/xa1042.md
- name: XA1043
href: messages/xa1043.md
- name: XA1048
href: messages/xa1048.md
- name: "XA2xxx: Linker"
items:
- name: "XA2xxx: Linker"
Expand Down
73 changes: 68 additions & 5 deletions Documentation/docs-mobile/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,9 +688,39 @@ A string property that specifies the Android
[instrumentation](https://developer.android.com/reference/android/app/Instrumentation)
runner class name to use when launching the application via `dotnet run`.

When [`$(EnableMSTestRunner)`](#enablemstestrunner) is `true` and this property
is not set, the instrumentation runner class name is automatically resolved from
the generated `AndroidManifest.xml` in the intermediate output.
When this property is not set, `dotnet run` resolves what to launch from the
generated `AndroidManifest.xml` in the intermediate output:

* If [`$(AndroidUseInstrumentation)`](#androiduseinstrumentation) is `true`, the
first `<instrumentation/>` element is used.
* Otherwise the launchable `<activity/>` is preferred, and the first
`<instrumentation/>` element is used when the app declares no launchable
activity. This makes it possible to `dotnet run` an app whose only entry point
is an `Android.App.Instrumentation` subclass, such as a
[BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) host.

If the app declares neither, the build fails with
[XA1043](../messages/xa1043.md). If `$(AndroidUseInstrumentation)` is `true` but
the app declares no `<instrumentation/>`, the build fails with
[XA1048](../messages/xa1048.md).

When an instrumentation is launched, `dotnet run` runs
`adb shell am instrument -w -r` and exits with a non-zero exit code if the
instrumentation crashes or calls `Instrumentation.Finish()` with
`Result.Canceled`. Setting [`$(WaitForExit)`](#waitforexit) to `false` drops the
`-w`, so `dotnet run` returns as soon as the instrumentation is started and no
results are reported.

Any arguments after `--` are forwarded to the instrumentation as `am instrument`
extras. Arguments of the form `KEY=VALUE` become `-e KEY VALUE`, and all
remaining arguments are joined into a single `-e args "..."` extra:

```dotnetcli
dotnet run -- --filter *MyBenchmark*
```

is delivered to `Instrumentation.OnCreate(Bundle?)` as
`arguments.GetString("args")`.

Introduced in .NET 11.

Expand Down Expand Up @@ -1253,6 +1283,24 @@ get a `XA1034` build error.

Added in .NET 8.

## AndroidUseInstrumentation

A boolean property that indicates the application is launched through its
`<instrumentation/>` element rather than an `<activity/>`, such as a test or
[BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) host.

When `true`, `dotnet run` resolves
[`$(AndroidInstrumentation)`](#androidinstrumentation) from the generated
`AndroidManifest.xml` and launches it with `adb shell am instrument`, even if
the app also declares a launchable activity.

The default value is `true` when
[`$(EnableMSTestRunner)`](#enablemstestrunner) is `true`, and `false` otherwise.
Note that an app with no launchable `<activity/>` launches through its
`<instrumentation/>` regardless of this property.

Introduced in .NET 11.

## AndroidUseInterpreter

A boolean property that causes the `.apk` to contain the mono
Expand Down Expand Up @@ -1797,7 +1845,22 @@ When `$(WaitForExit)` not `false` (the default), `dotnet run` will:
* Force-stop the application when Ctrl+C is pressed

When `$(WaitForExit)` is `false`, `dotnet run` will simply launch the
application using `adb shell am start` and return immediately without
waiting for the application to exit or streaming any output.
application and return immediately without waiting for the application to exit
or streaming any output.

This property also controls whether `adb shell am instrument` is passed `-w`
when the application is launched through its
[`$(AndroidInstrumentation)`](#androidinstrumentation):

| `$(WaitForExit)` | Launching an `<activity/>` | Launching an `<instrumentation/>` |
| ---------------- | ---------------------------- | ----------------------------------- |
| `true` (default) | `adb shell am start -S -W` | `adb shell am instrument -w -r` |
| `false` | `adb shell am start -S` | `adb shell am instrument -r` |

Because `adb shell am instrument` only reports results once the instrumentation
completes, `$(WaitForExit)` must not be `false` if you need the instrumentation's
output or a meaningful exit code. This matters for scenarios such as running
tests or a [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) host,
which is why `dotnet test` always uses the default.

Introduced in .NET 11.
3 changes: 3 additions & 0 deletions Documentation/docs-mobile/messages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,13 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
+ [XA1039](xa1039.md): The Android Support libraries are not supported in .NET 9 and later, please migrate to AndroidX. See https://aka.ms/xamarin/androidx for more details.
+ [XA1040](xa1040.md): The NativeAOT runtime on Android is an experimental feature and not yet suitable for production use. File issues at: https://github.com/dotnet/android/issues
+ [XA1041](xa1041.md): The MSBuild property 'MonoAndroidAssetPrefix' has an invalid value of 'c:\Foo\Assets'. The value is expected to be a directory path representing the relative location of your Assets or Resources
+ [XA1042](xa1042.md): The &lt;instrumentation&gt; element in '{0}' is missing the android:name attribute.
+ [XA1043](xa1043.md): Could not determine what to launch: '{0}' does not contain a launchable &lt;activity&gt; or an &lt;instrumentation&gt; element.
+ [XA1044](xa1044.md): The MSBuild property '{0}' is not compatible with the {1} runtime. The build cannot continue while this property is enabled.
+ [XA1045](xa1045.md): Input file `{0}` does not start with `<replacements/>`.
+ [XA1046](xa1046.md): Attribute '{0}' in element '{1}' has value '{2}' that cannot be parsed as boolean; {3} line {4}.
+ [XA1047](xa1047.md): Required attribute '{0}' missing from element '{1}'; {2} line {3}.
+ [XA1048](xa1048.md): '{0}' does not contain an &lt;instrumentation&gt; element.

## XA2xxx: Linker

Expand Down
45 changes: 45 additions & 0 deletions Documentation/docs-mobile/messages/xa1042.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: .NET for Android error XA1042
description: XA1042 error code
ms.date: 07/29/2026
f1_keywords:
- "XA1042"
---

# .NET for Android error XA1042

## Example messages

```dotnetcli
error XA1042: The <instrumentation> element in 'obj/Debug/net11.0-android/android/AndroidManifest.xml' is missing the android:name attribute.
```

## Issue

`dotnet run` launches an app without a launchable `<activity>` through its
`<instrumentation>` element, but the `<instrumentation>` element found in
`AndroidManifest.xml` has no `android:name` attribute, so there is no class to
launch.

## Solution

Give the `<instrumentation>` element a name. If it is declared in your hand
written `AndroidManifest.xml`, add the attribute:

```xml
<instrumentation android:name="com.companyname.myapp.MyInstrumentation" />
```

If the element is generated from an `Android.App.Instrumentation` subclass, set
the `Name` property on the `[Instrumentation]` attribute:

```csharp
[Instrumentation (Name = "com.companyname.myapp.MyInstrumentation")]
public class MyInstrumentation : Instrumentation
{
}
```

Alternatively, set the
[`$(AndroidInstrumentation)`](../building-apps/build-properties.md#androidinstrumentation)
MSBuild property to the class name to launch.
50 changes: 50 additions & 0 deletions Documentation/docs-mobile/messages/xa1043.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: .NET for Android error XA1043
description: XA1043 error code
ms.date: 07/29/2026
f1_keywords:
- "XA1043"
---

# .NET for Android error XA1043

## Example messages

```dotnetcli
error XA1043: Could not determine what to launch: 'obj/Debug/net11.0-android/android/AndroidManifest.xml' does not contain a launchable <activity> or an <instrumentation> element. Set the $(AndroidLaunchActivity) or $(AndroidInstrumentation) MSBuild property to specify one.
```

## Issue

`dotnet run` needs something to launch on the device. It looks for a launchable
`<activity>` in `AndroidManifest.xml`, and falls back to an `<instrumentation>`
element for apps that have no user interface, such as a benchmark host. Neither
was found.

## Solution

Declare an activity with a `MAIN`/`LAUNCHER` intent filter:

```csharp
[Activity (MainLauncher = true)]
public class MainActivity : Activity
{
}
```

Or, for an app that has no activity, declare an instrumentation:

```csharp
[Instrumentation (Name = "com.companyname.myapp.MyInstrumentation")]
public class MyInstrumentation : Instrumentation
{
protected MyInstrumentation (IntPtr handle, JniHandleOwnership ownership)
: base (handle, ownership) { }
}
```

You can also name the class to launch explicitly with the
[`$(AndroidLaunchActivity)`](../building-apps/build-properties.md#androidlaunchactivity)
or
[`$(AndroidInstrumentation)`](../building-apps/build-properties.md#androidinstrumentation)
MSBuild properties.
46 changes: 46 additions & 0 deletions Documentation/docs-mobile/messages/xa1048.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: .NET for Android error XA1048
description: XA1048 error code
ms.date: 07/29/2026
f1_keywords:
- "XA1048"
---

# .NET for Android error XA1048

## Example messages

```dotnetcli
error XA1048: 'obj/Debug/net11.0-android/android/AndroidManifest.xml' does not contain an <instrumentation> element. Add an Android.App.Instrumentation subclass to the application, or set the $(AndroidInstrumentation) MSBuild property to the name of one.
```

## Issue

The application opted into launching through an
[`Android.App.Instrumentation`](https://developer.android.com/reference/android/app/Instrumentation),
either by setting
[`$(AndroidUseInstrumentation)`](../building-apps/build-properties.md#androiduseinstrumentation)
or
[`$(EnableMSTestRunner)`](../building-apps/build-properties.md#enablemstestrunner)
to `true`, but no `<instrumentation>` element was found in `AndroidManifest.xml`.

This differs from [XA1043](xa1043.md), which is reported when the application has
neither a launchable `<activity>` nor an `<instrumentation>`.

## Solution

Declare an instrumentation in the application:

```csharp
[Instrumentation (Name = "com.companyname.myapp.MyInstrumentation")]
public class MyInstrumentation : Instrumentation
{
protected MyInstrumentation (IntPtr handle, JniHandleOwnership ownership)
: base (handle, ownership) { }
}
```

You can also name the class to launch explicitly with the
[`$(AndroidInstrumentation)`](../building-apps/build-properties.md#androidinstrumentation)
MSBuild property, or set `$(AndroidUseInstrumentation)` to `false` to launch the
application's activity instead.
25 changes: 25 additions & 0 deletions src/Microsoft.Android.Run/AdbHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ public static ProcessStartInfo CreateStartInfo (string adbPath, string? adbTarge
};
}

/// <summary>
/// Builds a <see cref="ProcessStartInfo"/> from a pre-split argument list.
/// <see cref="ProcessStartInfo.ArgumentList"/> quotes each element for us, so
/// values containing spaces or double quotes survive the trip through the
/// operating system's command line parser untouched.
/// </summary>
public static ProcessStartInfo CreateStartInfo (string adbPath, string? adbTarget, IEnumerable<string> arguments)
{
var psi = new ProcessStartInfo {
FileName = adbPath,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
if (!string.IsNullOrEmpty (adbTarget)) {
// `adbTarget` is an already formatted switch such as `-s emulator-5554`.
foreach (var part in adbTarget.Split (' ', StringSplitOptions.RemoveEmptyEntries))
psi.ArgumentList.Add (part);
}
foreach (var argument in arguments)
psi.ArgumentList.Add (argument);
return psi;
}

public static async Task<(int ExitCode, string Output, string Error)> RunAsync (string adbPath, string? adbTarget, string arguments, CancellationToken cancellationToken, bool verbose = false)
{
var psi = CreateStartInfo (adbPath, adbTarget, arguments);
Expand Down
Loading
Loading