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