Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
use super::{
utils::{
get_context, get_package_manager, has_system_feature, with_attached, JNIEnv, JObject,
JResult,
},
PackageManager,
};
/**
* The Android audio features
*/
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "java-interface")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AudioFeature {
LowLatency,
Output,
Pro,
Microphone,
Midi,
}
impl From<AudioFeature> for &'static str {
fn from(feature: AudioFeature) -> Self {
use AudioFeature::*;
match feature {
LowLatency => PackageManager::FEATURE_AUDIO_LOW_LATENCY,
Output => PackageManager::FEATURE_AUDIO_OUTPUT,
Pro => PackageManager::FEATURE_AUDIO_PRO,
Microphone => PackageManager::FEATURE_MICROPHONE,
Midi => PackageManager::FEATURE_MIDI,
}
}
}
impl AudioFeature {
/**
* Check availability of an audio feature using Android Java API
*/
pub fn has(&self) -> Result<bool, String> {
let context = get_context();
with_attached(context, |env, activity| {
try_check_system_feature(env, &activity, (*self).into())
})
.map_err(|error| error.to_string())
}
}
fn try_check_system_feature<'j>(
env: &mut JNIEnv<'j>,
activity: &JObject<'j>,
feature: &str,
) -> JResult<bool> {
let package_manager = get_package_manager(env, activity)?;
has_system_feature(env, &package_manager, feature)
}

View File

@@ -0,0 +1,167 @@
use num_derive::FromPrimitive;
use crate::AudioFormat;
pub(crate) struct Context;
impl Context {
pub const AUDIO_SERVICE: &'static str = "audio";
}
pub(crate) struct PackageManager;
impl PackageManager {
pub const FEATURE_AUDIO_LOW_LATENCY: &'static str = "android.hardware.audio.low_latency";
pub const FEATURE_AUDIO_OUTPUT: &'static str = "android.hardware.audio.output";
pub const FEATURE_AUDIO_PRO: &'static str = "android.hardware.audio.pro";
pub const FEATURE_MICROPHONE: &'static str = "android.hardware.microphone";
pub const FEATURE_MIDI: &'static str = "android.software.midi";
}
pub(crate) struct AudioManager;
impl AudioManager {
pub const PROPERTY_OUTPUT_SAMPLE_RATE: &'static str =
"android.media.property.OUTPUT_SAMPLE_RATE";
pub const PROPERTY_OUTPUT_FRAMES_PER_BUFFER: &'static str =
"android.media.property.OUTPUT_FRAMES_PER_BUFFER";
pub const GET_DEVICES_INPUTS: i32 = 1 << 0;
pub const GET_DEVICES_OUTPUTS: i32 = 1 << 1;
pub const GET_DEVICES_ALL: i32 = Self::GET_DEVICES_INPUTS | Self::GET_DEVICES_OUTPUTS;
}
/**
* The Android audio device info
*/
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "java-interface")))]
#[derive(Debug, Clone)]
pub struct AudioDeviceInfo {
/**
* Device identifier
*/
pub id: i32,
/**
* The type of device
*/
pub device_type: AudioDeviceType,
/**
* The device can be used for playback and/or capture
*/
pub direction: AudioDeviceDirection,
/**
* Device address
*/
pub address: String,
/**
* Device product name
*/
pub product_name: String,
/**
* Available channel configurations
*/
pub channel_counts: Vec<i32>,
/**
* Supported sample rates
*/
pub sample_rates: Vec<i32>,
/**
* Supported audio formats
*/
pub formats: Vec<AudioFormat>,
}
/**
* The type of audio device
*/
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "java-interface")))]
#[derive(Debug, Clone, Copy, FromPrimitive)]
#[non_exhaustive]
#[repr(i32)]
pub enum AudioDeviceType {
Unknown = 0,
AuxLine = 19,
BleBroadcast = 30,
BleHeadset = 26,
BleSpeaker = 27,
BluetoothA2DP = 8,
BluetoothSCO = 7,
BuiltinEarpiece = 1,
BuiltinMic = 15,
BuiltinSpeaker = 2,
BuiltinSpeakerSafe = 24,
Bus = 21,
Dock = 13,
Fm = 14,
FmTuner = 16,
Hdmi = 9,
HdmiArc = 10,
HdmiEarc = 29,
HearingAid = 23,
Ip = 20,
LineAnalog = 5,
LineDigital = 6,
RemoteSubmix = 25,
Telephony = 18,
TvTuner = 17,
UsbAccessory = 12,
UsbDevice = 11,
UsbHeadset = 22,
WiredHeadphones = 4,
WiredHeadset = 3,
Unsupported = -1,
}
/**
* The direction of audio device
*/
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "java-interface")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum AudioDeviceDirection {
Dumb = 0,
Input = AudioManager::GET_DEVICES_INPUTS,
Output = AudioManager::GET_DEVICES_OUTPUTS,
InputOutput = AudioManager::GET_DEVICES_ALL,
}
impl AudioDeviceDirection {
pub fn new(is_input: bool, is_output: bool) -> Self {
use self::AudioDeviceDirection::*;
match (is_input, is_output) {
(true, true) => InputOutput,
(false, true) => Output,
(true, false) => Input,
_ => Dumb,
}
}
pub fn is_input(&self) -> bool {
0 < *self as i32 & AudioDeviceDirection::Input as i32
}
pub fn is_output(&self) -> bool {
0 < *self as i32 & AudioDeviceDirection::Output as i32
}
}
impl AudioFormat {
pub(crate) const ENCODING_PCM_16BIT: i32 = 2;
//pub(crate) const ENCODING_PCM_8BIT: i32 = 3;
pub(crate) const ENCODING_PCM_FLOAT: i32 = 4;
pub(crate) fn from_encoding(encoding: i32) -> Option<AudioFormat> {
match encoding {
AudioFormat::ENCODING_PCM_16BIT => Some(AudioFormat::I16),
AudioFormat::ENCODING_PCM_FLOAT => Some(AudioFormat::F32),
_ => None,
}
}
}

