mod ser_cdc;
mod usb_conn;
mod usb_info;
pub use ser_cdc::*;
pub mod usb {
pub use crate::usb_conn::*;
pub use crate::usb_info::*;
#[inline(always)]
pub(crate) fn with_jni_env_activity<R>(
f: impl FnOnce(&mut jni::JNIEnv, &jni::objects::JObject<'static>) -> Result<R, Error>,
) -> Result<R, Error> {
let ctx = ndk_context::android_context();
let jvm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.map_err(|_| Error::JniEnv)?;
let context = unsafe { jni::objects::JObject::from_raw(ctx.context().cast()) };
let mut env = jvm
.attach_current_thread_permanently()
.map_err(|_| Error::JniEnv)?;
f(&mut env, &context)
}
#[inline(always)]
pub(crate) fn with_jni_env<R>(
f: impl FnOnce(&mut jni::JNIEnv) -> Result<R, Error>,
) -> Result<R, Error> {
let ctx = ndk_context::android_context();
let jvm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.map_err(|_| Error::JniEnv)?;
let mut env = jvm
.attach_current_thread_permanently()
.map_err(|_| Error::JniEnv)?;
f(&mut env)
}
#[inline(always)]
pub(crate) fn jni_call_ret_obj<'local, 'other_local, O>(
env: &mut jni::JNIEnv<'local>,
obj: O,
name: &str,
sig: &str,
args: &[jni::objects::JValueGen<&jni::objects::JObject<'other_local>>],
) -> Result<jni::objects::AutoLocal<'local, jni::objects::JObject<'local>>, Error>
where
O: AsRef<jni::objects::JObject<'other_local>>,
{
env.call_method(obj, name, sig, args)
.and_then(|o| o.l())
.map(|o| env.auto_local(o))
.map_err(jerr)
}
#[derive(Clone, Debug)]
pub enum Error {
Access,
Transfer(i32),
BadParam,
JniEnv,
NotSet,
NotSupported,
NoDevice,
JavaException(String),
Other(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Error::*;
match self {
Access => write!(f, "Permission denied"),
Transfer(n) => write!(f, "USB transfer failed, {} from UsbDeviceConnection", n),
BadParam => write!(f, "Bad function parameter passed (android-usbser)"),
JniEnv => write!(f, "Failed to get JNI environment or the current context"),
NotSet => write!(f, "A setter function of the Java object returned false"),
NotSupported => write!(f, "Probably not supported in current OS"),
NoDevice => write!(f, "Device not found"),
JavaException(ref s) => write!(f, "Unexpected Java exception: {}", s),
Other(ref s) => write!(f, "Unexpected error: {}", s),
}
}
}
impl std::error::Error for Error {}
pub(crate) fn jerr(e: jni::errors::Error) -> Error {
match e {
jni::errors::Error::JavaException => {
with_jni_env(|env| {
let _ = env.exception_describe();
if env.exception_occurred().is_ok() {
env.exception_clear().unwrap(); Ok(Error::JavaException(format!("{:?}", e)))
} else {
Ok(Error::JavaException(String::new()))
}
})
.unwrap()
}
_ => Error::Other(format!("{:?}", e)),
}
}
pub(crate) fn android_api_level() -> i32 {
use std::sync::OnceLock;
static API_LEVEL: OnceLock<i32> = OnceLock::new();
*API_LEVEL.get_or_init(|| {
with_jni_env(|env| {
let os_build_class = env.find_class("android/os/Build$VERSION").map_err(jerr)?;
env.get_static_field(os_build_class, "SDK_INT", "I")
.and_then(|v| v.i())
.map_err(jerr)
})
.unwrap_or(1)
})
}
}