Skip to content

Commit 8312a20

Browse files
committed
ndk: Add definitions and bindings for API levels
`android/api-levels.h` from the NDK defines constants for all (NDK-supported) API levels and both a getter for the "current apps' target API level" as well as the API level of the device the app is running on. Create bindings to allow users to query this information at runtime from their Rust code as well. The equivalent of the NDK's `__ANDROID_API__` define are our `api-level-xx` crate features, used to restrict APIs (and inexistant linker symbols) at build-time. (As there is no full-fledged Android build system involved, users of the `ndk` crate are expected to keep this in sync with the minimum/target API level passed to their build tool of choice.)
1 parent 378e710 commit 8312a20

File tree

10 files changed

+139
-6
lines changed

10 files changed

+139
-6
lines changed

ndk-sys/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
- **Breaking:** Removed `__ANDROID_API__` constant as it is always defined to the value of `__ANDROID_API_FUTURE__`. (#479)
34
- Regenerate bindings with `bindgen 0.71.1`. (#487)
45
- Include API bindings from `sys/system_properties.h`. (#495)
56

ndk-sys/generate_bindings.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ while read ARCH && read TARGET ; do
3131
--blocklist-item 'C?_?JNIEnv' \
3232
--blocklist-item '_?JavaVM' \
3333
--blocklist-item '_?j\w+' \
34+
--blocklist-item '__ANDROID_API__' \
3435
--newtype-enum '\w+_(result|status)_t' \
3536
--newtype-enum 'ACameraDevice_request_template' \
3637
--newtype-enum 'ADataSpace' \

ndk-sys/src/ffi_aarch64.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ pub const __BIONIC__: u32 = 1;
4444
pub const __WORDSIZE: u32 = 64;
4545
pub const __bos_level: u32 = 0;
4646
pub const __ANDROID_API_FUTURE__: u32 = 10000;
47-
pub const __ANDROID_API__: u32 = 10000;
4847
pub const __ANDROID_API_G__: u32 = 9;
4948
pub const __ANDROID_API_I__: u32 = 14;
5049
pub const __ANDROID_API_J__: u32 = 16;

ndk-sys/src/ffi_arm.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ pub const __BIONIC__: u32 = 1;
171171
pub const __WORDSIZE: u32 = 32;
172172
pub const __bos_level: u32 = 0;
173173
pub const __ANDROID_API_FUTURE__: u32 = 10000;
174-
pub const __ANDROID_API__: u32 = 10000;
175174
pub const __ANDROID_API_G__: u32 = 9;
176175
pub const __ANDROID_API_I__: u32 = 14;
177176
pub const __ANDROID_API_J__: u32 = 16;

ndk-sys/src/ffi_i686.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ pub const __BIONIC__: u32 = 1;
3434
pub const __WORDSIZE: u32 = 32;
3535
pub const __bos_level: u32 = 0;
3636
pub const __ANDROID_API_FUTURE__: u32 = 10000;
37-
pub const __ANDROID_API__: u32 = 10000;
3837
pub const __ANDROID_API_G__: u32 = 9;
3938
pub const __ANDROID_API_I__: u32 = 14;
4039
pub const __ANDROID_API_J__: u32 = 16;

ndk-sys/src/ffi_x86_64.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ pub const __BIONIC__: u32 = 1;
3434
pub const __WORDSIZE: u32 = 64;
3535
pub const __bos_level: u32 = 0;
3636
pub const __ANDROID_API_FUTURE__: u32 = 10000;
37-
pub const __ANDROID_API__: u32 = 10000;
3837
pub const __ANDROID_API_G__: u32 = 9;
3938
pub const __ANDROID_API_I__: u32 = 14;
4039
pub const __ANDROID_API_J__: u32 = 16;

ndk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased
22

33
- image_reader: Add `ImageReader::new_with_data_space()` constructor and `ImageReader::data_space()` getter from API level 34. (#474)
4+
- Add `api_level` module with definitions and bindings for Android API levels. (#479)
45
- Add bindings for querying and setting Android System Properties. (#495)
56

67
# 0.9.0 (2024-04-26)

ndk/src/api_level.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//! Bindings for [API levels]
2+
//!
3+
//! Defines functions and constants for working with Android API levels.
4+
//!
5+
//! [API levels]: https://developer.android.com/ndk/reference/group/apilevels
6+
7+
use num_enum::{FromPrimitive, IntoPrimitive};
8+
use thiserror::Error;
9+
10+
/// Android API levels, equivalent to the constants defined in `<android/api-level.h>` and the Java
11+
/// [`Build.VERSION_CODES`] constants.
12+
///
13+
/// [`Build.VERSION_CODES`]: https://developer.android.com/reference/android/os/Build.VERSION_CODES
14+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
15+
#[repr(u32)]
16+
#[non_exhaustive]
17+
pub enum ApiLevel {
18+
/// Magic version number for an Android OS build which has not yet turned into an official
19+
/// release.
20+
#[doc(alias = "__ANDROID_API_FUTURE__")]
21+
Future = ffi::__ANDROID_API_FUTURE__,
22+
23+
/// Names the Gingerbread API level (9)
24+
#[doc(alias = "__ANDROID_API_G__")]
25+
G = ffi::__ANDROID_API_G__,
26+
/// Names the Ice-Cream Sandwich API level (14)
27+
#[doc(alias = "__ANDROID_API_I__")]
28+
I = ffi::__ANDROID_API_I__,
29+
/// Names the Jellybean API level (16)
30+
#[doc(alias = "__ANDROID_API_J__")]
31+
J = ffi::__ANDROID_API_J__,
32+
/// Names the Jellybean MR1 API level (17)
33+
#[doc(alias = "__ANDROID_API_J_MR1__")]
34+
JMr1 = ffi::__ANDROID_API_J_MR1__,
35+
/// Names the Jellybean MR2 API level (18)
36+
#[doc(alias = "__ANDROID_API_J_MR2__")]
37+
JMr2 = ffi::__ANDROID_API_J_MR2__,
38+
/// Names the KitKat API level (19)
39+
#[doc(alias = "__ANDROID_API_K__")]
40+
K = ffi::__ANDROID_API_K__,
41+
/// Names the Lollipop API level (21)
42+
#[doc(alias = "__ANDROID_API_L__")]
43+
L = ffi::__ANDROID_API_L__,
44+
/// Names the Lollipop MR1 API level (22)
45+
#[doc(alias = "__ANDROID_API_L_MR1__")]
46+
LMr1 = ffi::__ANDROID_API_L_MR1__,
47+
/// Names the Marshmallow API level (23)
48+
#[doc(alias = "__ANDROID_API_M__")]
49+
M = ffi::__ANDROID_API_M__,
50+
/// Names the Nougat API level (24)
51+
#[doc(alias = "__ANDROID_API_N__")]
52+
N = ffi::__ANDROID_API_N__,
53+
/// Names the Nougat MR1 API level (25)
54+
#[doc(alias = "__ANDROID_API_N_MR1__")]
55+
NMr1 = ffi::__ANDROID_API_N_MR1__,
56+
/// Names the Oreo API level (26)
57+
#[doc(alias = "__ANDROID_API_O__")]
58+
O = ffi::__ANDROID_API_O__,
59+
/// Names the Oreo MR1 API level (27)
60+
#[doc(alias = "__ANDROID_API_O_MR1__")]
61+
OMr1 = ffi::__ANDROID_API_O_MR1__,
62+
/// Names the Pie API level (28)
63+
#[doc(alias = "__ANDROID_API_P__")]
64+
P = ffi::__ANDROID_API_P__,
65+
/// Names the Android 10 (aka "Q" or "Quince Tart") API level (29)
66+
#[doc(alias = "__ANDROID_API_Q__")]
67+
Q = ffi::__ANDROID_API_Q__,
68+
/// Names the Android 11 (aka "R" or "Red Velvet Cake") API level (30)
69+
#[doc(alias = "__ANDROID_API_R__")]
70+
R = ffi::__ANDROID_API_R__,
71+
/// Names the Android 12 (aka "S" or "Snowcone") API level (31)
72+
#[doc(alias = "__ANDROID_API_S__")]
73+
S = ffi::__ANDROID_API_S__,
74+
/// Names the Android 13 (aka "T" or "Tiramisu") API level (33)
75+
#[doc(alias = "__ANDROID_API_T__")]
76+
T = ffi::__ANDROID_API_T__,
77+
/// Names the Android 14 (aka "U" or "UpsideDownCake") API level (34)
78+
#[doc(alias = "__ANDROID_API_U__")]
79+
U = ffi::__ANDROID_API_U__,
80+
/// Names the Android 15 (aka "V" or "VanillaIceCream") API level (35)
81+
#[doc(alias = "__ANDROID_API_V__")]
82+
V = ffi::__ANDROID_API_V__,
83+
#[doc(hidden)]
84+
#[num_enum(catch_all)]
85+
__Unknown(u32),
86+
}
87+
88+
/// Returns the `targetSdkVersion` from `AndroidManifest.xml` of the caller, or [`ApiLevel::Future`]
89+
/// if there is no known target SDK version (for code not running in the context of an app).
90+
///
91+
/// See also [`device_api_level()`].
92+
#[cfg(feature = "api-level-24")]
93+
#[doc(alias = "android_get_application_target_sdk_version")]
94+
pub fn application_target_sdk_version() -> ApiLevel {
95+
let version = unsafe { ffi::android_get_application_target_sdk_version() };
96+
u32::try_from(version)
97+
// Docs suggest that it would only return `Future`
98+
.expect("Unexpected sign bit in `application_target_sdk_version()`")
99+
.into()
100+
}
101+
102+
/// Possible failures returned by [`device_api_level()`].
103+
#[derive(Debug, Error)]
104+
pub enum DeviceApiLevelError {
105+
#[cfg(not(feature = "api-level-29"))]
106+
#[error("`__system_property_get(\"ro.build.version.sdk\")` failed")]
107+
FallbackPropertyGetFailed(#[from] super::system_properties::GetError<std::num::ParseIntError>),
108+
#[error("device_api_level() encountered a negative version code")]
109+
TryFromIntError(#[from] std::num::TryFromIntError),
110+
}
111+
112+
/// Returns the API level of the device we're actually running on.
113+
///
114+
/// The returned value is equivalent to the Java [`Build.VERSION.SDK_INT`] API.
115+
///
116+
/// [`Build.VERSION.SDK_INT`]: https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT
117+
///
118+
/// Below `api-level-29` this falls back to reading the `"ro.build.version.sdk"` system property,
119+
/// with the possibility to return more types of errors.
120+
///
121+
/// See also [`application_target_sdk_version()`].
122+
#[doc(alias = "android_get_device_api_level")]
123+
pub fn device_api_level() -> Result<ApiLevel, DeviceApiLevelError> {
124+
#[cfg(not(feature = "api-level-29"))]
125+
let version = super::system_properties::get::<i32>(unsafe {
126+
// TODO: Switch to C-string literal since MSRV 1.77
127+
std::ffi::CStr::from_bytes_with_nul_unchecked(b"ro.build.version.sdk\0")
128+
})?;
129+
130+
#[cfg(feature = "api-level-29")]
131+
let version = unsafe { ffi::android_get_device_api_level() };
132+
133+
Ok(u32::try_from(version)?.into())
134+
}

ndk/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
)]
1212
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
1313

14+
pub mod api_level;
1415
pub mod asset;
1516
pub mod audio;
1617
pub mod bitmap;

ndk/src/system_properties.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ pub fn get_raw(name: &CStr) -> Result<CString, GetRawError> {
6464
}
6565

6666
/// Possible failures returned by [`get()`].
67-
#[allow(missing_debug_implementations)] // Our MSRV is too low for derive(Debug) to emit bounds on T.
68-
#[derive(Error)]
67+
#[derive(Debug, Error)]
6968
pub enum GetError<T> {
7069
#[error("Property is missing or empty")]
7170
MissingOrEmpty,

0 commit comments

Comments
 (0)