blob: 251a25fb59cb3251c244208bf141760ff5ff5bd3 [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of JNI functions.
use crate::dispatcher::Dispatcher;
use crate::error::{Error, Result};
use crate::helper::{boolean_result_helper, byte_result_helper, option_result_helper};
use crate::jclass_name::{
CONFIG_STATUS_DATA_CLASS, POWER_STATS_CLASS, TLV_DATA_CLASS, UWB_RANGING_DATA_CLASS,
VENDOR_RESPONSE_CLASS,
};
use crate::unique_jvm;
use std::convert::TryInto;
use std::iter::zip;
use jni::errors::Error as JNIError;
use jni::objects::{GlobalRef, JObject, JString, JValue};
use jni::signature::JavaType;
use jni::sys::{
jboolean, jbyte, jbyteArray, jint, jintArray, jlong, jobject, jobjectArray, jshortArray,
};
use jni::JNIEnv;
use log::{debug, error};
use num_traits::cast::FromPrimitive;
use uwb_core::error::Error as UwbCoreError;
use uwb_core::params::{
AppConfigTlv, CountryCode, RawAppConfigTlv, RawVendorMessage, SetAppConfigResponse,
};
use uwb_core::uci::uci_manager_sync::UciManagerSync;
use uwb_uci_packets::{
AppConfigTlvType, CapTlv, Controlee, PowerStats, ResetConfig, SessionState, SessionType,
StatusCode, UpdateMulticastListAction,
};
/// Macro capturing the name of the function calling this macro.
///
/// function_name()! -> &'static str
/// Returns the function name as 'static reference.
macro_rules! function_name {
() => {{
// Declares function f inside current function.
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
// type name of f is struct_or_crate_name::calling_function_name::f
let name = type_name_of(f);
// Find and cut the rest of the path:
// Third to last character, up to the first semicolon: is calling_function_name
match &name[..name.len() - 3].rfind(':') {
Some(pos) => &name[pos + 1..name.len() - 3],
None => &name[..name.len() - 3],
}
}};
}
/// Initialize native library. Captures VM:
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeInit(
env: JNIEnv,
_obj: JObject,
) -> jboolean {
logger::init(
logger::Config::default()
.with_tag_on_device("uwb")
.with_min_level(log::Level::Trace)
.with_filter("trace,jni=info"),
);
debug!("{}: enter", function_name!());
boolean_result_helper(native_init(env), function_name!())
}
fn native_init(env: JNIEnv) -> Result<()> {
let jvm = env.get_java_vm()?;
unique_jvm::set_once(jvm).map_err(|e| e.into())
}
/// Get max session number
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetMaxSessionNumber(
_env: JNIEnv,
_obj: JObject,
) -> jint {
debug!("{}: enter", function_name!());
5
}
/// get mutable reference to Dispatcher.
///
/// # Safety
/// Must be called from a Java object holding a valid or null mDispatcherPointer, and remains valid
/// until env and obj goes out of scope.
unsafe fn get_dispatcher<'a>(env: JNIEnv<'a>, obj: JObject<'a>) -> Result<&'a mut Dispatcher> {
let dispatcher_ptr_value = env.get_field(obj, "mDispatcherPointer", "J")?.j()?;
if dispatcher_ptr_value == 0 {
return Err(Error::UwbCoreError(UwbCoreError::BadParameters));
}
let dispatcher_ptr = dispatcher_ptr_value as *mut Dispatcher;
Ok(&mut *dispatcher_ptr)
}
/// get mutable reference to UciManagerSync with chip_id.
///
/// # Safety
/// Must be called from a Java object holding a valid or null mDispatcherPointer, and remains valid
/// until env and obj goes out of scope.
unsafe fn get_uci_manager<'a>(
env: JNIEnv<'a>,
obj: JObject<'a>,
chip_id: JString,
) -> Result<&'a mut UciManagerSync> {
// Safety: get_dispatcher and get_uci_manager has the same assumption.
let dispatcher_ref = get_dispatcher(env, obj)?;
let chip_id_str = String::from(env.get_string(chip_id)?);
match dispatcher_ref.manager_map.get_mut(&chip_id_str) {
Some(m) => Ok(m),
None => Err(Error::UwbCoreError(UwbCoreError::BadParameters)),
}
}
/// Turn on Single UWB chip.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoInitialize(
env: JNIEnv,
obj: JObject,
chip_id: JString,
) -> jboolean {
debug!("{}: enter", function_name!());
boolean_result_helper(native_do_initialize(env, obj, chip_id), function_name!())
}
fn native_do_initialize(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.open_hal().map_err(|e| e.into())
}
/// Turn off single UWB chip.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoDeinitialize(
env: JNIEnv,
obj: JObject,
chip_id: JString,
) -> jboolean {
debug!("{}: enter", function_name!());
boolean_result_helper(native_do_deinitialize(env, obj, chip_id), function_name!())
}
fn native_do_deinitialize(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.close_hal(true).map_err(|e| e.into())
}
/// Get nanos. Not currently used and returns placeholder value.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetTimestampResolutionNanos(
_env: JNIEnv,
_obj: JObject,
) -> jlong {
debug!("{}: enter", function_name!());
0
}
/// Reset a single UWB device by sending UciDeviceReset command. Return value defined by
/// <AndroidRoot>/external/uwb/src/rust/uwb_uci_packets/uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDeviceReset(
env: JNIEnv,
obj: JObject,
_reset_config: jbyte,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(native_device_reset(env, obj, chip_id), function_name!())
}
fn native_device_reset(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.device_reset(ResetConfig::UwbsReset).map_err(|e| e.into())
}
/// Init the session on a single UWB device. Return value defined by uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionInit(
env: JNIEnv,
obj: JObject,
session_id: jint,
session_type: jbyte,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(
native_session_init(env, obj, session_id, session_type, chip_id),
function_name!(),
)
}
fn native_session_init(
env: JNIEnv,
obj: JObject,
session_id: jint,
session_type: jbyte,
chip_id: JString,
) -> Result<()> {
let session_type =
SessionType::from_u8(session_type as u8).ok_or(UwbCoreError::BadParameters)?;
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_init(session_id as u32, session_type).map_err(|e| e.into())
}
/// DeInit the session on a single UWB device. Return value defined by uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionDeInit(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(native_session_deinit(env, obj, session_id, chip_id), function_name!())
}
fn native_session_deinit(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_deinit(session_id as u32).map_err(|e| e.into())
}
/// Get session count on a single UWB device. return -1 if failed
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionCount(
env: JNIEnv,
obj: JObject,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
match option_result_helper(native_get_session_count(env, obj, chip_id), function_name!()) {
// Max session count is 5, will not overflow i8
Some(c) => c as i8,
None => -1,
}
}
fn native_get_session_count(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<u8> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_get_count().map_err(|e| e.into())
}
/// Start ranging on a single UWB device. Return value defined by uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStart(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(native_ranging_start(env, obj, session_id, chip_id), function_name!())
}
fn native_ranging_start(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.range_start(session_id as u32).map_err(|e| e.into())
}
/// Stop ranging on a single UWB device. Return value defined by uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStop(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(native_ranging_stop(env, obj, session_id, chip_id), function_name!())
}
fn native_ranging_stop(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.range_stop(session_id as u32).map_err(|e| e.into())
}
/// Get session stateon a single UWB device. Return -1 if failed
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionState(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
match option_result_helper(
native_get_session_state(env, obj, session_id, chip_id),
function_name!(),
) {
// SessionState does not overflow i8
Some(s) => s as i8,
None => -1,
}
}
fn native_get_session_state(
env: JNIEnv,
obj: JObject,
session_id: jint,
chip_id: JString,
) -> Result<SessionState> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_get_state(session_id as u32).map_err(|e| e.into())
}
fn parse_app_config_tlv_vec(no_of_params: i32, mut byte_array: &[u8]) -> Result<Vec<AppConfigTlv>> {
let mut parsed_tlvs_len = 0;
let received_tlvs_len = byte_array.len();
let mut tlvs = Vec::<AppConfigTlv>::new();
for _ in 0..no_of_params {
// The tlv consists of the type of payload in 1 byte, the length of payload as u8
// in 1 byte, and the payload.
const TLV_HEADER_SIZE: usize = 2;
let tlv = RawAppConfigTlv::parse(byte_array).map_err(|_| UwbCoreError::BadParameters)?;
byte_array =
byte_array.get(tlv.v.len() + TLV_HEADER_SIZE..).ok_or(UwbCoreError::BadParameters)?;
parsed_tlvs_len += tlv.v.len() + TLV_HEADER_SIZE;
tlvs.push(tlv.into());
}
if parsed_tlvs_len != received_tlvs_len {
return Err(Error::UwbCoreError(UwbCoreError::BadParameters));
};
Ok(tlvs)
}
fn create_set_config_response(response: SetAppConfigResponse, env: JNIEnv) -> Result<jbyteArray> {
let uwb_config_status_class = env.find_class(CONFIG_STATUS_DATA_CLASS)?;
let mut buf = Vec::<u8>::new();
for config_status in &response.config_status {
buf.push(config_status.cfg_id as u8);
buf.push(config_status.status as u8);
}
let config_status_jbytearray = env.byte_array_from_slice(&buf)?;
let config_status_jobject = env.new_object(
uwb_config_status_class,
"(II[B)V",
&[
JValue::Int(response.status as i32),
JValue::Int(response.config_status.len() as i32),
JValue::Object(JObject::from(config_status_jbytearray)),
],
)?;
Ok(*config_status_jobject)
}
/// Set app configurations on a single UWB device. Return null JObject if failed.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetAppConfigurations(
env: JNIEnv,
obj: JObject,
session_id: jint,
no_of_params: jint,
_app_config_param_len: jint, // TODO(ningyuan): Obsolete parameter
app_config_params: jbyteArray,
chip_id: JString,
) -> jbyteArray {
debug!("{}: enter", function_name!());
match option_result_helper(
native_set_app_configurations(
env,
obj,
session_id,
no_of_params,
app_config_params,
chip_id,
),
function_name!(),
) {
Some(config_response) => create_set_config_response(config_response, env)
.map_err(|e| {
error!("{} failed with {:?}", function_name!(), &e);
e
})
.unwrap_or(*JObject::null()),
None => *JObject::null(),
}
}
fn native_set_app_configurations(
env: JNIEnv,
obj: JObject,
session_id: jint,
no_of_params: jint,
app_config_params: jbyteArray,
chip_id: JString,
) -> Result<SetAppConfigResponse> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
let config_byte_array = env.convert_byte_array(app_config_params)?;
let tlvs = parse_app_config_tlv_vec(no_of_params, &config_byte_array)?;
uci_manager.session_set_app_config(session_id as u32, tlvs).map_err(|e| e.into())
}
fn create_get_config_response(tlvs: Vec<AppConfigTlv>, env: JNIEnv) -> Result<jbyteArray> {
let tlv_data_class = env.find_class(TLV_DATA_CLASS)?;
let tlvs_len = tlvs.len();
let mut buf = Vec::<u8>::new();
for tlv in tlvs.into_iter() {
let tlv = tlv.into_inner();
buf.push(tlv.cfg_id as u8);
buf.push(tlv.v.len() as u8);
buf.extend(&tlv.v);
}
let tlvs_jbytearray = env.byte_array_from_slice(&buf)?;
let tlvs_jobject = env.new_object(
tlv_data_class,
"(II[B)V",
&[
JValue::Int(StatusCode::UciStatusOk as i32),
JValue::Int(tlvs_len as i32),
JValue::Object(JObject::from(tlvs_jbytearray)),
],
)?;
Ok(*tlvs_jobject)
}
/// Get app configurations on a single UWB device. Return null JObject if failed.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetAppConfigurations(
env: JNIEnv,
obj: JObject,
session_id: jint,
_no_of_params: jint,
_app_config_param_len: jint,
app_config_params: jbyteArray,
chip_id: JString,
) -> jbyteArray {
debug!("{}: enter", function_name!());
match option_result_helper(
native_get_app_configurations(env, obj, session_id, app_config_params, chip_id),
function_name!(),
) {
Some(v) => create_get_config_response(v, env)
.map_err(|e| {
error!("{} failed with {:?}", function_name!(), &e);
e
})
.unwrap_or(*JObject::null()),
None => *JObject::null(),
}
}
fn native_get_app_configurations(
env: JNIEnv,
obj: JObject,
session_id: jint,
app_config_params: jbyteArray,
chip_id: JString,
) -> Result<Vec<AppConfigTlv>> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
let app_config_bytearray = env.convert_byte_array(app_config_params)?;
uci_manager
.session_get_app_config(
session_id as u32,
app_config_bytearray
.into_iter()
.map(AppConfigTlvType::from_u8)
.collect::<Option<Vec<_>>>()
.ok_or(UwbCoreError::BadParameters)?,
)
.map_err(|e| e.into())
}
fn create_cap_response(tlvs: Vec<CapTlv>, env: JNIEnv) -> Result<jbyteArray> {
let tlv_data_class = env.find_class(TLV_DATA_CLASS)?;
let mut buf = Vec::<u8>::new();
for tlv in &tlvs {
buf.push(tlv.t as u8);
buf.push(tlv.v.len() as u8);
buf.extend(&tlv.v);
}
let tlvs_jbytearray = env.byte_array_from_slice(&buf)?;
let tlvs_jobject = env.new_object(
tlv_data_class,
"(II[B)V",
&[
JValue::Int(StatusCode::UciStatusOk as i32),
JValue::Int(tlvs.len() as i32),
JValue::Object(JObject::from(tlvs_jbytearray)),
],
)?;
Ok(*tlvs_jobject)
}
/// Get capability info on a single UWB device. Return null JObject if failed.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetCapsInfo(
env: JNIEnv,
obj: JObject,
chip_id: JString,
) -> jbyteArray {
debug!("{}: enter", function_name!());
match option_result_helper(native_get_caps_info(env, obj, chip_id), function_name!()) {
Some(v) => create_cap_response(v, env)
.map_err(|e| {
error!("{} failed with {:?}", function_name!(), &e);
e
})
.unwrap_or(*JObject::null()),
None => *JObject::null(),
}
}
fn native_get_caps_info(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<Vec<CapTlv>> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.core_get_caps_info().map_err(|e| e.into())
}
/// Update multicast list on a single UWB device. Return value defined by uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdateV1(
env: JNIEnv,
obj: JObject,
session_id: jint,
action: jbyte,
no_of_controlee: jbyte,
addresses: jshortArray,
sub_session_ids: jintArray,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(
native_controller_multicast_list_update(
env,
obj,
session_id,
action,
no_of_controlee,
addresses,
sub_session_ids,
chip_id,
),
function_name!(),
)
}
// Function is used only once that copies arguments from JNI
#[allow(clippy::too_many_arguments)]
fn native_controller_multicast_list_update(
env: JNIEnv,
obj: JObject,
session_id: jint,
action: jbyte,
no_of_controlee: jbyte,
addresses: jshortArray,
sub_session_ids: jintArray,
chip_id: JString,
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it goes
// out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
let mut address_list = vec![
0i16;
env.get_array_length(addresses)?.try_into().map_err(|_| {
Error::UwbCoreError(UwbCoreError::BadParameters)
})?
];
env.get_short_array_region(addresses, 0, &mut address_list)?;
let mut sub_session_id_list = vec![
0i32;
env.get_array_length(sub_session_ids)?.try_into().map_err(
|_| Error::UwbCoreError(UwbCoreError::BadParameters)
)?
];
env.get_int_array_region(sub_session_ids, 0, &mut sub_session_id_list)?;
if address_list.len() != sub_session_id_list.len()
|| address_list.len() != no_of_controlee as usize
{
return Err(Error::UwbCoreError(UwbCoreError::BadParameters));
}
let controlee_list = zip(address_list, sub_session_id_list)
.map(|(a, s)| Controlee { short_address: a as u16, subsession_id: s as u32 })
.collect::<Vec<Controlee>>();
uci_manager
.session_update_controller_multicast_list(
session_id as u32,
UpdateMulticastListAction::from_u8(action as u8)
.ok_or(Error::UwbCoreError(UwbCoreError::BadParameters))?,
controlee_list,
)
.map_err(|e| e.into())
}
/// Set country code on a single UWB device. Return value defined by uci_packets.pdl
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetCountryCode(
env: JNIEnv,
obj: JObject,
country_code: jbyteArray,
chip_id: JString,
) -> jbyte {
debug!("{}: enter", function_name!());
byte_result_helper(native_set_country_code(env, obj, country_code, chip_id), function_name!())
}
fn native_set_country_code(
env: JNIEnv,
obj: JObject,
country_code: jbyteArray,
chip_id: JString,
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it goes
// out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
let country_code = env.convert_byte_array(country_code)?;
debug!("Country code: {:?}", country_code);
if country_code.len() != 2 {
return Err(Error::UwbCoreError(UwbCoreError::BadParameters));
}
uci_manager
.android_set_country_code(
CountryCode::new(&[country_code[0], country_code[1]])
.ok_or(Error::UwbCoreError(UwbCoreError::BadParameters))?,
)
.map_err(|e| e.into())
}
/// Set log mode.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetLogMode(
env: JNIEnv,
obj: JObject,
log_mode_jstring: JString,
) -> jboolean {
debug!("{}: enter", function_name!());
boolean_result_helper(native_set_log_mode(env, obj, log_mode_jstring), function_name!())
}
fn native_set_log_mode(env: JNIEnv, obj: JObject, log_mode_jstring: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it goes
// out of scope.
let dispatcher = unsafe { get_dispatcher(env, obj) }?;
let logger_mode_str = String::from(env.get_string(log_mode_jstring)?);
debug!("UCI log: log started in {} mode", &logger_mode_str);
let logger_mode = logger_mode_str.try_into()?;
dispatcher.set_logger_mode(logger_mode).map_err(|e| e.into())
}
fn create_vendor_response(msg: RawVendorMessage, env: JNIEnv) -> Result<jobject> {
let vendor_response_class = env.find_class(VENDOR_RESPONSE_CLASS)?;
match env.new_object(
vendor_response_class,
"(BII[B)V",
&[
JValue::Byte(StatusCode::UciStatusOk as i8),
JValue::Int(msg.gid as i32),
JValue::Int(msg.oid as i32),
JValue::Object(JObject::from(env.byte_array_from_slice(&msg.payload)?)),
],
) {
Ok(obj) => Ok(*obj),
Err(e) => Err(e.into()),
}
}
fn create_invalid_vendor_response(env: JNIEnv) -> Result<jobject> {
let vendor_response_class = env.find_class(VENDOR_RESPONSE_CLASS)?;
match env.new_object(
vendor_response_class,
"(BII[B)V",
&[
JValue::Byte(StatusCode::UciStatusFailed as i8),
JValue::Int(-1),
JValue::Int(-1),
JValue::Object(JObject::null()),
],
) {
Ok(obj) => Ok(*obj),
Err(e) => Err(e.into()),
}
}
/// Send Raw vendor command on a single UWB device. Returns an invalid response if failed.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSendRawVendorCmd(
env: JNIEnv,
obj: JObject,
gid: jint,
oid: jint,
payload_jarray: jbyteArray,
chip_id: JString,
) -> jobject {
debug!("{}: enter", function_name!());
match option_result_helper(
native_send_raw_vendor_cmd(env, obj, gid, oid, payload_jarray, chip_id),
function_name!(),
) {
// Note: unwrap() here is not desirable, but unavoidable given non-null object is returned
// even for failing cases.
Some(msg) => create_vendor_response(msg, env)
.map_err(|e| {
error!("{} failed with {:?}", function_name!(), &e);
e
})
.unwrap_or_else(|_| create_invalid_vendor_response(env).unwrap()),
None => create_invalid_vendor_response(env).unwrap(),
}
}
fn native_send_raw_vendor_cmd(
env: JNIEnv,
obj: JObject,
gid: jint,
oid: jint,
payload_jarray: jbyteArray,
chip_id: JString,
) -> Result<RawVendorMessage> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
let payload = env.convert_byte_array(payload_jarray)?;
uci_manager.raw_vendor_cmd(gid as u32, oid as u32, payload).map_err(|e| e.into())
}
fn create_power_stats(power_stats: PowerStats, env: JNIEnv) -> Result<jobject> {
let power_stats_class = env.find_class(POWER_STATS_CLASS)?;
match env.new_object(
power_stats_class,
"(IIII)V",
&[
JValue::Int(power_stats.idle_time_ms as i32),
JValue::Int(power_stats.tx_time_ms as i32),
JValue::Int(power_stats.rx_time_ms as i32),
JValue::Int(power_stats.total_wake_count as i32),
],
) {
Ok(o) => Ok(*o),
Err(e) => Err(e.into()),
}
}
/// Get UWB power stats on a single UWB device. Returns a null object if failed.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetPowerStats(
env: JNIEnv,
obj: JObject,
chip_id: JString,
) -> jobject {
debug!("{}: enter", function_name!());
match option_result_helper(native_get_power_stats(env, obj, chip_id), function_name!()) {
Some(ps) => create_power_stats(ps, env)
.map_err(|e| {
error!("{} failed with {:?}", function_name!(), &e);
e
})
.unwrap_or(*JObject::null()),
None => *JObject::null(),
}
}
fn native_get_power_stats(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<PowerStats> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
uci_manager.android_get_power_stats().map_err(|e| e.into())
}
/// Get the class loader object. Has to be called from a JNIEnv where the local java classes are
/// loaded. Results in a global reference to the class loader object that can be used to look for
/// classes in other native thread.
fn get_class_loader_obj(env: &JNIEnv) -> Result<GlobalRef> {
let ranging_data_class = env.find_class(&UWB_RANGING_DATA_CLASS)?;
let ranging_data_class_class = env.get_object_class(ranging_data_class)?;
let get_class_loader_method =
env.get_method_id(ranging_data_class_class, "getClassLoader", "()Ljava/lang/ClassLoader;")?;
let class_loader = env.call_method_unchecked(
ranging_data_class,
get_class_loader_method,
JavaType::Object("java/lang/ClassLoader".into()),
&[JValue::Void],
)?;
let class_loader_jobject = class_loader.l()?;
Ok(env.new_global_ref(class_loader_jobject)?)
}
/// Create the dispatcher. Returns pointer to Dispatcher casted as jlong that owns the dispatcher.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDispatcherNew(
env: JNIEnv,
obj: JObject,
chip_ids_jarray: jobjectArray,
) -> jlong {
debug!("{}: enter", function_name!());
match option_result_helper(native_dispatcher_new(env, obj, chip_ids_jarray), function_name!()) {
Some(ptr) => ptr as jlong,
None => *JObject::null() as jlong,
}
}
fn native_dispatcher_new(
env: JNIEnv,
obj: JObject,
chip_ids_jarray: jobjectArray,
) -> Result<*mut Dispatcher> {
let chip_ids_len: i32 = env.get_array_length(chip_ids_jarray)?;
let chip_ids = (0..chip_ids_len)
.map(|i| env.get_string(env.get_object_array_element(chip_ids_jarray, i)?.into()))
.collect::<std::result::Result<Vec<_>, JNIError>>()?;
let chip_ids = chip_ids.into_iter().map(String::from).collect::<Vec<String>>();
let class_loader_obj = get_class_loader_obj(&env)?;
Dispatcher::new_as_ptr(
unique_jvm::get_static_ref().ok_or(UwbCoreError::Unknown)?,
class_loader_obj,
env.new_global_ref(obj)?,
&chip_ids,
)
.map_err(|e| e.into())
}
/// Destroys the dispatcher.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDispatcherDestroy(
env: JNIEnv,
obj: JObject,
) {
debug!("{}: enter", function_name!());
if option_result_helper(native_dispatcher_destroy(env, obj), function_name!()).is_some() {
debug!("The dispatcher is successfully destroyed.");
}
}
fn native_dispatcher_destroy(env: JNIEnv, obj: JObject) -> Result<()> {
let dispatcher_ptr_long = env.get_field(obj, "mDispatcherPointer", "J")?.j()?;
// Safety: Java side owns Dispatcher through the pointer, and asks it to be destroyed
unsafe {
Dispatcher::destroy_ptr(dispatcher_ptr_long as *mut Dispatcher);
}
Ok(())
}