Skip to content

Conversation

@jonpryor
Copy link
Contributor

Context: https://developer.android.com/about/versions/16/qpr2/
Context: https://android-developers.googleblog.com/2025/08/android-16-qpr2-beta-1-is-here.html

Android 16 Quarterly Platform Release 2 (QPR2) Beta 1 has been released.

The Android 16 QPR2 Beta 1 Announcement suggests the following timeline:

Currently, this will be usable in its preview form to main users who explicitly target net10.0-android36.1.

This may be released in .NET 10 GA (2025-Nov) as net10.0-android36.0, or may be released later as net10.0-android36.1.

~~ Changes ~~

Update create-android-api.csproj to use java-source-utils.jar instead of param-name-importer.dll, as param-name-importer.dll was not able to import API-CANARY's android-stubs-src.jar:

create-android-api failed with 2 error(s) (1.0s)
  EXEC : error (3050: 47) Invalid character: '''.

More specifically:

% dotnet bin/Debug/lib/packs/Microsoft.Android.Sdk.Darwin/36.0.0/tools/param-name-importer.dll \
  "-source-stub-zip=$HOME/android-toolchain/sdk/platforms/android-CANARY/android-stubs-src.jar" \
  "-output-text=src/Mono.Android/Profiles/api-CANARY.params.txt" \
  -verbose -framework-only
…
java/lang/Character.java
Error (3050:47) Invalid character: '''.

As param-name-importer.dll uses an Irony-based Java source parser, and we have a better Java source parser which can also output the param-name-importer.dll text format in java-source-utils.jar use java-source-utils.jar instead.

Android 16 QPR2 adds an additional wrinkle: "minor" SDK versions are now A Thing™; see:

  • <uses-sdk/>:

    It's not possible to specify that an app either targets or requires a minor SDK version.

  • Using new APIs with major and minor releases:

    The new SDK_INT_FULL constant can be used for API checks…

    if (SDK_INT_FULL >= VERSION_CODES_FULL.[MAJOR or MINOR RELEASE]) {
      // Use APIs introduced in a major or minor release
    }
    

    You can also use the Build.getMinorSdkVersion() method to
    get just the minor SDK version:

    minorSdkVersion = Build.getMinorSdkVersion(Build.VERSION_CODES_FULL.BAKLAVA);
    

What these mean is that "Android 16 QPR2" will not be API-37. It will be "API-36+": <uses-sdk/> will specify e.g. android:targetSdkVersion="36" (not 37), and runtime guards against Android 16 QPR2 will need to check against e.g. Build.VERSION_CODES_FULL#BAKLAVA_1, documented as "Android 36.1".

This in turn means that our historical approach to adding new API levels does not immediately apply. API-CANARY will not be API-37. net10.0-android37.0 -- when it exists -- will not bind API-CANARY; it will bind Android 17, presumably in 2026.

So what do we call it?

We will need to call it net10.0-android36.1:

  • net10.0-android36.0 is "plain"/"original" API-36.
  • net10.0-android36.1 will be Android 16 QPR2 / API-CANARY.

This is something we've never had to support before, so lots of associated changes to the build system are required to support it. Among the changes is the assumption that an API level is "just" an int.

jonpryor and others added 4 commits August 22, 2025 15:29
Context: https://developer.android.com/about/versions/16/qpr2/
Context: https://android-developers.googleblog.com/2025/08/android-16-qpr2-beta-1-is-here.html

Android 16 Quarterly Platform Release 2 (QPR2) Beta 1 has been released.

  * [API-CANARY Beta 1 vs. API-36][0]

The Android 16 QPR2 Beta 1 [Announcement][1] suggests the following
timeline:

  * Aug/Sep: Unstable Betas
  * Oct: Stable beta with a [Platform Stability milestone][2]
  * ???: Final

Currently, this will be usable in its preview form to `main` users
who explicitly target `net10.0-android36.1`.

This *may* be released in .NET 10 GA (2025-Nov) as
`net10.0-android36.0`, or may be released later as
`net10.0-android36.1`.

~~ Changes ~~

