1 /*
2  * Copyright (c) 2024 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 #include "ime_aging_manager.h"
17 
18 #include "common_timer_errors.h"
19 #include "peruser_session.h"
20 
21 namespace OHOS {
22 namespace MiscServices {
23 constexpr uint32_t MAX_CACHES_SIZE = 5;
24 constexpr uint32_t AGING_TIME = 60;
25 constexpr uint32_t TIMER_TASK_INTERNAL = 60000;
ImeAgingManager()26 ImeAgingManager::ImeAgingManager() : timer_("imeCacheTimer"), timerId_(0)
27 {
28 }
29 
GetInstance()30 ImeAgingManager &ImeAgingManager::GetInstance()
31 {
32     static ImeAgingManager ImeAgingManager;
33     return ImeAgingManager;
34 }
35 
Push(const std::string & bundleName,const std::shared_ptr<ImeData> & imeData)36 bool ImeAgingManager::Push(const std::string &bundleName, const std::shared_ptr<ImeData> &imeData)
37 {
38     if (bundleName.empty() || imeData == nullptr || imeData->core == nullptr || imeData->agent == nullptr) {
39         IMSA_HILOGE("invalid ime data");
40         return false;
41     }
42     auto imeCache = std::make_shared<AgingIme>(*imeData, std::chrono::system_clock::now());
43 
44     std::lock_guard<std::recursive_mutex> lock(cacheMutex_);
45     auto it = imeCaches_.find(bundleName);
46     if (it != imeCaches_.end()) {
47         it->second = imeCache;
48         return true;
49     }
50     if (imeCaches_.empty()) {
51         StartAging();
52     }
53     if (imeCaches_.size() == MAX_CACHES_SIZE) {
54         ClearOldest();
55     }
56     imeCaches_.insert({ bundleName, imeCache });
57     IMSA_HILOGI("push ime: %{public}s", bundleName.c_str());
58     return true;
59 }
60 
Pop(const std::string & bundleName)61 std::shared_ptr<ImeData> ImeAgingManager::Pop(const std::string &bundleName)
62 {
63     std::lock_guard<std::recursive_mutex> lock(cacheMutex_);
64     auto it = imeCaches_.find(bundleName);
65     if (it == imeCaches_.end()) {
66         return nullptr;
67     }
68     auto ime = it->second->data;
69     if (ime.core->AsObject() != nullptr && ime.deathRecipient != nullptr) {
70         ime.core->AsObject()->RemoveDeathRecipient(ime.deathRecipient);
71         ime.deathRecipient = nullptr;
72     }
73     imeCaches_.erase(bundleName);
74     if (imeCaches_.empty()) {
75         StopAging();
76     }
77     IMSA_HILOGI("pop ime: %{public}s", bundleName.c_str());
78     return std::make_shared<ImeData>(ime);
79 }
80 
Clear()81 void ImeAgingManager::Clear()
82 {
83     std::lock_guard<std::recursive_mutex> lock(cacheMutex_);
84     for (auto it = imeCaches_.begin(); it != imeCaches_.end();) {
85         auto core = it->second->data.core;
86         if (core != nullptr) {
87             IMSA_HILOGI("clear ime: %{public}s", it->first.c_str());
88             ClearIme(it->second);
89         }
90         it = imeCaches_.erase(it);
91     }
92     StopAging();
93     IMSA_HILOGI("done");
94 }
95 
ClearOldest()96 void ImeAgingManager::ClearOldest()
97 {
98     std::lock_guard<std::recursive_mutex> lock(cacheMutex_);
99     auto oldestIme = imeCaches_.begin();
100     for (auto it = imeCaches_.begin(); it != imeCaches_.end(); it++) {
101         if (it->second->timestamp < oldestIme->second->timestamp) {
102             oldestIme = it;
103         }
104     }
105     auto core = oldestIme->second->data.core;
106     if (core != nullptr) {
107         IMSA_HILOGI("clear ime: %{public}s", oldestIme->first.c_str());
108         ClearIme(oldestIme->second);
109     }
110     imeCaches_.erase(oldestIme);
111 }
112 
AgingCache()113 void ImeAgingManager::AgingCache()
114 {
115     std::lock_guard<std::recursive_mutex> lock(cacheMutex_);
116     for (auto it = imeCaches_.begin(); it != imeCaches_.end();) {
117         // each IME can be kept for 60 seconds, and then be stopped.
118         auto now = std::chrono::system_clock::now();
119         if (std::chrono::duration_cast<std::chrono::seconds>(now - it->second->timestamp).count() < AGING_TIME) {
120             it++;
121             continue;
122         }
123         auto core = it->second->data.core;
124         if (core != nullptr) {
125             IMSA_HILOGI("clear ime: %{public}s", it->first.c_str());
126             ClearIme(it->second);
127         }
128         it = imeCaches_.erase(it);
129     }
130     if (imeCaches_.empty()) {
131         StopAging();
132     }
133 }
134 
ClearIme(const std::shared_ptr<AgingIme> & ime)135 void ImeAgingManager::ClearIme(const std::shared_ptr<AgingIme> &ime)
136 {
137     auto imeData = ime->data;
138     if (imeData.core == nullptr) {
139         return;
140     }
141     if (imeData.core->AsObject() != nullptr && imeData.deathRecipient != nullptr) {
142         imeData.core->AsObject()->RemoveDeathRecipient(imeData.deathRecipient);
143     }
144     imeData.core->StopInputService(true);
145 }
146 
StartAging()147 void ImeAgingManager::StartAging()
148 {
149     std::lock_guard<std::mutex> lock(timerMutex_);
150     IMSA_HILOGD("run in");
151     uint32_t ret = timer_.Setup();
152     if (ret != Utils::TIMER_ERR_OK) {
153         IMSA_HILOGE("failed to create timer");
154         return;
155     }
156     timerId_ = timer_.Register([this]() { AgingCache(); }, TIMER_TASK_INTERNAL, false);
157 }
158 
StopAging()159 void ImeAgingManager::StopAging()
160 {
161     std::lock_guard<std::mutex> lock(timerMutex_);
162     IMSA_HILOGD("run in");
163     timer_.Unregister(timerId_);
164     timer_.Shutdown(false);
165 }
166 } // namespace MiscServices
167 } // namespace OHOS
168