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