Update `create-android-api.csproj` to use `java-source-utils.jar`
instead of `param-name-importer.dll`, as `param-name-importer.dll`
was not able to import API-CANARY's `android-stubs-src.jar`:

	create-android-api failed with 2 error(s) (1.0s)
	  EXEC : error (3050: 47) Invalid character: '''.

More specifically:

	% dotnet bin/Debug/lib/packs/Microsoft.Android.Sdk.Darwin/36.0.0/tools/param-name-importer.dll \
	  "-source-stub-zip=$HOME/android-toolchain/sdk/platforms/android-CANARY/android-stubs-src.jar" \
	  "-output-text=src/Mono.Android/Profiles/api-CANARY.params.txt" \
	  -verbose -framework-only
	…
	java/lang/Character.java
	Error (3050:47) Invalid character: '''.

As `param-name-importer.dll` uses an Irony-based Java source parser,
and *we have a better* Java source parser which can *also* output the
`param-name-importer.dll` text format in `java-source-utils.jar`
use `java-source-utils.jar` instead.

Android 16 QPR2 adds an additional wrinkle: "minor" SDK versions are
now A Thing™; see:

  * [`<uses-sdk/>`][3]:

    > It's not possible to specify that an app either targets or requires a minor SDK version.

  * [Using new APIs with major and minor releases][4]:

    > The new [`SDK_INT_FULL`][5] constant can be used for API checks…
	>
	>     if (SDK_INT_FULL >= VERSION_CODES_FULL.[MAJOR or MINOR RELEASE]) {
	>       // Use APIs introduced in a major or minor release
	>     }
    >
	> You can also use the [`Build.getMinorSdkVersion()`][6] method to
	> get just the minor SDK version:
	>
	>     minorSdkVersion = Build.getMinorSdkVersion(Build.VERSION_CODES_FULL.BAKLAVA);

What these mean is that "Android 16 QPR2" *will not* be API-37.
It will be "API-36+": `<uses-sdk/>` will specify e.g.
`android:targetSdkVersion="36"` (***not*** `37`), and runtime guards
against Android 16 QPR2 will need to check against e.g.
[`Build.VERSION_CODES_FULL#BAKLAVA_1`][7], documented as "Android 36.1".

This in turn means that our historical approach to adding new API
levels does not immediately apply.  API-CANARY *will not be* API-37.
`net10.0-android37.0` -- when it exists -- will not bind API-CANARY;
it will bind Android 17, presumably in 2026.

So what do we call it?

We will need to call it `net10.0-android36.1`:

  * `net10.0-android36.0` is "plain"/"original" API-36.
  * `net10.0-android36.1` will be Android 16 QPR2 / API-CANARY.

This is something we've never had to support before, so lots of
associated changes to the build system are required to support it.
Among the changes is the assumption that an API level is "just" an int.

[0]: https://developer.android.com/sdk/api_diff/b-1-beta1/changes
[1]: https://android-developers.googleblog.com/2025/08/android-16-qpr2-beta-1-is-here.html
[2]: https://developer.android.com/about/versions/16/qpr2/overview#platform-stability
[3]: https://developer.android.com/guide/topics/manifest/uses-sdk-element
[4]: https://developer.android.com/about/versions/16/features#using-new
[5]: https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT_FULL
[6]: https://developer.android.com/reference/android/os/Build#getMinorSdkVersion(int)
[7]: https://developer.android.com/reference/android/os/Build.VERSION_CODES_FULL#BAKLAVA_1
Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

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

Some next steps, we need to probably use double.TryParse() here:

static AndroidVersion ToVersion (ITaskItem item)
{
/*
<AndroidApiInfo Include="v12.0.99">
<Name>Sv2</Name>
<Level>32</Level>
<Id>Sv2</Id>
<Stable>False</Stable>
</AndroidApiInfo>
*/
int.TryParse (item.GetMetadata ("Level"), out int apiLevel);
bool.TryParse (item.GetMetadata ("Stable"), out bool stable);
return new AndroidVersion (apiLevel, item.ItemSpec.TrimStart ('v'), item.GetMetadata ("Name"), item.GetMetadata ("Id"), stable);
}

But then, the AndroidVersion type doesn't have a non-int value to use!

We could maybe extend the type here and add a property?

But then AndroidVersion also needs updates for any other software using it.

@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

jonpryor added a commit to jonpryor/xamarin-android-tools that referenced this pull request Aug 25, 2025
Context: dotnet/android#10438

9 months ago in [The First Developer Preview of Android 16][0]:

