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