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 device profile sys
17 
18 #![allow(dead_code)]
19 
20 use std::{ ffi::{ c_char, c_int, 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     CProfileChangeNotification,
26     CIProfileEventCb,
27     CIProfileEvents,
28     CSubscribeInfo,
29     CISubscribeInfos,
30     SubscribeProfileEvents,
31     UnsubscribeProfileEvents,
32     RET_OK,
33 };
34 
35 const LOG_LABEL: HiLogLabel = HiLogLabel {
36     log_type: LogType::LogCore,
37     domain: 0xD002220,
38     tag: "DeviceProfile",
39 };
40 
41 /// Represents a service characteristic profile.
42 pub struct ServiceCharacteristicProfile;
43 
44 /// Represents an event related to a profile.
45 #[repr(u32)]
46 #[derive(Copy, Clone)]
47 pub enum ProfileEvent {
48     Unknown = 0,
49     SyncCompleted = 1,
50     ProfileChanged = 2,
51 }
52 
53 impl From<ProfileEvent> for u32 {
fromnull54     fn from(value: ProfileEvent) -> u32 {
55         match value {
56             ProfileEvent::Unknown => { 0 },
57             ProfileEvent::SyncCompleted => { 1 },
58             ProfileEvent::ProfileChanged => { 2 },
59         }
60     }
61 }
62 
63 impl TryFrom<u32> for ProfileEvent {
64     type Error = FusionErrorCode;
65 
try_fromnull66     fn try_from(value: u32) -> Result<Self, Self::Error> {
67         match value {
68             _ if u32::from(ProfileEvent::Unknown) == value => { Ok(ProfileEvent::Unknown) },
69             _ if u32::from(ProfileEvent::SyncCompleted) == value => { Ok(ProfileEvent::SyncCompleted) },
70             _ if u32::from(ProfileEvent::ProfileChanged) == value => { Ok(ProfileEvent::ProfileChanged) },
71             _ => { Err(FusionErrorCode::Fail) },
72         }
73     }
74 }
75 
76 /// Represents information for subscribing to profile events.
77 pub struct SubscribeInfo {
78     profile_event: ProfileEvent,
79     extra_info: String,
80 }
81 
82 /// Represents options for synchronizing a profile.
83 pub struct SyncOptions;
84 /// Represents the result of a profile synchronization operation.
85 pub struct SyncResult;
86 
87 /// Represents types of changes that can occur to a profile.
88 #[repr(i32)]
89 #[derive(Copy, Clone)]
90 pub enum ProfileChangeType {
91     Unknown,
92     Inserted,
93     Updated,
94     Deleted,
95 }
96 
97 /// Represents an entry in a profile with key-value information and change type.
98 pub struct ProfileEntry {
99     /// The key of the profile entry.
100     pub key: String,
101     /// The value of the profile entry.
102     pub value: String,
103     /// The change type of the profile entry.
104     pub change_type: ProfileChangeType,
105 }
106 
107 /// Represents a notification for profile changes, containing affected profile entries,
108 /// device ID, and whether the change is local.
109 pub struct ProfileChangeNotification {
110     /// The profile entries affected by the change.
111     pub profile_entries: Vec<ProfileEntry>,
112     /// The ID of the device associated with the change.
113     pub device_id: String,
114     /// Indicates whether the change is local to the current device.
115     pub is_local: bool,
116 }
117 
118 /// Trait defining the callback methods for profile events.
119 pub trait IProfileEventCallback {
120     /// This method is called when a profile change notification is received.
on_profile_changednull121     fn on_profile_changed(&self, change_notification: &ProfileChangeNotification);
122 }
123 
124 struct ProfileEventCallback {
125     interface: CIProfileEventCb,
126     instance: Arc<dyn IProfileEventCallback>,
127 }
128 
129 impl ProfileEventCallback {
130     /// Structures with C-compatible layouts that safely interconvert the first structure field and structure instance.
131     /// Based on the C17 standard
132     ///
133     /// # Note
134     ///
135     /// This function performs a conversion of the pointer type to `*mut Self` and returns it.
136     /// Please note that the pointer `cb` is a raw pointer that needs to be handled carefully to avoid memory
137     /// safety issues and undefined behavior.
138     /// Make sure that the returned pointer is not null before dereferencing it.
from_interfacenull139     fn from_interface(cb: *mut CIProfileEventCb) -> Option<&'static mut Self>
140     {
141         let cb_ptr = cb as *mut Self;
142         // SAFETY: `cb_ptr` is valid, because `as_mut` has null pointer checking.
143         unsafe {
144             cb_ptr.as_mut()
145         }
146     }
147     /// Clone a `CIProfileEventCb` instance.
148     ///
149     /// # Note
150     ///
151     /// Please note that the pointer `cb` is a raw pointer that needs to be handled carefully to avoid memory
152     /// safety issues and undefined behavior.
153     /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
clonenull154     extern "C" fn clone(cb: *mut CIProfileEventCb) -> *mut CIProfileEventCb
155     {
156         if let Some(callback_mut) = ProfileEventCallback::from_interface(cb) {
157             let callback_box = Box::new(Self {
158                 interface: CIProfileEventCb {
159                     clone: Some(Self::clone),
160                     destruct: Some(Self::destruct),
161                     on_profile_changed: Some(Self::on_profile_changed),
162                 },
163                 instance: callback_mut.instance.clone(),
164             });
165             Box::into_raw(callback_box) as *mut CIProfileEventCb
166         } else {
167             error!(LOG_LABEL, "Failed to clone a CIProfileEventCb instance");
168             std::ptr::null_mut()
169         }
170     }
171     /// Destruct a `CIProfileEventCb` instance.
172     ///
173     /// # Note
174     ///
175     /// Please note that the pointer `cb` is a raw pointer that needs to be handled carefully to avoid memory
176     /// safety issues and undefined behavior.
177     /// Make sure to properly dereference and manipulate the data using appropriate safe Rust code.
destructnull178     extern "C" fn destruct(cb: *mut CIProfileEventCb)
179     {
180         if let Some(callback_mut) = ProfileEventCallback::from_interface(cb) {
181             // SAFETY: `callback_mut` is valid, becauce has been matched to `Some`.
182             unsafe { drop(Box::from_raw(callback_mut as *mut ProfileEventCallback)) };
183         } else {
184             error!(LOG_LABEL, "Failed to destruct a CIProfileEventCb instance");
185         }
186     }
187 
188     /// This callback function is invoked when a profile change notification occurs.
on_profile_changednull189     extern "C" fn on_profile_changed(_cb: *mut CIProfileEventCb, _notification: *const CProfileChangeNotification)
190     {
191         todo!()
192     }
193 }
194 
195 impl From<Arc<dyn IProfileEventCallback>> for ProfileEventCallback {
fromnull196     fn from(value: Arc<dyn IProfileEventCallback>) -> Self {
197         Self {
198             interface: CIProfileEventCb {
199                 clone: Some(Self::clone),
200                 destruct: None,
201                 on_profile_changed: Some(Self::on_profile_changed),
202             },
203             instance: value,
204         }
205     }
206 }
207 
208 /// Represents a device profile.
209 pub struct DeviceProfile;
210 
211 impl DeviceProfile {
check_return_codenull212     fn check_return_code(&self, ret: i32) -> FusionResult<()>
213     {
214         (ret == RET_OK).then_some(()).ok_or(FusionErrorCode::Fail)
215     }
216 
217     /// Updates the device profile with the specified `ServiceCharacteristicProfile`.
put_device_profilenull218     pub fn put_device_profile(_profile: &ServiceCharacteristicProfile) -> FusionResult<()>
219     {
220         todo!()
221     }
222 
223     /// Retrieves the device profile for the specified UDID and service ID.
get_device_profilenull224     pub fn get_device_profile(_udid: &str, _service_id: &str,
225         _profile: &ServiceCharacteristicProfile) -> FusionResult<()>
226     {
227         todo!()
228     }
229 
230     /// Subscribes to the specified profile events.
231     ///
232     /// # Arguments
233     ///
234     /// * `subscribe_infos` - A slice of `SubscribeInfo` structures representing the events to subscribe to.
235     /// * `event_callback` - A reference to the `IProfileEventCallback` trait object for handling profile events.
236     /// * `failed_events` - A mutable vector to store any events that failed to subscribe.
237     ///
238     /// # Returns
239     ///
240     /// An empty `FusionResult` indicating success or an error if subscribing to events fails.
subscribe_profile_eventsnull241     pub fn subscribe_profile_events(&self, subscribe_infos: &[SubscribeInfo],
242         event_callback: &Arc<dyn IProfileEventCallback>,
243         failed_events: &mut Vec<ProfileEvent>) -> FusionResult<()>
244     {
245         call_debug_enter!("DeviceProfile::subscribe_profile_events");
246         let mut subscriptions = Vec::<CSubscribeInfo>::new();
247         for info in subscribe_infos.iter() {
248             subscriptions.push(CSubscribeInfo {
249                 profile_event: info.profile_event as u32,
250                 extra_info: info.extra_info.as_ptr() as *const c_char,
251             });
252         }
253         let subscriptions_borrowed = CISubscribeInfos {
254             clone: None,
255             destruct: None,
256             subscribe_infos: subscriptions.as_ptr(),
257             num_of_subscribe_infos: subscriptions.len(),
258         };
259 
260         let mut event_cb = ProfileEventCallback::from(event_callback.clone());
261         let event_cb_ptr: *mut ProfileEventCallback = &mut event_cb;
262         let mut failed_ptr: *mut CIProfileEvents = std::ptr::null_mut();
263 
264         // SAFETY: no `None` here, cause `subscriptions_borrowed`, `event_cb_ptr` and `failed_ptr` are valid.
265         let ret = unsafe {
266             SubscribeProfileEvents(&subscriptions_borrowed, event_cb_ptr as *mut CIProfileEventCb, &mut failed_ptr)
267         };
268         if !failed_ptr.is_null() {
269             // SAFETY: `failed_ptr` is valid, because null pointer check has been performed.
270             let profile_events_slice = unsafe {
271                 std::slice::from_raw_parts((*failed_ptr).profile_events, (*failed_ptr).num_of_profile_events)
272             };
273             for profile_event in profile_events_slice.iter() {
274                 if let Ok(e) = ProfileEvent::try_from(*profile_event) {
275                     failed_events.push(e);
276                 }
277             }
278             // SAFETY: `failed_ptr` is valid, because null pointer check has been performed.
279             unsafe {
280                 if let Some(destruct) = (*failed_ptr).destruct {
281                     destruct(failed_ptr);
282                 }
283             }
284         }
285         Ok(err_log!(self.check_return_code(ret), "SubscribeProfileEvents"))
286     }
287 
288     /// Unsubscribes from the specified profile events.
289     ///
290     /// # Arguments
291     ///
292     /// * `profile_events` - A slice of `ProfileEvent` enumerations representing the events to unsubscribe from.
293     /// * `event_callback` - A reference to the `IProfileEventCallback` trait object for handling profile events.
294     /// * `failed_events` - A mutable vector to store any events that failed to unsubscribe.
295     ///
296     /// # Returns
297     ///
298     /// An empty `FusionResult` indicating success or an error if unsubscribing from events fails.
unsubscribe_profile_eventsnull299     pub fn unsubscribe_profile_events(&self, profile_events: &[ProfileEvent],
300         event_callback: &Arc<dyn IProfileEventCallback>,
301         failed_events: &mut Vec<ProfileEvent>) -> FusionResult<()>
302     {
303         call_debug_enter!("DeviceProfile::unsubscribe_profile_events");
304         let mut profileevents = Vec::<u32>::new();
305         for info in profile_events.iter() {
306             profileevents.push((*info).into());
307         }
308         let profile_events_borrowed = CIProfileEvents {
309             clone: None,
310             destruct: None,
311             profile_events: profileevents.as_ptr() as *mut u32,
312             num_of_profile_events: profileevents.len(),
313         };
314 
315         let mut event_cb = ProfileEventCallback::from(event_callback.clone());
316         let event_cb_ptr: *mut ProfileEventCallback = &mut event_cb;
317         let mut failed_ptr: *mut CIProfileEvents = std::ptr::null_mut();
318 
319         // SAFETY:  no `None` here, cause `profile_events_borrowed`, `event_cb_ptr` and `failed_ptr` are valid.
320         let ret = unsafe {
321             UnsubscribeProfileEvents(&profile_events_borrowed, event_cb_ptr as *mut CIProfileEventCb, &mut failed_ptr)
322         };
323         if !failed_ptr.is_null() {
324             // SAFETY: `failed_ptr` is valid, because null pointer check has been performed.
325             let profile_events_slice = unsafe {
326                 std::slice::from_raw_parts((*failed_ptr).profile_events, (*failed_ptr).num_of_profile_events)
327             };
328             for profile_event in profile_events_slice.iter() {
329                 if let Ok(e) = ProfileEvent::try_from(*profile_event) {
330                     failed_events.push(e);
331                 }
332             }
333             // SAFETY: `failed_ptr`is valid, because null pointer check has been performed.
334             unsafe {
335                 if let Some(destruct) = (*failed_ptr).destruct {
336                     destruct(failed_ptr);
337                 }
338             }
339         }
340         Ok(err_log!(self.check_return_code(ret), "UnsubscribeProfileEvents"))
341     }
342 
343     /// Synchronizes the device profile with the specified options.
sync_device_profilenull344     pub fn sync_device_profile(_sync_options: &SyncOptions,
345                                _sync_callback: &Arc<dyn IProfileEventCallback>) -> FusionResult<()>
346     {
347         todo!()
348     }
349 }
350