> **Two Android API releases in 2025**
>
>   * This preview is for the next major release of Android with a
>     planned launch in Q2 of 2025. This release is similar to all of
>     our API releases in the past, where we can have planned
>     behavior changes that are often tied to a targetSdkVersion.
>
>   * …
>
>   * We plan to have another release in Q4 of 2025 which also will
>     include new developer APIs. The Q2 major release will be the
>     only release in 2025 to include planned behavior changes that
>     could affect apps.

The 3rd bullet point is a "25Q4 MINOR SDK RELEASE" , thus introducing
the *concept* of a "minor" SDK version, with semantics:

  * [`<uses-sdk/>`][3]:

    > It's not possible to specify that an app either targets or
    > requires a minor SDK version.

  * [Using new APIs with major and minor releases][4]:

    > The new [`SDK_INT_FULL`][5] constant can be used for API checks…
    >
    >     if (SDK_INT_FULL >= VERSION_CODES_FULL.[MAJOR or MINOR RELEASE]) {
    >       // Use APIs introduced in a major or minor release
    >     }
    >
    > You can also use the [`Build.getMinorSdkVersion()`][6] method to
    > get just the minor SDK version:
    >
    >     minorSdkVersion = Build.getMinorSdkVersion(Build.VERSION_CODES_FULL.BAKLAVA);

Update `AndroidVersion` and `AndroidVersions` to better support the
concept of "minor SDK releases":

  * Add a new `AndroidVersion.VersionCodeFull` property, which is a
    `System.Version` -- not an `int` -- for which `Version.Major`
    matches `AndroidVersion.ApiLevel`.

  * Add a new internal `AndroidVersion.Ids` property, which is the=
    full set of "aliases" that should be checked when doing an "id"
    match.  This simplifies `AndroidVersions` logic.

    `Ids` contains: `ApiLevel`, VersionCodeFull`, and `Id`.

  * Change `AndroidVersions.AlternateIds` into a set-only property
    which updates `AndroidVersion.Ids`.

  * Bump `$(LangVersion)`=9.0 to use target-typed `new()`.

[0]: https://android-developers.googleblog.com/2024/11/the-first-developer-preview-android16.html
[3]: https://developer.android.com/guide/topics/manifest/uses-sdk-element
[4]: https://developer.android.com/about/versions/16/features#using-new
[5]: https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT_FULL
[6]: https://developer.android.com/reference/android/os/Build#getMinorSdkVersion(int)
jonathanpeppers pushed a commit to dotnet/android-tools that referenced this pull request Aug 25, 2025
Context: dotnet/android#10438

9 months ago in [The First Developer Preview of Android 16][0]:

> **Two Android API releases in 2025**
>
>   * This preview is for the next major release of Android with a
>     planned launch in Q2 of 2025. This release is similar to all of
>     our API releases in the past, where we can have planned
>     behavior changes that are often tied to a targetSdkVersion.
>
>   * …
>
>   * We plan to have another release in Q4 of 2025 which also will
>     include new developer APIs. The Q2 major release will be the
>     only release in 2025 to include planned behavior changes that
>     could affect apps.

The 3rd bullet point is a "25Q4 MINOR SDK RELEASE" , thus introducing
the *concept* of a "minor" SDK version, with semantics:

  * [`<uses-sdk/>`][3]:

    > It's not possible to specify that an app either targets or
    > requires a minor SDK version.

  * [Using new APIs with major and minor releases][4]:

    > The new [`SDK_INT_FULL`][5] constant can be used for API checks…
    >
    >     if (SDK_INT_FULL >= VERSION_CODES_FULL.[MAJOR or MINOR RELEASE]) {
    >       // Use APIs introduced in a major or minor release
    >     }
    >
    > You can also use the [`Build.getMinorSdkVersion()`][6] method to
    > get just the minor SDK version:
    >
    >     minorSdkVersion = Build.getMinorSdkVersion(Build.VERSION_CODES_FULL.BAKLAVA);

Update `AndroidVersion` and `AndroidVersions` to better support the
concept of "minor SDK releases":

  * Add a new `AndroidVersion.VersionCodeFull` property, which is a
    `System.Version` -- not an `int` -- for which `Version.Major`
    matches `AndroidVersion.ApiLevel`.

  * Add a new internal `AndroidVersion.Ids` property, which is the=
    full set of "aliases" that should be checked when doing an "id"
    match.  This simplifies `AndroidVersions` logic.

    `Ids` contains: `ApiLevel`, VersionCodeFull`, and `Id`.

  * Change `AndroidVersions.AlternateIds` into a set-only property
    which updates `AndroidVersion.Ids`.

  * Bump `$(LangVersion)`=9.0 to use target-typed `new()`.

