• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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