View File

@@ -0,0 +1,88 @@
use num_traits::FromPrimitive;
use crate::AudioFormat;
use super::{
utils::{
call_method_no_args_ret_bool, call_method_no_args_ret_char_sequence,
call_method_no_args_ret_int, call_method_no_args_ret_int_array,
call_method_no_args_ret_string, get_context, get_devices, get_system_service,
with_attached, JNIEnv, JObject, JResult,
},
AudioDeviceDirection, AudioDeviceInfo, AudioDeviceType, Context,
};
impl AudioDeviceInfo {
/**
* Request audio devices using Android Java API
*/
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "java-interface")))]
pub fn request(direction: AudioDeviceDirection) -> Result<Vec<AudioDeviceInfo>, String> {
let context = get_context();
with_attached(context, |env, context| {
let sdk_version = env
.get_static_field("android/os/Build$VERSION", "SDK_INT", "I")?
.i()?;
if sdk_version >= 23 {
try_request_devices_info(env, &context, direction)
} else {
Err(jni::errors::Error::MethodNotFound {
name: "".into(),
sig: "".into(),
})
}
})
.map_err(|error| error.to_string())
}
}
fn try_request_devices_info<'j>(
env: &mut JNIEnv<'j>,
context: &JObject<'j>,
direction: AudioDeviceDirection,
) -> JResult<Vec<AudioDeviceInfo>> {
let audio_manager = get_system_service(env, context, Context::AUDIO_SERVICE)?;
let devices = get_devices(env, &audio_manager, direction as i32)?;
let length = env.get_array_length(&devices)?;
(0..length)
.map(|index| {
let device = env.get_object_array_element(&devices, index)?;
let id = call_method_no_args_ret_int(env, &device, "getId")?;
let address = call_method_no_args_ret_string(env, &device, "getAddress")?;
let address = String::from(env.get_string(&address)?);
let product_name =
call_method_no_args_ret_char_sequence(env, &device, "getProductName")?;
let product_name = String::from(env.get_string(&product_name)?);
let device_type =
FromPrimitive::from_i32(call_method_no_args_ret_int(env, &device, "getType")?)
.unwrap_or(AudioDeviceType::Unsupported);
let direction = AudioDeviceDirection::new(
call_method_no_args_ret_bool(env, &device, "isSource")?,
call_method_no_args_ret_bool(env, &device, "isSink")?,
);
let channel_counts =
call_method_no_args_ret_int_array(env, &device, "getChannelCounts")?;
let sample_rates = call_method_no_args_ret_int_array(env, &device, "getSampleRates")?;
let formats = call_method_no_args_ret_int_array(env, &device, "getEncodings")?
.into_iter()
.filter_map(AudioFormat::from_encoding)
.collect::<Vec<_>>();
Ok(AudioDeviceInfo {
id,
address,
product_name,
device_type,
direction,
channel_counts,
sample_rates,
formats,
})
})
.collect::<Result<Vec<_>, _>>()
}

View File

@@ -0,0 +1,79 @@
use super::{
utils::{
get_context, get_property, get_system_service, with_attached, JNIEnv, JObject, JResult,
},
AudioManager, Context,
};
use crate::DefaultStreamValues;
impl DefaultStreamValues {
/**
* Try request defaults from AudioManager properties.
*/
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "java-interface")))]
pub fn init() -> Result<(), String> {
let activity = get_context();
let values = with_attached(activity, |env, context| {
let sdk_version = env
.get_static_field("android/os/Build$VERSION", "SDK_INT", "I")?
.i()?;
if sdk_version < 17 {
Err(jni::errors::Error::MethodNotFound {
name: "".into(),
sig: "".into(),
})
} else if sdk_version < 26 {
try_request_default_stream_values(env, &context).map(Some)
} else {
// not necessary
Ok(None)
}
});
match values {
Ok(Some((sample_rate, frames_per_burst))) => {
if let Some(value) = sample_rate {
Self::set_sample_rate(value);
}
if let Some(value) = frames_per_burst {
Self::set_frames_per_burst(value);
}
Ok(())
}
Ok(None) => Ok(()),
Err(error) => Err(error.to_string()),
}
}
}
fn try_request_default_stream_values<'j>(
env: &mut JNIEnv<'j>,
context: &JObject<'j>,
) -> JResult<(Option<i32>, Option<i32>)> {
let audio_manager = get_system_service(env, context, Context::AUDIO_SERVICE)?;
let sample_rate = get_property(
env,
&audio_manager,
AudioManager::PROPERTY_OUTPUT_SAMPLE_RATE,
)?;
let sample_rate = env.get_string(&sample_rate)?;
let frames_per_burst = get_property(
env,
&audio_manager,
AudioManager::PROPERTY_OUTPUT_FRAMES_PER_BUFFER,
)?;
let frames_per_burst = env.get_string(&frames_per_burst)?;
Ok((
(*sample_rate).to_str().ok().and_then(|s| s.parse().ok()),
(*frames_per_burst)
.to_str()
.ok()
.and_then(|s| s.parse().ok()),
))
}