[0]: https://android-developers.googleblog.com/2024/11/the-first-developer-preview-android16.html
[3]: https://developer.android.com/guide/topics/manifest/uses-sdk-element
[4]: https://developer.android.com/about/versions/16/features#using-new
[5]: https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT_FULL
[6]: https://developer.android.com/reference/android/os/Build#getMinorSdkVersion(int)
Context: dotnet/android-tools@31609ca

Changes: dotnet/android-tools@c4cb3db...31609ca

  * dotnet/android-tools@31609ca: [Xamarin.Android.Tools.AndroidSdk] "Minor" SDK version support (dotnet/android-tools#261)

@jonathanpeppers [noted][0]:

> We could maybe extend the type here and add a property?
>
> But then `AndroidVersion` also needs updates for any other software using it.

dotnet/android-tools@31609cac updates `AndroidVersion` and
`AndroidVersions` to better support the concept of "minor" SDK versions,
allowing lookup via "minor" version on
`AndroidVersions.GetIdFromApiLevel(string)`.  This would allow
`MonoAndroidHelper.SupportedVersions.GetIdFromApiLevel("36.1")` to
find CANARY, if and when appropriate.

Update `GenerateSupportedPlatforms.cs` to look at
`AndroidVersion.VersionCodeFull` instead of `AndroidVersion.ApiLevel`.
This allows minor SDK versions to be present in
`@(AndroidSdkSupportedTargetPlatformVersion)`.

Update `GeneratedMonoAndroidProjitemsFile.cs` so that the generated
`bin\Build$(Configuration)\Mono.Android.Apis.projitems` file contains
`%(AndroidApiInfo.VersionCodeFull)` item metadata.

Update `Mono.Android.targets` so that the generated `AndroidApiInfo.xml`
file contains `<VersionCodeFull/>`, based on `%(VersionCodeFull)`.

[0]: dotnet#10438 (review)
@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@jonpryor jonpryor marked this pull request as ready for review August 25, 2025 22:49
@jonpryor jonpryor requested a review from grendello as a code owner August 25, 2025 22:49
@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@grendello

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Aug 26, 2025
Context: dotnet#10438

We can always call `Assert.Inconclusive()`, to prevent failing tests from contributors.
@jonathanpeppers
Copy link
Member

I'll merge this shortly, and I think this one will be a lot more green:

One thing that seems missing are some of the packs here:

image

We should have a Microsoft.Android.Ref.36.1.*.nupkg and Microsoft.Android.Runtime.36.1.*.nupkg.

Which then confuses me how these passed:

image

So, I wonder if this new test is just using API 36 and not 36.1. I can make it call a new API to verify that.

jonathanpeppers added a commit that referenced this pull request Aug 26, 2025
Context: #10438

We can always call `Assert.Inconclusive()`, to prevent failing tests from contributors.

Also updated `PerformanceTest.cs`, as two tests needed to call `AssertCommercialBuild()`.
Reviewing jpobst's PR from Baklava DP1:
* dotnet@2dd4f74

It looks like we are missing some changes in `Configuration.props` and other files.
@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

We will eventually need to do this for all the values, but one step at a time.
@jonathanpeppers

This comment was marked as outdated.

@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

jonpryor and others added 6 commits September 10, 2025 15:14
More failing tests!

	GetDependencyWhenBuildToolsAreMissingTest
	platforms/android-36.0 should be a dependency.
	at Xamarin.Android.Build.Tests.AndroidDependenciesTests.GetDependencyWhenBuildToolsAreMissingTest()

The assertion:

	StringAssertEx.Contains ($"platforms/android-{apiLevel}", builder.LastBuildOutput, $"platforms/android-{apiLevel} should be a dependency.");

and with the new "Versions Everywhere" approach, `apiLevel` is now
a Version, and thus `apiLevel.ToString()` is `36.0`, *not* `36`.

However, the "obvious" fix of using `apiLevel.Major` is not future-
proof.  *Eventually* API-CANARY will be stable as `android-36.1`,
at which point we *will* want the full `apiLevel.ToString()` output.

Try to address this by adding a new `StringAssertEx.Contains()`
overload which allows providing a set of expected values, and if
*any* value is found, the assert succeeds:

	StringAssertEx.Contains (
	    anyOf: new [] { $"platforms/android-{apiLevel}", $"platforms/android-{apiLevel.Major}" },
	    collection: builder.LastBuildOutput,
	    message: $"platforms/android-{apiLevel} should be a dependency.");

This will now check for *both* `platforms/android-36` (which will
succeed now) *and* `platforms/android-36.0` (which will fail now, but
is needed for a future 36.1).
@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@jonathanpeppers
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jonpryor
Copy link
Contributor Author

Commit message:

Context: https://developer.android.com/about/versions/16/qpr2/
Context: https://android-developers.googleblog.com/2025/08/android-16-qpr2-beta-1-is-here.html

Changes: https://github.com/dotnet/java-interop/compare/a5d7370963ea835f075ab779a79d1aac5b206014...f07b53855063a297a4b4d3d3b96900dd3729e82c

  * dotnet/java-interop@f07b5385: [generator] Support major.minor API levels (dotnet/java-interop#1360)

Android 16 Quarterly Platform Release 2 (QPR2) Beta 1 has been released.

  * [API-CANARY Beta 1 vs. API-36][0]

The Android 16 QPR2 Beta 1 [Announcement][1] suggests the following
timeline:

  * Aug/Sep: Unstable Betas
  * Oct: Stable beta with a [Platform Stability milestone][2]
  * ???: Final

Android 16 QPR2 adds an additional wrinkle: "minor" SDK versions are
now A Thing™; see:

  * [`<uses-sdk/>`][3]:

    > It's not possible to specify that an app either targets or requires a minor SDK version.

  * [Using new APIs with major and minor releases][4]:

    > The new [`SDK_INT_FULL`][5] constant can be used for API checks…
    >
    >     if (SDK_INT_FULL >= VERSION_CODES_FULL.[MAJOR or MINOR RELEASE]) {
    >       // Use APIs introduced in a major or minor release
    >     }
    >
    > You can also use the [`Build.getMinorSdkVersion()`][6] method to
    > get just the minor SDK version:
    >
    >     minorSdkVersion = Build.getMinorSdkVersion(Build.VERSION_CODES_FULL.BAKLAVA);

What these mean is that "Android 16 QPR2" *will not* be API-37.
It will be "API-36.1": `<uses-sdk/>` will specify e.g.
`android:targetSdkVersion="36"` (***not*** `37`), and runtime guards
against Android 16 QPR2 will need to check against e.g.
[`Build.VERSION_CODES_FULL#BAKLAVA_1`][7], documented as "Android 36.1".

This in turn means that our historical approach to adding new API
levels does not immediately apply.  API-CANARY *will not be* API-37.
`net10.0-android37.0` -- when it exists -- will not bind API-CANARY;
it will bind Android 17, presumably in 2026.

So what do we call it?

We will need to call it `net10.0-android36.1`:

  * `net10.0-android36.0` is "plain"/"original" API-36.
  * `net10.0-android36.1` will be Android 16 QPR2 / API-CANARY.

This will be usable in its preview form to `main` users
who explicitly target `net10.0-android36.1`.

This is unlikely to be released in .NET 10 GA (2025-Nov) as
`net10.0-android36.1`.

~~ Changes: Minor SDK Versions ~~

"Minor" SDK versions are something we've never had to support before,
so lots of associated changes to the build system and tooling are
required to support it.  Among the changes is the assumption that an
API level is "just" an int.

Update `create-android-api.csproj` to use `java-source-utils.jar`
instead of `param-name-importer.dll`, as `param-name-importer.dll`
was not able to import API-CANARY's `android-stubs-src.jar`:

	create-android-api failed with 2 error(s) (1.0s)
	  EXEC : error (3050: 47) Invalid character: '''.

More specifically:

	% dotnet bin/Debug/lib/packs/Microsoft.Android.Sdk.Darwin/36.0.0/tools/param-name-importer.dll \
	  "-source-stub-zip=$HOME/android-toolchain/sdk/platforms/android-CANARY/android-stubs-src.jar" \
	  "-output-text=src/Mono.Android/Profiles/api-CANARY.params.txt" \
	  -verbose -framework-only
	
	java/lang/Character.java
	Error (3050:47) Invalid character: '''.

As `param-name-importer.dll` uses an Irony-based Java source parser,
and *we have a better* Java source parser which can *also* output the
`param-name-importer.dll` text format in `java-source-utils.jar`
use `java-source-utils.jar` instead.

Update `GenerateSupportedPlatforms.cs` to look at
`AndroidVersion.VersionCodeFull` instead of `AndroidVersion.ApiLevel`.
This allows minor SDK versions to be present in
`@(AndroidSdkSupportedTargetPlatformVersion)`.

Update `GeneratedMonoAndroidProjitemsFile.cs` so that the generated
`bin\Build$(Configuration)\Mono.Android.Apis.projitems` file contains
`%(AndroidApiInfo.VersionCodeFull)` item metadata.

Update `Mono.Android.targets` so that the generated `AndroidApiInfo.xml`
file contains `<VersionCodeFull/>`, based on `%(VersionCodeFull)`.

Add `InstallAndRunTests.DotNetInstallAndRunPreviewAPILevels()`, which
uses the new `net10.0-android36.1` target framework and references
one of the new Preview APIs.

Add a new set of `PublicAPI.*.txt` files so that the Public API
analyzer can "do its thing".
`src/Mono.Android/PublicAPI/API-36.1/PublicAPI.Shipped.txt`
is based on the union of `PublicAPI.Shipped.txt` and
`PublicApi.Unshipped.txt` for API-36.

Update the workloads to use `Microsoft.Android.Runtime.Mono.36.[RID]`
packs.  Fixes:

	…/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(119,5): error NETSDK1181: Error getting pack version: Pack 'Microsoft.Android.Runtime.Mono.36.1.android-arm64' was not present in workload manifests.
	…/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(119,5): error NETSDK1181: Error getting pack version: Pack 'Microsoft.Android.Runtime.Mono.36.1.android-x64' was not present in workload manifests.

Update `XABuildConfig` so that all `*ApiLevel` fields are now
`System.Version`, ***not*** `int`.  This allows them to properly
contain minor values.  This in turn impacts "everything", changing
many `int`s to `Versions`s.

Additionally, update `XASdkTests.DotNetTargetFrameworks` to *not*
always append `.0`.  The use of `Version` handles this for us, nicely.

Update `build.gradle` generation within unit tests to ensure that
`compileSdk` et. al are `int` values.

~~ Changes: Disable lint checks for `@(AndroidGradleProject)` Tests ~~

Some `@(AndroidGradleProject)` tests were failing on CI:

	…/Microsoft.Android.Sdk.Bindings.Gradle.targets(83,5): error XAGRDL0000: FAILURE: Build failed with an exception.
	…/Microsoft.Android.Sdk.Bindings.Gradle.targets(83,5): error XAGRDL0000:
	…/Microsoft.Android.Sdk.Bindings.Gradle.targets(83,5): error XAGRDL0000: * What went wrong:
	…/Microsoft.Android.Sdk.Bindings.Gradle.targets(83,5): error XAGRDL0000: Execution failed for task ':TestModule:lintVitalAnalyzeRelease'.
	…/Microsoft.Android.Sdk.Bindings.Gradle.targets(83,5): error XAGRDL0000: > A failure occurred while executing com.android.build.gradle.internal.lint.AndroidLintWorkAction
	…/Microsoft.Android.Sdk.Bindings.Gradle.targets(83,5): error XAGRDL0000:    > For input string: "36.0"

After a fair bit of digging around -- read the `.binlog`, find
the `gradlew` invocation, *manually* run the `gradlew` invocation
while adding `-d` go get diagnostic output, which in turn provides
the `lint` invocation -- we see that `lint` is crashing with:

	Exception in thread "main" java.lang.NumberFormatException: For input string: "36.0"
		at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
		at java.base/java.lang.Integer.parseInt(Integer.java:668)
		at java.base/java.lang.Integer.parseInt(Integer.java:786)
		at com.android.tools.lint.client.api.SimplePlatformLookup$Companion.platformFromSourceProp(SimplePlatformLookup.kt:256)
		at com.android.tools.lint.client.api.SimplePlatformLookup$Companion.addPlatforms(SimplePlatformLookup.kt:146)
		at com.android.tools.lint.client.api.SimplePlatformLookup$Companion.access$addPlatforms(SimplePlatformLookup.kt:113)
		at com.android.tools.lint.client.api.SimplePlatformLookup.<init>(SimplePlatformLookup.kt:65)
		at com.android.tools.lint.client.api.SimplePlatformLookup$Companion.get(SimplePlatformLookup.kt:118)
		at com.android.tools.lint.client.api.LintClient.getPlatformLookup(LintClient.kt:1045)
		at com.android.tools.lint.client.api.LintClient.getCompileTarget(LintClient.kt:1063)
		at com.android.tools.lint.detector.api.Project.getBuildTarget(Project.java:959)
		at com.android.tools.lint.LintCliClient$pickBuildTarget$2.invoke(LintCliClient.kt:1464)
		at com.android.tools.lint.LintCliClient$pickBuildTarget$2.invoke(LintCliClient.kt:1464)
		at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:210)
		at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:170)
		at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:194)
		at com.android.tools.lint.LintCliClient.pickBuildTarget(LintCliClient.kt:1947)
		at com.android.tools.lint.LintCliClient.getBootClassPath(LintCliClient.kt:1443)
		at com.android.tools.lint.Main$MainLintClient.getBootClassPath(Main.java:805)
		at com.android.tools.lint.LintCliClient.initializeProjects(LintCliClient.kt:1421)
		at com.android.tools.lint.client.api.LintClient.performInitializeProjects$lint_api(LintClient.kt:1009)
		at com.android.tools.lint.client.api.LintDriver.initializeProjectRoots(LintDriver.kt:569)
		at com.android.tools.lint.client.api.LintDriver.doAnalyze(LintDriver.kt:484)
		at com.android.tools.lint.client.api.LintDriver.analyzeOnly(LintDriver.kt:443)
		at com.android.tools.lint.LintCliClient$analyzeOnly$1.invoke(LintCliClient.kt:233)
		at com.android.tools.lint.LintCliClient$analyzeOnly$1.invoke(LintCliClient.kt:233)
		at com.android.tools.lint.LintCliClient.run(LintCliClient.kt:275)
		at com.android.tools.lint.LintCliClient.run$default(LintCliClient.kt:258)
		at com.android.tools.lint.LintCliClient.analyzeOnly(LintCliClient.kt:233)
		at com.android.tools.lint.Main.run(Main.java:1761)
		at com.android.tools.lint.Main.run(Main.java:284)
		at com.android.tools.lint.Main.main(Main.java:221)

