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 #define LOG_TAG "AutoCache"
16 #include "store/auto_cache.h"
17
18 #include <cinttypes>
19
20 #include "changeevent/remote_change_event.h"
21 #include "eventcenter/event_center.h"
22 #include "log_print.h"
23 #include "screenlock/screen_lock.h"
24 #include "utils/anonymous.h"
25 namespace OHOS::DistributedData {
GetInstance()26 AutoCache &AutoCache::GetInstance()
27 {
28 static AutoCache cache;
29 return cache;
30 }
31
RegCreator(int32_t type, Creator creator)32 int32_t AutoCache::RegCreator(int32_t type, Creator creator)
33 {
34 if (type >= MAX_CREATOR_NUM) {
35 return E_ERROR;
36 }
37 creators_[type] = creator;
38 return 0;
39 }
40
Bind(std::shared_ptr<Executor> executor)41 void AutoCache::Bind(std::shared_ptr<Executor> executor)
42 {
43 if (executor == nullptr || taskId_ != Executor::INVALID_TASK_ID) {
44 return;
45 }
46 executor_ = std::move(executor);
47 }
48
AutoCache()49 AutoCache::AutoCache()
50 {
51 }
52
~AutoCache()53 AutoCache::~AutoCache()
54 {
55 GarbageCollect(true);
56 if (executor_ != nullptr) {
57 executor_->Remove(taskId_, true);
58 }
59 }
60
GetStore(const StoreMetaData &meta, const Watchers &watchers)61 AutoCache::Store AutoCache::GetStore(const StoreMetaData &meta, const Watchers &watchers)
62 {
63 Store store;
64 if ((meta.area >= GeneralStore::EL4 && ScreenManager::GetInstance()->IsLocked()) ||
65 meta.storeType >= MAX_CREATOR_NUM || meta.storeType < 0 || !creators_[meta.storeType] ||
66 disables_.ContainIf(meta.tokenId, [&meta](const std::set<std::string> &stores) -> bool {
67 return stores.count(meta.storeId) != 0;
68 })) {
69 return store;
70 }
71
72 stores_.Compute(
73 meta.tokenId, [this, &meta, &watchers, &store](auto &, std::map<std::string, Delegate> &stores) -> bool {
74 if (disableStores_.count(meta.dataDir) != 0) {
75 ZLOGW("store is closing, tokenId:0x%{public}x user:%{public}s"
76 "bundleName:%{public}s storeName:%{public}s",
77 meta.tokenId, meta.user.c_str(), meta.bundleName.c_str(), meta.GetStoreAlias().c_str());
78 return !stores.empty();
79 }
80 auto it = stores.find(meta.storeId);
81 if (it != stores.end()) {
82 if (!watchers.empty()) {
83 it->second.SetObservers(watchers);
84 }
85 store = it->second;
86 return !stores.empty();
87 }
88 auto *dbStore = creators_[meta.storeType](meta);
89 if (dbStore == nullptr) {
90 ZLOGE("creator failed. storeName:%{public}s", meta.GetStoreAlias().c_str());
91 return !stores.empty();
92 }
93 dbStore->SetExecutor(executor_);
94 auto result = stores.emplace(std::piecewise_construct, std::forward_as_tuple(meta.storeId),
95 std::forward_as_tuple(dbStore, watchers, atoi(meta.user.c_str()), meta));
96 store = result.first->second;
97 StartTimer();
98 return !stores.empty();
99 });
100 return store;
101 }
102
GetStoresIfPresent(uint32_t tokenId, const std::string &storeName)103 AutoCache::Stores AutoCache::GetStoresIfPresent(uint32_t tokenId, const std::string &storeName)
104 {
105 Stores stores;
106 stores_.ComputeIfPresent(
107 tokenId, [&stores, &storeName](auto &, std::map<std::string, Delegate> &delegates) -> bool {
108 if (storeName.empty()) {
109 for (auto &[_, delegate] : delegates) {
110 stores.push_back(delegate);
111 }
112 } else {
113 auto it = delegates.find(storeName);
114 if (it != delegates.end()) {
115 stores.push_back(it->second);
116 }
117 }
118 return !stores.empty();
119 });
120 return stores;
121 }
122
123 // Should be used within stores_'s thread safe methods
StartTimer()124 void AutoCache::StartTimer()
125 {
126 if (executor_ == nullptr || taskId_ != Executor::INVALID_TASK_ID) {
127 return;
128 }
129 taskId_ = executor_->Schedule(
130 [this]() {
131 GarbageCollect(false);
132 stores_.DoActionIfEmpty([this]() {
133 if (executor_ == nullptr || taskId_ == Executor::INVALID_TASK_ID) {
134 return;
135 }
136 executor_->Remove(taskId_);
137 ZLOGD("remove timer,taskId: %{public}" PRIu64, taskId_);
138 taskId_ = Executor::INVALID_TASK_ID;
139 });
140 },
141 std::chrono::minutes(INTERVAL), std::chrono::minutes(INTERVAL));
142 ZLOGD("start timer,taskId: %{public}" PRIu64, taskId_);
143 }
144
CloseStore(uint32_t tokenId, const std::string &storeId)145 void AutoCache::CloseStore(uint32_t tokenId, const std::string &storeId)
146 {
147 ZLOGD("close store start, store:%{public}s, token:%{public}u", Anonymous::Change(storeId).c_str(), tokenId);
148 std::set<std::string> storeIds;
149 std::list<Delegate> closeStores;
150 bool isScreenLocked = ScreenManager::GetInstance()->IsLocked();
151 stores_.ComputeIfPresent(tokenId,
152 [this, &storeId, isScreenLocked, &storeIds, &closeStores](auto &key, auto &delegates) {
153 auto it = delegates.begin();
154 while (it != delegates.end()) {
155 if ((storeId == it->first || storeId.empty()) &&
156 (!isScreenLocked || it->second.GetArea() < GeneralStore::EL4) &&
157 disableStores_.count(it->second.GetDataDir()) == 0) {
158 disableStores_.insert(it->second.GetDataDir());
159 storeIds.insert(it->first);
160 closeStores.emplace(closeStores.end(), it->second);
161 } else {
162 ++it;
163 }
164 }
165 return !delegates.empty();
166 });
167 closeStores.clear();
168 stores_.ComputeIfPresent(tokenId, [this, &storeIds](auto &key, auto &delegates) {
169 for (auto it = delegates.begin(); it != delegates.end();) {
170 if (storeIds.count(it->first) != 0) {
171 disableStores_.erase(it->second.GetDataDir());
172 it = delegates.erase(it);
173 } else {
174 ++it;
175 }
176 }
177 return !delegates.empty();
178 });
179 }
180
CloseExcept(const std::set<int32_t> &users)181 void AutoCache::CloseExcept(const std::set<int32_t> &users)
182 {
183 bool isScreenLocked = ScreenManager::GetInstance()->IsLocked();
184 stores_.EraseIf([&users, isScreenLocked](const auto &tokenId, std::map<std::string, Delegate> &delegates) {
185 if (delegates.empty() || users.count(delegates.begin()->second.GetUser()) != 0) {
186 return delegates.empty();
187 }
188
189 for (auto it = delegates.begin(); it != delegates.end();) {
190 // if the kv store is BUSY we wait more INTERVAL minutes again
191 if ((isScreenLocked && it->second.GetArea() >= GeneralStore::EL4) || !it->second.Close()) {
192 ++it;
193 } else {
194 it = delegates.erase(it);
195 }
196 }
197 return delegates.empty();
198 });
199 }
200
SetObserver(uint32_t tokenId, const std::string &storeId, const AutoCache::Watchers &watchers)201 void AutoCache::SetObserver(uint32_t tokenId, const std::string &storeId, const AutoCache::Watchers &watchers)
202 {
203 stores_.ComputeIfPresent(tokenId, [&storeId, &watchers](auto &key, auto &stores) {
204 ZLOGD("tokenId:0x%{public}x storeId:%{public}s observers:%{public}zu", key,
205 Anonymous::Change(storeId).c_str(), watchers.size());
206 auto it = stores.find(storeId);
207 if (it != stores.end()) {
208 it->second.SetObservers(watchers);
209 }
210 return true;
211 });
212 }
213
GarbageCollect(bool isForce)214 void AutoCache::GarbageCollect(bool isForce)
215 {
216 auto current = std::chrono::steady_clock::now();
217 bool isScreenLocked = ScreenManager::GetInstance()->IsLocked();
218 stores_.EraseIf([¤t, isForce, isScreenLocked](auto &key, std::map<std::string, Delegate> &delegates) {
219 for (auto it = delegates.begin(); it != delegates.end();) {
220 // if the store is BUSY we wait more INTERVAL minutes again
221 if ((!isScreenLocked || it->second.GetArea() < GeneralStore::EL4) && (isForce || it->second < current) &&
222 it->second.Close()) {
223 it = delegates.erase(it);
224 } else {
225 ++it;
226 }
227 }
228 return delegates.empty();
229 });
230 }
231
Enable(uint32_t tokenId, const std::string &storeId)232 void AutoCache::Enable(uint32_t tokenId, const std::string &storeId)
233 {
234 disables_.ComputeIfPresent(tokenId, [&storeId](auto key, std::set<std::string> &stores) {
235 stores.erase(storeId);
236 return !(stores.empty() || storeId.empty());
237 });
238 }
239
Disable(uint32_t tokenId, const std::string &storeId)240 void AutoCache::Disable(uint32_t tokenId, const std::string &storeId)
241 {
242 disables_.Compute(tokenId, [&storeId](auto key, std::set<std::string> &stores) {
243 stores.insert(storeId);
244 return !stores.empty();
245 });
246 CloseStore(tokenId, storeId);
247 }
248
Delegate(GeneralStore *delegate, const Watchers &watchers, int32_t user, const StoreMetaData &meta)249 AutoCache::Delegate::Delegate(GeneralStore *delegate, const Watchers &watchers, int32_t user, const StoreMetaData &meta)
250 : store_(delegate), watchers_(watchers), user_(user), meta_(meta)
251 {
252 time_ = std::chrono::steady_clock::now() + std::chrono::minutes(INTERVAL);
253 if (store_ != nullptr) {
254 store_->Watch(Origin::ORIGIN_ALL, *this);
255 }
256 }
257
Delegate(const Delegate& delegate)258 AutoCache::Delegate::Delegate(const Delegate& delegate)
259 {
260 store_ = delegate.store_;
261 if (store_ != nullptr) {
262 store_->AddRef();
263 }
264 }
265
~Delegate()266 AutoCache::Delegate::~Delegate()
267 {
268 if (store_ != nullptr) {
269 store_->Close(true);
270 store_->Unwatch(Origin::ORIGIN_ALL, *this);
271 store_->Release();
272 store_ = nullptr;
273 }
274 }
275
operator Store()276 AutoCache::Delegate::operator Store()
277 {
278 time_ = std::chrono::steady_clock::now() + std::chrono::minutes(INTERVAL);
279 if (store_ != nullptr) {
280 store_->AddRef();
281 return Store(store_, [](GeneralStore *store) { store->Release(); });
282 }
283 return nullptr;
284 }
285
operator <(const AutoCache::Time &time) const286 bool AutoCache::Delegate::operator<(const AutoCache::Time &time) const
287 {
288 return time_ < time;
289 }
290
Close()291 bool AutoCache::Delegate::Close()
292 {
293 if (store_ != nullptr) {
294 auto status = store_->Close();
295 if (status == Error::E_BUSY) {
296 return false;
297 }
298 store_->Unwatch(Origin::ORIGIN_ALL, *this);
299 }
300 return true;
301 }
302
SetObservers(const AutoCache::Watchers &watchers)303 void AutoCache::Delegate::SetObservers(const AutoCache::Watchers &watchers)
304 {
305 std::unique_lock<decltype(mutex_)> lock(mutex_);
306 watchers_ = watchers;
307 }
308
GetUser() const309 int32_t AutoCache::Delegate::GetUser() const
310 {
311 return user_;
312 }
313
GetArea() const314 int32_t AutoCache::Delegate::GetArea() const
315 {
316 return meta_.area;
317 }
318
GetDataDir() const319 const std::string& AutoCache::Delegate::GetDataDir() const
320 {
321 return meta_.dataDir;
322 }
323
OnChange(const Origin &origin, const PRIFields &primaryFields, ChangeInfo &&values)324 int32_t AutoCache::Delegate::OnChange(const Origin &origin, const PRIFields &primaryFields, ChangeInfo &&values)
325 {
326 std::vector<std::string> tables;
327 for (const auto &[table, value] : values) {
328 tables.emplace_back(table);
329 }
330 PostDataChange(meta_, tables);
331 Watchers watchers;
332 {
333 std::unique_lock<decltype(mutex_)> lock(mutex_);
334 watchers = watchers_;
335 }
336 size_t remain = watchers.size();
337 for (auto &watcher : watchers) {
338 remain--;
339 if (watcher == nullptr) {
340 continue;
341 }
342 watcher->OnChange(origin, primaryFields, (remain != 0) ? ChangeInfo(values) : std::move(values));
343 }
344 return Error::E_OK;
345 }
346
OnChange(const Origin &origin, const Fields &fields, ChangeData &&datas)347 int32_t AutoCache::Delegate::OnChange(const Origin &origin, const Fields &fields, ChangeData &&datas)
348 {
349 std::vector<std::string> tables;
350 for (const auto &[table, value] : datas) {
351 tables.emplace_back(table);
352 }
353 PostDataChange(meta_, tables);
354 Watchers watchers;
355 {
356 std::unique_lock<decltype(mutex_)> lock(mutex_);
357 watchers = watchers_;
358 }
359 size_t remain = watchers.size();
360 for (auto &watcher : watchers) {
361 remain--;
362 if (watcher == nullptr) {
363 continue;
364 }
365 watcher->OnChange(origin, fields, (remain != 0) ? ChangeData(datas) : std::move(datas));
366 }
367 return Error::E_OK;
368 }
369
PostDataChange(const StoreMetaData &meta, const std::vector<std::string> &tables)370 void AutoCache::Delegate::PostDataChange(const StoreMetaData &meta, const std::vector<std::string> &tables)
371 {
372 RemoteChangeEvent::DataInfo info;
373 info.userId = meta.user;
374 info.storeId = meta.storeId;
375 info.deviceId = meta.deviceId;
376 info.bundleName = meta.bundleName;
377 info.tables = tables;
378 auto evt = std::make_unique<RemoteChangeEvent>(RemoteChangeEvent::DATA_CHANGE, std::move(info));
379 EventCenter::GetInstance().PostEvent(std::move(evt));
380 }
381 } // namespace OHOS::DistributedData