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 //! This module exposes the native JNI API for communicating with the UWB core service.
16 //!
17 //! Internally after the UWB core service is instantiated, the pointer to the service is saved
18 //! on the calling Java side.
19 use jni::objects::{GlobalRef, JObject, JValue};
20 use jni::signature::ReturnType;
21 use jni::sys::{jboolean, jbyte, jbyteArray, jint, jlong, jobject, jvalue};
22 use jni::JNIEnv;
23 use log::{debug, error};
24 use tokio::runtime::Runtime;
25 
26 use uci_hal_android::uci_hal_android::UciHalAndroid;
27 use uwb_core::params::{AppConfigParams, CountryCode};
28 use uwb_core::service::{default_runtime, UwbService, UwbServiceBuilder};
29 use uwb_core::uci::pcapng_uci_logger_factory::PcapngUciLoggerFactoryBuilder;
30 use uwb_uci_packets::{SessionType, UpdateMulticastListAction};
31 
32 use crate::callback::UwbServiceCallbackBuilderImpl;
33 use crate::context::JniContext;
34 use crate::error::{Error, Result};
35 use crate::object_mapping::{
36     CccOpenRangingParamsJni, CountryCodeJni, FiraControleeParamsJni, FiraOpenSessionParamsJni,
37     PowerStatsJni, PowerStatsWithEnv,
38 };
39 use crate::unique_jvm;
40 
41 /// Initialize native logging and capture static JavaVM ref
42 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeInit( env: JNIEnv, obj: JObject, )43 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeInit(
44     env: JNIEnv,
45     obj: JObject,
46 ) {
47     logger::init(
48         logger::Config::default()
49             .with_tag_on_device("uwb")
50             .with_max_level(log::LevelFilter::Trace)
51             .with_filter("trace,jni=info"),
52     );
53 
54     match env.get_java_vm() {
55         Ok(vm) => {
56             unique_jvm::set_once(vm);
57         }
58         Err(err) => {
59             error!("Couldn't get a JavaVM from JNIEnv: {:?}", err);
60         }
61     };
62 }
63 
64 /// Create a new UWB service and return the pointer
65 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceNew( env: JNIEnv, obj: JObject, ) -> jlong66 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceNew(
67     env: JNIEnv,
68     obj: JObject,
69 ) -> jlong {
70     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceNew : enter");
71     let vm = match unique_jvm::get_static_ref() {
72         Some(vm_ref) => vm_ref,
73         None => {
74             error!("Failed to get JavaVM reference");
75             return *JObject::null() as jlong;
76         }
77     };
78     let callback_obj = match env.new_global_ref(obj) {
79         Ok(cb) => cb,
80         Err(err) => {
81             error!("Couldn't create global ref for callback obj: {:?}", err);
82             return *JObject::null() as jlong;
83         }
84     };
85     let class_loader_obj = match get_class_loader_obj(&env) {
86         Ok(cl) => cl,
87         Err(err) => {
88             error!("Couldn't get class loader obj: {:?}", err);
89             return *JObject::null() as jlong;
90         }
91     };
92     let runtime = match default_runtime() {
93         Some(r) => r,
94         None => {
95             error!("Couldn't build tokio Runtime.");
96             return *JObject::null() as jlong;
97         }
98     };
99     let uci_logger_factory = match PcapngUciLoggerFactoryBuilder::new()
100         .log_path("/data/misc/apexdata/com.android.uwb/log".into())
101         .filename_prefix("uwb_uci".to_owned())
102         .runtime_handle(runtime.handle().to_owned())
103         .build()
104     {
105         Some(m) => m,
106         None => {
107             error!("Couldn't build uci_logger_factory.");
108             return *JObject::null() as jlong;
109         }
110     };
111     if let Some(uwb_service) = UwbServiceBuilder::new()
112         .runtime_handle(runtime.handle().to_owned())
113         .callback_builder(UwbServiceCallbackBuilderImpl::new(vm, callback_obj, class_loader_obj))
114         .uci_hal(UciHalAndroid::new("default"))
115         .uci_logger_factory(uci_logger_factory)
116         .build()
117     {
118         let service_wrapper = UwbServiceWrapper::new(uwb_service, runtime);
119         return Box::into_raw(Box::new(service_wrapper)) as jlong;
120     }
121 
122     error!("Failed to create Uwb Service");
123     *JObject::null() as jlong
124 }
125 
126 /// Destroy the UWB service object
127 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceDestroy( env: JNIEnv, obj: JObject, )128 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceDestroy(
129     env: JNIEnv,
130     obj: JObject,
131 ) {
132     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeUwbServiceDestroy : enter");
133     let ctx = JniContext::new(env, obj);
134     let uwb_service_ptr = match ctx.long_getter("getUwbServicePtr") {
135         Ok(val) => val,
136         Err(err) => {
137             error!("Failed to get pointer value with: {:?}", err);
138             return;
139         }
140     };
141 
142     unsafe {
143         drop(Box::from_raw(uwb_service_ptr as *mut UwbServiceWrapper));
144     }
145     debug!("Uwb Service successfully destroyed.");
146 }
147 
148 /// Enable the UWB service
149 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeEnable( env: JNIEnv, obj: JObject, ) -> jboolean150 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeEnable(
151     env: JNIEnv,
152     obj: JObject,
153 ) -> jboolean {
154     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeEnable : enter");
155     boolean_result_helper(enable(JniContext::new(env, obj)), "enable")
156 }
157 
158 /// Disable the UWB service
159 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeDisable( env: JNIEnv, obj: JObject, ) -> jboolean160 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeDisable(
161     env: JNIEnv,
162     obj: JObject,
163 ) -> jboolean {
164     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeDisable : enter");
165     boolean_result_helper(disable(JniContext::new(env, obj)), "disable")
166 }
167 
168 /// Initialize a new UWB session
169 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeInitSession( env: JNIEnv, obj: JObject, session_id: jint, session_type: jbyte, app_config_params: JObject, ) -> jboolean170 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeInitSession(
171     env: JNIEnv,
172     obj: JObject,
173     session_id: jint,
174     session_type: jbyte,
175     app_config_params: JObject,
176 ) -> jboolean {
177     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeInitSession : enter");
178     boolean_result_helper(
179         init_session(JniContext::new(env, obj), session_id, session_type, app_config_params),
180         "init_session",
181     )
182 }
183 
184 /// De-initialize an existing UWB session
185 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeDeinitSession( env: JNIEnv, obj: JObject, session_id: jint, ) -> jboolean186 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeDeinitSession(
187     env: JNIEnv,
188     obj: JObject,
189     session_id: jint,
190 ) -> jboolean {
191     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeDeinitSession : enter");
192     boolean_result_helper(
193         deinit_session(JniContext::new(env, obj), session_id as u32),
194         "deinit_session",
195     )
196 }
197 
198 /// Start ranging for an existing UWB session
199 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeStartRanging( env: JNIEnv, obj: JObject, session_id: jint, ) -> jboolean200 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeStartRanging(
201     env: JNIEnv,
202     obj: JObject,
203     session_id: jint,
204     // TODO(cante): figure out what to do with the return object from start raning
205 ) -> jboolean {
206     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeStartRanging : enter");
207     boolean_result_helper(
208         start_ranging(JniContext::new(env, obj), session_id as u32),
209         "start_raning",
210     )
211 }
212 
213 /// Stop ranging
214 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeStopRanging( env: JNIEnv, obj: JObject, session_id: jint, ) -> jboolean215 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeStopRanging(
216     env: JNIEnv,
217     obj: JObject,
218     session_id: jint,
219 ) -> jboolean {
220     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeStopRanging : enter");
221     boolean_result_helper(
222         stop_ranging(JniContext::new(env, obj), session_id as u32),
223         "stop_ranging",
224     )
225 }
226 
227 /// Reconfigure an existing UWB sessions with the given parameters
228 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeReconfigure( env: JNIEnv, obj: JObject, session_id: jint, app_config_params: JObject, ) -> jboolean229 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeReconfigure(
230     env: JNIEnv,
231     obj: JObject,
232     session_id: jint,
233     app_config_params: JObject,
234 ) -> jboolean {
235     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeReconfigure( : enter");
236     boolean_result_helper(
237         reconfigure(JniContext::new(env, obj), session_id as u32, app_config_params),
238         "reconfigure",
239     )
240 }
241 
242 /// Update controller multicast list
243 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeUpdateControllerMulticastList( env: JNIEnv, obj: JObject, session_id: jint, update_multicast_list_action: jbyte, controlees: JObject, ) -> jboolean244 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeUpdateControllerMulticastList(
245     env: JNIEnv,
246     obj: JObject,
247     session_id: jint,
248     update_multicast_list_action: jbyte,
249     controlees: JObject,
250 ) -> jboolean {
251     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeUpdateControllerMulticastList");
252     boolean_result_helper(
253         update_controller_multicast_list(
254             JniContext::new(env, obj),
255             session_id,
256             update_multicast_list_action,
257             controlees,
258         ),
259         "update_controller_multicast_list",
260     )
261 }
262 
263 /// Set country code
264 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeSetCountryCode( env: JNIEnv, obj: JObject, country_code: jbyteArray, ) -> jboolean265 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeSetCountryCode(
266     env: JNIEnv,
267     obj: JObject,
268     country_code: jbyteArray,
269 ) -> jboolean {
270     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeSetCountryCode: enter");
271     boolean_result_helper(
272         set_country_code(JniContext::new(env, obj), country_code),
273         "set_country_code",
274     )
275 }
276 
277 /// Send raw vendor command
278 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeSendRawVendorCmd( env: JNIEnv, obj: JObject, mt: jint, gid: jint, oid: jint, payload: jbyteArray, ) -> jobject279 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeSendRawVendorCmd(
280     env: JNIEnv,
281     obj: JObject,
282     mt: jint,
283     gid: jint,
284     oid: jint,
285     payload: jbyteArray,
286 ) -> jobject {
287     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeSendRawVendorCmd: enter");
288     object_result_helper(
289         send_raw_vendor_cmd(JniContext::new(env, obj), mt, gid, oid, payload),
290         "send_raw_vendor_cmd",
291     )
292 }
293 
294 /// Retrieve UWB power stats
295 #[no_mangle]
Java_com_android_server_uwb_indev_UwbServiceCore_nativeGetPowerStats( env: JNIEnv, obj: JObject, ) -> jobject296 pub extern "system" fn Java_com_android_server_uwb_indev_UwbServiceCore_nativeGetPowerStats(
297     env: JNIEnv,
298     obj: JObject,
299 ) -> jobject {
300     debug!("Java_com_android_server_uwb_indev_UwbServiceCore_nativeGetPowerStats: enter");
301     object_result_helper(get_power_stats(JniContext::new(env, obj)), "get_power_stats")
302 }
303 
enable(ctx: JniContext) -> Result<()>304 fn enable(ctx: JniContext) -> Result<()> {
305     let uwb_service = get_uwb_service(ctx)?;
306     Ok(uwb_service.enable()?)
307 }
308 
disable(ctx: JniContext) -> Result<()>309 fn disable(ctx: JniContext) -> Result<()> {
310     let uwb_service = get_uwb_service(ctx)?;
311     Ok(uwb_service.disable()?)
312 }
313 
init_session( ctx: JniContext, session_id: jint, session_type: jbyte, app_config_params: JObject, ) -> Result<()>314 fn init_session(
315     ctx: JniContext,
316     session_id: jint,
317     session_type: jbyte,
318     app_config_params: JObject,
319 ) -> Result<()> {
320     let uwb_service = get_uwb_service(ctx)?;
321 
322     let session_id = session_id as u32;
323     let session_type = session_type as u8;
324     let session_type = SessionType::try_from(session_type)
325         .map_err(|_| Error::Parse(format!("Invalid session type. Received {}", session_type)))?;
326     let params = match session_type {
327         SessionType::Ccc => {
328             AppConfigParams::try_from(CccOpenRangingParamsJni::new(ctx.env, app_config_params))
329         }
330         _ => AppConfigParams::try_from(FiraOpenSessionParamsJni::new(ctx.env, app_config_params)),
331     }?;
332 
333     Ok(uwb_service.init_session(session_id, session_type, params)?)
334 }
335 
deinit_session(ctx: JniContext, session_id: u32) -> Result<()>336 fn deinit_session(ctx: JniContext, session_id: u32) -> Result<()> {
337     let uwb_service = get_uwb_service(ctx)?;
338     Ok(uwb_service.deinit_session(session_id)?)
339 }
340 
start_ranging(ctx: JniContext, session_id: u32) -> Result<AppConfigParams>341 fn start_ranging(ctx: JniContext, session_id: u32) -> Result<AppConfigParams> {
342     let uwb_service = get_uwb_service(ctx)?;
343     Ok(uwb_service.start_ranging(session_id)?)
344 }
345 
stop_ranging(ctx: JniContext, session_id: u32) -> Result<()>346 fn stop_ranging(ctx: JniContext, session_id: u32) -> Result<()> {
347     let uwb_service = get_uwb_service(ctx)?;
348     Ok(uwb_service.stop_ranging(session_id)?)
349 }
350 
reconfigure(ctx: JniContext, session_id: u32, app_config_params: JObject) -> Result<()>351 fn reconfigure(ctx: JniContext, session_id: u32, app_config_params: JObject) -> Result<()> {
352     let uwb_service = get_uwb_service(ctx)?;
353     // TODO(b/244786744): Implement using session_params from uwb service
354     todo!();
355 }
356 
update_controller_multicast_list( ctx: JniContext, session_id: jint, update_multicast_list_action: jbyte, controlees: JObject, ) -> Result<()>357 fn update_controller_multicast_list(
358     ctx: JniContext,
359     session_id: jint,
360     update_multicast_list_action: jbyte,
361     controlees: JObject,
362 ) -> Result<()> {
363     let uwb_service = get_uwb_service(ctx)?;
364 
365     let session_id = session_id as u32;
366     let action = update_multicast_list_action as u8;
367     let action = UpdateMulticastListAction::try_from(action).map_err(|_| {
368         Error::Parse(format!("Invalid value for UpdateMulticastListAction. Received {}", action))
369     })?;
370     let controlees = match FiraControleeParamsJni::new(ctx.env, controlees).as_vec() {
371         Ok(val) => val,
372         Err(err) => {
373             return Err(Error::Parse(format!("Couldn't parse controlees. {:?}", err)));
374         }
375     };
376     Ok(uwb_service.update_controller_multicast_list(session_id, action, controlees)?)
377 }
378 
set_country_code(ctx: JniContext, country_code: jbyteArray) -> Result<()>379 fn set_country_code(ctx: JniContext, country_code: jbyteArray) -> Result<()> {
380     let uwb_service = get_uwb_service(ctx)?;
381 
382     let country_code = match CountryCode::try_from(CountryCodeJni::new(ctx.env, country_code)) {
383         Ok(val) => val,
384         Err(err) => {
385             return Err(Error::Parse(format!("Invalid country code: {:?}", err)));
386         }
387     };
388     Ok(uwb_service.android_set_country_code(country_code)?)
389 }
390 
send_raw_vendor_cmd( ctx: JniContext, mt: jint, gid: jint, oid: jint, payload: jbyteArray, ) -> Result<jobject>391 fn send_raw_vendor_cmd(
392     ctx: JniContext,
393     mt: jint,
394     gid: jint,
395     oid: jint,
396     payload: jbyteArray,
397 ) -> Result<jobject> {
398     let uwb_service = get_uwb_service(ctx)?;
399 
400     let mt = mt as u32;
401     let gid = gid as u32;
402     let oid = oid as u32;
403     let payload = match ctx.env.convert_byte_array(payload) {
404         Ok(val) => val,
405         Err(err) => {
406             return Err(Error::Parse(format!("Failed to convert payload {:?}", err)));
407         }
408     };
409     let vendor_message = uwb_service.raw_uci_cmd(mt, gid, oid, payload);
410     // TODO(cante): figure out if we send RawUciMessage back in a callback
411     todo!();
412 }
413 
get_power_stats(ctx: JniContext) -> Result<jobject>414 fn get_power_stats(ctx: JniContext) -> Result<jobject> {
415     let uwb_service = get_uwb_service(ctx)?;
416 
417     let power_stats = uwb_service.android_get_power_stats()?;
418     let ps_jni = PowerStatsJni::try_from(PowerStatsWithEnv::new(ctx.env, power_stats))?;
419     Ok(ps_jni.jni_context.obj.into_raw())
420 }
421 
get_uwb_service(ctx: JniContext) -> Result<&mut UwbServiceWrapper>422 fn get_uwb_service(ctx: JniContext) -> Result<&mut UwbServiceWrapper> {
423     let uwb_service_ptr = ctx.long_getter("getUwbServicePtr")?;
424     if uwb_service_ptr == 0i64 {
425         return Err(Error::Jni(jni::errors::Error::NullPtr("Uwb Service is not initialized")));
426     }
427     // Safety: Uwb Service pointer must not be a null pointer
428     // and it must point to a valid Uwb Service object.
429     // This can be ensured because the Uwb Service is created in an earlier stage and
430     // won't be deleted before calling doDeinitialize.
431     unsafe { Ok(&mut *(uwb_service_ptr as *mut UwbServiceWrapper)) }
432 }
433 
boolean_result_helper<T>(result: Result<T>, function_name: &str) -> jboolean434 fn boolean_result_helper<T>(result: Result<T>, function_name: &str) -> jboolean {
435     match result {
436         Ok(_) => true as jboolean,
437         Err(err) => {
438             error!("{} failed with: {:?}", function_name, err);
439             false as jboolean
440         }
441     }
442 }
443 
object_result_helper(result: Result<jobject>, function_name: &str) -> jobject444 fn object_result_helper(result: Result<jobject>, function_name: &str) -> jobject {
445     match result {
446         Ok(obj) => obj,
447         Err(err) => {
448             error!("{} failed with {:?}", function_name, err);
449             *JObject::null()
450         }
451     }
452 }
453 
454 /// Get the class loader object. Has to be called from a JNIEnv where the local java classes are
455 /// loaded. Results in a global reference to the class loader object that can be used to look for
456 /// classes in other native thread.
get_class_loader_obj(env: &JNIEnv) -> Result<GlobalRef>457 fn get_class_loader_obj(env: &JNIEnv) -> Result<GlobalRef> {
458     let uwb_service_core_class = env.find_class("com/android/server/uwb/indev/UwbServiceCore")?;
459     let uwb_service_core_obj = env.get_object_class(uwb_service_core_class)?;
460     let get_class_loader_method =
461         env.get_method_id(uwb_service_core_obj, "getClassLoader", "()Ljava/lang/ClassLoader;")?;
462     let class_loader = env.call_method_unchecked(
463         uwb_service_core_class,
464         get_class_loader_method,
465         ReturnType::Object,
466         &[jvalue::from(JValue::Void)],
467     )?;
468     let class_loader_jobject = class_loader.l()?;
469     Ok(env.new_global_ref(class_loader_jobject)?)
470 }
471 
472 /// The wrapper of UwbService that is used to keep the outlived tokio runtime.
473 ///
474 /// This wrapper implements Deref and DerefMut for UwbService target, so we could get the reference
475 /// of inner UwbService easily.
476 struct UwbServiceWrapper {
477     /// The wrapped UwbService.
478     service: UwbService,
479 
480     /// The working runtime, which should outlives the UwbService.
481     ///
482     /// Because the fields of a struct are dropped in declaration order, this field is guaranteed
483     /// to be dropped after UwbService.
484     _runtime: Runtime,
485 }
486 
487 impl UwbServiceWrapper {
new(service: UwbService, runtime: Runtime) -> Self488     pub fn new(service: UwbService, runtime: Runtime) -> Self {
489         Self { service, _runtime: runtime }
490     }
491 }
492 
493 impl std::ops::Deref for UwbServiceWrapper {
494     type Target = UwbService;
495 
deref(&self) -> &Self::Target496     fn deref(&self) -> &Self::Target {
497         &self.service
498     }
499 }
500 
501 impl std::ops::DerefMut for UwbServiceWrapper {
deref_mut(&mut self) -> &mut Self::Target502     fn deref_mut(&mut self) -> &mut Self::Target {
503         &mut self.service
504     }
505 }
506