1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Defines the callback structure that knows how to communicate with Java UWB service
16 //! when certain events happen such as device reset, data or notification received
17 //! and status changes.
18
19 use std::collections::HashMap;
20
21 use jni::objects::{GlobalRef, JClass, JMethodID, JObject, JValue};
22 use jni::signature::TypeSignature;
23 use jni::sys::jvalue;
24 use jni::{AttachGuard, JavaVM};
25 use log::error;
26
27 use uwb_core::params::uci_packets::{DeviceState, ReasonCode, SessionId, SessionState};
28 use uwb_core::service::{UwbServiceCallback, UwbServiceCallbackBuilder};
29 use uwb_core::uci::SessionRangeData;
30
31 use crate::error::{Error, Result};
32 use crate::object_mapping::{SessionRangeDataWithEnv, UwbRangingDataJni};
33
34 pub struct UwbServiceCallbackBuilderImpl {
35 vm: &'static JavaVM,
36 callback_obj: GlobalRef,
37 class_loader_obj: GlobalRef,
38 }
39 impl UwbServiceCallbackBuilderImpl {
new(vm: &'static JavaVM, callback_obj: GlobalRef, class_loader_obj: GlobalRef) -> Self40 pub fn new(vm: &'static JavaVM, callback_obj: GlobalRef, class_loader_obj: GlobalRef) -> Self {
41 Self { vm, callback_obj, class_loader_obj }
42 }
43 }
44 impl UwbServiceCallbackBuilder<UwbServiceCallbackImpl> for UwbServiceCallbackBuilderImpl {
build(self) -> Option<UwbServiceCallbackImpl>45 fn build(self) -> Option<UwbServiceCallbackImpl> {
46 let env = match self.vm.attach_current_thread() {
47 Ok(env) => env,
48 Err(err) => {
49 error!("Failed to attached callback thread to JVM: {:?}", err);
50 return None;
51 }
52 };
53 Some(UwbServiceCallbackImpl::new(env, self.class_loader_obj, self.callback_obj))
54 }
55 }
56
57 pub struct UwbServiceCallbackImpl {
58 env: AttachGuard<'static>,
59 class_loader_obj: GlobalRef,
60 callback_obj: GlobalRef,
61 jmethod_id_map: HashMap<String, JMethodID>,
62 jclass_map: HashMap<String, GlobalRef>,
63 }
64
65 impl UwbServiceCallbackImpl {
new( env: AttachGuard<'static>, class_loader_obj: GlobalRef, callback_obj: GlobalRef, ) -> Self66 pub fn new(
67 env: AttachGuard<'static>,
68 class_loader_obj: GlobalRef,
69 callback_obj: GlobalRef,
70 ) -> Self {
71 Self {
72 env,
73 class_loader_obj,
74 callback_obj,
75 jmethod_id_map: HashMap::new(),
76 jclass_map: HashMap::new(),
77 }
78 }
79
find_local_class(&mut self, class_name: &str) -> Result<GlobalRef>80 fn find_local_class(&mut self, class_name: &str) -> Result<GlobalRef> {
81 let class_name_jobject = *self.env.new_string(class_name)?;
82
83 let jclass = match self.jclass_map.get(class_name) {
84 Some(jclass) => jclass.clone(),
85 None => {
86 let class_value = self
87 .env
88 .call_method(
89 self.class_loader_obj.as_obj(),
90 "findClass",
91 "(Ljava/lang/String;)Ljava/lang/Class;",
92 &[JValue::Object(class_name_jobject)],
93 )?
94 .l()?;
95
96 let jclass_global_ref = self.env.new_global_ref(JClass::from(class_value))?;
97 self.jclass_map.insert(class_name.to_owned(), jclass_global_ref.clone());
98 jclass_global_ref
99 }
100 };
101
102 Ok(jclass)
103 }
104
cached_jni_call(&mut self, name: &str, sig: &str, args: &[jvalue]) -> Result<()>105 fn cached_jni_call(&mut self, name: &str, sig: &str, args: &[jvalue]) -> Result<()> {
106 let type_signature = TypeSignature::from_str(sig)?;
107 if type_signature.args.len() != args.len() {
108 return Err(Error::Jni(jni::errors::Error::InvalidArgList(type_signature)));
109 }
110 let name_signature = name.to_owned() + sig;
111 let jmethod_id = match self.jmethod_id_map.get(&name_signature) {
112 Some(jmethod_id) => jmethod_id.to_owned(),
113 None => {
114 let jmethod_id = self.env.get_method_id(self.callback_obj.as_obj(), name, sig)?;
115 self.jmethod_id_map.insert(name_signature.clone(), jmethod_id);
116 jmethod_id.to_owned()
117 }
118 };
119
120 self.env.call_method_unchecked(
121 self.callback_obj.as_obj(),
122 jmethod_id,
123 type_signature.ret,
124 args,
125 )?;
126 Ok(())
127 }
128 }
129
130 impl UwbServiceCallback for UwbServiceCallbackImpl {
on_service_reset(&mut self, success: bool)131 fn on_service_reset(&mut self, success: bool) {
132 let result = self.cached_jni_call(
133 "onServiceResetReceived",
134 "(Z)V",
135 &[jvalue::from(JValue::Bool(success as u8))],
136 );
137 result_helper("on_service_reset", result);
138 }
139
on_uci_device_status_changed(&mut self, state: DeviceState)140 fn on_uci_device_status_changed(&mut self, state: DeviceState) {
141 let result = self.cached_jni_call(
142 "onDeviceStatusNotificationReceived",
143 "(I)V",
144 &[jvalue::from(JValue::Int(state as i32))],
145 );
146 result_helper("on_uci_device_status_changed", result);
147 }
148
on_session_state_changed( &mut self, session_id: SessionId, session_state: SessionState, reason_code: ReasonCode, )149 fn on_session_state_changed(
150 &mut self,
151 session_id: SessionId,
152 session_state: SessionState,
153 reason_code: ReasonCode,
154 ) {
155 let result = self.cached_jni_call(
156 "onSessionStatusNotificationReceived",
157 "(JII)V",
158 &[
159 jvalue::from(JValue::Long(session_id as i64)),
160 jvalue::from(JValue::Int(session_state as i32)),
161 jvalue::from(JValue::Int(i32::from(reason_code))),
162 ],
163 );
164 result_helper("on_session_state_changed", result);
165 }
166
on_range_data_received(&mut self, session_id: SessionId, range_data: SessionRangeData)167 fn on_range_data_received(&mut self, session_id: SessionId, range_data: SessionRangeData) {
168 let uwb_ranging_data_obj =
169 match self.find_local_class("com/android/server/uwb/data/UwbRangingData") {
170 Ok(ranging_class) => ranging_class,
171 Err(err) => {
172 error!("UWB Callback Service: Failed to load uwb ranging jclass: {:?}", err);
173 return;
174 }
175 };
176 let uwb_two_way_measurement_obj = match self
177 .find_local_class("com/android/server/uwb/data/UwbTwoWayMeasurement")
178 {
179 Ok(measurement_class) => measurement_class,
180 Err(err) => {
181 error!("UWB Callback Service: Failed to load uwb measurement jclass: {:?}", err);
182 return;
183 }
184 };
185
186 let uwb_ranging_data_jclass = JClass::from(uwb_ranging_data_obj.as_obj());
187 let uwb_two_way_measurement_jclass = JClass::from(uwb_two_way_measurement_obj.as_obj());
188 let session_range_with_env = SessionRangeDataWithEnv::new(
189 *self.env,
190 uwb_ranging_data_jclass,
191 uwb_two_way_measurement_jclass,
192 range_data,
193 );
194 let uwb_ranging_data_jni = match UwbRangingDataJni::try_from(session_range_with_env) {
195 Ok(v) => v,
196 Err(err) => {
197 error!("UWB Service Callback: Failed to convert UwbRangingDataJni: {:?}", err);
198 return;
199 }
200 };
201
202 let uwb_raning_data_jobject = uwb_ranging_data_jni.jni_context.obj;
203
204 let result = self.cached_jni_call(
205 "onRangeDataNotificationReceived",
206 "(JLcom/android/server/uwb/data/UwbRangingData;)V",
207 &[
208 jvalue::from(JValue::Long(session_id as i64)),
209 jvalue::from(JValue::Object(uwb_raning_data_jobject)),
210 ],
211 );
212 result_helper("on_range_data_received", result);
213 }
214
on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>)215 fn on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>) {
216 let payload_i8 = payload.iter().map(|b| b.to_owned() as i8).collect::<Vec<_>>();
217 let payload_jbyte_array = match self.env.new_byte_array(payload_i8.len() as i32) {
218 Ok(p) => p,
219 Err(err) => {
220 error!("Uwb Service Callback: Failed to create jbyteArray: {:?}", err);
221 return;
222 }
223 };
224 if let Err(err) =
225 self.env.set_byte_array_region(payload_jbyte_array, 0, payload_i8.as_slice())
226 {
227 error!("UWB Service Callback: Failed to set byte array: {:?}", err);
228 return;
229 }
230
231 // Safety: payload_jbyte_array safely instantiated above.
232 let payload_jobject = unsafe { JObject::from_raw(payload_jbyte_array) };
233
234 let result = self.cached_jni_call(
235 "onVendorUciNotificationReceived",
236 "(II[B)V",
237 &[
238 jvalue::from(JValue::Int(gid as i32)),
239 jvalue::from(JValue::Int(oid as i32)),
240 jvalue::from(JValue::Object(payload_jobject)),
241 ],
242 );
243
244 result_helper("on_range_data_received", result);
245 }
246 }
247
result_helper(function_name: &str, result: Result<()>)248 fn result_helper(function_name: &str, result: Result<()>) {
249 if let Err(err) = result {
250 error!("UWB Service Callback: {} failed: {:?}", function_name, err);
251 }
252 }
253