From d9dd843a7497e4f617eb42db0c2714f3f8105a07 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 25 Jul 2024 10:30:27 +0200 Subject: [PATCH] WIP: api-level bindings --- ndk-sys/generate_bindings.sh | 1 + ndk/src/api_level.rs | 154 +++++++++++++++++++++++++++++++++++ ndk/src/lib.rs | 1 + 3 files changed, 156 insertions(+) create mode 100644 ndk/src/api_level.rs diff --git a/ndk-sys/generate_bindings.sh b/ndk-sys/generate_bindings.sh index 0978e69d..57391a1a 100755 --- a/ndk-sys/generate_bindings.sh +++ b/ndk-sys/generate_bindings.sh @@ -30,6 +30,7 @@ while read ARCH && read TARGET ; do --blocklist-item 'C?_?JNIEnv' \ --blocklist-item '_?JavaVM' \ --blocklist-item '_?j\w+' \ + --blocklist-item '__ANDROID_API__' \ --newtype-enum '\w+_(result|status)_t' \ --newtype-enum 'ACameraDevice_request_template' \ --newtype-enum 'ADataSpace' \ diff --git a/ndk/src/api_level.rs b/ndk/src/api_level.rs new file mode 100644 index 00000000..bc2c3ff5 --- /dev/null +++ b/ndk/src/api_level.rs @@ -0,0 +1,154 @@ +//! `__ANDROID_API__` is the [API +//! level](https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels) +//! this code is being built for. The resulting binaries are only guaranteed to +//! be compatible with devices which have an API level greater than or equal to +//! `__ANDROID_API__`. +//! +//! For NDK and APEX builds, this macro will always be defined. It is set +//! automatically by Clang using the version suffix that is a part of the target +//! name. For example, `__ANDROID_API__` will be 24 when Clang is given the +//! argument `-target aarch64-linux-android24`. +//! +//! For non-APEX OS code, this defaults to __ANDROID_API_FUTURE__. +//! +//! The value of `__ANDROID_API__` can be compared to the named constants in +//! ``. +//! +//! The interpretation of `__ANDROID_API__` is similar to the AndroidManifest.xml +//! `minSdkVersion`. In most cases `__ANDROID_API__` will be identical to +//! `minSdkVersion`, but as it is a build time constant it is possible for +//! library code to use a different value than the app it will be included in. +//! When libraries and applications build for different API levels, the +//! `minSdkVersion` of the application must be at least as high as the highest +//! API level used by any of its libraries which are loaded unconditionally. +//! +//! Note that in some cases the resulting binaries may load successfully on +//! devices with an older API level. That behavior should not be relied upon, +//! even if you are careful to avoid using new APIs, as the toolchain may make +//! use of new features by default. For example, additional FORTIFY features may +//! implicitly make use of new APIs, SysV hashes may be omitted in favor of GNU +//! hashes to improve library load times, or relocation packing may be enabled to +//! reduce binary size. +//! +//! See android_get_device_api_level(), +//! android_get_application_target_sdk_version() and +//! https://android.googlesource.com/platform/bionic/+/master/docs/defines.md. + +use num_enum::{FromPrimitive, IntoPrimitive}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, IntoPrimitive)] +#[repr(u32)] +#[non_exhaustive] +pub enum ApiLevel { + /** Names the Gingerbread API level (9) */ + G = ffi::__ANDROID_API_G__, + + /** Names the Ice-Cream Sandwich API level (14) */ + I = ffi::__ANDROID_API_I__, + + /** Names the Jellybean API level (16) */ + J = ffi::__ANDROID_API_J__, + + /** Names the Jellybean MR1 API level (17) */ + J_MR1 = ffi::__ANDROID_API_J_MR1__, + + /** Names the Jellybean MR2 API level (18) */ + J_MR2 = ffi::__ANDROID_API_J_MR2__, + + /** Names the KitKat API level (19) */ + K = ffi::__ANDROID_API_K__, + + /** Names the Lollipop API level (21) */ + L = ffi::__ANDROID_API_L__, + + /** Names the Lollipop MR1 API level (22) */ + L_MR1 = ffi::__ANDROID_API_L_MR1__, + + /** Names the Marshmallow API level (23) */ + M = ffi::__ANDROID_API_M__, + + /** Names the Nougat API level (24) */ + N = ffi::__ANDROID_API_N__, + + /** Names the Nougat MR1 API level (25) */ + N_MR1 = ffi::__ANDROID_API_N_MR1__, + + /** Names the Oreo API level (26) */ + O = ffi::__ANDROID_API_O__, + + /** Names the Oreo MR1 API level (27) */ + O_MR1 = ffi::__ANDROID_API_O_MR1__, + + /** Names the Pie API level (28) */ + P = ffi::__ANDROID_API_P__, + + /** Names the Android 10 (aka "Q" or "Quince Tart") API level (29) */ + Q = ffi::__ANDROID_API_Q__, + + /** Names the Android 11 (aka "R" or "Red Velvet Cake") API level (30) */ + R = ffi::__ANDROID_API_R__, + + /** Names the Android 12 (aka "S" or "Snowcone") API level (31) */ + S = ffi::__ANDROID_API_S__, + + /** Names the Android 13 (aka "T" or "Tiramisu") API level (33) */ + T = ffi::__ANDROID_API_T__, + + /** Names the Android 14 (aka "U" or "UpsideDownCake") API level (34) */ + U = ffi::__ANDROID_API_U__, + + /** Names the "V" API level (35) */ + V = ffi::__ANDROID_API_V__, + + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), +} + +/// Returns the `targetSdkVersion` of the caller, or [`ffi::__ANDROID_API_FUTURE__`] if there is no +/// known target SDK version (for code not running in the context of an app). +/// +/// The returned values correspond to the named constants in ``, and is +/// equivalent to the `AndroidManifest.xml` `targetSdkVersion`. +/// +/// See also [`device_api_level()`]. +#[cfg(feature = "api-level-24")] +pub fn application_target_sdk_version() -> i32 { + #[cfg(feature = "api-level-29")] + unsafe { + ffi::android_get_application_target_sdk_version() + } + + #[cfg(not(feature = "api-level-29"))] + { + use std::ffi::{c_char, c_int}; + let mut value = [0; 92]; + if unsafe { + // TODO: from sys/cdefs.h, available in libc + extern "C" { + fn __system_property_get(__name: *const c_char, __value: *mut c_char) -> c_int; + } + // libc:: + __system_property_get(c"ro.build.version.sdk".as_ptr(), value.as_mut_ptr()) + } < 1 + { + -1 + } else { + std::str::from_utf8(&value) + .unwrap_or("") + .parse() + .unwrap_or(-1) + // TODO: Return -1 if this is 0? + } + } +} + +/// Returns the API level of the device we're actually running on, or `-1` on failure. The returned +/// values correspond to the named constants in ``, and is equivalent to the +/// Java `Build.VERSION.SDK_INT` API. +/// +/// See also [`application_target_sdk_version()`]. +#[cfg(feature = "api-level-29")] +pub fn device_api_level() -> i32 { + unsafe { ffi::android_get_device_api_level() } +} diff --git a/ndk/src/lib.rs b/ndk/src/lib.rs index 81e2ef61..25cba58e 100644 --- a/ndk/src/lib.rs +++ b/ndk/src/lib.rs @@ -11,6 +11,7 @@ )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +pub mod api_level; pub mod asset; pub mod audio; pub mod bitmap;