blob: 780aa057a1e4af46cf22e716d9bde6033d90757e [file] [log] [blame]
//! jni for uwb native stack
use android_logger::FilterBuilder;
use jni::objects::{JObject, JValue};
use jni::sys::{
jarray, jboolean, jbyte, jbyteArray, jint, jintArray, jlong, jobject, jshort, jshortArray,
jsize,
};
use jni::JNIEnv;
use log::{error, info, LevelFilter};
use num_traits::ToPrimitive;
use uwb_uci_packets::{
GetCapsInfoRspPacket, Packet, SessionGetAppConfigRspPacket, SessionSetAppConfigRspPacket,
StatusCode, UciResponseChild, UciResponsePacket, UciVendor_9_ResponseChild,
UciVendor_A_ResponseChild, UciVendor_B_ResponseChild, UciVendor_E_ResponseChild,
UciVendor_F_ResponseChild,
};
use uwb_uci_rust::error::UwbErr;
use uwb_uci_rust::event_manager::EventManagerImpl as EventManager;
use uwb_uci_rust::uci::{uci_hrcv::UciResponse, Dispatcher, DispatcherImpl, JNICommand};
trait Context<'a> {
fn convert_byte_array(&self, array: jbyteArray) -> Result<Vec<u8>, jni::errors::Error>;
fn get_array_length(&self, array: jarray) -> Result<jsize, jni::errors::Error>;
fn get_short_array_region(
&self,
array: jshortArray,
start: jsize,
buf: &mut [jshort],
) -> Result<(), jni::errors::Error>;
fn get_int_array_region(
&self,
array: jintArray,
start: jsize,
buf: &mut [jint],
) -> Result<(), jni::errors::Error>;
fn get_dispatcher(&self) -> Result<&'a mut dyn Dispatcher, UwbErr>;
}
struct JniContext<'a> {
env: JNIEnv<'a>,
obj: JObject<'a>,
}
impl<'a> JniContext<'a> {
fn new(env: JNIEnv<'a>, obj: JObject<'a>) -> Self {
Self { env, obj }
}
}
impl<'a> Context<'a> for JniContext<'a> {
fn convert_byte_array(&self, array: jbyteArray) -> Result<Vec<u8>, jni::errors::Error> {
self.env.convert_byte_array(array)
}
fn get_array_length(&self, array: jarray) -> Result<jsize, jni::errors::Error> {
self.env.get_array_length(array)
}
fn get_short_array_region(
&self,
array: jshortArray,
start: jsize,
buf: &mut [jshort],
) -> Result<(), jni::errors::Error> {
self.env.get_short_array_region(array, start, buf)
}
fn get_int_array_region(
&self,
array: jintArray,
start: jsize,
buf: &mut [jint],
) -> Result<(), jni::errors::Error> {
self.env.get_int_array_region(array, start, buf)
}
fn get_dispatcher(&self) -> Result<&'a mut dyn Dispatcher, UwbErr> {
let dispatcher_ptr_value = self.env.get_field(self.obj, "mDispatcherPointer", "J")?;
let dispatcher_ptr = dispatcher_ptr_value.j()?;
if dispatcher_ptr == 0i64 {
error!("The dispatcher is not initialized.");
return Err(UwbErr::NoneDispatcher);
}
// Safety: dispatcher pointer must not be a null pointer and it must point to a valid dispatcher object.
// This can be ensured because the dispatcher is created in an earlier stage and
// won't be deleted before calling doDeinitialize.
unsafe { Ok(&mut *(dispatcher_ptr as *mut DispatcherImpl)) }
}
}
/// Initialize UWB
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeInit(
_env: JNIEnv,
_obj: JObject,
) -> jboolean {
let crates_log_lvl_filter = FilterBuilder::new()
.filter(None, LevelFilter::Trace) // default log level
.filter(Some("jni"), LevelFilter::Info) // reduced log level for jni crate
.build();
android_logger::init_once(
android_logger::Config::default()
.with_tag("uwb")
.with_min_level(log::Level::Trace)
.with_filter(crates_log_lvl_filter),
);
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeInit: enter");
true as jboolean
}
/// Get max session number
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetMaxSessionNumber(
_env: JNIEnv,
_obj: JObject,
) -> jint {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetMaxSessionNumber: enter");
5
}
/// Turn on UWB. initialize the GKI module and HAL module for UWB device.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoInitialize(
env: JNIEnv,
obj: JObject,
) -> jboolean {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoInitialize: enter");
boolean_result_helper(do_initialize(&JniContext::new(env, obj)), "DoInitialize")
}
/// Turn off UWB. Deinitilize the GKI and HAL module, power of the UWB device.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoDeinitialize(
env: JNIEnv,
obj: JObject,
) -> jboolean {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoDeinitialize: enter");
boolean_result_helper(do_deinitialize(&JniContext::new(env, obj)), "DoDeinitialize")
}
/// get nanos
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetTimestampResolutionNanos(
_env: JNIEnv,
_obj: JObject,
) -> jlong {
info!(
"Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetTimestampResolutionNanos: enter"
);
0
}
/// reset the device
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDeviceReset(
env: JNIEnv,
obj: JObject,
reset_config: jbyte,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeDeviceReset: enter");
byte_result_helper(reset_device(&JniContext::new(env, obj), reset_config as u8), "ResetDevice")
}
/// init the session
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionInit(
env: JNIEnv,
obj: JObject,
session_id: jint,
session_type: jbyte,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionInit: enter");
byte_result_helper(
session_init(&JniContext::new(env, obj), session_id as u32, session_type as u8),
"SessionInit",
)
}
/// deinit the session
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionDeInit(
env: JNIEnv,
obj: JObject,
session_id: jint,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionDeInit: enter");
byte_result_helper(
session_deinit(&JniContext::new(env, obj), session_id as u32),
"SessionDeInit",
)
}
/// get session count
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionCount(
env: JNIEnv,
obj: JObject,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionCount: enter");
match get_session_count(&JniContext::new(env, obj)) {
Ok(count) => count,
Err(e) => {
error!("GetSessionCount failed with {:?}", e);
-1
}
}
}
/// start the ranging
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStart(
env: JNIEnv,
obj: JObject,
session_id: jint,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStart: enter");
byte_result_helper(ranging_start(&JniContext::new(env, obj), session_id as u32), "RangingStart")
}
/// stop the ranging
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStop(
env: JNIEnv,
obj: JObject,
session_id: jint,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStop: enter");
byte_result_helper(ranging_stop(&JniContext::new(env, obj), session_id as u32), "RangingStop")
}
/// get the session state
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionState(
env: JNIEnv,
obj: JObject,
session_id: jint,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionState: enter");
match get_session_state(&JniContext::new(env, obj), session_id as u32) {
Ok(state) => state,
Err(e) => {
error!("GetSessionState failed with {:?}", e);
-1
}
}
}
/// set app configurations
#[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,
app_config_params: jbyteArray,
) -> jbyteArray {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetAppConfigurations: enter");
match set_app_configurations(
&JniContext::new(env, obj),
session_id as u32,
no_of_params as u32,
app_config_param_len as u32,
app_config_params,
) {
Ok(data) => {
let uwb_config_status_class =
env.find_class("com/android/server/uwb/data/UwbConfigStatusData").unwrap();
let mut buf: Vec<u8> = Vec::new();
for iter in data.get_cfg_status() {
buf.push(iter.cfg_id as u8);
buf.push(iter.status as u8);
}
let cfg_jbytearray = env.byte_array_from_slice(&buf).unwrap();
let uwb_config_status_object = env.new_object(
uwb_config_status_class,
"(II[B)V",
&[
JValue::Int(data.get_status().to_i32().unwrap()),
JValue::Int(data.get_cfg_status().len().to_i32().unwrap()),
JValue::Object(JObject::from(cfg_jbytearray)),
],
);
*uwb_config_status_object.unwrap()
}
Err(e) => {
error!("SetAppConfig failed with: {:?}", e);
*JObject::null()
}
}
}
/// get app configurations
#[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,
) -> jbyteArray {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetAppConfigurations: enter");
match get_app_configurations(
&JniContext::new(env, obj),
session_id as u32,
no_of_params as u32,
app_config_param_len as u32,
app_config_params,
) {
Ok(data) => {
let uwb_tlv_info_class =
env.find_class("com/android/server/uwb/data/UwbTlvData").unwrap();
let mut buf: Vec<u8> = Vec::new();
for tlv in data.get_tlvs() {
buf.push(tlv.cfg_id as u8);
buf.push(tlv.v.len() as u8);
buf.extend(&tlv.v);
}
let tlv_jbytearray = env.byte_array_from_slice(&buf).unwrap();
let uwb_tlv_info_object = env.new_object(
uwb_tlv_info_class,
"(II[B)V",
&[
JValue::Int(data.get_status().to_i32().unwrap()),
JValue::Int(data.get_tlvs().len().to_i32().unwrap()),
JValue::Object(JObject::from(tlv_jbytearray)),
],
);
*uwb_tlv_info_object.unwrap()
}
Err(e) => {
error!("GetAppConfig failed with: {:?}", e);
*JObject::null()
}
}
}
/// get capability info
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetCapsInfo(
env: JNIEnv,
obj: JObject,
) -> jbyteArray {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetCapsInfo: enter");
match get_caps_info(&JniContext::new(env, obj)) {
Ok(data) => {
let uwb_tlv_info_class =
env.find_class("com/android/server/uwb/data/UwbTlvData").unwrap();
let mut buf: Vec<u8> = Vec::new();
for tlv in data.get_tlvs() {
buf.push(tlv.t as u8);
buf.push(tlv.v.len() as u8);
buf.extend(&tlv.v);
}
let tlv_jbytearray = env.byte_array_from_slice(&buf).unwrap();
let uwb_tlv_info_object = env.new_object(
uwb_tlv_info_class,
"(II[B)V",
&[
JValue::Int(data.get_status().to_i32().unwrap()),
JValue::Int(data.get_tlvs().len().to_i32().unwrap()),
JValue::Object(JObject::from(tlv_jbytearray)),
],
);
*uwb_tlv_info_object.unwrap()
}
Err(e) => {
error!("GetCapsInfo failed with: {:?}", e);
*JObject::null()
}
}
}
/// update multicast list
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdate(
env: JNIEnv,
obj: JObject,
session_id: jint,
action: jbyte,
no_of_controlee: jbyte,
addresses: jshortArray,
sub_session_ids: jintArray,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdate: enter");
byte_result_helper(
multicast_list_update(
&JniContext::new(env, obj),
session_id as u32,
action as u8,
no_of_controlee as u8,
addresses,
sub_session_ids,
),
"ControllerMulticastListUpdate",
)
}
/// set country code
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetCountryCode(
env: JNIEnv,
obj: JObject,
country_code: jbyteArray,
) -> jbyte {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetCountryCode: enter");
byte_result_helper(set_country_code(&JniContext::new(env, obj), country_code), "SetCountryCode")
}
/// set country code
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSendRawVendorCmd(
env: JNIEnv,
obj: JObject,
gid: jint,
oid: jint,
payload: jbyteArray,
) -> jobject {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeRawVendor: enter");
let uwb_vendor_uci_response_class =
env.find_class("com/android/server/uwb/data/UwbVendorUciResponse").unwrap();
match send_raw_vendor_cmd(
&JniContext::new(env, obj),
gid.try_into().expect("invalid gid"),
oid.try_into().expect("invalid oid"),
payload,
) {
Ok((gid, oid, payload)) => *env
.new_object(
uwb_vendor_uci_response_class,
"(BII[B)V",
&[
JValue::Byte(StatusCode::UciStatusOk.to_i8().unwrap()),
JValue::Int(gid.to_i32().unwrap()),
JValue::Int(oid.to_i32().unwrap()),
JValue::Object(JObject::from(
env.byte_array_from_slice(payload.as_ref()).unwrap(),
)),
],
)
.unwrap(),
Err(e) => {
error!("send raw uci cmd failed with: {:?}", e);
*env.new_object(
uwb_vendor_uci_response_class,
"(BII[B)V",
&[
JValue::Byte(StatusCode::UciStatusFailed.to_i8().unwrap()),
JValue::Int(-1),
JValue::Int(-1),
JValue::Object(JObject::null()),
],
)
.unwrap()
}
}
}
/// retrieve the UWB power stats
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetPowerStats(
env: JNIEnv,
obj: JObject,
) -> jobject {
info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetPowerStats: enter");
let uwb_power_stats_class =
env.find_class("com/android/server/uwb/info/UwbPowerStats").unwrap();
match get_power_stats(&JniContext::new(env, obj)) {
Ok(para) => {
let power_stats = env.new_object(uwb_power_stats_class, "(IIII)V", &para).unwrap();
*power_stats
}
Err(e) => {
error!("Get power stats failed with: {:?}", e);
*JObject::null()
}
}
}
fn boolean_result_helper(result: Result<(), UwbErr>, function_name: &str) -> jboolean {
match result {
Ok(()) => true as jboolean,
Err(err) => {
error!("{} failed with: {:?}", function_name, err);
false as jboolean
}
}
}
fn byte_result_helper(result: Result<(), UwbErr>, function_name: &str) -> jbyte {
match result {
Ok(()) => StatusCode::UciStatusOk.to_i8().unwrap(),
Err(err) => {
error!("{} failed with: {:?}", function_name, err);
match err {
UwbErr::StatusCode(status_code) => status_code
.to_i8()
.unwrap_or_else(|| StatusCode::UciStatusFailed.to_i8().unwrap()),
_ => StatusCode::UciStatusFailed.to_i8().unwrap(),
}
}
}
}
fn do_initialize<'a, T: Context<'a>>(context: &T) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
dispatcher.send_jni_command(JNICommand::Enable)?;
match uwa_get_device_info(dispatcher) {
Ok(res) => {
if let UciResponse::GetDeviceInfoRsp(device_info) = res {
dispatcher.set_device_info(Some(device_info));
}
}
Err(e) => {
error!("GetDeviceInfo failed with: {:?}", e);
return Err(UwbErr::failed());
}
}
Ok(())
}
fn do_deinitialize<'a, T: Context<'a>>(context: &T) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
dispatcher.block_on_jni_command(JNICommand::Disable(true))?;
dispatcher.send_jni_command(JNICommand::Exit)?;
Ok(())
}
// unused, but leaving this behind if we want to use it later.
#[allow(dead_code)]
fn get_specification_info<'a, T: Context<'a>>(context: &T) -> Result<[JValue<'a>; 16], UwbErr> {
let dispatcher = context.get_dispatcher()?;
match dispatcher.get_device_info() {
Some(data) => {
Ok([
JValue::Int((data.get_uci_version() & 0xFF).into()),
JValue::Int(((data.get_uci_version() >> 8) & 0xF).into()),
JValue::Int(((data.get_uci_version() >> 12) & 0xF).into()),
JValue::Int((data.get_mac_version() & 0xFF).into()),
JValue::Int(((data.get_mac_version() >> 8) & 0xF).into()),
JValue::Int(((data.get_mac_version() >> 12) & 0xF).into()),
JValue::Int((data.get_phy_version() & 0xFF).into()),
JValue::Int(((data.get_phy_version() >> 8) & 0xF).into()),
JValue::Int(((data.get_phy_version() >> 12) & 0xF).into()),
JValue::Int((data.get_uci_test_version() & 0xFF).into()),
JValue::Int(((data.get_uci_test_version() >> 8) & 0xF).into()),
JValue::Int(((data.get_uci_test_version() >> 12) & 0xF).into()),
JValue::Int(1), // fira_major_version
JValue::Int(0), // fira_minor_version
JValue::Int(1), // ccc_major_version
JValue::Int(0), // ccc_minor_version
])
}
None => {
error!("Fail to get specification info.");
Err(UwbErr::failed())
}
}
}
fn session_init<'a, T: Context<'a>>(
context: &T,
session_id: u32,
session_type: u8,
) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher
.block_on_jni_command(JNICommand::UciSessionInit(session_id, session_type))?
{
UciResponse::SessionInitRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
fn session_deinit<'a, T: Context<'a>>(context: &T, session_id: u32) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher.block_on_jni_command(JNICommand::UciSessionDeinit(session_id))? {
UciResponse::SessionDeinitRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
fn get_session_count<'a, T: Context<'a>>(context: &T) -> Result<jbyte, UwbErr> {
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciSessionGetCount)? {
UciResponse::SessionGetCountRsp(rsp) => match status_code_to_res(rsp.get_status()) {
Ok(()) => Ok(rsp.get_session_count() as jbyte),
Err(err) => Err(err),
},
_ => Err(UwbErr::failed()),
}
}
fn ranging_start<'a, T: Context<'a>>(context: &T, session_id: u32) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher.block_on_jni_command(JNICommand::UciStartRange(session_id))? {
UciResponse::RangeStartRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
fn ranging_stop<'a, T: Context<'a>>(context: &T, session_id: u32) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher.block_on_jni_command(JNICommand::UciStopRange(session_id))? {
UciResponse::RangeStopRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
fn get_session_state<'a, T: Context<'a>>(context: &T, session_id: u32) -> Result<jbyte, UwbErr> {
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciGetSessionState(session_id))? {
UciResponse::SessionGetStateRsp(data) => Ok(data.get_session_state() as jbyte),
_ => Err(UwbErr::failed()),
}
}
fn set_app_configurations<'a, T: Context<'a>>(
context: &T,
session_id: u32,
no_of_params: u32,
app_config_param_len: u32,
app_config_params: jintArray,
) -> Result<SessionSetAppConfigRspPacket, UwbErr> {
let app_configs = context.convert_byte_array(app_config_params)?;
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciSetAppConfig {
session_id,
no_of_params,
app_config_param_len,
app_configs,
})? {
UciResponse::SessionSetAppConfigRsp(data) => Ok(data),
_ => Err(UwbErr::failed()),
}
}
fn get_app_configurations<'a, T: Context<'a>>(
context: &T,
session_id: u32,
no_of_params: u32,
app_config_param_len: u32,
app_config_params: jintArray,
) -> Result<SessionGetAppConfigRspPacket, UwbErr> {
let app_configs = context.convert_byte_array(app_config_params)?;
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciGetAppConfig {
session_id,
no_of_params,
app_config_param_len,
app_configs,
})? {
UciResponse::SessionGetAppConfigRsp(data) => Ok(data),
_ => Err(UwbErr::failed()),
}
}
fn get_caps_info<'a, T: Context<'a>>(context: &T) -> Result<GetCapsInfoRspPacket, UwbErr> {
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciGetCapsInfo)? {
UciResponse::GetCapsInfoRsp(data) => Ok(data),
_ => Err(UwbErr::failed()),
}
}
fn multicast_list_update<'a, T: Context<'a>>(
context: &T,
session_id: u32,
action: u8,
no_of_controlee: u8,
addresses: jshortArray,
sub_session_ids: jintArray,
) -> Result<(), UwbErr> {
let mut address_list = vec![0i16; context.get_array_length(addresses)?.try_into().unwrap()];
context.get_short_array_region(addresses, 0, &mut address_list)?;
let mut sub_session_id_list =
vec![0i32; context.get_array_length(sub_session_ids)?.try_into().unwrap()];
context.get_int_array_region(sub_session_ids, 0, &mut sub_session_id_list)?;
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher.block_on_jni_command(JNICommand::UciSessionUpdateMulticastList {
session_id,
action,
no_of_controlee,
address_list: address_list.to_vec(),
sub_session_id_list: sub_session_id_list.to_vec(),
})? {
UciResponse::SessionUpdateControllerMulticastListRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
fn set_country_code<'a, T: Context<'a>>(
context: &T,
country_code: jbyteArray,
) -> Result<(), UwbErr> {
let code = context.convert_byte_array(country_code)?;
if code.len() != 2 {
return Err(UwbErr::failed());
}
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher.block_on_jni_command(JNICommand::UciSetCountryCode { code })? {
UciResponse::AndroidSetCountryCodeRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
fn get_vendor_uci_payload(data: UciResponsePacket) -> Result<Vec<u8>, UwbErr> {
match data.specialize() {
UciResponseChild::UciVendor_9_Response(evt) => match evt.specialize() {
UciVendor_9_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
UciVendor_9_ResponseChild::None => Ok(Vec::new()),
},
UciResponseChild::UciVendor_A_Response(evt) => match evt.specialize() {
UciVendor_A_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
UciVendor_A_ResponseChild::None => Ok(Vec::new()),
},
UciResponseChild::UciVendor_B_Response(evt) => match evt.specialize() {
UciVendor_B_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
UciVendor_B_ResponseChild::None => Ok(Vec::new()),
},
UciResponseChild::UciVendor_E_Response(evt) => match evt.specialize() {
UciVendor_E_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
UciVendor_E_ResponseChild::None => Ok(Vec::new()),
},
UciResponseChild::UciVendor_F_Response(evt) => match evt.specialize() {
UciVendor_F_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
UciVendor_F_ResponseChild::None => Ok(Vec::new()),
},
_ => {
error!("Invalid vendor response with gid {:?}", data.get_group_id());
Err(UwbErr::Specialize(data.to_vec()))
}
}
}
fn send_raw_vendor_cmd<'a, T: Context<'a>>(
context: &T,
gid: u32,
oid: u32,
payload: jbyteArray,
) -> Result<(i32, i32, Vec<u8>), UwbErr> {
let payload = context.convert_byte_array(payload)?;
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciRawVendorCmd { gid, oid, payload })? {
UciResponse::RawVendorRsp(response) => Ok((
response.get_group_id().to_i32().unwrap(),
response.get_opcode().to_i32().unwrap(),
get_vendor_uci_payload(response)?,
)),
_ => Err(UwbErr::failed()),
}
}
fn status_code_to_res(status_code: StatusCode) -> Result<(), UwbErr> {
match status_code {
StatusCode::UciStatusOk => Ok(()),
_ => Err(UwbErr::StatusCode(status_code)),
}
}
/// create a dispatcher instance
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDispatcherNew(
env: JNIEnv,
obj: JObject,
) -> jlong {
let eventmanager = match EventManager::new(env, obj) {
Ok(evtmgr) => evtmgr,
Err(err) => {
error!("Fail to create event manager{:?}", err);
return *JObject::null() as jlong;
}
};
match DispatcherImpl::new(eventmanager) {
Ok(dispatcher) => Box::into_raw(Box::new(dispatcher)) as jlong,
Err(err) => {
error!("Fail to create dispatcher {:?}", err);
*JObject::null() as jlong
}
}
}
/// destroy the dispatcher instance
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDispatcherDestroy(
env: JNIEnv,
obj: JObject,
) {
let dispatcher_ptr_value = match env.get_field(obj, "mDispatcherPointer", "J") {
Ok(value) => value,
Err(err) => {
error!("Failed to get the pointer with: {:?}", err);
return;
}
};
let dispatcher_ptr = match dispatcher_ptr_value.j() {
Ok(value) => value,
Err(err) => {
error!("Failed to get the pointer with: {:?}", err);
return;
}
};
// Safety: dispatcher pointer must not be a null pointer and must point to a valid dispatcher object.
// This can be ensured because the dispatcher is created in an earlier stage and
// won't be deleted before calling this destroy function.
// This function will early return if the instance is already destroyed.
let _boxed_dispatcher = unsafe { Box::from_raw(dispatcher_ptr as *mut DispatcherImpl) };
info!("The dispatcher successfully destroyed.");
}
fn get_power_stats<'a, T: Context<'a>>(context: &T) -> Result<[JValue<'a>; 4], UwbErr> {
let dispatcher = context.get_dispatcher()?;
match dispatcher.block_on_jni_command(JNICommand::UciGetPowerStats)? {
UciResponse::AndroidGetPowerStatsRsp(data) => Ok([
JValue::Int(data.get_stats().idle_time_ms as i32),
JValue::Int(data.get_stats().tx_time_ms as i32),
JValue::Int(data.get_stats().rx_time_ms as i32),
JValue::Int(data.get_stats().total_wake_count as i32),
]),
_ => Err(UwbErr::failed()),
}
}
fn uwa_get_device_info(dispatcher: &dyn Dispatcher) -> Result<UciResponse, UwbErr> {
let res = dispatcher.block_on_jni_command(JNICommand::UciGetDeviceInfo)?;
Ok(res)
}
fn reset_device<'a, T: Context<'a>>(context: &T, reset_config: u8) -> Result<(), UwbErr> {
let dispatcher = context.get_dispatcher()?;
let res = match dispatcher.block_on_jni_command(JNICommand::UciDeviceReset { reset_config })? {
UciResponse::DeviceResetRsp(data) => data,
_ => return Err(UwbErr::failed()),
};
status_code_to_res(res.get_status())
}
#[cfg(test)]
mod mock_context;
#[cfg(test)]
mod mock_dispatcher;
#[cfg(test)]
mod tests {
use super::*;
use crate::mock_context::MockContext;
use crate::mock_dispatcher::MockDispatcher;
#[test]
fn test_boolean_result_helper() {
assert_eq!(true as jboolean, boolean_result_helper(Ok(()), "Foo"));
assert_eq!(false as jboolean, boolean_result_helper(Err(UwbErr::Undefined), "Foo"));
}
#[test]
fn test_byte_result_helper() {
assert_eq!(StatusCode::UciStatusOk.to_i8().unwrap(), byte_result_helper(Ok(()), "Foo"));
assert_eq!(
StatusCode::UciStatusFailed.to_i8().unwrap(),
byte_result_helper(Err(UwbErr::Undefined), "Foo")
);
assert_eq!(
StatusCode::UciStatusRejected.to_i8().unwrap(),
byte_result_helper(Err(UwbErr::StatusCode(StatusCode::UciStatusRejected)), "Foo")
);
}
#[test]
fn test_do_initialize() {
let packet = uwb_uci_packets::GetDeviceInfoRspBuilder {
status: StatusCode::UciStatusOk,
uci_version: 0,
mac_version: 0,
phy_version: 0,
uci_test_version: 0,
vendor_spec_info: vec![],
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_send_jni_command(JNICommand::Enable, Ok(()));
dispatcher.expect_block_on_jni_command(
JNICommand::UciGetDeviceInfo,
Ok(UciResponse::GetDeviceInfoRsp(packet.clone())),
);
let mut context = MockContext::new(dispatcher);
let result = do_initialize(&context);
let device_info = context.get_mock_dispatcher().get_device_info().clone();
assert!(result.is_ok());
assert_eq!(device_info.unwrap().to_vec(), packet.to_vec());
}
#[test]
fn test_do_deinitialize() {
let mut dispatcher = MockDispatcher::new();
dispatcher
.expect_block_on_jni_command(JNICommand::Disable(true), Ok(UciResponse::DisableRsp));
dispatcher.expect_send_jni_command(JNICommand::Exit, Ok(()));
let context = MockContext::new(dispatcher);
let result = do_deinitialize(&context);
assert!(result.is_ok());
}
#[test]
fn test_get_specification_info() {
let packet = uwb_uci_packets::GetDeviceInfoRspBuilder {
status: StatusCode::UciStatusOk,
uci_version: 0x1234,
mac_version: 0x5678,
phy_version: 0x9ABC,
uci_test_version: 0x1357,
vendor_spec_info: vec![],
}
.build();
let expected_array = [
0x34, 0x2, 0x1, // uci_version
0x78, 0x6, 0x5, // mac_version.
0xBC, 0xA, 0x9, // phy_version.
0x57, 0x3, 0x1, // uci_test_version.
1, // fira_major_version
0, // fira_minor_version
1, // ccc_major_version
0, // ccc_minor_version
];
let mut dispatcher = MockDispatcher::new();
dispatcher.set_device_info(Some(packet));
let context = MockContext::new(dispatcher);
let results = get_specification_info(&context).unwrap();
for (idx, result) in results.iter().enumerate() {
assert_eq!(TryInto::<jint>::try_into(*result).unwrap(), expected_array[idx]);
}
}
#[test]
fn test_session_init() {
let session_id = 1234;
let session_type = 5;
let packet =
uwb_uci_packets::SessionInitRspBuilder { status: StatusCode::UciStatusOk }.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciSessionInit(session_id, session_type),
Ok(UciResponse::SessionInitRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = session_init(&context, session_id, session_type);
assert!(result.is_ok());
}
#[test]
fn test_session_deinit() {
let session_id = 1234;
let packet =
uwb_uci_packets::SessionDeinitRspBuilder { status: StatusCode::UciStatusOk }.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciSessionDeinit(session_id),
Ok(UciResponse::SessionDeinitRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = session_deinit(&context, session_id);
assert!(result.is_ok());
}
#[test]
fn test_get_session_count() {
let session_count = 7;
let packet = uwb_uci_packets::SessionGetCountRspBuilder {
status: StatusCode::UciStatusOk,
session_count,
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciSessionGetCount,
Ok(UciResponse::SessionGetCountRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = get_session_count(&context).unwrap();
assert_eq!(result, session_count as jbyte);
}
#[test]
fn test_ranging_start() {
let session_id = 1234;
let packet =
uwb_uci_packets::RangeStartRspBuilder { status: StatusCode::UciStatusOk }.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciStartRange(session_id),
Ok(UciResponse::RangeStartRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = ranging_start(&context, session_id);
assert!(result.is_ok());
}
#[test]
fn test_ranging_stop() {
let session_id = 1234;
let packet =
uwb_uci_packets::RangeStopRspBuilder { status: StatusCode::UciStatusOk }.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciStopRange(session_id),
Ok(UciResponse::RangeStopRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = ranging_stop(&context, session_id);
assert!(result.is_ok());
}
#[test]
fn test_get_session_state() {
let session_id = 1234;
let session_state = uwb_uci_packets::SessionState::SessionStateActive;
let packet = uwb_uci_packets::SessionGetStateRspBuilder {
status: StatusCode::UciStatusOk,
session_state,
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciGetSessionState(session_id),
Ok(UciResponse::SessionGetStateRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = get_session_state(&context, session_id).unwrap();
assert_eq!(result, session_state as jbyte);
}
#[test]
fn test_set_app_configurations() {
let session_id = 1234;
let no_of_params = 3;
let app_config_param_len = 5;
let app_configs = vec![1, 2, 3, 4, 5];
let fake_app_config_params = std::ptr::null_mut();
let packet = uwb_uci_packets::SessionSetAppConfigRspBuilder {
status: StatusCode::UciStatusOk,
cfg_status: vec![],
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciSetAppConfig {
session_id,
no_of_params,
app_config_param_len,
app_configs: app_configs.clone(),
},
Ok(UciResponse::SessionSetAppConfigRsp(packet.clone())),
);
let mut context = MockContext::new(dispatcher);
context.expect_convert_byte_array(fake_app_config_params, Ok(app_configs));
let result = set_app_configurations(
&context,
session_id,
no_of_params,
app_config_param_len,
fake_app_config_params,
)
.unwrap();
assert_eq!(result.to_vec(), packet.to_vec());
}
#[test]
fn test_get_app_configurations() {
let session_id = 1234;
let no_of_params = 3;
let app_config_param_len = 5;
let app_configs = vec![1, 2, 3, 4, 5];
let fake_app_config_params = std::ptr::null_mut();
let packet = uwb_uci_packets::SessionGetAppConfigRspBuilder {
status: StatusCode::UciStatusOk,
tlvs: vec![],
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciGetAppConfig {
session_id,
no_of_params,
app_config_param_len,
app_configs: app_configs.clone(),
},
Ok(UciResponse::SessionGetAppConfigRsp(packet.clone())),
);
let mut context = MockContext::new(dispatcher);
context.expect_convert_byte_array(fake_app_config_params, Ok(app_configs));
let result = get_app_configurations(
&context,
session_id,
no_of_params,
app_config_param_len,
fake_app_config_params,
)
.unwrap();
assert_eq!(result.to_vec(), packet.to_vec());
}
#[test]
fn test_get_caps_info() {
let packet = uwb_uci_packets::GetCapsInfoRspBuilder {
status: StatusCode::UciStatusOk,
tlvs: vec![],
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciGetCapsInfo,
Ok(UciResponse::GetCapsInfoRsp(packet.clone())),
);
let context = MockContext::new(dispatcher);
let result = get_caps_info(&context).unwrap();
assert_eq!(result.to_vec(), packet.to_vec());
}
#[test]
fn test_multicast_list_update() {
let session_id = 1234;
let action = 3;
let no_of_controlee = 5;
let fake_addresses = std::ptr::null_mut();
let address_list = Box::new([1, 3, 5, 7, 9]);
let fake_sub_session_ids = std::ptr::null_mut();
let sub_session_id_list = Box::new([2, 4, 6, 8, 10]);
let packet = uwb_uci_packets::SessionUpdateControllerMulticastListRspBuilder {
status: StatusCode::UciStatusOk,
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciSessionUpdateMulticastList {
session_id,
action,
no_of_controlee,
address_list: address_list.to_vec(),
sub_session_id_list: sub_session_id_list.to_vec(),
},
Ok(UciResponse::SessionUpdateControllerMulticastListRsp(packet)),
);
let mut context = MockContext::new(dispatcher);
context.expect_get_array_length(fake_addresses, Ok(address_list.len() as jsize));
context.expect_get_short_array_region(fake_addresses, 0, Ok(address_list));
context
.expect_get_array_length(fake_sub_session_ids, Ok(sub_session_id_list.len() as jsize));
context.expect_get_int_array_region(fake_sub_session_ids, 0, Ok(sub_session_id_list));
let result = multicast_list_update(
&context,
session_id,
action,
no_of_controlee,
fake_addresses,
fake_sub_session_ids,
);
assert!(result.is_ok());
}
#[test]
fn test_set_country_code() {
let fake_country_code = std::ptr::null_mut();
let country_code = "US".as_bytes().to_vec();
let packet =
uwb_uci_packets::AndroidSetCountryCodeRspBuilder { status: StatusCode::UciStatusOk }
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciSetCountryCode { code: country_code.clone() },
Ok(UciResponse::AndroidSetCountryCodeRsp(packet)),
);
let mut context = MockContext::new(dispatcher);
context.expect_convert_byte_array(fake_country_code, Ok(country_code));
let result = set_country_code(&context, fake_country_code);
assert!(result.is_ok());
}
#[test]
fn test_send_raw_vendor_cmd() {
let gid = 2;
let oid = 4;
let opcode = 6;
let fake_payload = std::ptr::null_mut();
let payload = vec![1, 2, 4, 8];
let response = vec![3, 6, 9];
let packet = uwb_uci_packets::UciVendor_9_ResponseBuilder {
opcode,
payload: Some(response.clone().into()),
}
.build()
.into();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciRawVendorCmd { gid, oid, payload: payload.clone() },
Ok(UciResponse::RawVendorRsp(packet)),
);
let mut context = MockContext::new(dispatcher);
context.expect_convert_byte_array(fake_payload, Ok(payload));
let result = send_raw_vendor_cmd(&context, gid, oid, fake_payload).unwrap();
assert_eq!(result.0, uwb_uci_packets::GroupId::VendorReserved9 as i32);
assert_eq!(result.1, opcode as i32);
assert_eq!(result.2, response);
}
#[test]
fn test_get_power_stats() {
let idle_time_ms = 5;
let tx_time_ms = 4;
let rx_time_ms = 3;
let total_wake_count = 2;
let packet = uwb_uci_packets::AndroidGetPowerStatsRspBuilder {
stats: uwb_uci_packets::PowerStats {
status: StatusCode::UciStatusOk,
idle_time_ms,
tx_time_ms,
rx_time_ms,
total_wake_count,
},
}
.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciGetPowerStats,
Ok(UciResponse::AndroidGetPowerStatsRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = get_power_stats(&context).unwrap();
assert_eq!(TryInto::<jint>::try_into(result[0]).unwrap(), idle_time_ms as jint);
assert_eq!(TryInto::<jint>::try_into(result[1]).unwrap(), tx_time_ms as jint);
assert_eq!(TryInto::<jint>::try_into(result[2]).unwrap(), rx_time_ms as jint);
assert_eq!(TryInto::<jint>::try_into(result[3]).unwrap(), total_wake_count as jint);
}
#[test]
fn test_reset_device() {
let reset_config = uwb_uci_packets::ResetConfig::UwbsReset as u8;
let packet =
uwb_uci_packets::DeviceResetRspBuilder { status: StatusCode::UciStatusOk }.build();
let mut dispatcher = MockDispatcher::new();
dispatcher.expect_block_on_jni_command(
JNICommand::UciDeviceReset { reset_config },
Ok(UciResponse::DeviceResetRsp(packet)),
);
let context = MockContext::new(dispatcher);
let result = reset_device(&context, reset_config);
assert!(result.is_ok());
}
}