diff options
author | Ante Culo <cante@google.com> | 2022-10-12 19:25:05 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2022-10-12 19:25:05 +0000 |
commit | 453fc29faa21de9b2cdd97b1b8cfe9638bb208f1 (patch) | |
tree | fccc581cba50f7fd0714bd334e64d9917ecefc29 | |
parent | 44cb4d54b2e6ee21fd26e4f28202f9ad6307c985 (diff) | |
parent | a585d0d9c958ca7075a7cfebcd14ef9ce22efd00 (diff) | |
download | Uwb-453fc29faa21de9b2cdd97b1b8cfe9638bb208f1.tar.gz |
Merge "Implement UWB Service core callback JNI for Android" into tm-mainline-prod
-rw-r--r-- | indev_uwb_adaptation/java/com/android/server/uwb/indev/IUwbServiceListener.java | 4 | ||||
-rw-r--r-- | indev_uwb_adaptation/java/com/android/server/uwb/indev/UwbServiceCore.java | 6 | ||||
-rw-r--r-- | indev_uwb_adaptation/jni/src/api.rs | 58 | ||||
-rw-r--r-- | indev_uwb_adaptation/jni/src/callback.rs | 201 | ||||
-rw-r--r-- | indev_uwb_adaptation/jni/src/lib.rs | 1 | ||||
-rw-r--r-- | indev_uwb_adaptation/jni/src/object_mapping.rs | 237 | ||||
-rw-r--r-- | indev_uwb_adaptation/jni/src/unique_jvm.rs | 43 |
7 files changed, 530 insertions, 20 deletions
diff --git a/indev_uwb_adaptation/java/com/android/server/uwb/indev/IUwbServiceListener.java b/indev_uwb_adaptation/java/com/android/server/uwb/indev/IUwbServiceListener.java index 6ac9e4c0..e9b850b0 100644 --- a/indev_uwb_adaptation/java/com/android/server/uwb/indev/IUwbServiceListener.java +++ b/indev_uwb_adaptation/java/com/android/server/uwb/indev/IUwbServiceListener.java @@ -42,12 +42,12 @@ interface IUwbServiceListener { /** * Interface for receiving Session Status Notification * - * @param id : Session ID + * @param sessionId : Session ID * @param state : Session State * @param reasonCode : Reason Code - UCI GENERIC SPECIFICATION Table 15 : state change with * reason codes */ - void onSessionStatusNotificationReceived(long id, int state, int reasonCode); + void onSessionStatusNotificationReceived(long sessionId, int state, int reasonCode); /** * Interface for receiving Device Status Notification diff --git a/indev_uwb_adaptation/java/com/android/server/uwb/indev/UwbServiceCore.java b/indev_uwb_adaptation/java/com/android/server/uwb/indev/UwbServiceCore.java index 006a5390..091f6a4a 100644 --- a/indev_uwb_adaptation/java/com/android/server/uwb/indev/UwbServiceCore.java +++ b/indev_uwb_adaptation/java/com/android/server/uwb/indev/UwbServiceCore.java @@ -31,7 +31,7 @@ import java.util.Arrays; /** * Uwb Service Core placeholder */ -public class UwbServiceCore { +public class UwbServiceCore implements IUwbServiceListener { private static final String TAG = UwbServiceCore.class.getSimpleName(); protected IUwbServiceListener mUwbServiceListener; @@ -39,7 +39,7 @@ public class UwbServiceCore { public UwbServiceCore() { System.loadLibrary("uwb_adaptation_jni"); - nativeInitLogging(); + nativeInit(); } public long getUwbServicePtr() { @@ -239,7 +239,7 @@ public class UwbServiceCore { return nativeGetPowerStats(); } - private native void nativeInitLogging(); + private native void nativeInit(); private native long nativeUwbServiceNew(); diff --git a/indev_uwb_adaptation/jni/src/api.rs b/indev_uwb_adaptation/jni/src/api.rs index db2e2ef6..b41fd38f 100644 --- a/indev_uwb_adaptation/jni/src/api.rs +++ b/indev_uwb_adaptation/jni/src/api.rs @@ -16,7 +16,8 @@ //! //! Internally after the UWB core service is instantiated, the pointer to the service is saved //! on the calling Java side. -use jni::objects::JObject; +use jni::objects::{GlobalRef, JObject, JValue}; +use jni::signature::JavaType; use jni::sys::{jboolean, jbyte, jbyteArray, jint, jlong, jobject}; use jni::JNIEnv; use log::{debug, error}; @@ -35,10 +36,11 @@ use crate::object_mapping::{ CccOpenRangingParamsJni, CountryCodeJni, FiraControleeParamsJni, FiraOpenSessionParamsJni, PowerStatsJni, PowerStatsWithEnv, }; +use crate::unique_jvm; -/// Initialize native logging +/// Initialize native logging and capture static JavaVM ref #[no_mangle] -pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeInitLogging( +pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeInit( env: JNIEnv, obj: JObject, ) { @@ -48,6 +50,15 @@ pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeIn .with_min_level(log::Level::Trace) .with_filter("trace,jni=info"), ); + + match env.get_java_vm() { + Ok(vm) => { + unique_jvm::set_once(vm); + } + Err(err) => { + error!("Couldn't get a JavaVM from JNIEnv: {:?}", err); + } + }; } /// Create a new UWB service and return the pointer @@ -57,8 +68,29 @@ pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeUw obj: JObject, ) -> jlong { debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceNew : enter"); + let vm = match unique_jvm::get_static_ref() { + Some(vm_ref) => vm_ref, + None => { + error!("Failed to get JavaVM reference"); + return *JObject::null() as jlong; + } + }; + let callback_obj = match env.new_global_ref(obj) { + Ok(cb) => cb, + Err(err) => { + error!("Couldn't create global ref for callback obj: {:?}", err); + return *JObject::null() as jlong; + } + }; + let class_loader_obj = match get_class_loader_obj(&env) { + Ok(cl) => cl, + Err(err) => { + error!("Couldn't get class loader obj: {:?}", err); + return *JObject::null() as jlong; + } + }; if let Some(uwb_service) = UwbServiceBuilder::new() - .callback_builder(UwbServiceCallbackBuilderImpl {}) + .callback_builder(UwbServiceCallbackBuilderImpl::new(vm, callback_obj, class_loader_obj)) .uci_hal(UciHalAndroid::new("default")) .uci_logger(UciLoggerNull::default()) .build() @@ -402,3 +434,21 @@ fn object_result_helper(result: Result<jobject>, function_name: &str) -> jobject } } } + +/// 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 uwb_service_core_class = env.find_class("com/android/server/uwb/indev/UwbServiceCore")?; + let uwb_service_core_obj = env.get_object_class(uwb_service_core_class)?; + let get_class_loader_method = + env.get_method_id(uwb_service_core_obj, "getClassLoader", "()Ljava/lang/ClassLoader;")?; + let class_loader = env.call_method_unchecked( + uwb_service_core_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)?) +} diff --git a/indev_uwb_adaptation/jni/src/callback.rs b/indev_uwb_adaptation/jni/src/callback.rs index d94b28de..706af6fe 100644 --- a/indev_uwb_adaptation/jni/src/callback.rs +++ b/indev_uwb_adaptation/jni/src/callback.rs @@ -15,26 +15,129 @@ //! Defines the callback structure that knows how to communicate with Java UWB service //! when certain events happen such as device reset, data or notification received //! and status changes. + +use std::collections::HashMap; + +use jni::objects::{GlobalRef, JClass, JMethodID, JObject, JValue}; +use jni::signature::TypeSignature; +use jni::{AttachGuard, JavaVM}; +use log::error; + use uwb_core::params::uci_packets::{DeviceState, ReasonCode, SessionId, SessionState}; use uwb_core::service::{UwbServiceCallback, UwbServiceCallbackBuilder}; use uwb_core::uci::SessionRangeData; -pub struct UwbServiceCallbackBuilderImpl {} +use crate::error::{Error, Result}; +use crate::object_mapping::{SessionRangeDataWithEnv, UwbRangingDataJni}; + +pub struct UwbServiceCallbackBuilderImpl { + vm: &'static JavaVM, + callback_obj: GlobalRef, + class_loader_obj: GlobalRef, +} +impl UwbServiceCallbackBuilderImpl { + pub fn new(vm: &'static JavaVM, callback_obj: GlobalRef, class_loader_obj: GlobalRef) -> Self { + Self { vm, callback_obj, class_loader_obj } + } +} impl UwbServiceCallbackBuilder<UwbServiceCallbackImpl> for UwbServiceCallbackBuilderImpl { fn build(self) -> Option<UwbServiceCallbackImpl> { - Some(UwbServiceCallbackImpl {}) + let env = match self.vm.attach_current_thread() { + Ok(env) => env, + Err(err) => { + error!("Failed to attached callback thread to JVM: {:?}", err); + return None; + } + }; + Some(UwbServiceCallbackImpl::new(env, self.class_loader_obj, self.callback_obj)) + } +} + +pub struct UwbServiceCallbackImpl { + env: AttachGuard<'static>, + class_loader_obj: GlobalRef, + callback_obj: GlobalRef, + jmethod_id_map: HashMap<String, JMethodID<'static>>, + jclass_map: HashMap<String, GlobalRef>, +} + +impl UwbServiceCallbackImpl { + pub fn new( + env: AttachGuard<'static>, + class_loader_obj: GlobalRef, + callback_obj: GlobalRef, + ) -> Self { + Self { + env, + class_loader_obj, + callback_obj, + jmethod_id_map: HashMap::new(), + jclass_map: HashMap::new(), + } + } + + fn find_local_class(&mut self, class_name: &str) -> Result<GlobalRef> { + let jclass = match self.jclass_map.get(class_name) { + Some(jclass) => jclass.clone(), + None => { + let class_value = self + .env + .call_method( + self.class_loader_obj.as_obj(), + "findClass", + "(Ljava/lang/String;)Ljava/lang/Class;", + &[JValue::Object(JObject::from(self.env.new_string(class_name)?))], + )? + .l()?; + + let jclass_global_ref = self.env.new_global_ref(JClass::from(class_value))?; + self.jclass_map.insert(class_name.to_owned(), jclass_global_ref.clone()); + jclass_global_ref + } + }; + + Ok(jclass) + } + + fn cached_jni_call(&mut self, name: &str, sig: &str, args: &[JValue]) -> Result<()> { + let type_signature = TypeSignature::from_str(sig)?; + if type_signature.args.len() != args.len() { + return Err(Error::Jni(jni::errors::Error::InvalidArgList(type_signature))); + } + let name_signature = name.to_owned() + sig; + let jmethod_id = match self.jmethod_id_map.get(&name_signature) { + Some(jmethod_id) => jmethod_id.to_owned(), + None => { + let jmethod_id = self.env.get_method_id(self.callback_obj.as_obj(), name, sig)?; + self.jmethod_id_map.insert(name_signature.clone(), jmethod_id); + jmethod_id.to_owned() + } + }; + + self.env.call_method_unchecked( + self.callback_obj.as_obj(), + jmethod_id, + type_signature.ret, + args, + )?; + Ok(()) } } -// TODO(b/244785972): implement this with caching -pub struct UwbServiceCallbackImpl {} impl UwbServiceCallback for UwbServiceCallbackImpl { fn on_service_reset(&mut self, success: bool) { - todo!(); // call java + let result = + self.cached_jni_call("onServiceResetReceived", "(Z)V", &[JValue::Bool(success as u8)]); + result_helper("on_service_reset", result); } fn on_uci_device_status_changed(&mut self, state: DeviceState) { - todo!(); // call java + let result = self.cached_jni_call( + "onDeviceStatusNotificationReceived", + "(I)V", + &[JValue::Int(state as i32)], + ); + result_helper("on_uci_device_status_changed", result); } fn on_session_state_changed( @@ -43,14 +146,94 @@ impl UwbServiceCallback for UwbServiceCallbackImpl { session_state: SessionState, reason_code: ReasonCode, ) { - todo!(); // call java + let result = self.cached_jni_call( + "onSessionStatusNotificationReceived", + "(JII)V", + &[ + JValue::Long(session_id as i64), + JValue::Int(session_state as i32), + JValue::Int(reason_code as i32), + ], + ); + result_helper("on_session_state_changed", result); } fn on_range_data_received(&mut self, session_id: SessionId, range_data: SessionRangeData) { - todo!(); // call java + let uwb_ranging_data_obj = + match self.find_local_class("com/android/server/uwb/data/UwbRangingData") { + Ok(ranging_class) => ranging_class, + Err(err) => { + error!("UWB Callback Service: Failed to load uwb ranging jclass: {:?}", err); + return; + } + }; + let uwb_two_way_measurement_obj = match self + .find_local_class("com/android/server/uwb/data/UwbTwoWayMeasurement") + { + Ok(measurement_class) => measurement_class, + Err(err) => { + error!("UWB Callback Service: Failed to load uwb measurement jclass: {:?}", err); + return; + } + }; + + let uwb_ranging_data_jclass = JClass::from(uwb_ranging_data_obj.as_obj()); + let uwb_two_way_measurement_jclass = JClass::from(uwb_two_way_measurement_obj.as_obj()); + let session_range_with_env = SessionRangeDataWithEnv::new( + *self.env, + uwb_ranging_data_jclass, + uwb_two_way_measurement_jclass, + range_data, + ); + let uwb_ranging_data_jni = match UwbRangingDataJni::try_from(session_range_with_env) { + Ok(v) => v, + Err(err) => { + error!("UWB Service Callback: Failed to convert UwbRangingDataJni: {:?}", err); + return; + } + }; + + let uwb_raning_data_jobject = uwb_ranging_data_jni.jni_context.obj; + + let result = self.cached_jni_call( + "onRangeDataNotificationReceived", + "(JLcom/android/server/uwb/data/UwbRangingData;)V", + &[JValue::Long(session_id as i64), JValue::Object(uwb_raning_data_jobject)], + ); + result_helper("on_range_data_received", result); } fn on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>) { - todo!(); // call java + let payload_i8 = payload.iter().map(|b| b.to_owned() as i8).collect::<Vec<_>>(); + let payload_jbyte_array = match self.env.new_byte_array(payload_i8.len() as i32) { + Ok(p) => p, + Err(err) => { + error!("Uwb Service Callback: Failed to create jbyteArray: {:?}", err); + return; + } + }; + if let Err(err) = + self.env.set_byte_array_region(payload_jbyte_array, 0, payload_i8.as_slice()) + { + error!("UWB Service Callback: Failed to set byte array: {:?}", err); + return; + } + let result = self.cached_jni_call( + "onVendorUciNotificationReceived", + "(II[B)V", + &[ + JValue::Int(gid as i32), + JValue::Int(oid as i32), + JValue::Object(JObject::from(payload_jbyte_array)), + ], + ); + + result_helper("on_range_data_received", result); + } +} + +fn result_helper(function_name: &str, result: Result<()>) { + if let Err(err) = result { + error!("UWB Service Callback: {} failed: {:?}", function_name, err); } } diff --git a/indev_uwb_adaptation/jni/src/lib.rs b/indev_uwb_adaptation/jni/src/lib.rs index cabffcba..e2bbda3f 100644 --- a/indev_uwb_adaptation/jni/src/lib.rs +++ b/indev_uwb_adaptation/jni/src/lib.rs @@ -22,5 +22,6 @@ mod callback; mod context; mod error; mod object_mapping; +mod unique_jvm; pub mod api; diff --git a/indev_uwb_adaptation/jni/src/object_mapping.rs b/indev_uwb_adaptation/jni/src/object_mapping.rs index 46e9ee55..b94d6edb 100644 --- a/indev_uwb_adaptation/jni/src/object_mapping.rs +++ b/indev_uwb_adaptation/jni/src/object_mapping.rs @@ -14,7 +14,7 @@ //! This module contains structures and methods for converting //! Java objects into native rust objects and vice versa. -use jni::objects::{JObject, JValue}; +use jni::objects::{JClass, JObject, JValue}; use jni::sys::{jbyteArray, jint}; use jni::JNIEnv; use num_traits::FromPrimitive; @@ -28,7 +28,11 @@ use uwb_core::params::{ RangeDataNtfConfig, RangingRoundUsage, RframeConfig, StsConfig, StsLength, TxAdaptivePayloadPower, UwbAddress, UwbChannel, }; -use uwb_uci_packets::{Controlee, PowerStats}; +use uwb_core::uci::{RangingMeasurements, SessionRangeData}; +use uwb_uci_packets::{ + Controlee, ExtendedAddressTwoWayRangingMeasurement, MacAddressIndicator, PowerStats, + ShortAddressTwoWayRangingMeasurement, StatusCode, +}; use crate::context::JniContext; use crate::error::{Error, Result}; @@ -467,6 +471,235 @@ impl<'a> TryFrom<PowerStatsWithEnv<'a>> for PowerStatsJni<'a> { } } +pub struct SessionRangeDataWithEnv<'a> { + env: JNIEnv<'a>, + uwb_ranging_data_jclass: JClass<'a>, + uwb_two_way_measurement_jclass: JClass<'a>, + session_range_data: SessionRangeData, +} + +impl<'a> SessionRangeDataWithEnv<'a> { + pub fn new( + env: JNIEnv<'a>, + uwb_ranging_data_jclass: JClass<'a>, + uwb_two_way_measurement_jclass: JClass<'a>, + session_range_data: SessionRangeData, + ) -> Self { + Self { env, uwb_ranging_data_jclass, uwb_two_way_measurement_jclass, session_range_data } + } +} +pub struct UwbRangingDataJni<'a> { + pub jni_context: JniContext<'a>, +} + +impl<'a> TryFrom<SessionRangeDataWithEnv<'a>> for UwbRangingDataJni<'a> { + type Error = Error; + fn try_from(data_obj: SessionRangeDataWithEnv<'a>) -> Result<Self> { + let (mac_address_indicator, measurements_size) = match data_obj + .session_range_data + .ranging_measurements + { + RangingMeasurements::Short(ref m) => (MacAddressIndicator::ShortAddress, m.len()), + RangingMeasurements::Extended(ref m) => (MacAddressIndicator::ExtendedAddress, m.len()), + }; + let measurements_jni = UwbTwoWayMeasurementJni::try_from(RangingMeasurementsWithEnv::new( + data_obj.env, + data_obj.uwb_two_way_measurement_jclass, + data_obj.session_range_data.ranging_measurements, + ))?; + let ranging_data_jni = data_obj.env.new_object( + data_obj.uwb_ranging_data_jclass, + "(JJIJIII[Lcom/android/server/uwb/data/UwbTwoWayMeasurement;)V", + &[ + JValue::Long(data_obj.session_range_data.sequence_number as i64), + JValue::Long(data_obj.session_range_data.session_id as i64), + JValue::Int(data_obj.session_range_data.rcr_indicator as i32), + JValue::Long(data_obj.session_range_data.current_ranging_interval_ms as i64), + JValue::Int(data_obj.session_range_data.ranging_measurement_type as i32), + JValue::Int(mac_address_indicator as i32), + JValue::Int(measurements_size as i32), + JValue::Object(measurements_jni.jni_context.obj), + ], + )?; + + Ok(UwbRangingDataJni { jni_context: JniContext::new(data_obj.env, ranging_data_jni) }) + } +} + +// Byte size of mac address length: +const SHORT_MAC_ADDRESS_LEN: i32 = 2; +const EXTENDED_MAC_ADDRESS_LEN: i32 = 8; + +enum MacAddress { + Short(u16), + Extended(u64), +} +impl MacAddress { + fn into_ne_bytes_i8(self) -> Vec<i8> { + match self { + MacAddress::Short(val) => val.to_ne_bytes().into_iter().map(|b| b as i8).collect(), + MacAddress::Extended(val) => val.to_ne_bytes().into_iter().map(|b| b as i8).collect(), + } + } +} +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, +} + +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), + } + } +} + +pub struct RangingMeasurementsWithEnv<'a> { + env: JNIEnv<'a>, + uwb_two_way_measurement_jclass: JClass<'a>, + ranging_measurements: RangingMeasurements, +} +impl<'a> RangingMeasurementsWithEnv<'a> { + pub fn new( + env: JNIEnv<'a>, + uwb_two_way_measurement_jclass: JClass<'a>, + ranging_measurements: RangingMeasurements, + ) -> Self { + Self { env, uwb_two_way_measurement_jclass, ranging_measurements } + } +} +pub struct UwbTwoWayMeasurementJni<'a> { + pub jni_context: JniContext<'a>, +} + +impl<'a> TryFrom<RangingMeasurementsWithEnv<'a>> for UwbTwoWayMeasurementJni<'a> { + type Error = Error; + fn try_from(measurements_obj: RangingMeasurementsWithEnv<'a>) -> Result<Self> { + let (measurements_vec, byte_arr_size) = match measurements_obj.ranging_measurements { + RangingMeasurements::Short(m) => ( + m.into_iter().map(TwoWayRangingMeasurement::from).collect::<Vec<_>>(), + SHORT_MAC_ADDRESS_LEN, + ), + RangingMeasurements::Extended(m) => ( + m.into_iter().map(TwoWayRangingMeasurement::from).collect::<Vec<_>>(), + EXTENDED_MAC_ADDRESS_LEN, + ), + }; + let address_jbytearray = measurements_obj.env.new_byte_array(byte_arr_size)?; + let zero_initiated_measurement_jobject = measurements_obj.env.new_object( + measurements_obj.uwb_two_way_measurement_jclass, + "([BIIIIIIIIIIIII)V", + &[ + JValue::Object(JObject::from(address_jbytearray)), + 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), + ], + )?; + let measurements_array_jobject = measurements_obj.env.new_object_array( + measurements_vec.len() as i32, + measurements_obj.uwb_two_way_measurement_jclass, + zero_initiated_measurement_jobject, + )?; + for (i, measurement) in measurements_vec.into_iter().enumerate() { + let mac_address_bytes = measurement.mac_address.into_ne_bytes_i8(); + let mac_address_bytes_jbytearray = + measurements_obj.env.new_byte_array(byte_arr_size)?; + measurements_obj.env.set_byte_array_region( + mac_address_bytes_jbytearray, + 0, + mac_address_bytes.as_slice(), + )?; + let measurement_jobject = measurements_obj.env.new_object( + measurements_obj.uwb_two_way_measurement_jclass, + "([BIIIIIIIIIIIII)V", + &[ + JValue::Object(JObject::from(mac_address_bytes_jbytearray)), + JValue::Int(measurement.status as i32), + 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), + ], + )?; + measurements_obj.env.set_object_array_element( + measurements_array_jobject, + i as i32, + measurement_jobject, + )?; + } + + Ok(UwbTwoWayMeasurementJni { + jni_context: JniContext::new(measurements_obj.env, measurements_array_jobject.into()), + }) + } +} + /// Boilerplate code macro for defining int getters macro_rules! int_field { ($field: ident, $ret: ty, $method: expr) => { diff --git a/indev_uwb_adaptation/jni/src/unique_jvm.rs b/indev_uwb_adaptation/jni/src/unique_jvm.rs new file mode 100644 index 00000000..6d52446e --- /dev/null +++ b/indev_uwb_adaptation/jni/src/unique_jvm.rs @@ -0,0 +1,43 @@ +// 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. + +//! takes a JavaVM to a static reference. +//! +//! JavaVM is shared as multiple JavaVM within a single process is not allowed +//! per [JNI spec](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html) +//! The unique JavaVM need to be shared over (potentially) different threads. + +use jni::JavaVM; +use std::sync::Once; + +static mut JVM: Option<JavaVM> = None; +static INIT: Once = Once::new(); +/// set_once sets the unique JavaVM that can be then accessed using get_static_ref() +/// +/// The function shall only be called once. +pub(crate) fn set_once(jvm: JavaVM) { + // Safety: follows [this pattern](https://doc.rust-lang.org/std/sync/struct.Once.html). + // Modification to static mut is nested inside call_once. + unsafe { + INIT.call_once(|| { + JVM = Some(jvm); + }); + } +} +/// Gets a 'static reference to the unique JavaVM. Returns None if set_once() was never called. +pub(crate) fn get_static_ref() -> Option<&'static JavaVM> { + // Safety: follows [this pattern](https://doc.rust-lang.org/std/sync/struct.Once.html). + // Modification to static mut is nested inside call_once. + unsafe { JVM.as_ref() } +} |