summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnte Culo <cante@google.com>2022-10-12 19:25:05 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2022-10-12 19:25:05 +0000
commit453fc29faa21de9b2cdd97b1b8cfe9638bb208f1 (patch)
treefccc581cba50f7fd0714bd334e64d9917ecefc29
parent44cb4d54b2e6ee21fd26e4f28202f9ad6307c985 (diff)
parenta585d0d9c958ca7075a7cfebcd14ef9ce22efd00 (diff)
downloadUwb-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.java4
-rw-r--r--indev_uwb_adaptation/java/com/android/server/uwb/indev/UwbServiceCore.java6
-rw-r--r--indev_uwb_adaptation/jni/src/api.rs58
-rw-r--r--indev_uwb_adaptation/jni/src/callback.rs201
-rw-r--r--indev_uwb_adaptation/jni/src/lib.rs1
-rw-r--r--indev_uwb_adaptation/jni/src/object_mapping.rs237
-rw-r--r--indev_uwb_adaptation/jni/src/unique_jvm.rs43
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() }
+}