| // 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 NotificationManagerAndroid and its builder. |
| |
| use crate::jclass_name::{ |
| MULTICAST_LIST_UPDATE_STATUS_CLASS, UWB_DL_TDOA_MEASUREMENT_CLASS, |
| UWB_OWR_AOA_MEASUREMENT_CLASS, UWB_RANGING_DATA_CLASS, UWB_TWO_WAY_MEASUREMENT_CLASS, |
| }; |
| |
| use std::collections::HashMap; |
| use std::sync::Arc; |
| |
| use jni::errors::Error as JNIError; |
| use jni::objects::{GlobalRef, JClass, JMethodID, JObject, JValue}; |
| use jni::signature::TypeSignature; |
| use jni::sys::jvalue; |
| use jni::{AttachGuard, JavaVM}; |
| use log::{debug, error}; |
| use uwb_core::error::{Error as UwbError, Result as UwbResult}; |
| use uwb_core::params::UwbAddress; |
| use uwb_core::uci::uci_manager_sync::{NotificationManager, NotificationManagerBuilder}; |
| use uwb_core::uci::{ |
| CoreNotification, DataRcvNotification, RangingMeasurements, SessionNotification, |
| SessionRangeData, |
| }; |
| use uwb_uci_packets::{ |
| ControleeStatus, ExtendedAddressDlTdoaRangingMeasurement, |
| ExtendedAddressOwrAoaRangingMeasurement, ExtendedAddressTwoWayRangingMeasurement, |
| MacAddressIndicator, RangingMeasurementType, SessionState, |
| ShortAddressDlTdoaRangingMeasurement, ShortAddressOwrAoaRangingMeasurement, |
| ShortAddressTwoWayRangingMeasurement, StatusCode, |
| }; |
| |
| // Byte size of mac address length: |
| const SHORT_MAC_ADDRESS_LEN: i32 = 2; |
| const EXTENDED_MAC_ADDRESS_LEN: i32 = 8; |
| const MAX_ANCHOR_LOCATION_LEN: i32 = 12; |
| const MAX_RANGING_ROUNDS_LEN: i32 = 16; |
| |
| // Maximum allowed number of Java Object to be allocated inside with_local_frame |
| const MAX_JAVA_OBJECTS_CAPACITY: i32 = 50; |
| |
| enum MacAddress { |
| Short(u16), |
| Extended(u64), |
| } |
| impl MacAddress { |
| fn into_ne_bytes(self) -> Vec<u8> { |
| match self { |
| MacAddress::Short(val) => val.to_ne_bytes().into(), |
| MacAddress::Extended(val) => val.to_ne_bytes().into(), |
| } |
| } |
| } |
| |
| struct TwoWayRangingMeasurement { |
| mac_address: MacAddress, |
| status: StatusCode, |
| nlos: u8, |
| distance: u16, |
| aoa_azimuth: u16, |
| aoa_azimuth_fom: u8, |
| aoa_elevation: u16, |
| aoa_elevation_fom: u8, |
| aoa_destination_azimuth: u16, |
| aoa_destination_azimuth_fom: u8, |
| aoa_destination_elevation: u16, |
| aoa_destination_elevation_fom: u8, |
| slot_index: u8, |
| rssi: u8, |
| } |
| |
| struct OwrAoaRangingMeasurement { |
| mac_address: MacAddress, |
| status: StatusCode, |
| nlos: u8, |
| frame_sequence_number: u8, |
| block_index: u16, |
| aoa_azimuth: u16, |
| aoa_azimuth_fom: u8, |
| aoa_elevation: u16, |
| aoa_elevation_fom: u8, |
| } |
| |
| impl From<ShortAddressTwoWayRangingMeasurement> for TwoWayRangingMeasurement { |
| fn from(measurement: ShortAddressTwoWayRangingMeasurement) -> Self { |
| TwoWayRangingMeasurement { |
| mac_address: MacAddress::Short(measurement.mac_address), |
| status: (measurement.status), |
| nlos: (measurement.nlos), |
| distance: (measurement.distance), |
| aoa_azimuth: (measurement.aoa_azimuth), |
| aoa_azimuth_fom: (measurement.aoa_azimuth_fom), |
| aoa_elevation: (measurement.aoa_elevation), |
| aoa_elevation_fom: (measurement.aoa_elevation_fom), |
| aoa_destination_azimuth: (measurement.aoa_destination_azimuth), |
| aoa_destination_azimuth_fom: (measurement.aoa_destination_azimuth_fom), |
| aoa_destination_elevation: (measurement.aoa_destination_elevation), |
| aoa_destination_elevation_fom: (measurement.aoa_destination_elevation_fom), |
| slot_index: (measurement.slot_index), |
| rssi: (measurement.rssi), |
| } |
| } |
| } |
| |
| impl From<ExtendedAddressTwoWayRangingMeasurement> for TwoWayRangingMeasurement { |
| fn from(measurement: ExtendedAddressTwoWayRangingMeasurement) -> Self { |
| TwoWayRangingMeasurement { |
| mac_address: MacAddress::Extended(measurement.mac_address), |
| status: (measurement.status), |
| nlos: (measurement.nlos), |
| distance: (measurement.distance), |
| aoa_azimuth: (measurement.aoa_azimuth), |
| aoa_azimuth_fom: (measurement.aoa_azimuth_fom), |
| aoa_elevation: (measurement.aoa_elevation), |
| aoa_elevation_fom: (measurement.aoa_elevation_fom), |
| aoa_destination_azimuth: (measurement.aoa_destination_azimuth), |
| aoa_destination_azimuth_fom: (measurement.aoa_destination_azimuth_fom), |
| aoa_destination_elevation: (measurement.aoa_destination_elevation), |
| aoa_destination_elevation_fom: (measurement.aoa_destination_elevation_fom), |
| slot_index: (measurement.slot_index), |
| rssi: (measurement.rssi), |
| } |
| } |
| } |
| |
| impl From<ShortAddressOwrAoaRangingMeasurement> for OwrAoaRangingMeasurement { |
| fn from(measurement: ShortAddressOwrAoaRangingMeasurement) -> Self { |
| OwrAoaRangingMeasurement { |
| mac_address: MacAddress::Short(measurement.mac_address), |
| status: (measurement.status), |
| nlos: (measurement.nlos), |
| frame_sequence_number: (measurement.frame_sequence_number), |
| block_index: (measurement.block_index), |
| aoa_azimuth: (measurement.aoa_azimuth), |
| aoa_azimuth_fom: (measurement.aoa_azimuth_fom), |
| aoa_elevation: (measurement.aoa_elevation), |
| aoa_elevation_fom: (measurement.aoa_elevation_fom), |
| } |
| } |
| } |
| |
| impl From<ExtendedAddressOwrAoaRangingMeasurement> for OwrAoaRangingMeasurement { |
| fn from(measurement: ExtendedAddressOwrAoaRangingMeasurement) -> Self { |
| OwrAoaRangingMeasurement { |
| mac_address: MacAddress::Extended(measurement.mac_address), |
| status: (measurement.status), |
| nlos: (measurement.nlos), |
| frame_sequence_number: (measurement.frame_sequence_number), |
| block_index: (measurement.block_index), |
| aoa_azimuth: (measurement.aoa_azimuth), |
| aoa_azimuth_fom: (measurement.aoa_azimuth_fom), |
| aoa_elevation: (measurement.aoa_elevation), |
| aoa_elevation_fom: (measurement.aoa_elevation_fom), |
| } |
| } |
| } |
| |
| struct DlTdoaRangingMeasurement { |
| mac_address: MacAddress, |
| pub status: u8, |
| pub message_type: u8, |
| pub message_control: u16, |
| pub block_index: u16, |
| pub round_index: u8, |
| pub nlos: u8, |
| pub aoa_azimuth: u16, |
| pub aoa_azimuth_fom: u8, |
| pub aoa_elevation: u16, |
| pub aoa_elevation_fom: u8, |
| pub rssi: u8, |
| pub tx_timestamp: u64, |
| pub rx_timestamp: u64, |
| pub anchor_cfo: u16, |
| pub cfo: u16, |
| pub initiator_reply_time: u32, |
| pub responder_reply_time: u32, |
| pub initiator_responder_tof: u16, |
| pub dt_anchor_location: Vec<u8>, |
| pub ranging_rounds: Vec<u8>, |
| } |
| |
| impl From<ExtendedAddressDlTdoaRangingMeasurement> for DlTdoaRangingMeasurement { |
| fn from(measurement: ExtendedAddressDlTdoaRangingMeasurement) -> Self { |
| DlTdoaRangingMeasurement { |
| mac_address: MacAddress::Extended(measurement.mac_address), |
| status: (measurement.measurement.status), |
| message_type: (measurement.measurement.message_type), |
| message_control: (measurement.measurement.message_control), |
| block_index: (measurement.measurement.block_index), |
| round_index: (measurement.measurement.round_index), |
| nlos: (measurement.measurement.nlos), |
| aoa_azimuth: (measurement.measurement.aoa_azimuth), |
| aoa_azimuth_fom: (measurement.measurement.aoa_azimuth_fom), |
| aoa_elevation: (measurement.measurement.aoa_elevation), |
| aoa_elevation_fom: (measurement.measurement.aoa_elevation_fom), |
| rssi: (measurement.measurement.rssi), |
| tx_timestamp: (measurement.measurement.tx_timestamp), |
| rx_timestamp: (measurement.measurement.rx_timestamp), |
| anchor_cfo: (measurement.measurement.anchor_cfo), |
| cfo: (measurement.measurement.cfo), |
| initiator_reply_time: (measurement.measurement.initiator_reply_time), |
| responder_reply_time: (measurement.measurement.responder_reply_time), |
| initiator_responder_tof: (measurement.measurement.initiator_responder_tof), |
| dt_anchor_location: (measurement.measurement.dt_anchor_location), |
| ranging_rounds: (measurement.measurement.ranging_rounds), |
| } |
| } |
| } |
| |
| impl From<ShortAddressDlTdoaRangingMeasurement> for DlTdoaRangingMeasurement { |
| fn from(measurement: ShortAddressDlTdoaRangingMeasurement) -> Self { |
| DlTdoaRangingMeasurement { |
| mac_address: MacAddress::Short(measurement.mac_address), |
| status: (measurement.measurement.status), |
| message_type: (measurement.measurement.message_type), |
| message_control: (measurement.measurement.message_control), |
| block_index: (measurement.measurement.block_index), |
| round_index: (measurement.measurement.round_index), |
| nlos: (measurement.measurement.nlos), |
| aoa_azimuth: (measurement.measurement.aoa_azimuth), |
| aoa_azimuth_fom: (measurement.measurement.aoa_azimuth_fom), |
| aoa_elevation: (measurement.measurement.aoa_elevation), |
| aoa_elevation_fom: (measurement.measurement.aoa_elevation_fom), |
| rssi: (measurement.measurement.rssi), |
| tx_timestamp: (measurement.measurement.tx_timestamp), |
| rx_timestamp: (measurement.measurement.rx_timestamp), |
| anchor_cfo: (measurement.measurement.anchor_cfo), |
| cfo: (measurement.measurement.cfo), |
| initiator_reply_time: (measurement.measurement.initiator_reply_time), |
| responder_reply_time: (measurement.measurement.responder_reply_time), |
| initiator_responder_tof: (measurement.measurement.initiator_responder_tof), |
| dt_anchor_location: (measurement.measurement.dt_anchor_location), |
| ranging_rounds: (measurement.measurement.ranging_rounds), |
| } |
| } |
| } |
| |
| pub(crate) struct NotificationManagerAndroid { |
| pub chip_id: String, |
| // 'static annotation is needed as env is 'sent' by tokio::task::spawn_local. |
| pub env: AttachGuard<'static>, |
| /// Global reference to the class loader object (java/lang/ClassLoader) from the java thread |
| /// that local java UCI classes can be loaded. |
| /// See http://yangyingchao.github.io/android/2015/01/13/Android-JNI-FindClass-Error.html |
| pub class_loader_obj: GlobalRef, |
| /// Global reference to the java class holding the various UCI notification callback functions. |
| pub callback_obj: GlobalRef, |
| // *_jmethod_id are cached for faster callback using call_method_unchecked |
| pub jmethod_id_map: HashMap<String, JMethodID>, |
| // jclass are cached for faster callback |
| pub jclass_map: HashMap<String, GlobalRef>, |
| } |
| |
| // TODO(b/246678053): Need to add callbacks for Data Packet Rx, and Data Packet Tx events (like |
| // DATA_CREDIT_NTF, DATA_STATUS_NTF). |
| impl NotificationManagerAndroid { |
| /// Finds JClass stored in jclass map. Should be a member function, but disjoint field borrow |
| /// checker fails and mutability of individual fields has to be annotated. |
| fn find_local_class<'a>( |
| jclass_map: &'a mut HashMap<String, GlobalRef>, |
| class_loader_obj: &'a GlobalRef, |
| env: &'a AttachGuard<'static>, |
| class_name: &'a str, |
| ) -> Result<JClass<'a>, JNIError> { |
| // Look for cached class |
| if jclass_map.get(class_name).is_none() { |
| // Find class using the class loader object, needed as this call is initiated from a |
| // different native thread. |
| |
| let env_class_name = *env.new_string(class_name).map_err(|e| { |
| error!("UCI JNI: failed to create Java String: {e:?}"); |
| e |
| })?; |
| let class_value = env |
| .call_method( |
| class_loader_obj.as_obj(), |
| "findClass", |
| "(Ljava/lang/String;)Ljava/lang/Class;", |
| &[JValue::Object(env_class_name)], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: failed to find java class {}: {:?}", class_name, e); |
| e |
| })?; |
| let jclass = match class_value.l() { |
| Ok(obj) => Ok(JClass::from(obj)), |
| Err(e) => { |
| error!("UCI JNI: failed to find java class {}: {:?}", class_name, e); |
| Err(e) |
| } |
| }?; |
| // Cache JClass as a global reference. |
| jclass_map.insert( |
| class_name.to_owned(), |
| env.new_global_ref(jclass).map_err(|e| { |
| error!("UCI JNI: global reference conversion failed: {:?}", e); |
| e |
| })?, |
| ); |
| } |
| // Return JClass |
| Ok(jclass_map.get(class_name).unwrap().as_obj().into()) |
| } |
| |
| fn cached_jni_call( |
| &mut self, |
| name: &str, |
| sig: &str, |
| args: &[jvalue], |
| ) -> Result<JObject, JNIError> { |
| debug!("UCI JNI: callback {}", name); |
| let type_signature = TypeSignature::from_str(sig).map_err(|e| { |
| error!("UCI JNI: Invalid type signature: {:?}", e); |
| e |
| })?; |
| if type_signature.args.len() != args.len() { |
| error!( |
| "UCI: type_signature requires {} args, but {} is provided", |
| type_signature.args.len(), |
| args.len() |
| ); |
| return Err(jni::errors::Error::InvalidArgList(type_signature)); |
| } |
| let name_signature = name.to_owned() + sig; |
| if self.jmethod_id_map.get(&name_signature).is_none() { |
| self.jmethod_id_map.insert( |
| name_signature.clone(), |
| self.env.get_method_id(self.callback_obj.as_obj(), name, sig).map_err(|e| { |
| error!("UCI JNI: failed to get method: {:?}", e); |
| e |
| })?, |
| ); |
| } |
| match self.env.call_method_unchecked( |
| self.callback_obj.as_obj(), |
| self.jmethod_id_map.get(&name_signature).unwrap().to_owned(), |
| type_signature.ret, |
| args, |
| ) { |
| Ok(_) => Ok(JObject::null()), |
| Err(e) => { |
| error!("UCI JNI: callback {} failed!", name); |
| Err(e) |
| } |
| } |
| } |
| |
| fn on_session_status_notification( |
| &mut self, |
| session_id: u32, |
| session_state: SessionState, |
| reason_code: u8, |
| ) -> Result<JObject, JNIError> { |
| self.cached_jni_call( |
| "onSessionStatusNotificationReceived", |
| "(JII)V", |
| &[ |
| jvalue::from(JValue::Long(session_id as i64)), |
| jvalue::from(JValue::Int(session_state as i32)), |
| jvalue::from(JValue::Int(reason_code as i32)), |
| ], |
| ) |
| } |
| |
| fn on_session_update_multicast_notification( |
| &mut self, |
| session_id: u32, |
| remaining_multicast_list_size: usize, |
| status_list: Vec<ControleeStatus>, |
| ) -> Result<JObject, JNIError> { |
| let remaining_multicast_list_size: i32 = |
| remaining_multicast_list_size.try_into().map_err(|_| JNIError::InvalidCtorReturn)?; |
| let count: i32 = status_list.len().try_into().map_err(|_| JNIError::InvalidCtorReturn)?; |
| let subsession_id_jlongarray = self.env.new_long_array(count)?; |
| let status_jintarray = self.env.new_int_array(count)?; |
| let (mac_address_vec, (subsession_id_vec, status_vec)): (Vec<[u8; 2]>, (Vec<_>, Vec<_>)) = |
| status_list |
| .into_iter() |
| .map(|cs| (cs.mac_address, (cs.subsession_id as i64, cs.status as i32))) |
| .unzip(); |
| |
| let mac_address_vec_i8 = |
| mac_address_vec.iter().flat_map(|&[a, b]| vec![a as i8, b as i8]).collect::<Vec<i8>>(); |
| let mac_address_slice: &[i8] = &mac_address_vec_i8; |
| let mac_address_jbytearray = self.env.new_byte_array(mac_address_slice.len() as i32)?; |
| |
| self.env.set_byte_array_region(mac_address_jbytearray, 0, mac_address_slice)?; |
| self.env.set_long_array_region(subsession_id_jlongarray, 0, &subsession_id_vec)?; |
| self.env.set_int_array_region(status_jintarray, 0, &status_vec)?; |
| let multicast_update_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| MULTICAST_LIST_UPDATE_STATUS_CLASS, |
| )?; |
| let method_sig = "(L".to_owned() + MULTICAST_LIST_UPDATE_STATUS_CLASS + ";)V"; |
| |
| // Safety: mac_address_jintarray is safely instantiated above. |
| let mac_address_jobject = unsafe { JObject::from_raw(mac_address_jbytearray) }; |
| |
| // Safety: subsession_id_jlongarray is safely instantiated above. |
| let subsession_id_jobject = unsafe { JObject::from_raw(subsession_id_jlongarray) }; |
| |
| // Safety: status_jintarray is safely instantiated above. |
| let status_jobject = unsafe { JObject::from_raw(status_jintarray) }; |
| |
| let multicast_update_jobject = self.env.new_object( |
| multicast_update_jclass, |
| "(JII[B[J[I)V", |
| &[ |
| JValue::Long(session_id as i64), |
| JValue::Int(remaining_multicast_list_size), |
| JValue::Int(count), |
| JValue::Object(mac_address_jobject), |
| JValue::Object(subsession_id_jobject), |
| JValue::Object(status_jobject), |
| ], |
| )?; |
| self.cached_jni_call( |
| "onMulticastListUpdateNotificationReceived", |
| &method_sig, |
| &[jvalue::from(JValue::Object(multicast_update_jobject))], |
| ) |
| } |
| |
| // TODO(b/246678053): Re-factor usage of the RangingMeasurement enum below, to extract the |
| // fields in a common/caller method (and preferably not handle TwoWay/OwrAoa in this method). |
| fn on_session_dl_tdoa_range_data_notification( |
| &mut self, |
| range_data: SessionRangeData, |
| ) -> Result<JObject, JNIError> { |
| let raw_notification_jbytearray = |
| self.env.byte_array_from_slice(&range_data.raw_ranging_data)?; |
| let measurement_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| UWB_DL_TDOA_MEASUREMENT_CLASS, |
| )?; |
| let bytearray_len: i32 = match &range_data.ranging_measurements { |
| uwb_core::uci::RangingMeasurements::ShortAddressTwoWay(_) => SHORT_MAC_ADDRESS_LEN, |
| uwb_core::uci::RangingMeasurements::ExtendedAddressTwoWay(_) => { |
| EXTENDED_MAC_ADDRESS_LEN |
| } |
| uwb_core::uci::RangingMeasurements::ShortAddressDltdoa(_) => SHORT_MAC_ADDRESS_LEN, |
| uwb_core::uci::RangingMeasurements::ExtendedAddressDltdoa(_) => { |
| EXTENDED_MAC_ADDRESS_LEN |
| } |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| }; |
| let address_jbytearray = self.env.new_byte_array(bytearray_len)?; |
| let anchor_location = self.env.new_byte_array(MAX_ANCHOR_LOCATION_LEN)?; |
| let active_ranging_rounds = self.env.new_byte_array(MAX_RANGING_ROUNDS_LEN)?; |
| |
| // Safety: address_jbytearray is safely instantiated above. |
| let address_jobject = unsafe { JObject::from_raw(address_jbytearray) }; |
| // Safety: anchor_location is safely instantiated above. |
| let anchor_jobject = unsafe { JObject::from_raw(anchor_location) }; |
| // Safety: active_ranging_rounds is safely instantiated above. |
| let active_ranging_rounds_jobject = unsafe { JObject::from_raw(active_ranging_rounds) }; |
| |
| let zero_initiated_measurement_jobject = self |
| .env |
| .new_object( |
| measurement_jclass, |
| "([BIIIIIIIIIIIJJIIJJI[B[B)V", |
| &[ |
| JValue::Object(address_jobject), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Long(0), |
| JValue::Long(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Long(0), |
| JValue::Long(0), |
| JValue::Int(0), |
| JValue::Object(anchor_jobject), |
| JValue::Object(active_ranging_rounds_jobject), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: measurement object creation failed: {:?}", e); |
| e |
| })?; |
| let measurement_count: i32 = match &range_data.ranging_measurements { |
| RangingMeasurements::ShortAddressTwoWay(v) => v.len(), |
| RangingMeasurements::ExtendedAddressTwoWay(v) => v.len(), |
| RangingMeasurements::ShortAddressDltdoa(v) => v.len(), |
| RangingMeasurements::ExtendedAddressDltdoa(v) => v.len(), |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| } |
| .try_into() |
| .map_err(|_| JNIError::InvalidCtorReturn)?; |
| let mac_indicator = match &range_data.ranging_measurements { |
| RangingMeasurements::ShortAddressTwoWay(_) => MacAddressIndicator::ShortAddress, |
| RangingMeasurements::ExtendedAddressTwoWay(_) => MacAddressIndicator::ExtendedAddress, |
| RangingMeasurements::ShortAddressDltdoa(_) => MacAddressIndicator::ShortAddress, |
| RangingMeasurements::ExtendedAddressDltdoa(_) => MacAddressIndicator::ExtendedAddress, |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| }; |
| |
| let measurements_jobjectarray = self.env.new_object_array( |
| measurement_count, |
| measurement_jclass, |
| zero_initiated_measurement_jobject, |
| )?; |
| |
| for (i, measurement) in match range_data.ranging_measurements { |
| RangingMeasurements::ShortAddressDltdoa(v) => { |
| v.into_iter().map(DlTdoaRangingMeasurement::from).collect::<Vec<_>>() |
| } |
| RangingMeasurements::ExtendedAddressDltdoa(v) => { |
| v.into_iter().map(DlTdoaRangingMeasurement::from).collect::<Vec<_>>() |
| } |
| _ => Vec::new(), |
| } |
| .into_iter() |
| .enumerate() |
| { |
| // cast to i8 as java do not support unsigned: |
| let mac_address_i8 = measurement |
| .mac_address |
| .into_ne_bytes() |
| .iter() |
| .map(|b| b.to_owned() as i8) |
| .collect::<Vec<_>>(); |
| let mac_address_jbytearray = self.env.new_byte_array(mac_address_i8.len() as i32)?; |
| self.env.set_byte_array_region(mac_address_jbytearray, 0, &mac_address_i8)?; |
| |
| let dt_anchor_location_jbytearray = |
| self.env.byte_array_from_slice(&measurement.dt_anchor_location)?; |
| |
| let ranging_rounds_jbytearray = |
| self.env.byte_array_from_slice(&measurement.ranging_rounds)?; |
| |
| // Safety: mac_address_jbytearray is safely instantiated above. |
| let mac_address_jobject = unsafe { JObject::from_raw(mac_address_jbytearray) }; |
| // Safety: dt_anchor_location_jbytearray is safely instantiated above. |
| let dt_anchor_location_jobject = |
| unsafe { JObject::from_raw(dt_anchor_location_jbytearray) }; |
| // Safety: ranging_rounds_jbytearray is safely instantiated above. |
| let ranging_rounds_jobject = unsafe { JObject::from_raw(ranging_rounds_jbytearray) }; |
| |
| let measurement_jobject = self |
| .env |
| .new_object( |
| measurement_jclass, |
| "([BIIIIIIIIIIIJJIIJJI[B[B)V", |
| &[ |
| JValue::Object(mac_address_jobject), |
| JValue::Int(measurement.status as i32), |
| JValue::Int(measurement.message_type as i32), |
| JValue::Int(measurement.message_control as i32), |
| JValue::Int(measurement.block_index as i32), |
| JValue::Int(measurement.round_index as i32), |
| JValue::Int(measurement.nlos as i32), |
| JValue::Int(measurement.aoa_azimuth as i32), |
| JValue::Int(measurement.aoa_azimuth_fom as i32), |
| JValue::Int(measurement.aoa_elevation as i32), |
| JValue::Int(measurement.aoa_elevation_fom as i32), |
| JValue::Int(measurement.rssi as i32), |
| JValue::Long(measurement.tx_timestamp as i64), |
| JValue::Long(measurement.rx_timestamp as i64), |
| JValue::Int(measurement.anchor_cfo as i32), |
| JValue::Int(measurement.cfo as i32), |
| JValue::Long(measurement.initiator_reply_time as i64), |
| JValue::Long(measurement.responder_reply_time as i64), |
| JValue::Int(measurement.initiator_responder_tof as i32), |
| JValue::Object(dt_anchor_location_jobject), |
| JValue::Object(ranging_rounds_jobject), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: measurement object creation failed: {:?}", e); |
| e |
| })?; |
| self.env |
| .set_object_array_element(measurements_jobjectarray, i as i32, measurement_jobject) |
| .map_err(|e| { |
| error!("UCI JNI: measurement object copy failed: {:?}", e); |
| e |
| })?; |
| } |
| // Create UwbRangingData |
| let ranging_data_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| UWB_RANGING_DATA_CLASS, |
| )?; |
| |
| let method_sig = "(JJIJIII[L".to_owned() + UWB_DL_TDOA_MEASUREMENT_CLASS + ";[B)V"; |
| |
| // Safety: measurements_jobjectarray is safely instantiated above. |
| let measurements_jobject = unsafe { JObject::from_raw(measurements_jobjectarray) }; |
| // Safety: raw_notification_jbytearray is safely instantiated above. |
| let raw_notification_jobject = unsafe { JObject::from_raw(raw_notification_jbytearray) }; |
| |
| let range_data_jobject = self |
| .env |
| .new_object( |
| ranging_data_jclass, |
| &method_sig, |
| &[ |
| JValue::Long(range_data.sequence_number as i64), |
| // session_token below has already been mapped to session_id by uci layer. |
| JValue::Long(range_data.session_token as i64), |
| JValue::Int(range_data.rcr_indicator as i32), |
| JValue::Long(range_data.current_ranging_interval_ms as i64), |
| JValue::Int(range_data.ranging_measurement_type as i32), |
| JValue::Int(mac_indicator as i32), |
| JValue::Int(measurement_count), |
| JValue::Object(measurements_jobject), |
| JValue::Object(raw_notification_jobject), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: Ranging Data object creation failed: {:?}", e); |
| e |
| })?; |
| |
| let method_sig = "(L".to_owned() + UWB_RANGING_DATA_CLASS + ";)V"; |
| self.cached_jni_call( |
| "onRangeDataNotificationReceived", |
| &method_sig, |
| &[jvalue::from(JValue::Object(range_data_jobject))], |
| ) |
| } |
| |
| fn on_two_way_range_data_notification( |
| &mut self, |
| bytearray_len: i32, |
| measurement_count: i32, |
| measurements: Vec<TwoWayRangingMeasurement>, |
| ) -> Result<jni::sys::jobjectArray, JNIError> { |
| let measurement_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| UWB_TWO_WAY_MEASUREMENT_CLASS, |
| )?; |
| let address_jbytearray = self.env.new_byte_array(bytearray_len)?; |
| |
| // Safety: address_jbytearray is safely instantiated above. |
| let address_jobject = unsafe { JObject::from_raw(address_jbytearray) }; |
| |
| let zero_initiated_measurement_jobject = self |
| .env |
| .new_object( |
| measurement_jclass, |
| "([BIIIIIIIIIIIII)V", |
| &[ |
| JValue::Object(address_jobject), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| JValue::Int(0), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: measurement object creation failed: {:?}", e); |
| e |
| })?; |
| |
| let measurements_jobjectarray = self.env.new_object_array( |
| measurement_count, |
| measurement_jclass, |
| zero_initiated_measurement_jobject, |
| )?; |
| for (i, measurement) in measurements.into_iter().enumerate() { |
| // cast to i8 as java do not support unsigned: |
| let mac_address_i8 = measurement |
| .mac_address |
| .into_ne_bytes() |
| .iter() |
| .map(|b| b.to_owned() as i8) |
| .collect::<Vec<_>>(); |
| let mac_address_jbytearray = self.env.new_byte_array(mac_address_i8.len() as i32)?; |
| self.env.set_byte_array_region(mac_address_jbytearray, 0, &mac_address_i8)?; |
| // casting as i32 is fine since it is wider than actual integer type. |
| |
| // Safety: mac_address_jbytearray is safely instantiated above. |
| let mac_address_jobject = unsafe { JObject::from_raw(mac_address_jbytearray) }; |
| let measurement_jobject = self |
| .env |
| .new_object( |
| measurement_jclass, |
| "([BIIIIIIIIIIIII)V", |
| &[ |
| JValue::Object(mac_address_jobject), |
| JValue::Int(i32::from(measurement.status)), |
| JValue::Int(measurement.nlos as i32), |
| JValue::Int(measurement.distance as i32), |
| JValue::Int(measurement.aoa_azimuth as i32), |
| JValue::Int(measurement.aoa_azimuth_fom as i32), |
| JValue::Int(measurement.aoa_elevation as i32), |
| JValue::Int(measurement.aoa_elevation_fom as i32), |
| JValue::Int(measurement.aoa_destination_azimuth as i32), |
| JValue::Int(measurement.aoa_destination_azimuth_fom as i32), |
| JValue::Int(measurement.aoa_destination_elevation as i32), |
| JValue::Int(measurement.aoa_destination_elevation_fom as i32), |
| JValue::Int(measurement.slot_index as i32), |
| JValue::Int(measurement.rssi as i32), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: measurement object creation failed: {:?}", e); |
| e |
| })?; |
| self.env |
| .set_object_array_element(measurements_jobjectarray, i as i32, measurement_jobject) |
| .map_err(|e| { |
| error!("UCI JNI: measurement object copy failed: {:?}", e); |
| e |
| })?; |
| } |
| |
| Ok(measurements_jobjectarray) |
| } |
| |
| fn on_session_owr_aoa_range_data_notification( |
| &mut self, |
| range_data: SessionRangeData, |
| ) -> Result<JObject, JNIError> { |
| if range_data.ranging_measurement_type != RangingMeasurementType::OwrAoa { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| |
| let raw_notification_jbytearray = |
| self.env.byte_array_from_slice(&range_data.raw_ranging_data)?; |
| |
| let (mac_indicator, measurement): (MacAddressIndicator, OwrAoaRangingMeasurement) = |
| match range_data.ranging_measurements { |
| RangingMeasurements::ExtendedAddressOwrAoa(m) => { |
| (MacAddressIndicator::ExtendedAddress, m.into()) |
| } |
| RangingMeasurements::ShortAddressOwrAoa(m) => { |
| (MacAddressIndicator::ShortAddress, m.into()) |
| } |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| }; |
| |
| // cast to i8 as java do not support unsigned. |
| let mac_address_i8 = measurement |
| .mac_address |
| .into_ne_bytes() |
| .iter() |
| .map(|b| b.to_owned() as i8) |
| .collect::<Vec<_>>(); |
| // casting as i32 is fine since it is wider than actual integer type. |
| let mac_address_jbytearray = self.env.new_byte_array(mac_address_i8.len() as i32)?; |
| self.env.set_byte_array_region(mac_address_jbytearray, 0, &mac_address_i8)?; |
| // Safety: mac_address_jbytearray is safely instantiated above. |
| let mac_address_jobject = unsafe { JObject::from_raw(mac_address_jbytearray) }; |
| |
| let measurement_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| UWB_OWR_AOA_MEASUREMENT_CLASS, |
| )?; |
| let measurement_jobject = self |
| .env |
| .new_object( |
| measurement_jclass, |
| "([BIIIIIIII)V", |
| &[ |
| JValue::Object(mac_address_jobject), |
| JValue::Int(i32::from(measurement.status)), |
| JValue::Int(measurement.nlos as i32), |
| JValue::Int(measurement.frame_sequence_number as i32), |
| JValue::Int(measurement.block_index as i32), |
| JValue::Int(measurement.aoa_azimuth as i32), |
| JValue::Int(measurement.aoa_azimuth_fom as i32), |
| JValue::Int(measurement.aoa_elevation as i32), |
| JValue::Int(measurement.aoa_elevation_fom as i32), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: OwrAoA measurement jobject creation failed: {:?}", e); |
| e |
| })?; |
| |
| // Create UwbRangingData |
| let ranging_data_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| UWB_RANGING_DATA_CLASS, |
| )?; |
| let method_sig = "(JJIJIIIL".to_owned() + UWB_OWR_AOA_MEASUREMENT_CLASS + ";[B)V"; |
| |
| // Safety: raw_notification_jobject is safely instantiated above. |
| let raw_notification_jobject = unsafe { JObject::from_raw(raw_notification_jbytearray) }; |
| |
| let range_data_jobject = self |
| .env |
| .new_object( |
| ranging_data_jclass, |
| &method_sig, |
| &[ |
| JValue::Long(range_data.sequence_number as i64), |
| // session_token below has already been mapped to session_id by uci layer. |
| JValue::Long(range_data.session_token as i64), |
| JValue::Int(range_data.rcr_indicator as i32), |
| JValue::Long(range_data.current_ranging_interval_ms as i64), |
| JValue::Int(range_data.ranging_measurement_type as i32), |
| JValue::Int(mac_indicator as i32), |
| JValue::Int(1), // measurement_count |
| JValue::Object(measurement_jobject), |
| JValue::Object(raw_notification_jobject), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: Ranging Data object creation failed: {:?}", e); |
| e |
| })?; |
| let method_sig = "(L".to_owned() + UWB_RANGING_DATA_CLASS + ";)V"; |
| self.cached_jni_call( |
| "onRangeDataNotificationReceived", |
| &method_sig, |
| &[jvalue::from(JValue::Object(range_data_jobject))], |
| ) |
| } |
| |
| fn on_session_two_way_range_data_notification( |
| &mut self, |
| range_data: SessionRangeData, |
| ) -> Result<JObject, JNIError> { |
| let raw_notification_jbytearray = |
| self.env.byte_array_from_slice(&range_data.raw_ranging_data)?; |
| |
| let (bytearray_len, mac_indicator) = match &range_data.ranging_measurements { |
| RangingMeasurements::ExtendedAddressTwoWay(_) => { |
| (EXTENDED_MAC_ADDRESS_LEN, MacAddressIndicator::ExtendedAddress) |
| } |
| RangingMeasurements::ShortAddressTwoWay(_) => { |
| (SHORT_MAC_ADDRESS_LEN, MacAddressIndicator::ShortAddress) |
| } |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| }; |
| |
| let measurement_count: i32 = match &range_data.ranging_measurements { |
| RangingMeasurements::ShortAddressTwoWay(v) => v.len().try_into(), |
| RangingMeasurements::ExtendedAddressTwoWay(v) => v.len().try_into(), |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| } |
| .map_err(|_| JNIError::InvalidCtorReturn)?; |
| |
| let measurements_jobjectarray = match range_data.ranging_measurement_type { |
| RangingMeasurementType::TwoWay => { |
| let measurements = match range_data.ranging_measurements { |
| RangingMeasurements::ExtendedAddressTwoWay(v) => { |
| v.into_iter().map(TwoWayRangingMeasurement::from).collect::<Vec<_>>() |
| } |
| RangingMeasurements::ShortAddressTwoWay(v) => { |
| v.into_iter().map(TwoWayRangingMeasurement::from).collect::<Vec<_>>() |
| } |
| _ => return Err(JNIError::InvalidCtorReturn), |
| }; |
| self.on_two_way_range_data_notification( |
| bytearray_len, |
| measurement_count, |
| measurements, |
| )? |
| } |
| _ => { |
| return Err(JNIError::InvalidCtorReturn); |
| } |
| }; |
| |
| // Create UwbRangingData |
| let ranging_data_jclass = NotificationManagerAndroid::find_local_class( |
| &mut self.jclass_map, |
| &self.class_loader_obj, |
| &self.env, |
| UWB_RANGING_DATA_CLASS, |
| )?; |
| let method_sig = "(JJIJIII[L".to_owned() + UWB_TWO_WAY_MEASUREMENT_CLASS + ";[B)V"; |
| |
| // Safety: measurements_jobjectarray is safely instantiated above. |
| let measurements_jobject = unsafe { JObject::from_raw(measurements_jobjectarray) }; |
| // Safety: raw_notification_jobject is safely instantiated above. |
| let raw_notification_jobject = unsafe { JObject::from_raw(raw_notification_jbytearray) }; |
| let range_data_jobject = self |
| .env |
| .new_object( |
| ranging_data_jclass, |
| &method_sig, |
| &[ |
| JValue::Long(range_data.sequence_number as i64), |
| // session_token below has already been mapped to session_id by uci layer. |
| JValue::Long(range_data.session_token as i64), |
| JValue::Int(range_data.rcr_indicator as i32), |
| JValue::Long(range_data.current_ranging_interval_ms as i64), |
| JValue::Int(range_data.ranging_measurement_type as i32), |
| JValue::Int(mac_indicator as i32), |
| JValue::Int(measurement_count), |
| JValue::Object(measurements_jobject), |
| JValue::Object(raw_notification_jobject), |
| ], |
| ) |
| .map_err(|e| { |
| error!("UCI JNI: Ranging Data object creation failed: {:?}", e); |
| e |
| })?; |
| let method_sig = "(L".to_owned() + UWB_RANGING_DATA_CLASS + ";)V"; |
| self.cached_jni_call( |
| "onRangeDataNotificationReceived", |
| &method_sig, |
| &[jvalue::from(JValue::Object(range_data_jobject))], |
| ) |
| } |
| |
| fn on_data_transfer_status_notification( |
| &mut self, |
| session_id: u32, |
| uci_sequence_number: u8, |
| status_code: u8, |
| ) -> Result<JObject, JNIError> { |
| self.cached_jni_call( |
| "onDataSendStatus", |
| "(JIJ)V", |
| &[ |
| jvalue::from(JValue::Long(session_id as i64)), |
| jvalue::from(JValue::Int(status_code as i32)), |
| jvalue::from(JValue::Long(uci_sequence_number as i64)), |
| ], |
| ) |
| } |
| } |
| |
| impl NotificationManager for NotificationManagerAndroid { |
| fn on_core_notification(&mut self, core_notification: CoreNotification) -> UwbResult<()> { |
| debug!("UCI JNI: core notification callback."); |
| let env = *self.env; |
| env.with_local_frame(MAX_JAVA_OBJECTS_CAPACITY, || { |
| let env_chip_id_jobject = *self.env.new_string(&self.chip_id).unwrap(); |
| |
| match core_notification { |
| CoreNotification::DeviceStatus(device_state) => self.cached_jni_call( |
| "onDeviceStatusNotificationReceived", |
| "(ILjava/lang/String;)V", |
| &[ |
| jvalue::from(JValue::Int(device_state as i32)), |
| jvalue::from(JValue::Object(env_chip_id_jobject)), |
| ], |
| ), |
| CoreNotification::GenericError(generic_error) => self.cached_jni_call( |
| "onCoreGenericErrorNotificationReceived", |
| "(ILjava/lang/String;)V", |
| &[ |
| jvalue::from(JValue::Int(i32::from(generic_error))), |
| jvalue::from(JValue::Object(env_chip_id_jobject)), |
| ], |
| ), |
| } |
| }) |
| .map_err(|_| UwbError::ForeignFunctionInterface)?; |
| |
| Ok(()) |
| } |
| |
| fn on_session_notification( |
| &mut self, |
| session_notification: SessionNotification, |
| ) -> UwbResult<()> { |
| debug!("UCI JNI: session notification callback."); |
| let env = *self.env; |
| env.with_local_frame(MAX_JAVA_OBJECTS_CAPACITY, || { |
| match session_notification { |
| // session_token below has already been mapped to session_id by uci layer. |
| SessionNotification::Status { session_token, session_state, reason_code } => { |
| self.on_session_status_notification(session_token, session_state, reason_code) |
| } |
| SessionNotification::UpdateControllerMulticastList { |
| session_token, |
| remaining_multicast_list_size, |
| status_list, |
| } => self.on_session_update_multicast_notification( |
| session_token, |
| remaining_multicast_list_size, |
| status_list, |
| ), |
| // TODO(b/246678053): Match here on range_data.ranging_measurement_type instead. |
| SessionNotification::SessionInfo(range_data) => { |
| match range_data.ranging_measurements { |
| uwb_core::uci::RangingMeasurements::ShortAddressTwoWay(_) => { |
| self.on_session_two_way_range_data_notification(range_data) |
| } |
| uwb_core::uci::RangingMeasurements::ExtendedAddressTwoWay(_) => { |
| self.on_session_two_way_range_data_notification(range_data) |
| } |
| uwb_core::uci::RangingMeasurements::ShortAddressOwrAoa(_) => { |
| self.on_session_owr_aoa_range_data_notification(range_data) |
| } |
| uwb_core::uci::RangingMeasurements::ExtendedAddressOwrAoa(_) => { |
| self.on_session_owr_aoa_range_data_notification(range_data) |
| } |
| uwb_core::uci::RangingMeasurements::ShortAddressDltdoa(_) => { |
| self.on_session_dl_tdoa_range_data_notification(range_data) |
| } |
| uwb_core::uci::RangingMeasurements::ExtendedAddressDltdoa(_) => { |
| self.on_session_dl_tdoa_range_data_notification(range_data) |
| } |
| } |
| } |
| SessionNotification::DataTransferStatus { |
| session_token, |
| uci_sequence_number, |
| status, |
| } => self.on_data_transfer_status_notification( |
| session_token, |
| uci_sequence_number, |
| u8::from(status), |
| ), |
| // This session notification should not come here, as it's handled within |
| // UciManager, for internal state management related to sending data packet(s). |
| SessionNotification::DataCredit { session_token, credit_availability } => { |
| error!( |
| "UCI JNI: Received unexpected DataCredit notification for \ |
| session_token {}, credit_availability {:?}", |
| session_token, credit_availability |
| ); |
| Err(JNIError::InvalidCtorReturn) |
| } |
| } |
| }) |
| .map_err(|_| UwbError::ForeignFunctionInterface)?; |
| Ok(()) |
| } |
| |
| fn on_vendor_notification( |
| &mut self, |
| vendor_notification: uwb_core::params::RawUciMessage, |
| ) -> UwbResult<()> { |
| debug!("UCI JNI: vendor notification callback."); |
| let env = *self.env; |
| env.with_local_frame(MAX_JAVA_OBJECTS_CAPACITY, || { |
| let payload_jbytearray = |
| self.env.byte_array_from_slice(&vendor_notification.payload)?; |
| |
| // Safety: payload_jbytearray safely instantiated above. |
| let payload_jobject = unsafe { JObject::from_raw(payload_jbytearray) }; |
| self.cached_jni_call( |
| "onVendorUciNotificationReceived", |
| "(II[B)V", |
| &[ |
| // Java only has signed integer. The range for signed int32 should be sufficient. |
| jvalue::from(JValue::Int( |
| vendor_notification |
| .gid |
| .try_into() |
| .map_err(|_| JNIError::InvalidCtorReturn)?, |
| )), |
| jvalue::from(JValue::Int( |
| vendor_notification |
| .oid |
| .try_into() |
| .map_err(|_| JNIError::InvalidCtorReturn)?, |
| )), |
| jvalue::from(JValue::Object(payload_jobject)), |
| ], |
| ) |
| }) |
| .map_err(|_| UwbError::ForeignFunctionInterface)?; |
| Ok(()) |
| } |
| |
| fn on_data_rcv_notification( |
| &mut self, |
| data_rcv_notification: DataRcvNotification, |
| ) -> UwbResult<()> { |
| debug!("UCI JNI: Data Rcv notification callback."); |
| let env = *self.env; |
| env.with_local_frame(MAX_JAVA_OBJECTS_CAPACITY, || { |
| let source_address_jbytearray = match &data_rcv_notification.source_address { |
| UwbAddress::Short(a) => self.env.byte_array_from_slice(a)?, |
| UwbAddress::Extended(a) => self.env.byte_array_from_slice(a)?, |
| }; |
| let payload_jbytearray = |
| self.env.byte_array_from_slice(&data_rcv_notification.payload)?; |
| // Safety: source_address_jbytearray safely instantiated above. |
| let source_address_jobject = unsafe { JObject::from_raw(source_address_jbytearray) }; |
| // Safety: payload_jbytearray safely instantiated above. |
| let payload_jobject = unsafe { JObject::from_raw(payload_jbytearray) }; |
| self.cached_jni_call( |
| "onDataReceived", |
| "(JIJ[BII[B)V", |
| &[ |
| // session_token below has already been mapped to session_id by uci layer. |
| jvalue::from(JValue::Long(data_rcv_notification.session_token as i64)), |
| jvalue::from(JValue::Int(data_rcv_notification.status as i32)), |
| jvalue::from(JValue::Long(data_rcv_notification.uci_sequence_num as i64)), |
| jvalue::from(JValue::Object(source_address_jobject)), |
| jvalue::from(JValue::Int(data_rcv_notification.source_fira_component as i32)), |
| jvalue::from(JValue::Int(data_rcv_notification.dest_fira_component as i32)), |
| jvalue::from(JValue::Object(payload_jobject)), |
| ], |
| ) |
| }) |
| .map_err(|_| UwbError::ForeignFunctionInterface)?; |
| Ok(()) |
| } |
| } |
| pub(crate) struct NotificationManagerAndroidBuilder { |
| pub chip_id: String, |
| pub vm: &'static Arc<JavaVM>, |
| pub class_loader_obj: GlobalRef, |
| pub callback_obj: GlobalRef, |
| } |
| |
| impl NotificationManagerBuilder for NotificationManagerAndroidBuilder { |
| type NotificationManager = NotificationManagerAndroid; |
| |
| fn build(self) -> Option<Self::NotificationManager> { |
| if let Ok(env) = self.vm.attach_current_thread() { |
| Some(NotificationManagerAndroid { |
| chip_id: self.chip_id, |
| env, |
| class_loader_obj: self.class_loader_obj, |
| callback_obj: self.callback_obj, |
| jmethod_id_map: HashMap::new(), |
| jclass_map: HashMap::new(), |
| }) |
| } else { |
| None |
| } |
| } |
| } |