181
vendor/oboe/src/java_interface/utils.rs vendored Normal file
View File

@@ -0,0 +1,181 @@
use jni::sys::jobject;
use ndk_context::AndroidContext;
use std::sync::Arc;
pub use jni::Executor;
pub use jni::{
errors::Result as JResult,
objects::{JIntArray, JObject, JObjectArray, JString},
JNIEnv, JavaVM,
};
pub fn get_context() -> AndroidContext {
ndk_context::android_context()
}
pub fn with_attached<F, R>(context: AndroidContext, closure: F) -> JResult<R>
where
for<'j> F: FnOnce(&mut JNIEnv<'j>, JObject<'j>) -> JResult<R>,
{
let vm = Arc::new(unsafe { JavaVM::from_raw(context.vm().cast())? });
let context = context.context();
let context = unsafe { JObject::from_raw(context as jobject) };
Executor::new(vm).with_attached(|env| closure(env, context))
}
pub fn call_method_no_args_ret_int_array<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
method: &str,
) -> JResult<Vec<i32>> {
let array: JIntArray = env.call_method(subject, method, "()[I", &[])?.l()?.into();
let length = env.get_array_length(&array)?;
let mut values = Vec::with_capacity(length as usize);
env.get_int_array_region(array, 0, values.as_mut())?;
Ok(values)
}
pub fn call_method_no_args_ret_int<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
method: &str,
) -> JResult<i32> {
env.call_method(subject, method, "()I", &[])?.i()
}
pub fn call_method_no_args_ret_bool<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
method: &str,
) -> JResult<bool> {
env.call_method(subject, method, "()Z", &[])?.z()
}
pub fn call_method_no_args_ret_string<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
method: &str,
) -> JResult<JString<'j>> {
Ok(env
.call_method(subject, method, "()Ljava/lang/String;", &[])?
.l()?
.into())
}
pub fn call_method_no_args_ret_char_sequence<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
method: &str,
) -> JResult<JString<'j>> {
let cseq = env
.call_method(subject, method, "()Ljava/lang/CharSequence;", &[])?
.l()?;
Ok(env
.call_method(&cseq, "toString", "()Ljava/lang/String;", &[])?
.l()?
.into())
}
pub fn call_method_string_arg_ret_bool<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
name: &str,
arg: impl AsRef<str>,
) -> JResult<bool> {
env.call_method(
subject,
name,
"(Ljava/lang/String;)Z",
&[(&env.new_string(arg)?).into()],
)?
.z()
}
pub fn call_method_string_arg_ret_string<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
name: &str,
arg: impl AsRef<str>,
) -> JResult<JString<'j>> {
Ok(env
.call_method(
subject,
name,
"(Ljava/lang/String;)Ljava/lang/String;",
&[(&env.new_string(arg)?).into()],
)?
.l()?
.into())
}
pub fn call_method_string_arg_ret_object<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
method: &str,
arg: &str,
) -> JResult<JObject<'j>> {
env.call_method(
subject,
method,
"(Ljava/lang/String;)Ljava/lang/Object;",
&[(&env.new_string(arg)?).into()],
)?
.l()
}
pub fn get_package_manager<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
) -> JResult<JObject<'j>> {
env.call_method(
subject,
"getPackageManager",
"()Landroid/content/pm/PackageManager;",
&[],
)?
.l()
}
pub fn has_system_feature<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
name: &str,
) -> JResult<bool> {
call_method_string_arg_ret_bool(env, subject, "hasSystemFeature", name)
}
pub fn get_system_service<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
name: &str,
) -> JResult<JObject<'j>> {
call_method_string_arg_ret_object(env, subject, "getSystemService", name)
}
pub fn get_property<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
name: &str,
) -> JResult<JString<'j>> {
call_method_string_arg_ret_string(env, subject, "getProperty", name)
}
pub fn get_devices<'j>(
env: &mut JNIEnv<'j>,
subject: &JObject<'j>,
flags: i32,
) -> JResult<JObjectArray<'j>> {
env.call_method(
subject,
"getDevices",
"(I)[Landroid/media/AudioDeviceInfo;",
&[flags.into()],
)?
.l()
.map(From::from)
}