1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
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
16 //! rust adapter sys
17
18 #![allow(dead_code)]
19
20 use std::{ ffi::{ c_char, CStr, CString }, sync::Arc };
21 use hilog_rust::{ error, hilog, HiLogLabel, LogType };
22 use fusion_utils_rust::{ call_debug_enter, FusionErrorCode, FusionResult, err_log };
23
24 use crate::binding::{
25 CICrossStateListener,
26 CIString,
27 CIStringVector,
28 UpdateCrossSwitchState,
29 SyncCrossSwitchState,
30 GetCrossSwitchState,
31 RegisterCrossStateListener,
32 UnregisterCrossStateListener,
33 GetLocalNetworkId,
34 RET_OK,
35 EMPTY_LENGTH,
36 };
37
38 const LOG_LABEL: HiLogLabel = HiLogLabel {
39 log_type: LogType::LogCore,
40 domain: 0xD002220,
41 tag: "DeviceProfileAdapter",
42 };
43
44 #[repr(C)]
45 struct CrossStateListener {
46 interface: CICrossStateListener,
47 callback: Arc<dyn Fn(&str, bool)>,
48 }
49
50 impl CrossStateListener {
51 /// Structures with C-compatible layouts that safely interconvert the first structure field and structure instance.
52 /// Based on the C17 standard
53 ///
54 /// # Note
55 ///
56 /// This function performs a conversion of a raw pointer to a mutable reference of `Self` type.
57 /// Please note that the pointer `listener` is a raw pointer that needs to be handled carefully to avoid memory
58 /// safety issues and undefined behavior.
59 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
from_interface(listener: *mut CICrossStateListener) -> Option<&'static mut Self>60 fn from_interface(listener: *mut CICrossStateListener) -> Option<&'static mut Self>
61 {
62 let listener_ptr = listener as *mut Self;
63 // SAFETY: `listener_ptr` is valid, because `as_mut` has null pointer checking.
64 unsafe {
65 listener_ptr.as_mut()
66 }
67 }
68 /// Clone a `CICrossStateListener` instance.
69 ///
70 /// # Note
71 ///
72 /// Please note that the pointer `listener` is a raw pointer that needs to be handled carefully to avoid memory
73 /// safety issues and undefined behavior.
74 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
clone(listener: *mut CICrossStateListener) -> *mut CICrossStateListener75 extern "C" fn clone(listener: *mut CICrossStateListener) -> *mut CICrossStateListener
76 {
77 call_debug_enter!("CrossStateListener::clone");
78 if let Some(listener_mut) = CrossStateListener::from_interface(listener) {
79 let listener_box = Box::new(Self {
80 interface: CICrossStateListener {
81 clone: Some(Self::clone),
82 destruct: Some(Self::destruct),
83 on_update: Some(Self::on_update),
84 },
85 callback: listener_mut.callback.clone(),
86 });
87 Box::into_raw(listener_box) as *mut CICrossStateListener
88 } else {
89 error!(LOG_LABEL, "Failed to clone a CICrossStateListener instance");
90 std::ptr::null_mut()
91 }
92 }
93
94 /// Destruct a `CICrossStateListener` instance.
95 ///
96 /// # Note
97 ///
98 /// Please note that the pointer `listener` is a raw pointer that needs to be handled carefully to avoid memory
99 /// safety issues and undefined behavior.
100 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
destruct(listener: *mut CICrossStateListener)101 extern "C" fn destruct(listener: *mut CICrossStateListener)
102 {
103 call_debug_enter!("CrossStateListener::destruct");
104 if let Some(listener_mut) = CrossStateListener::from_interface(listener) {
105 // SAFETY: `listener_mut` is valid, becauce has been matched to `Some`.
106 unsafe { drop(Box::from_raw(listener_mut as *mut CrossStateListener)) };
107 } else {
108 error!(LOG_LABEL, "Failed to destruct a CICrossStateListener instance");
109 }
110 }
111 /// Handle a state update event from a device.
112 ///
113 /// # Note
114 ///
115 /// Please note that the pointer `listener` and `device_id` are raw pointers that need to be handled carefully
116 /// to avoid memory safety issues and undefined behavior and ensure `state` is valid(0 for off, 1 for on).
117 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
on_update(listener: *mut CICrossStateListener, device_id: *const c_char, state: u32)118 extern "C" fn on_update(listener: *mut CICrossStateListener, device_id: *const c_char, state: u32)
119 {
120 call_debug_enter!("CrossStateListener::on_update");
121 if let Some(listener_mut) = CrossStateListener::from_interface(listener) {
122 // SAFETY: `listener_mut` is valid, cause has been performed check.
123 // `device_id` and `state` are valid, which are ensured by the caller.
124 unsafe {
125 if let Ok(id) = CStr::from_ptr(device_id).to_str() {
126 (listener_mut.callback)(id, state != 0);
127 } else {
128 error!(LOG_LABEL, "Invalid device id");
129 }
130 }
131 } else {
132 error!(LOG_LABEL, "Failed to handle a state update event from a device");
133 }
134 }
135 }
136
137 impl<F> From<Arc<F>> for CrossStateListener
138 where
139 F: Fn(&str, bool) + 'static {
from(value: Arc<F>) -> Self140 fn from(value: Arc<F>) -> Self
141 {
142 Self {
143 interface: CICrossStateListener {
144 clone: Some(Self::clone),
145 destruct: None,
146 on_update: Some(Self::on_update),
147 },
148 callback: value,
149 }
150 }
151 }
152
153 #[repr(C)]
154 struct StringVector {
155 interface: CIStringVector,
156 data: Vec<String>,
157 }
158
159 impl StringVector {
160 /// Structures with C-compatible layouts that safely interconvert the first structure field and structure instance.
161 /// Based on the C17 standard
162 ///
163 /// # Note
164 ///
165 /// This function performs a conversion of a raw pointer to a mutable reference of `Self` type.
166 /// Please note that the pointer `strings` is a raw pointer that needs to be handled carefully to avoid memory
167 /// safety issues and undefined behavior.
168 /// Make sure that the returned pointer is not null before dereferencing it.
from_interface(strings: *mut CIStringVector) -> Option<&'static mut Self>169 fn from_interface(strings: *mut CIStringVector) -> Option<&'static mut Self>
170 {
171 let strings_ptr = strings as *mut Self;
172 // SAFETY: `strings_ptr` is valid, because `as_mut` has null pointer checking.
173 unsafe {
174 strings_ptr.as_mut()
175 }
176 }
177 /// Clone a `CIStringVector` instance.
178 ///
179 /// # Note
180 ///
181 /// Please note that the pointer `strings` is a raw pointer that needs to be handled carefully to avoid memory
182 /// safety issues and undefined behavior.
183 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
clone(strings: *mut CIStringVector) -> *mut CIStringVector184 extern "C" fn clone(strings: *mut CIStringVector) -> *mut CIStringVector
185 {
186 if let Some(strings_mut) = StringVector::from_interface(strings) {
187 let strings_box = Box::new(Self {
188 interface: CIStringVector {
189 clone: Some(Self::clone),
190 destruct: Some(Self::destruct),
191 get: Some(Self::get),
192 get_size: Some(Self::get_size),
193 },
194 data: strings_mut.data.clone(),
195 });
196 Box::into_raw(strings_box) as *mut CIStringVector
197 } else {
198 error!(LOG_LABEL, "Failed to clone a CIStringVector instance");
199 std::ptr::null_mut()
200 }
201 }
202 /// Destruct a `CIStringVector` instance.
203 ///
204 /// # Note
205 ///
206 /// Please note that the pointer `strings` is a raw pointer that needs to be handled carefully to avoid memory
207 /// safety issues and undefined behavior.
208 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
destruct(strings: *mut CIStringVector)209 extern "C" fn destruct(strings: *mut CIStringVector)
210 {
211 if let Some(strings_mut) = StringVector::from_interface(strings) {
212 // SAFETY: `strings_mut` is valid, becauce has been matched to `Some`.
213 unsafe { drop(Box::from_raw(strings_mut as *mut StringVector)) };
214 } else {
215 error!(LOG_LABEL, "Failed to destruct a CIStringVector instance");
216 }
217 }
218 /// Accesse an element at a specific index in a `CIStringVector` instance.
219 ///
220 /// # Note
221 ///
222 /// Please note that the pointer `strings` is a raw pointer that needs to be handled carefully to avoid memory
223 /// safety issues and undefined behavior.
224 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
get(strings: *mut CIStringVector, index: usize) -> *const c_char225 extern "C" fn get(strings: *mut CIStringVector, index: usize) -> *const c_char
226 {
227 if let Some(strings_mut) = StringVector::from_interface(strings) {
228 if index < strings_mut.data.len() {
229 strings_mut.data[index].as_ptr() as *const c_char
230 } else {
231 error!(LOG_LABEL, "index is out of bounds");
232 std::ptr::null()
233 }
234 } else {
235 error!(LOG_LABEL, "Failed to accesse CIStringVector instance");
236 std::ptr::null()
237 }
238 }
239 /// Get the number of elements in a `CIStringVector` instance.
240 ///
241 /// # Note
242 ///
243 /// Please note that the pointer `strings` is a raw pointer that needs to be handled carefully to avoid memory
244 /// safety issues and undefined behavior.
245 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
get_size(strings: *mut CIStringVector) -> usize246 extern "C" fn get_size(strings: *mut CIStringVector) -> usize
247 {
248 if let Some(strings_mut) = StringVector::from_interface(strings) {
249 strings_mut.data.len()
250 } else {
251 error!(LOG_LABEL, "Failed to get the number of CIStringVector instance");
252 EMPTY_LENGTH
253 }
254 }
255 }
256
257 impl From<&[String]> for StringVector {
from(value: &[String]) -> Self258 fn from(value: &[String]) -> Self
259 {
260 Self {
261 interface: CIStringVector {
262 clone: Some(StringVector::clone),
263 destruct: None,
264 get: Some(Self::get),
265 get_size: Some(Self::get_size),
266 },
267 data: value.to_vec(),
268 }
269 }
270 }
271
272 /// Implementation of device profile adapter.
273 #[derive(Default)]
274 pub struct DeviceProfileAdapter {
275 dummy: i32
276 }
277
278 impl DeviceProfileAdapter {
check_return_code(&self, ret: i32) -> FusionResult<()>279 fn check_return_code(&self, ret: i32) -> FusionResult<()>
280 {
281 (ret == RET_OK).then_some(()).ok_or(FusionErrorCode::Fail)
282 }
283
284 /// Update the cross switch state with a boolean value.
285 ///
286 /// # Arguments
287 ///
288 /// * `state` - A boolean value that represents the cross switch state. `true` for on, `false` for off.
289 ///
290 /// # Note
291 ///
292 /// This function assumes `state` parameter is a valid boolean value (`true` or `false`),
293 /// The caller needs to ensure that `state` is valid.
294 ///
295 /// # Returns
296 ///
297 /// Returns `FusionResult<()>` indicating whether the operation is successful. If the update succeeds,
298 /// it returns `Ok(())`. If the update fails, it returns the corresponding error information.
299
update_cross_switch_state(&self, state: bool) -> FusionResult<()>300 pub fn update_cross_switch_state(&self, state: bool) -> FusionResult<()>
301 {
302 call_debug_enter!("DeviceProfileAdapter::update_cross_switch_state");
303 // SAFETY: `state` is valid, which is ensured by the caller.
304 let ret = unsafe { UpdateCrossSwitchState(state as u32) };
305 Ok(err_log!(self.check_return_code(ret), "UpdateCrossSwitchState"))
306 }
307
308 /// Synchronize the cross switch state between multiple devices.
309 ///
310 /// # Arguments
311 ///
312 /// * `state` - A boolean value that represents the cross switch state. `true` for on, `false` for off.
313 /// * `device_ids` - A slice of strings representing the device IDs to sync with.
314 ///
315 /// # Note
316 ///
317 /// This function assumes the validity of `state` (`true` or `false`) and `device_ids` parameters.
318 /// The caller needs to ensure that `state` is valid and `device_ids` is a valid and properly initialized
319 /// slice of strings.
320 ///
321 /// # Returns
322 ///
323 /// Returns `FusionResult<()>` indicating whether the operation is successful. If the synchronization succeeds,
324 /// it returns `Ok(())`. If the synchronization fails, it returns the corresponding error information.
sync_cross_switch_state(&self, state: bool, device_ids: &[String]) -> FusionResult<()>325 pub fn sync_cross_switch_state(&self, state: bool, device_ids: &[String]) -> FusionResult<()>
326 {
327 call_debug_enter!("DeviceProfileAdapter::sync_cross_switch_state");
328 let mut device_ids = StringVector::from(device_ids);
329 // SAFETY: `state` and `device_ids` are valid, which are ensured by the caller.
330 let ret = unsafe {
331 let device_ids_ptr: *mut StringVector = &mut device_ids;
332 SyncCrossSwitchState(state as u32, device_ids_ptr as *mut CIStringVector)
333 };
334 Ok(err_log!(self.check_return_code(ret), "SyncCrossSwitchState"))
335 }
336
337 /// Get the cross switch state of a specific device.
338 ///
339 /// # Arguments
340 ///
341 /// * `device_id` - The device ID string representing the target device to get the cross switch state from.
342 ///
343 /// # Returns
344 ///
345 /// Returns `FusionResult<()>` indicating whether the operation is successful. If the getting succeeds,
346 /// it returns `Ok(())`. If the getting fails, it returns the corresponding error information.
get_cross_switch_state(&self, device_id: &str) -> FusionResult<()>347 pub fn get_cross_switch_state(&self, device_id: &str) -> FusionResult<()>
348 {
349 call_debug_enter!("DeviceProfileAdapter::get_cross_switch_state");
350 // SAFETY: `device_id` is valid, which is ensured by the caller.
351 let ret = unsafe { GetCrossSwitchState(device_id.as_ptr()) };
352 Ok(err_log!(self.check_return_code(ret), "GetCrossSwitchState"))
353 }
354
355 /// Register callback for a specific device.
356 ///
357 /// # Arguments
358 ///
359 /// * `device_id` - The device ID string representing the target device to register the listener for.
360 /// * `callback` - The callback function that will be invoked when the cross state changes. It takes two parameters:
361 /// the device ID and a boolean indicating the new cross state.
362 ///
363 /// # Returns
364 ///
365 /// Returns `FusionResult<()>` indicating whether the operation is successful. If the registration succeeds,
366 /// it returns `Ok(())`. If the registration fails, it returns the corresponding error information.
register_cross_state_listener<F>(&self, device_id: &str, callback: F) -> FusionResult<()> where F: Fn(&str, bool) + 'static367 pub fn register_cross_state_listener<F>(&self, device_id: &str, callback: F) -> FusionResult<()>
368 where
369 F: Fn(&str, bool) + 'static
370 {
371 call_debug_enter!("DeviceProfileAdapter::register_cross_state_listener");
372 let mut listener = CrossStateListener::from(Arc::new(callback));
373 // SAFETY: `device_id` and `listener_ptr` are valid, which are ensured by the caller.
374 let ret = unsafe {
375 let listener_ptr: *mut CrossStateListener = &mut listener;
376 RegisterCrossStateListener(device_id.as_ptr(), listener_ptr as *mut CICrossStateListener)
377 };
378 Ok(err_log!(self.check_return_code(ret), "RegisterCrossStateListener"))
379 }
380
381 /// Unregister callback for a specific device.
382 ///
383 /// # Arguments
384 ///
385 /// * `device_id` - The device ID string representing the target device to unregister the listener from.
386 ///
387 /// # Returns
388 ///
389 /// Returns `FusionResult<()>` indicating whether the operation is successful. If the unregistration succeeds,
390 /// it returns `Ok(())`. If the unregistration fails, it returns the corresponding error information.
unregister_cross_state_listener(&self, device_id: &str) -> FusionResult<()>391 pub fn unregister_cross_state_listener(&self, device_id: &str) -> FusionResult<()>
392 {
393 call_debug_enter!("DeviceProfileAdapter::unregister_cross_state_listener");
394 // SAFETY: `device_id` is valid, which is ensured by the caller.
395 let ret = unsafe { UnregisterCrossStateListener(device_id.as_ptr()) };
396 Ok(err_log!(self.check_return_code(ret), "UnregisterCrossStateListener"))
397 }
398 }
399
400 struct CStringGuard {
401 data: *mut CIString,
402 }
403
404 impl CStringGuard {
405 /// Retrieves the string data from the CIString object.
406 ///
407 /// Returns:
408 /// - Some(String): If the data is successfully retrieved and converted to a string, it is wrapped in `Some`.
409 /// - None: If the `data` or `get_data` pointer is null, or an error occurs during retrieval or conversion.
data(&self) -> Option<String>410 pub fn data(&self) -> Option<String>
411 {
412 if self.data.is_null() {
413 error!(LOG_LABEL, "data is null");
414 return None;
415 }
416 // SAFETY: `data` is valid, because null pointer check has been performed.
417 let data = unsafe { (*self.data).data };
418 if let Some(data) = data {
419 // SAFETY: `data` is valid, becauce has been matched to `Some`.
420 let res = unsafe { CStr::from_ptr(data(self.data)).to_str() };
421 res.ok().map(|id| id.to_string()).or_else(|| {
422 error!(LOG_LABEL, "Failed to convert CStr to str");
423 None
424 })
425 } else {
426 error!(LOG_LABEL, "data is null");
427 None
428 }
429 }
430 }
431
432 impl From<*mut CIString> for CStringGuard {
433 /// Constructs a `CStringGuard` object and assigns the given raw pointer `value` to the `data` field.
434 ///
435 /// # Note
436 ///
437 /// Please note that the pointer `value` is a raw pointer that needs to be handled carefully to avoid memory
438 /// safety issues and undefined behavior.
439 /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
from(value: *mut CIString) -> Self440 fn from(value: *mut CIString) -> Self
441 {
442 Self { data: value }
443 }
444 }
445
446 impl Drop for CStringGuard {
drop(&mut self)447 fn drop(&mut self)
448 {
449 if self.data.is_null() {
450 error!(LOG_LABEL, "data is null");
451 return;
452 }
453 // SAFETY: `data` is valid, because null pointer check has been performed.
454 unsafe {
455 if let Some(destruct) = (*self.data).destruct {
456 destruct(self.data);
457 }
458 }
459 }
460 }
461
462 /// Retrieves the local network ID using FFI.
463 //
464 /// Returns:
465 /// - Some(String): If the local network ID is successfully retrieved, it is wrapped in `Some`.
466 /// - None: If the local network ID is not available or an error occurs during retrieval.
get_local_network_id() -> Option<String>467 pub fn get_local_network_id() -> Option<String>
468 {
469 // SAFETY: The `CStringGuard::from` and `data` methods are all valid, which are ensured by the caller.
470 unsafe { CStringGuard::from(GetLocalNetworkId()).data() }
471 }
472