blob: b2aaed283412e5a07fbaefbb373125b0117e979a [file] [log] [blame]
/*
* Copyright (C) 2021 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.
*/
use crate::uci::uci_hrcv::UciNotification;
use jni::errors::{Error, Result};
use jni::objects::{GlobalRef, JClass, JMethodID, JObject, JValue, JValue::Void};
use jni::signature::JavaType;
use jni::sys::jobjectArray;
use jni::{AttachGuard, JNIEnv, JavaVM};
use log::error;
use num_traits::ToPrimitive;
use std::convert::TryInto;
use std::vec::Vec;
use uwb_uci_packets::{
DeviceStatusNtfPacket, ExtendedAddressTwoWayRangingMeasurement,
ExtendedMacTwoWayRangeDataNtfPacket, GenericErrorPacket, RangeDataNtfPacket,
SessionStatusNtfPacket, SessionUpdateControllerMulticastListNtfPacket,
ShortAddressTwoWayRangingMeasurement, ShortMacTwoWayRangeDataNtfPacket,
};
const UWB_RANGING_DATA_CLASS: &str = "com/android/uwb/data/UwbRangingData";
const UWB_TWO_WAY_MEASUREMENT_CLASS: &str = "com/android/uwb/data/UwbTwoWayMeasurement";
const MULTICAST_LIST_UPDATE_STATUS_CLASS: &str =
"com/android/uwb/data/UwbMulticastListUpdateStatus";
const SHORT_MAC_ADDRESS_LEN: usize = 2;
const EXTENDED_MAC_ADDRESS_LEN: usize = 8;
// TODO: Reconsider the best way to cache the JNIEnv. We currently attach and detach for every
// call, which the documentation warns could be expensive. We could attach the thread permanently,
// but that would not allow us to detach when we drop this structure. We could cache the
// AttachGuard in the EventManager, but it is not Send, so we should wait to see how this is used
// and how expensive the current approach is. We can call JavaVM's get_env method if we're already
// attached.
// TODO: We could consider caching the method ids rather than recomputing them each time at the cost
// of less safety.
// Manages calling Java callbacks through the JNI.
pub struct EventManager {
jvm: JavaVM,
obj: GlobalRef,
// cache used to lookup uwb classes in callback.
class_loader_obj: GlobalRef,
}
impl EventManager {
/// Creates a new EventManager.
pub fn new(env: JNIEnv, obj: JObject) -> Result<Self> {
let jvm = env.get_java_vm()?;
let obj = env.new_global_ref(obj)?;
let class_loader_obj = EventManager::get_classloader_obj(&env)?;
let class_loader_obj = env.new_global_ref(class_loader_obj)?;
Ok(EventManager { jvm, obj, class_loader_obj })
}
fn get_classloader_obj<'a>(env: &'a JNIEnv) -> Result<JObject<'a>> {
// Use UwbRangingData class to find the classloader used by the java service.
let ranging_data_class = env.find_class(&UWB_RANGING_DATA_CLASS)?;
let ranging_data_class_class = env.get_object_class(ranging_data_class)?;
let get_class_loader_method = env.get_method_id(
ranging_data_class_class,
"getClassLoader",
"()Ljava/lang/ClassLoader;",
)?;
let class_loader = env.call_method_unchecked(
ranging_data_class,
get_class_loader_method,
JavaType::Object("java/lang/ClassLoader".into()),
&[Void],
)?;
class_loader.l()
}
fn find_class<'a>(&'a self, env: &'a JNIEnv, class_name: &'a str) -> Result<JClass<'a>> {
let class_value = env.call_method(
self.class_loader_obj.as_obj(),
"findClass",
"(Ljava/lang/String;)Ljava/lang/Class;",
&[JValue::Object(JObject::from(env.new_string(class_name)?))],
)?;
class_value.l().map(|value| JClass::from(value))
}
fn handle_device_status_notification_received(
&self,
env: &JNIEnv,
data: DeviceStatusNtfPacket,
) -> Result<()> {
let state =
data.get_device_state().to_i32().expect("Failed converting device_state to i32");
env.call_method(
self.obj.as_obj(),
"onDeviceStatusNotificationReceived",
"(I)V",
&[JValue::Int(state)],
)
.map(|_| ()) // drop void method return
}
pub fn device_status_notification_received(&self, data: DeviceStatusNtfPacket) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let result = self.handle_device_status_notification_received(&env, data);
self.clear_exception(env);
result
}
fn handle_session_status_notification_received(
&self,
env: &JNIEnv,
data: SessionStatusNtfPacket,
) -> Result<()> {
let session_id =
data.get_session_id().to_i64().expect("Failed converting session_id to i64");
let state =
data.get_session_state().to_i32().expect("Failed converting session_state to i32");
let reason_code =
data.get_reason_code().to_i32().expect("Failed coverting reason_code to i32");
env.call_method(
self.obj.as_obj(),
"onSessionStatusNotificationReceived",
"(JII)V",
&[JValue::Long(session_id), JValue::Int(state), JValue::Int(reason_code)],
)
.map(|_| ()) // drop void method return
}
pub fn session_status_notification_received(&self, data: SessionStatusNtfPacket) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let result = self.handle_session_status_notification_received(&env, data);
self.clear_exception(env);
result
}
fn handle_core_generic_error_notification_received(
&self,
env: &JNIEnv,
data: GenericErrorPacket,
) -> Result<()> {
let status = data.get_status().to_i32().expect("Failed converting status to i32");
env.call_method(
self.obj.as_obj(),
"onCoreGenericErrorNotificationReceived",
"(I)V",
&[JValue::Int(status)],
)
.map(|_| ()) // drop void method return
}
pub fn core_generic_error_notification_received(&self, data: GenericErrorPacket) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let result = self.handle_core_generic_error_notification_received(&env, data);
self.clear_exception(env);
result
}
fn create_zeroed_two_way_measurement_java<'a>(
env: &'a JNIEnv,
two_way_measurement_class: JClass,
mac_address_java: jobjectArray,
) -> Result<JObject<'a>> {
env.new_object(
two_way_measurement_class,
"([BIIIIIIIIIIII)V",
&[
JValue::Object(JObject::from(mac_address_java)),
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),
],
)
}
fn create_short_mac_two_way_measurement_java<'a>(
env: &'a JNIEnv,
two_way_measurement_class: JClass,
two_way_measurement: &'a ShortAddressTwoWayRangingMeasurement,
) -> Result<JObject<'a>> {
let mac_address_arr = two_way_measurement.mac_address.to_ne_bytes();
let mac_address_java = env.new_byte_array(
SHORT_MAC_ADDRESS_LEN.to_i32().expect("Failed converting mac address len to i32"),
)?;
// Convert from [u8] to [i8] since java does not support unsigned byte.
let mac_address_arr_i8 = mac_address_arr.map(|x| x as i8);
env.set_byte_array_region(mac_address_java, 0, &mac_address_arr_i8)?;
env.new_object(
two_way_measurement_class,
"([BIIIIIIIIIIII)V",
&[
JValue::Object(JObject::from(mac_address_java)),
JValue::Int(
two_way_measurement.status.to_i32().expect("Failed converting status to i32"),
),
JValue::Int(
two_way_measurement.nlos.to_i32().expect("Failed converting nlos to i32"),
),
JValue::Int(
two_way_measurement
.distance
.to_i32()
.expect("Failed converting distance to i32"),
),
JValue::Int(
two_way_measurement
.aoa_azimuth
.to_i32()
.expect("Failed converting aoa azimuth to i32"),
),
JValue::Int(
two_way_measurement
.aoa_azimuth_fom
.to_i32()
.expect("Failed converting aoa azimuth fom to i32"),
),
JValue::Int(
two_way_measurement
.aoa_elevation
.to_i32()
.expect("Failed converting aoa elevation to i32"),
),
JValue::Int(
two_way_measurement
.aoa_elevation_fom
.to_i32()
.expect("Failed converting aoa elevaion fom to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_azimuth
.to_i32()
.expect("Failed converting dest aoa azimuth to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_azimuth_fom
.to_i32()
.expect("Failed converting dest aoa azimuth fom to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_elevation
.to_i32()
.expect("Failed converting dest aoa elevation to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_elevation_fom
.to_i32()
.expect("Failed converting dest aoa elevation azimuth to i32"),
),
JValue::Int(
two_way_measurement
.slot_index
.to_i32()
.expect("Failed converting slot index to i32"),
),
],
)
}
fn create_extended_mac_two_way_measurement_java<'a>(
env: &'a JNIEnv,
two_way_measurement_class: JClass,
two_way_measurement: &'a ExtendedAddressTwoWayRangingMeasurement,
) -> Result<JObject<'a>> {
let mac_address_arr = two_way_measurement.mac_address.to_ne_bytes();
let mac_address_java = env.new_byte_array(
EXTENDED_MAC_ADDRESS_LEN.to_i32().expect("Failed converting mac address len to i32"),
)?;
// Convert from [u8] to [i8] since java does not support unsigned byte.
let mac_address_arr_i8 = mac_address_arr.map(|x| x as i8);
env.set_byte_array_region(mac_address_java, 0, &mac_address_arr_i8)?;
env.new_object(
two_way_measurement_class,
"([BIIIIIIIIIIII)V",
&[
JValue::Object(JObject::from(mac_address_java)),
JValue::Int(
two_way_measurement.status.to_i32().expect("Failed converting status to i32"),
),
JValue::Int(
two_way_measurement.nlos.to_i32().expect("Failed converting nlos to i32"),
),
JValue::Int(
two_way_measurement
.distance
.to_i32()
.expect("Failed converting distance to i32"),
),
JValue::Int(
two_way_measurement
.aoa_azimuth
.to_i32()
.expect("Failed converting aoa azimuth to i32"),
),
JValue::Int(
two_way_measurement
.aoa_azimuth_fom
.to_i32()
.expect("Failed converting aoa azimuth fom to i32"),
),
JValue::Int(
two_way_measurement
.aoa_elevation
.to_i32()
.expect("Failed converting aoa elevation to i32"),
),
JValue::Int(
two_way_measurement
.aoa_elevation_fom
.to_i32()
.expect("Failed converting aoa elevaion fom to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_azimuth
.to_i32()
.expect("Failed converting dest aoa azimuth to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_azimuth_fom
.to_i32()
.expect("Failed converting dest aoa azimuth fom to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_elevation
.to_i32()
.expect("Failed converting dest aoa elevation to i32"),
),
JValue::Int(
two_way_measurement
.aoa_destination_elevation_fom
.to_i32()
.expect("Failed converting dest aoa elevation azimuth to i32"),
),
JValue::Int(
two_way_measurement
.slot_index
.to_i32()
.expect("Failed converting slot index to i32"),
),
],
)
}
fn create_range_data_java<'a>(
&'a self,
env: &'a JNIEnv,
data: RangeDataNtfPacket,
two_way_measurements_java: jobjectArray,
num_two_way_measurements: i32,
) -> Result<JObject<'a>> {
let ranging_data_class = self.find_class(env, &UWB_RANGING_DATA_CLASS)?;
env.new_object(
ranging_data_class,
"(JJIJIII[Lcom/android/uwb/data/UwbTwoWayMeasurement;)V",
&[
JValue::Long(
data.get_sequence_number().to_i64().expect("Failed converting seq num to i64"),
),
JValue::Long(
data.get_session_id().to_i64().expect("Failed converting session id to i64"),
),
JValue::Int(
data.get_rcr_indicator()
.to_i32()
.expect("Failed converting rcr indicator to i32"),
),
JValue::Long(
data.get_current_ranging_interval()
.to_i64()
.expect("Failed converting curr ranging interval to i32"),
),
JValue::Int(
data.get_ranging_measurement_type()
.to_i32()
.expect("Failed converting ranging measurement type to i32"),
),
JValue::Int(
data.get_mac_address_indicator()
.to_i32()
.expect("Failed converting mac address indicator to i32"),
),
JValue::Int(num_two_way_measurements),
JValue::Object(JObject::from(two_way_measurements_java)),
],
)
}
fn handle_short_range_data_notification(
&self,
env: &JNIEnv,
data: ShortMacTwoWayRangeDataNtfPacket,
) -> Result<()> {
let two_way_measurement_class = self.find_class(&env, &UWB_TWO_WAY_MEASUREMENT_CLASS)?;
let two_way_measurement_initial_java =
EventManager::create_zeroed_two_way_measurement_java(
&env,
two_way_measurement_class,
env.new_byte_array(
EXTENDED_MAC_ADDRESS_LEN
.to_i32()
.expect("Failed converting mac address len to i32"),
)?,
)?;
let num_two_way_measurements: i32 = data
.get_two_way_ranging_measurements()
.len()
.to_i32()
.expect("Failed converting len to i32");
let two_way_measurements_java = env.new_object_array(
num_two_way_measurements,
two_way_measurement_class,
two_way_measurement_initial_java,
)?;
for (i, two_way_measurement) in data.get_two_way_ranging_measurements().iter().enumerate() {
let two_way_measurement_java = EventManager::create_short_mac_two_way_measurement_java(
&env,
two_way_measurement_class,
two_way_measurement,
)?;
env.set_object_array_element(
two_way_measurements_java,
i.to_i32().expect("Failed converting idx to i32"),
two_way_measurement_java,
)?
}
let ranging_data_java = self.create_range_data_java(
&env,
data.into(),
two_way_measurements_java,
num_two_way_measurements,
)?;
env.call_method(
self.obj.as_obj(),
"onRangeDataNotificationReceived",
"(Lcom/android/uwb/data/UwbRangingData;)V",
&[JValue::Object(JObject::from(ranging_data_java))],
)
.map(|_| ()) // drop void method return
}
pub fn short_range_data_notification(
&self,
data: ShortMacTwoWayRangeDataNtfPacket,
) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let result = self.handle_short_range_data_notification(&env, data);
self.clear_exception(env);
result
}
fn handle_extended_range_data_notification(
&self,
env: &JNIEnv,
data: ExtendedMacTwoWayRangeDataNtfPacket,
) -> Result<()> {
let two_way_measurement_class = self.find_class(&env, &UWB_TWO_WAY_MEASUREMENT_CLASS)?;
let two_way_measurement_initial_java =
EventManager::create_zeroed_two_way_measurement_java(
&env,
two_way_measurement_class,
env.new_byte_array(
EXTENDED_MAC_ADDRESS_LEN
.to_i32()
.expect("Failed converting mac address len to i32"),
)?,
)?;
let num_two_way_measurements: i32 = data
.get_two_way_ranging_measurements()
.len()
.to_i32()
.expect("Failed converting len to i32");
let two_way_measurements_java = env.new_object_array(
num_two_way_measurements,
two_way_measurement_class,
two_way_measurement_initial_java,
)?;
for (i, two_way_measurement) in data.get_two_way_ranging_measurements().iter().enumerate() {
let two_way_measurement_java =
EventManager::create_extended_mac_two_way_measurement_java(
&env,
two_way_measurement_class,
two_way_measurement,
)?;
env.set_object_array_element(
two_way_measurements_java,
i.to_i32().expect("Failed converting idx to i32"),
two_way_measurement_java,
)?;
}
let ranging_data_java = self.create_range_data_java(
&env,
data.into(),
two_way_measurements_java,
num_two_way_measurements,
)?;
env.call_method(
self.obj.as_obj(),
"onRangeDataNotificationReceived",
"(Lcom/android/uwb/data/UwbRangingData;)V",
&[JValue::Object(JObject::from(ranging_data_java))],
)
.map(|_| ()) // drop void method return
}
pub fn extended_range_data_notification(
&self,
data: ExtendedMacTwoWayRangeDataNtfPacket,
) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let result = self.handle_extended_range_data_notification(&env, data);
self.clear_exception(env);
result
}
pub fn handle_session_update_controller_multicast_list_notification(
&self,
env: &JNIEnv,
data: SessionUpdateControllerMulticastListNtfPacket,
) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let uwb_multicast_update_class =
self.find_class(&env, &MULTICAST_LIST_UPDATE_STATUS_CLASS)?;
let controlee_status = data.get_controlee_status();
let count: i32 =
controlee_status.len().try_into().expect("Failed to convert controlee status length");
let mut mac_address_list: Vec<i32> = Vec::new();
let mut subsession_id_list: Vec<i64> = Vec::new();
let mut status_list: Vec<i32> = Vec::new();
for iter in controlee_status {
mac_address_list.push(iter.mac_address.into());
subsession_id_list.push(iter.subsession_id.into());
status_list.push(iter.status.into());
}
let mut mac_address_jintarray = env.new_int_array(count)?;
env.set_int_array_region(mac_address_jintarray, 0, mac_address_list.as_ref());
let mut subession_id_jlongarray = env.new_long_array(count)?;
env.set_long_array_region(subession_id_jlongarray, 0, subsession_id_list.as_ref());
let mut status_jintarray = env.new_int_array(count)?;
env.set_int_array_region(status_jintarray, 0, status_list.as_ref());
let uwb_multicast_update_object = env.new_object(
uwb_multicast_update_class,
"(JII[I[J[I)V",
&[
JValue::Long(
data.get_session_id().try_into().expect("Could not convert session_id"),
),
JValue::Int(
data.get_remaining_multicast_list_size()
.try_into()
.expect("Could not convert remaining multicast list size"),
),
JValue::Int(count.try_into().expect("Could not convert count")),
JValue::Object(JObject::from(mac_address_jintarray)),
JValue::Object(JObject::from(subession_id_jlongarray)),
JValue::Object(JObject::from(status_jintarray)),
],
)?;
env.call_method(
self.obj.as_obj(),
"onMulticastListUpdateNotificationReceived",
"(Lcom/android/uwb/data/UwbMulticastListUpdateStatus;)V",
&[JValue::Object(JObject::from(uwb_multicast_update_object))],
)
.map(|_| ()) // drop void method return
}
pub fn session_update_controller_multicast_list_notification(
&self,
data: SessionUpdateControllerMulticastListNtfPacket,
) -> Result<()> {
let env = self.jvm.attach_current_thread()?;
let result = self.handle_session_update_controller_multicast_list_notification(&env, data);
self.clear_exception(env);
result
}
// Attempts to clear an exception. If we do not do this, the exception continues being thrown
// when the control flow returns to Java. We discard errors here (after logging them) rather
// than propagating them to the caller since there's nothing they can do with that information.
fn clear_exception(&self, env: AttachGuard) {
match env.exception_check() {
Ok(true) => match env.exception_clear() {
Ok(()) => {} // We successfully cleared the exception.
Err(e) => error!("Error clearing JNI exception: {:?}", e),
},
Ok(false) => {} // No exception found.
Err(e) => error!("Error checking JNI exception: {:?}", e),
}
}
}