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 #include <thread>
16 
17 #include "trigger_connector_internal_impl.h"
18 #include "intell_voice_log.h"
19 #include "v1_1/iintell_voice_trigger_manager.h"
20 #include "scope_guard.h"
21 #include "trigger_callback_impl.h"
22 #include "memory_guard.h"
23 #include "iproxy_broker.h"
24 #include "intell_voice_death_recipient.h"
25 
26 #define LOG_TAG "TriggerConnector"
27 
28 using namespace std;
29 using namespace OHOS::HDI::ServiceManager::V1_0;
30 using namespace OHOS::IntellVoiceUtils;
31 using namespace OHOS::HDI::IntelligentVoice::Trigger::V1_0;
32 using namespace OHOS::HDI::IntelligentVoice::Trigger::V1_1;
33 
34 namespace OHOS {
35 namespace IntellVoiceTrigger {
36 static constexpr uint32_t MAX_RECOGNITION_EVENT_NUM = 100;
37 static const std::string INTELL_VOICE_TRIGGER_SERVICE = "intell_voice_trigger_manager_service";
38 static const std::string THREAD_NAME = "TriggerThread_";
39 
TriggerConnector(OnServiceStartCb cb,const IntellVoiceTriggerAdapterDsecriptor & desc)40 TriggerConnector::TriggerConnector(OnServiceStartCb cb, const IntellVoiceTriggerAdapterDsecriptor &desc) : cb_(cb)
41 {
42     desc_.adapterName = desc.adapterName;
43     LoadHdiAdapter();
44 }
45 
~TriggerConnector()46 TriggerConnector::~TriggerConnector()
47 {
48     TriggerHostManager::UnloadTriggerAdapter(desc_);
49 }
50 
LoadHdiAdapter()51 bool TriggerConnector::LoadHdiAdapter()
52 {
53     if (!TriggerHostManager::Init()) {
54         INTELL_VOICE_LOG_WARN("failed to init trigger host manager");
55         return false;
56     }
57 
58     TriggerHostManager::RegisterTriggerHDIDeathRecipient();
59     if (!TriggerHostManager::LoadTriggerAdapter(desc_)) {
60         INTELL_VOICE_LOG_ERROR("failed to load trigger adapter");
61         return false;
62     }
63 
64     return true;
65 }
66 
GetModule(std::shared_ptr<IIntellVoiceTriggerConnectorCallback> callback)67 std::shared_ptr<IIntellVoiceTriggerConnectorModule> TriggerConnector::GetModule(
68     std::shared_ptr<IIntellVoiceTriggerConnectorCallback> callback)
69 {
70     INTELL_VOICE_LOG_INFO("enter");
71     if (callback == nullptr) {
72         INTELL_VOICE_LOG_ERROR("callback is nullptr");
73         return nullptr;
74     }
75 
76     std::lock_guard<std::mutex> lock(mutex_);
77     do {
78         if (TriggerHostManager::GetAdapter() != nullptr) {
79             INTELL_VOICE_LOG_INFO("already load hdi adapter");
80             break;
81         }
82         INTELL_VOICE_LOG_INFO("start to load hdi adapter");
83         if (!LoadHdiAdapter()) {
84             getModuleFail_ = true;
85             return nullptr;
86         }
87     } while (0);
88 
89     getModuleFail_ = false;
90     std::shared_ptr<TriggerSession> session = std::make_shared<TriggerSession>(this, callback, activeSessions_.size());
91     if (session == nullptr) {
92         INTELL_VOICE_LOG_ERROR("failed to malloc session");
93         return nullptr;
94     }
95 
96     activeSessions_.insert(session);
97     return session;
98 }
99 
GetProperties()100 IntellVoiceTriggerProperties TriggerConnector::GetProperties()
101 {
102     IntellVoiceTriggerProperties properties;
103     return properties;
104 }
105 
OnReceive(const ServiceStatus & serviceStatus)106 void TriggerConnector::OnReceive(const ServiceStatus &serviceStatus)
107 {
108     if (serviceStatus.serviceName != INTELL_VOICE_TRIGGER_SERVICE) {
109         return;
110     }
111 
112     INTELL_VOICE_LOG_INFO("status:%{public}d", serviceStatus.status);
113     std::lock_guard<std::mutex> lock(mutex_);
114     if (serviceStatus.status == serviceState_) {
115         INTELL_VOICE_LOG_INFO("service state not change");
116         return;
117     }
118 
119     serviceState_ = serviceStatus.status;
120     if ((!getModuleFail_) || (serviceState_ != SERVIE_STATUS_START) || (TriggerHostManager::GetAdapter() != nullptr)) {
121         INTELL_VOICE_LOG_INFO("no need to notify mgr, getModuleFail:%{public}d, service state:%{public}d",
122             getModuleFail_, serviceState_);
123         return;
124     }
125 
126     if (cb_ != nullptr) {
127         cb_(desc_);
128     }
129 }
130 
TriggerSession(TriggerConnector * connector,std::shared_ptr<IIntellVoiceTriggerConnectorCallback> callback,uint32_t threadId)131 TriggerConnector::TriggerSession::TriggerSession(TriggerConnector *connector,
132     std::shared_ptr<IIntellVoiceTriggerConnectorCallback> callback, uint32_t threadId)
133     : TaskExecutor(THREAD_NAME + std::to_string(threadId), MAX_RECOGNITION_EVENT_NUM),
134     connector_(connector), callback_(callback)
135 {
136     TaskExecutor::StartThread();
137 }
138 
~TriggerSession()139 TriggerConnector::TriggerSession::~TriggerSession()
140 {
141     loadedModels_.clear();
142     TaskExecutor::StopThread();
143 }
144 
LoadModel(std::shared_ptr<GenericTriggerModel> model,int32_t & modelHandle)145 int32_t TriggerConnector::TriggerSession::LoadModel(std::shared_ptr<GenericTriggerModel> model, int32_t &modelHandle)
146 {
147     std::lock_guard<std::mutex> lock(connector_->mutex_);
148     if (connector_->TriggerHostManager::GetAdapter() == nullptr) {
149         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
150         return -1;
151     }
152     std::shared_ptr<Model> loadedModle = Model::Create(this);
153     if (loadedModle == nullptr) {
154         INTELL_VOICE_LOG_ERROR("failed to malloc intell voice model");
155         return -1;
156     }
157 
158     int32_t result = loadedModle->Load(model, modelHandle);
159     if (result != 0) {
160         INTELL_VOICE_LOG_ERROR("failed to load generic trigger model");
161         return result;
162     }
163     loadedModels_.insert(std::make_pair(modelHandle, loadedModle));
164     return result;
165 }
166 
UnloadModel(int32_t modelHandle)167 int32_t TriggerConnector::TriggerSession::UnloadModel(int32_t modelHandle)
168 {
169     std::lock_guard<std::mutex> lock(connector_->mutex_);
170     if (connector_->TriggerHostManager::GetAdapter() == nullptr) {
171         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
172         return -1;
173     }
174     auto it = loadedModels_.find(modelHandle);
175     if ((it == loadedModels_.end()) || (it->second == nullptr)) {
176         INTELL_VOICE_LOG_ERROR("failed to find model");
177         return -1;
178     }
179 
180     int32_t ret = it->second->Unload();
181     loadedModels_.erase(it);
182     return ret;
183 }
184 
Start(int32_t modelHandle)185 int32_t TriggerConnector::TriggerSession::Start(int32_t modelHandle)
186 {
187     std::lock_guard<std::mutex> lock(connector_->mutex_);
188     if (connector_->TriggerHostManager::GetAdapter() == nullptr) {
189         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
190         return -1;
191     }
192     auto it = loadedModels_.find(modelHandle);
193     if ((it == loadedModels_.end()) || (it->second == nullptr)) {
194         INTELL_VOICE_LOG_ERROR("failed to find model");
195         return -1;
196     }
197     return it->second->Start();
198 }
199 
Stop(int32_t modelHandle)200 int32_t TriggerConnector::TriggerSession::Stop(int32_t modelHandle)
201 {
202     std::lock_guard<std::mutex> lock(connector_->mutex_);
203     if (connector_->TriggerHostManager::GetAdapter() == nullptr) {
204         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
205         return -1;
206     }
207     auto it = loadedModels_.find(modelHandle);
208     if ((it == loadedModels_.end()) || (it->second == nullptr)) {
209         INTELL_VOICE_LOG_ERROR("failed to find model");
210         return -1;
211     }
212     return it->second->Stop();
213 }
214 
SetParams(const std::string & key,const std::string & value)215 int32_t TriggerConnector::TriggerSession::SetParams(const std::string& key, const std::string& value)
216 {
217     std::lock_guard<std::mutex> lock(connector_->mutex_);
218     if (connector_->TriggerHostManager::GetAdapterV1_1() == nullptr) {
219         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
220         return -1;
221     }
222 
223     int32_t ret = connector_->TriggerHostManager::GetAdapterV1_1()->SetParams(key, value);
224     if (ret != 0) {
225         INTELL_VOICE_LOG_ERROR("failed to set params");
226     }
227 
228     return ret;
229 }
230 
GetParams(const std::string & key,std::string & value)231 int32_t TriggerConnector::TriggerSession::GetParams(const std::string& key, std::string &value)
232 {
233     std::lock_guard<std::mutex> lock(connector_->mutex_);
234     if (connector_->TriggerHostManager::GetAdapterV1_1() == nullptr) {
235         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
236         return -1;
237     }
238 
239     return connector_->TriggerHostManager::GetAdapterV1_1()->GetParams(key, value);
240 }
241 
HandleRecognitionHdiEvent(std::shared_ptr<IntellVoiceRecognitionEvent> event,int32_t modelHandle)242 void TriggerConnector::TriggerSession::HandleRecognitionHdiEvent(
243     std::shared_ptr<IntellVoiceRecognitionEvent> event, int32_t modelHandle)
244 {
245     std::function<void()> func = std::bind(
246         &TriggerConnector::TriggerSession::ProcessRecognitionHdiEvent, this, event, modelHandle);
247     TaskExecutor::AddAsyncTask(func);
248 }
249 
ProcessRecognitionHdiEvent(std::shared_ptr<IntellVoiceRecognitionEvent> event,int32_t modelHandle)250 void TriggerConnector::TriggerSession::ProcessRecognitionHdiEvent(
251     std::shared_ptr<IntellVoiceRecognitionEvent> event, int32_t modelHandle)
252 {
253     if (event == nullptr) {
254         INTELL_VOICE_LOG_ERROR("event is nullptr");
255         return;
256     }
257     if (connector_ == nullptr) {
258         INTELL_VOICE_LOG_ERROR("connector is nullptr");
259         return;
260     }
261 
262     {
263         std::lock_guard<std::mutex> lock(connector_->mutex_);
264         auto it = loadedModels_.find(modelHandle);
265         if ((it == loadedModels_.end()) || (it->second == nullptr)) {
266             INTELL_VOICE_LOG_ERROR("can not find model, handle:%{public}d",  modelHandle);
267             return;
268         }
269 
270         if (it->second->GetState() != Model::ModelState::ACTIVE) {
271             INTELL_VOICE_LOG_ERROR("unexpected recognition event, handle:%{public}d",  modelHandle);
272             return;
273         }
274 
275         it->second->SetState(Model::ModelState::LOADED);
276     }
277     callback_->OnRecognition(modelHandle, *(event.get()));
278 }
279 
Create(TriggerSession * session)280 std::shared_ptr<TriggerConnector::TriggerSession::Model> TriggerConnector::TriggerSession::Model::Create(
281     TriggerSession *session)
282 {
283     return std::shared_ptr<Model>(new (std::nothrow) Model(session));
284 }
285 
Load(std::shared_ptr<GenericTriggerModel> model,int32_t & modelHandle)286 int32_t TriggerConnector::TriggerSession::Model::Load(std::shared_ptr<GenericTriggerModel> model, int32_t &modelHandle)
287 {
288     INTELL_VOICE_LOG_INFO("enter");
289     if (GetState() != IDLE) {
290         INTELL_VOICE_LOG_ERROR("model has already loaded");
291         return -1;
292     }
293 
294     callback_ = sptr<IIntellVoiceTriggerCallback>(new (std::nothrow) TriggerCallbackImpl(shared_from_this()));
295     if (callback_ == nullptr) {
296         INTELL_VOICE_LOG_ERROR("callback_ is nullptr");
297         return -1;
298     }
299 
300     IntellVoiceTriggerModel triggerModel;
301     triggerModel.data = CreateAshmemFromModelData(model->GetData());
302     if (triggerModel.data == nullptr) {
303         INTELL_VOICE_LOG_ERROR("data is nullptr");
304         return -1;
305     }
306     triggerModel.type = static_cast<IntellVoiceTriggerModelType>(model->GetType());
307     triggerModel.uid = static_cast<uint32_t>(model->GetUuid());
308 
309     ON_SCOPE_EXIT
310     {
311         INTELL_VOICE_LOG_INFO("close ashmem");
312         triggerModel.data->UnmapAshmem();
313         triggerModel.data->CloseAshmem();
314     };
315 
316     int32_t handle;
317     int32_t ret = session_->GetTriggerConnector()->TriggerHostManager::GetAdapter()->LoadModel(triggerModel,
318         callback_, 0, handle);
319     if (ret != 0) {
320         INTELL_VOICE_LOG_ERROR("failed to load model");
321         return ret;
322     }
323     (void)model;
324     handle_ = handle;
325     modelHandle = handle_;
326     SetState(LOADED);
327     return ret;
328 }
329 
Start()330 int32_t TriggerConnector::TriggerSession::Model::Start()
331 {
332     INTELL_VOICE_LOG_INFO("enter");
333     if (GetState() != LOADED) {
334         INTELL_VOICE_LOG_ERROR("model has not loaded");
335         return -1;
336     }
337 
338     int32_t ret = session_->GetTriggerConnector()->TriggerHostManager::GetAdapter()->Start(handle_);
339     if (ret != 0) {
340         INTELL_VOICE_LOG_ERROR("failed to load model");
341         return ret;
342     }
343 
344 #ifdef TRIGGER_MANAGER_TEST
345     std::thread(&TriggerConnector::TriggerSession::Model::TriggerManagerCallbackTest, this).detach();
346 #endif
347 
348     SetState(ACTIVE);
349     return ret;
350 }
351 
Stop()352 int32_t TriggerConnector::TriggerSession::Model::Stop()
353 {
354     INTELL_VOICE_LOG_INFO("enter");
355     if (GetState() != ACTIVE) {
356         INTELL_VOICE_LOG_ERROR("model has not activated");
357         return -1;
358     }
359 
360     int32_t ret = session_->GetTriggerConnector()->TriggerHostManager::GetAdapter()->Stop(handle_);
361     if (ret != 0) {
362         INTELL_VOICE_LOG_ERROR("failed to load model");
363         return ret;
364     }
365 
366     SetState(LOADED);
367     return ret;
368 }
369 
Unload()370 int32_t TriggerConnector::TriggerSession::Model::Unload()
371 {
372     INTELL_VOICE_LOG_INFO("enter");
373     if (GetState() == IDLE) {
374         INTELL_VOICE_LOG_ERROR("model has not loaded");
375         return -1;
376     }
377 
378     int32_t ret = session_->GetTriggerConnector()->TriggerHostManager::GetAdapter()->UnloadModel(handle_);
379     if (ret != 0) {
380         INTELL_VOICE_LOG_ERROR("failed to load model");
381         return ret;
382     }
383 
384     SetState(IDLE);
385     return ret;
386 }
387 
388 #ifdef TRIGGER_MANAGER_TEST
TriggerManagerCallbackTest()389 void TriggerConnector::TriggerSession::Model::TriggerManagerCallbackTest()
390 {
391     INTELL_VOICE_LOG_ERROR("enter");
392     IntellVoiceRecognitionEvent recognitionEvent;
393     recognitionEvent.status = static_cast<RecognitionStatus>(0);
394     recognitionEvent.type = static_cast<IntellVoiceTriggerModelType>(1);
395     recognitionEvent.modelHandle = handle_;
396     TriggerConnector::TriggerSession::Model::OnRecognitionHdiEvent(recognitionEvent, 0);
397 }
398 #endif
399 
OnRecognitionHdiEvent(const IntellVoiceRecognitionEvent & event,int32_t cookie)400 void TriggerConnector::TriggerSession::Model::OnRecognitionHdiEvent(
401     const IntellVoiceRecognitionEvent &event, int32_t cookie)
402 {
403     (void)cookie;
404     std::shared_ptr<IntellVoiceRecognitionEvent> recognitionEvent = std::make_shared<IntellVoiceRecognitionEvent>();
405     if (recognitionEvent == nullptr) {
406         INTELL_VOICE_LOG_ERROR("recognitionEvent is nullptr");
407         return;
408     }
409 
410     recognitionEvent->status = event.status;
411     recognitionEvent->type = event.type;
412     recognitionEvent->modelHandle = event.modelHandle;
413 
414     INTELL_VOICE_LOG_INFO("handle: %{public}d", handle_);
415     session_->HandleRecognitionHdiEvent(recognitionEvent, handle_);
416 }
417 
CreateAshmemFromModelData(const std::vector<uint8_t> & modelData)418 sptr<Ashmem> TriggerConnector::TriggerSession::Model::CreateAshmemFromModelData(const std::vector<uint8_t> &modelData)
419 {
420     if (modelData.size() == 0) {
421         INTELL_VOICE_LOG_ERROR("data is empty");
422         return nullptr;
423     }
424 
425     sptr<Ashmem> buffer = OHOS::Ashmem::CreateAshmem("ModelData", modelData.size());
426     if (buffer == nullptr) {
427         INTELL_VOICE_LOG_ERROR("failed to create ashmem");
428         return nullptr;
429     }
430 
431     if (!buffer->MapReadAndWriteAshmem()) {
432         INTELL_VOICE_LOG_ERROR("failed to map ashmem");
433         goto ERR_EXIT;
434     }
435 
436     if (!buffer->WriteToAshmem(modelData.data(), modelData.size(), 0)) {
437         INTELL_VOICE_LOG_ERROR("failed to write ashmem");
438         goto ERR_EXIT;
439     }
440 
441     INTELL_VOICE_LOG_INFO("model data size:%{public}zu", modelData.size());
442     return buffer;
443 
444 ERR_EXIT:
445     buffer->UnmapAshmem();
446     buffer->CloseAshmem();
447     return nullptr;
448 }
449 }  // namespace IntellVoiceTrigger
450 }  // namespace OHOS