Runtime: Android API level or OS version for platform checks/TFM

Created on 1 Jul 2020  路  9Comments  路  Source: dotnet/runtime

Neither the .NET 5 spec nor the minimum OS platform checks spec make it clear whether Android will be using the OS version (e.g. 4.3) or the API level (e.g. 21) for the version number.

@terrajobst @mhutch

untriaged

Most helpful comment

The simple answer is to just use the API level everywhere.

After some discussion, we think using the Android API level makes the most sense:

  1. Android developers, in general, use API level in AndroidManifest.xml files:
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  1. Java/Kotlin developers might use:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
     // ...
}

// or the value of LOLLIPOP
if (android.os.Build.VERSION.SDK_INT >= 21) {
     // ...
}
  1. Android C/C++ developers might use:
#include <android/api-level.h>

#if __ANDROID_API__ >= 21
// ...
#endif

// or
#ifdef __ANDROID_API_L__ 
// ...
#endif

If we used the Android OS version in .NET, it seems like it would just be a source of confusion. Android 5.0 (for API 21) is not used in any of the above examples.

Android API levels are integers, so that is going to be one difference here. We would not have a $(TFM) like net5.0-android30.1.

All 9 comments

See https://github.com/dotnet/designs/blob/master/accepted/2020/minimum-os-version/minimum-os-version.md#nuget-manifest.

There is a mention of net6.0-android9.0. Therefore, OS version is used.

@mhutch

My working assumption was that we pick one model (either API level or OS version) because the various MSBuild properties/custom attribute are based on each other:

  1. <TargetPlatformVersion> is initialized by parsing <TargetFramework>
  2. <TargetPlatformMinVersion> defaults to <TargetPlatformVersion>
  3. [TargetPlatform] is generated from <TargetPlatformVersion>
  4. [MinimumOSPlatform] is generated from <TargetPlatformMinVersion>

If some of these values are API levels while others are OS versions, we'd need a mapping table at least somewhere in MSBuild. If we can contain it there, it might be workable. For instance, we might say that [MinimumOSPlatform] is the API level we only have to apply the translation in (4). I assume that Android allows asking the OS whether it supports a given API level, so that calls to RuntimeInformation.IsOSPlatformOrLater() would be using API levels too.

I would, however, push back on a design where the attributes use API levels while the OS checks use OS version because this would mean that developer no longer knows which versions to use in checks (even if we somehow could apply the mapping to the analyzer).

Agreed we should be consistent between attributes and build props.

I'm a little conflicted on this. Ultimately API levels are an important part of the Android developer experience and ecosystem, and they will be used in some parts of the toolchain, either internally or developer-facing. As such, the the API level to OS version mappings will exist _somewhere_ in the tooling. However, using OS versions would be both consistent with our other platforms and more in line with how most folks think about Android.

However, using OS versions would be both consistent with our other platforms and more in line with how most folks think about Android.

I agree that they'd be more consistent with other platforms but I feel like most Android _developers_ think in API levels.

It's what you set in your AndroidManifest.xml uses-sdk or what the docs tell you about when an API was introduced, e.g. https://developer.android.com/reference/android/net/wifi/WifiInfo#getRxLinkSpeedMbps() says Added in API level 29.

You'd need to manually translate that back to an OS version before you can pass it to RuntimeInformation.IsOSPlatformOrLater().

I agree that they'd be more consistent with other platforms but I feel like most Android developers think in API levels.

Fair, but this functionality will affect folks developing xplat libraries and apps, who may not think in the same way as developers who focus on Android.

I don't have strong feelings on which should be the canonical form but I think we need to make it easy to see the mappings. For example, we could define a set of constants such that ANDROID_API_29 with a docs tooltip of "Android 10.0" or vice versa, etc.

Sorry, what I meant by consistent is within a given platform we are consistent between the various properties and attributes. I've got zero problems with Android using API levels while iOS uses OS versions. What I am concerned about is if an Android developer uses OS version for some properties but API levels in others. Does that make sense?

I'm torn.

The simple answer is to just use the API level everywhere. Thus:

This is also consistent with Android developer guidance, with <uses-sdk/> values, and more. The Xamarin.Android build system emits a set of __ANDROID_N__ defines, from 1 through the API level corresponding to $(TargetFrameworkVersion), such that __ANDROID_29__ is an existing symbol. I don't see why we would remove this functionality in .NET 6. (Therein lies part of the problem, actually: Xamarin.Android is already "inconsistent" here, with $(TargetFrameworkVersion) values being the OS version, but runtime version checks using API levels.)

Question: is using the API level everywhere even possible? Some "historical" constructs have required that values be parsable by Version.TryParse(), and Version.TryParse("29") is (was?) _not_ a happy camper. (This question may not be relevant in the future .NET 6 world order; I don't know.)

However, I think that the API levels are "weird" and "big", and that net6.0-android11 looks much nicer than net6.0-android30. (There is no rhyme or reason to this opinion.)

My heart wants OS versions. My brain wants the simplicity of API levels.

The simple answer is to just use the API level everywhere.

After some discussion, we think using the Android API level makes the most sense:

  1. Android developers, in general, use API level in AndroidManifest.xml files:
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  1. Java/Kotlin developers might use:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
     // ...
}

// or the value of LOLLIPOP
if (android.os.Build.VERSION.SDK_INT >= 21) {
     // ...
}
  1. Android C/C++ developers might use:
#include <android/api-level.h>

#if __ANDROID_API__ >= 21
// ...
#endif

// or
#ifdef __ANDROID_API_L__ 
// ...
#endif

If we used the Android OS version in .NET, it seems like it would just be a source of confusion. Android 5.0 (for API 21) is not used in any of the above examples.

Android API levels are integers, so that is going to be one difference here. We would not have a $(TFM) like net5.0-android30.1.

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

Was this page helpful?
0 / 5 - 0 ratings