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//! Plugin manager, On-demand load and unload dynamic library.
17//! Plugin module supply one group of interfaces, bussiness implement these interfaces
18//! as a plugin, this plugin can manage by plugin manager.
19//! Currently there are two plugins, one is mouse and key coordination, another is drag
20//! interacton. They develop base on plugin manager.
21
22#![allow(unused_variables)]
23#![allow(dead_code)]
24
25use std::collections::HashMap;
26use std::path::Path;
27use std::ffi::{ c_char, CString };
28
29use hilog_rust::{ debug, info, error, hilog, HiLogLabel, LogType };
30
31use fusion_data_rust::{ Intention, IPlugin };
32use fusion_utils_rust::call_debug_enter;
33use fusion_basic_server_rust::FusionBasicServer;
34
35const LOG_LABEL: HiLogLabel = HiLogLabel {
36    log_type: LogType::LogCore,
37    domain: 0xD002220,
38    tag: "FusionPluginManager"
39};
40
41/// Helper to define intefaces of library that will be used by [`PluginManager`]
42/// to interact with it.
43#[macro_export]
44macro_rules! export_plugin {
45    ($plugin_type:ty) => {
46        /// Create a new instance of [`IPlugin`] through which functionalities
47        /// of this module can be accessed.
48        /// # Safety
49        #[no_mangle]
50        pub unsafe extern "C" fn _create_plugin() -> *mut dyn IPlugin {
51            let boxed = Box::new(<$plugin_type>::default());
52            Box::into_raw(boxed)
53        }
54    };
55}
56
57/// Loading、unloading and bookkeeping of modules.
58#[derive(Default)]
59pub struct PluginManager {
60    loaders: HashMap<Intention, libloading::Library>
61}
62
63impl PluginManager {
64    fn instantiate_loader(&mut self, intention: &Intention) {
65        call_debug_enter!("PluginManager::instantiate_loader");
66        let file_path = match intention {
67            Intention::Drag => {
68                Path::new("/system/lib/libfusion_drag_server_ffi.z.so")
69            }
70            Intention::Coordination => {
71                Path::new("/system/lib/libfusion_coordination_server_ffi.z.so")
72            }
73            _ => {
74                return;
75            }
76        };
77
78        let lib_result = unsafe {
79            libloading::Library::new(file_path.as_os_str())
80        };
81        match lib_result {
82            Ok(lib) => {
83                info!(LOG_LABEL, "New Library instance created");
84                self.loaders.insert(intention.clone(), lib);
85            }
86            Err(_) => {
87                info!(LOG_LABEL, "Failed to create Library instance");
88            }
89        }
90    }
91
92    fn do_load_plugin(&mut self, intention: Intention) -> Option<Box<dyn IPlugin>> {
93        call_debug_enter!("PluginManager::do_load_plugin");
94        if !self.loaders.contains_key(&intention) {
95            self.instantiate_loader(&intention);
96        }
97        match self.loaders.get(&intention) {
98            Some(loader) => {
99                if let Ok(creator) = unsafe {
100                    loader.get::<libloading::Symbol<unsafe extern "C" fn() -> *mut dyn IPlugin>>(b"_create_plugin")
101                } {
102                    info!(LOG_LABEL, "Create plugin instance");
103                    let plugin_ptr = unsafe {
104                        creator()
105                    };
106                    if plugin_ptr.is_null() {
107                        error!(LOG_LABEL, "Failed to create plugin instance");
108                        None
109                    } else {
110                        Some(unsafe {
111                            info!(LOG_LABEL, "Plugin is loaded");
112                            Box::from_raw(plugin_ptr)
113                        })
114                    }
115                } else {
116                    error!(LOG_LABEL, "Symbol is not found");
117                    None
118                }
119            }
120            None => {
121                error!(LOG_LABEL, "Can not instantiate loader");
122                None
123            }
124        }
125    }
126
127    /// Load module identified by [`intention`].
128    pub fn load_plugin(&mut self, intention: Intention) -> Option<Box<dyn IPlugin>>
129    {
130        call_debug_enter!("PluginManager::load_plugin");
131        match intention {
132            Intention::Basic => {
133                Some(Box::<FusionBasicServer>::default())
134            }
135            _ => {
136                self.do_load_plugin(intention)
137            }
138        }
139    }
140
141    /// Unload module identified by [`intention`].
142    pub fn unload_plugin(&mut self, intention: Intention) {
143        call_debug_enter!("PluginManager::unload_plugin");
144        if let Some(loader) = self.loaders.remove(&intention) {
145            info!(LOG_LABEL, "Library({}) is unloaded", @public(intention as u32));
146        } else {
147            debug!(LOG_LABEL, "Library({}) has not been loaded", @public(intention as u32));
148        }
149    }
150}
151