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