Of note is `SimplePlatformLookup$Companion.platformFromSourceProp()`,
which reads the `source.properties` files within
`$ANDROID_HOME/platforms/android-*/source.properties`.

The problem is that API-CANARY's `source.properties` contains:

	AndroidVersion.ApiLevel=36.0

which `lint` doesn't like.

`lint` has been fixed to support minor SDK versions, e.g.

  * https://cs.android.com/android-studio/platform/tools/base/+/5d246c720b206b7075a826d1bd4549882dd7c085

However, that commit was merged in 2025-May, while the latest
cmdline-tools package is 19.0, released in 2025-March.

Which means we have no `lint` package which has the fix!

As a quick workaround, *disable* lint checks in the unit tests,
by adding the block:

	android {
	  lint {
	    checkReleaseBuilds = false
	  }
	}

This *should* prevent `lint` from running at all, thus "fixing"
the `NumberFormatException`.

Co-authored-by: Jonathan Peppers <[email protected]>

[0]: https://developer.android.com/sdk/api_diff/b-1-beta1/changes
[1]: https://android-developers.googleblog.com/2025/08/android-16-qpr2-beta-1-is-here.html
[2]: https://developer.android.com/about/versions/16/qpr2/overview#platform-stability
[3]: https://developer.android.com/guide/topics/manifest/uses-sdk-element
[4]: https://developer.android.com/about/versions/16/features#using-new
[5]: https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT_FULL
[6]: https://developer.android.com/reference/android/os/Build#getMinorSdkVersion(int)
[7]: https://developer.android.com/reference/android/os/Build.VERSION_CODES_FULL#BAKLAVA_1

@jonathanpeppers jonathanpeppers merged commit 35d471e into dotnet:main Sep 12, 2025
59 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Oct 13, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants