| RFC-0246: API Levels are 32 bits ||
|---|---|
| Status | Accepted |
| Areas | - General |
| Description | Redefines API levels as 32-bit numbers, rather than 64-bits |
| Gerrit change | - [1009253](https://fuchsia-review.googlesource.com/c/fuchsia/+/1009253) |
| Authors | - [hjfreyer@google.com](https://fuchsia-review.googlesource.com/q/owner:hjfreyer@google.com) |
| Reviewers | - [ddorwin@google.com](https://fuchsia-review.googlesource.com/q/owner:ddorwin@google.com) - [mkember@google.com](https://fuchsia-review.googlesource.com/q/owner:mkember@google.com) |
| Date submitted (year-month-day) | 2024-04-01 |
| Date reviewed (year-month-day) | 2024-04-29 |

[Edit this RFC](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/contribute/governance/rfcs/0246_api_levels_are_32_bits.md)
[Edit RFC metadata](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/contribute/governance/rfcs/_rfcs.yaml&searchAndJump=-+name:+'RFC-0246')

## Summary

This proposal changes the definition of an API Level to be an unsigned 32-bit
integer. This supersedes [RFC-0002](https://fuchsia.dev/docs/contribute/governance/rfcs/0002_platform_versioning), which defined it as an unsigned 64-bit
integer.

## Motivation

The following code can currently be found in
[`target_api_level.gni`](https://cs.opensource.google/fuchsia/fuchsia/+/main:build/config/fuchsia/target_api_level.gni;l=36;drc=0d3980127b610a5ad03f2216d3d6f83baa24f51e) (paraphrased for clarity):

    if (override_target_api_level == -1) {
        clang_fuchsia_api_level = 4294967295
        fidl_fuchsia_api_level = "LEGACY"
    }

Roughly speaking, this says that if the API level is not specified when building
in fuchsia.git, `clang_fuchsia_api_level` should be set to `0xFFFFFFFF`, and
`fidl_fuchsia_api_level` should be set to the string value `"LEGACY"`. This
string is later interpreted by `fidlc` to be an alias for the value
`0xFFFFFFFFFFFFFFFF`, as defined in [RFC-0083](https://fuchsia.dev/docs/contribute/governance/rfcs/0083_fidl_versioning).

This demonstrates two issues:

1. There is a "clang API level" and a "FIDL API level", and they are often given different values.
2. API levels are sometimes represented as strings, sometimes as integers.

### clang vs FIDL vs... ?

[RFC-0002](https://fuchsia.dev/docs/contribute/governance/rfcs/0002_platform_versioning) defined API levels as 64-bit integers, and subsequent RFCs have
allocated specific 64-bit integers and given them names and meaning. For
instance, `LEGACY` is identified by `0xFFFFFFFFFFFFFFFF`. But based on the code
above, we'll instead pass `0xFFFFFFFF` to Clang because, unfortunately, API
levels are limited to 32 bits in Clang.

Well, that's not exactly true.

Clang reasons about compatibility via the [`availability`
attribute](https://clang.llvm.org/docs/AttributeReference.html#availability), which represents versions as
[`VersionTuple`s](https://github.com/llvm/llvm-project/blob/b8b2a279d002f4d424d9b089bc32a0e5d6989dbb/llvm/include/llvm/Support/VersionTuple.h). `VersionTuple` packs a tuple of four
integers `major[.minor[.subminor[.build]]]`, representing a version, into a
128-bit structure. Fuchsia API levels are modeled as "major versions" in this
structure, and are therefore limited to 32-bits.

Clang is a critical part of the Fuchsia platform's versioning story, so this
inconsistency must be resolved.

### `string` vs `int` vs... ?

Host tools and build systems are inconsistent in terms of the type used to
represent API levels. Even within `fuchsia.git`'s build system, there is
inconsistency, as seen in the code block above.

This isn't necessarily a problem, and this RFC doesn't fully resolve it.
However, it will attempt to provide guidance for navigating this ambiguity.

## Stakeholders

*Facilitator:* abarth@google.com

*Reviewers:*

- ddorwin@google.com
- mkember@google.com
- haowei@google.com

*Consulted:*

- chaselatta@google.com
- phosek@google.com
- ianloic@google.com

*Socialization:*

Discussed on a [Google-internal bug](https://fxbug.dev/311675988) with members of the Platform
Versioning working group and the toolchain team.

## Requirements

API levels must be representable as strings, including on both command lines,
and in the name of files and directories.

API levels must be totally ordered, so we can say things like: "`Foo` was added
at API level `N`, and `N <= M`, therefore `Foo` is a part of API level `M`."

We must be able to assign some API levels special names and behaviors. The other
requirements in this section also apply to these "special" API levels.

## Design

### Integer representation

API levels will be redefined as unsigned 32-bit integers.

The lower half of this space (that is, API levels less than `0x80000000`) are
considered "normal" API levels. The upper half of this space is reserved. This
RFC and future RFCs will define the meaning of specific values greater than or
equal to `0x80000000`.

Tools that treat all API levels uniformly may ignore the distinction between
"normal" and reserved values, and treat them all as unsigned 32-bit integers,
ordered in the typical way.

Tools with special logic for particular API levels should reject input that
specifies a reserved ABI level value that it doesn't understand.

### String representation

API levels can be represented by strings in multiple different ways:

- Any UTF-8 string that represents a base-10 number in the interval \[0, 2^32^) is a string representation of an API level (for example, `"7"`).
- A special API level can be represented by its name, given in upper case (for example, `"HEAD"`).
- Tools should not accept more esoteric values like `"0016"`, or `"0x20"`, because doing so can create ambiguity. For example, is `"0016"` in octal or decimal? `"2A"` is likely hexadecimal, but if it is accepted as such, is `"29"` hexadecimal or decimal? However, string parsing logic is often handled by libraries beyond an individual tool author's control, so tools *may* accept such values.

Every API level has exactly one **canonical string representation**:

- For "normal" API levels, the base-10 UTF-8 string representation is canonical (for example, `"13"`).
- For "special" API levels, the name in upper-case is canonical (for example, `"NEXT"`).

Tools that need to know the canonical string representation for an API level
must return an error if they need to get the canonical representation for a
reserved API level (that is, one greater than or equal to `0x80000000`) for
which they don't know the special name.

### Special API Levels

The following API levels are given special names:

- `PLATFORM = 0xFFF00000 = 4293918720`. `PLATFORM` plays the role previously
  provided by `LEGACY`, in that by default, the platform will be built at API
  level `PLATFORM`. Previously, `LEGACY` had the value `0xFFFFFFFFFFFFFFFF` in
  FIDL, and could not be represented in Clang.

  `LEGACY` was deprecated by [RFC-0232](https://fuchsia.dev/docs/contribute/governance/rfcs/0232_fidl_bindings_for_multiple_api_levels) and is in the process of being removed
  from FIDL, but even after that work is complete, the platform build, C++, and
  Rust code will use `PLATFORM` to detect a normal platform build.

  `PLATFORM` is only useful in code that is used both in the OS build and as
  part of the IDK. In such libraries, a target API level less than `PLATFORM`
  means that the code is being built as part of the SDK, and can only use API
  elements available in the specific API level the build is targeting. If the
  target API level *equals* `PLATFORM`, the code is being built as part of the
  OS, and must provide support for *all* API levels in the "supported" or
  "sunset" phase. See [RFC-0239](https://fuchsia.dev/docs/contribute/governance/rfcs/0239_platform_versioning_in_practice) for more information.
- `HEAD = 0xFFE00000 = 4292870144`. Previously, `HEAD` had the value
  `0xFFFFFFFFFFFFFFFE` in FIDL and `0xFFFFFFFF` in Clang.

- `NEXT = 0xFFD00000 = 4291821568`. `NEXT` was described in [RFC-0239](https://fuchsia.dev/docs/contribute/governance/rfcs/0239_platform_versioning_in_practice), but was
  not assigned a numerical value.

This set may grow or shrink over time, as necessary.

Initially, these values will be hard-coded into SDKs that support targeting
`HEAD` or `NEXT`, but eventually they should be defined in
[`//sdk/version_history.json`](https://cs.opensource.google/fuchsia/fuchsia/+/main:/sdk/version_history.json).

### When to use integers vs strings

Command line tools accept input and produce output as strings, and as such, they
should accept any of the string representations for API levels described above,
and should preferentially output API levels using their canonical string form.
However, sometimes this is not possible or is very inconvenient, so use of
canonical string forms is not required. For example, Clang is not aware of the
names of special Fuchsia API levels, so the value of `-ffuchsia-api-level` must
be provided as an integer.

Within the implementation of build tools, storing an API level in integer form
is preferred.

Build systems may represent API levels in whichever way is most natural for that
build system.

## Performance

This change should have no impact on performance.

## Backwards Compatibility

The changes in the numerical values of `LEGACY` and `HEAD` are not
backwards-compatible, strictly speaking. However, the previous 64-bit values
were really only used within `fidlc` and were essentially invisible to users of
the Fuchsia SDK.

The current definition of `HEAD` as `0xFFFFFFFF` in C++ code is theoretically
visible to SDK users, but since no code outside of the Fuchsia source tree
currently targets `HEAD` or `LEGACY`, the change should also go unnoticed. LSC
presubmits will confirm this.

The new values of `PLATFORM` (previously, `LEGACY`), `HEAD`, and `NEXT` were
chosen to provide substantial gaps between them. This will allow us to create
additional special API levels without redefining the existing ones. Any such new
API levels we create should be added half-way between the two adjacent API
levels. In the worst case, we'll be able to subdivide each interval 20 times.

## Security considerations

This change should have no impact on security.

## Privacy considerations

This change should have no impact on privacy.

## Testing

This RFC primarily pertains to code in Fuchsia's build systems and SDKs. For
better or worse, dedicated automated testing of that code is minimal. However,
in practice, if the build system is broken, it will be noticed fairly quickly
when tests fail, build breaks, and local development flows go haywire.

## Documentation

The meaning and values of `NEXT`, `HEAD`, and `PLATFORM` will be included in
forthcoming documentation of the concepts introduced in RFC-0239.

## Drawbacks, alternatives, and unknowns

### Drawback: Is 32-bits enough?

If allocated sequentially, even if we issued a new API level every hour, it
would take around 245,000 years to run out of the 2^31^ API levels set
aside in this RFC. That seems sufficient.

However, API levels need not always be allocated densely. This very RFC defines
special 3 API levels with 1048576 unused API levels between each. Is that okay?

With 64-bit API levels, it's difficult to imagine the possibility of running
out, even if we allocate them exceptionally sparsely (for example, by leaving
thousands, millions, or billions of gaps between consecutive versions, as Clang
does with `VersionTuple`).

With 32-bit API levels, we must be a little more judicious in our allocation.

Putting myself at risk of becoming a historical laughing stock, I'll say it:
2^32^ API levels ought to be enough for anybody.

### Alternative: Switch to a Major.Minor scheme

`VersionTuple.h` was clearly written under the assumption that functionality
could be introduced or removed at versions that look like `12.5`, or even
`30.1.2.3`. Platforms that structure their versions this way can represent up to
2^125^^[1](https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0246_api_levels_are_32_bits.md.txt#fn1)^ distinct versions. Perhaps the fact that Fuchsia can
only use 2^32^ of these values indicates that Fuchsia's versioning
scheme is inappropriate?

There are multiple successful platforms that version their API surface with a
single integer (for example, Chromium and Android). There is insufficient reason
to believe that we cannot also be successful following the same strategy.
[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/contribute/governance/rfcs/0246_api_levels_are_32_bits.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/contribute/governance/rfcs/0246_api_levels_are_32_bits.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/contribute/governance/rfcs/0246_api_levels_are_32_bits.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/contribute/governance/rfcs/0246_api_levels_are_32_bits.md "Edit the source code of this page")

*** ** * ** ***

1. Clang uses the most significant bits of each of `minor`,
   `subminor`, and `build` as flags. [↩](https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0246_api_levels_are_32_bits.md.txt#fnref1)