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 std::set<std::string> storeIds;
148 std::list<Delegate> closeStores;
149 bool isScreenLocked = ScreenManager::GetInstance()->IsLocked();
150 stores_.ComputeIfPresent(tokenId,
151 [this, &storeId, isScreenLocked, &storeIds, &closeStores](auto &key, auto &delegates) {
152 auto it = delegates.begin();
153 while (it != delegates.end()) {
154 if ((storeId == it->first || storeId.empty()) &&
155 (!isScreenLocked || it->second.GetArea() < GeneralStore::EL4) &&
156 disableStores_.count(it->second.GetDataDir()) == 0) {
157 disableStores_.insert(it->second.GetDataDir());
158 storeIds.insert(it->first);
159 closeStores.emplace(closeStores.end(), it->second);
160 } else {
161 ++it;
162 }
163 }
164 return !delegates.empty();
165 });
166 closeStores.clear();
167 stores_.ComputeIfPresent(tokenId, [this, &storeIds](auto &key, auto &delegates) {
168 for (auto it = delegates.begin(); it != delegates.end();) {
169 if (storeIds.count(it->first) != 0) {
170 disableStores_.erase(it->second.GetDataDir());
171 it = delegates.erase(it);
172 } else {
173 ++it;
174 }
175 }
176 return !delegates.empty();
177 });
178 }
179
CloseExcept(const std::set<int32_t> & users)180 void AutoCache::CloseExcept(const std::set<int32_t> &users)
181 {
182 bool isScreenLocked = ScreenManager::GetInstance()->IsLocked();
183 stores_.EraseIf([&users, isScreenLocked](const auto &tokenId, std::map<std::string, Delegate> &delegates) {
184 if (delegates.empty() || users.count(delegates.begin()->second.GetUser()) != 0) {
185 return delegates.empty();
186 }
187
188 for (auto it = delegates.begin(); it != delegates.end();) {
189 // if the kv store is BUSY we wait more INTERVAL minutes again
190 if ((isScreenLocked && it->second.GetArea() >= GeneralStore::EL4) || !it->second.Close()) {
191 ++it;
192 } else {
193 it = delegates.erase(it);
194 }
195 }
196 return delegates.empty();
197 });
198 }
199
SetObserver(uint32_t tokenId,const std::string & storeId,const AutoCache::Watchers & watchers)200 void AutoCache::SetObserver(uint32_t tokenId, const std::string &storeId, const AutoCache::Watchers &watchers)
201 {
202 stores_.ComputeIfPresent(tokenId, [&storeId, &watchers](auto &key, auto &stores) {
203 ZLOGD("tokenId:0x%{public}x storeId:%{public}s observers:%{public}zu", key,
204 Anonymous::Change(storeId).c_str(), watchers.size());
205 auto it = stores.find(storeId);
206 if (it != stores.end()) {
207 it->second.SetObservers(watchers);
208 }
209 return true;
210 });
211 }
212
GarbageCollect(bool isForce)213 void AutoCache::GarbageCollect(bool isForce)
214 {
215 auto current = std::chrono::steady_clock::now();
216 bool isScreenLocked = ScreenManager::GetInstance()->IsLocked();
217 stores_.EraseIf([¤t, isForce, isScreenLocked](auto &key, std::map<std::string, Delegate> &delegates) {
218 for (auto it = delegates.begin(); it != delegates.end();) {
219 // if the store is BUSY we wait more INTERVAL minutes again
220 if ((!isScreenLocked || it->second.GetArea() < GeneralStore::EL4) && (isForce || it->second < current) &&
221 it->second.Close()) {
222 it = delegates.erase(it);
223 } else {
224 ++it;
225 }
226 }
227 return delegates.empty();
228 });
229 }
230
Enable(uint32_t tokenId,const std::string & storeId)231 void AutoCache::Enable(uint32_t tokenId, const std::string &storeId)
232 {
233 disables_.ComputeIfPresent(tokenId, [&storeId](auto key, std::set<std::string> &stores) {
234 stores.erase(storeId);
235 return !(stores.empty() || storeId.empty());
236 });
237 }
238
Disable(uint32_t tokenId,const std::string & storeId)239 void AutoCache::Disable(uint32_t tokenId, const std::string &storeId)
240 {
241 disables_.Compute(tokenId, [&storeId](auto key, std::set<std::string> &stores) {
242 stores.insert(storeId);
243 return !stores.empty();
244 });
245 CloseStore(tokenId, storeId);
246 }
247
Delegate(GeneralStore * delegate,const Watchers & watchers,int32_t user,const StoreMetaData & meta)248 AutoCache::Delegate::Delegate(GeneralStore *delegate, const Watchers &watchers, int32_t user, const StoreMetaData &meta)
249 : store_(delegate), watchers_(watchers), user_(user), meta_(meta)
250 {
251 time_ = std::chrono::steady_clock::now() + std::chrono::minutes(INTERVAL);
252 if (store_ != nullptr) {
253 store_->Watch(Origin::ORIGIN_ALL, *this);
254 }
255 }
256
Delegate(const Delegate & delegate)257 AutoCache::Delegate::Delegate(const Delegate& delegate)
258 {
259 store_ = delegate.store_;
260 if (store_ != nullptr) {
261 store_->AddRef();
262 }
263 }
264
~Delegate()265 AutoCache::Delegate::~Delegate()
266 {
267 if (store_ != nullptr) {
268 store_->Close(true);
269 store_->Unwatch(Origin::ORIGIN_ALL, *this);
270 store_->Release();
271 store_ = nullptr;
272 }
273 }
274
275 AutoCache::Delegate::operator Store()
276 {
277 time_ = std::chrono::steady_clock::now() + std::chrono::minutes(INTERVAL);
278 if (store_ != nullptr) {
279 store_->AddRef();
__anon4d0511fe0d02(GeneralStore *store) 280 return Store(store_, [](GeneralStore *store) { store->Release(); });
281 }
282 return nullptr;
283 }
284
operator <(const AutoCache::Time & time) const285 bool AutoCache::Delegate::operator<(const AutoCache::Time &time) const
286 {
287 return time_ < time;
288 }
289
Close()290 bool AutoCache::Delegate::Close()
291 {
292 if (store_ != nullptr) {
293 auto status = store_->Close();
294 if (status == Error::E_BUSY) {
295 return false;
296 }
297 store_->Unwatch(Origin::ORIGIN_ALL, *this);
298 }
299 return true;
300 }
301
SetObservers(const AutoCache::Watchers & watchers)302 void AutoCache::Delegate::SetObservers(const AutoCache::Watchers &watchers)
303 {
304 std::unique_lock<decltype(mutex_)> lock(mutex_);
305 watchers_ = watchers;
306 }
307
GetUser() const308 int32_t AutoCache::Delegate::GetUser() const
309 {
310 return user_;
311 }
312
GetArea() const313 int32_t AutoCache::Delegate::GetArea() const
314 {
315 return meta_.area;
316 }
317
GetDataDir() const318 const std::string& AutoCache::Delegate::GetDataDir() const
319 {
320 return meta_.dataDir;
321 }
322
OnChange(const Origin & origin,const PRIFields & primaryFields,ChangeInfo && values)323 int32_t AutoCache::Delegate::OnChange(const Origin &origin, const PRIFields &primaryFields, ChangeInfo &&values)
324 {
325 std::vector<std::string> tables;
326 for (const auto &[table, value] : values) {
327 tables.emplace_back(table);
328 }
329 PostDataChange(meta_, tables);
330 Watchers watchers;
331 {
332 std::unique_lock<decltype(mutex_)> lock(mutex_);
333 watchers = watchers_;
334 }
335 size_t remain = watchers.size();
336 for (auto &watcher : watchers) {
337 remain--;
338 if (watcher == nullptr) {
339 continue;
340 }
341 watcher->OnChange(origin, primaryFields, (remain != 0) ? ChangeInfo(values) : std::move(values));
342 }
343 return Error::E_OK;
344 }
345
OnChange(const Origin & origin,const Fields & fields,ChangeData && datas)346 int32_t AutoCache::Delegate::OnChange(const Origin &origin, const Fields &fields, ChangeData &&datas)
347 {
348 std::vector<std::string> tables;
349 for (const auto &[table, value] : datas) {
350 tables.emplace_back(table);
351 }
352 PostDataChange(meta_, tables);
353 Watchers watchers;
354 {
355 std::unique_lock<decltype(mutex_)> lock(mutex_);
356 watchers = watchers_;
357 }
358 size_t remain = watchers.size();
359 for (auto &watcher : watchers) {
360 remain--;
361 if (watcher == nullptr) {
362 continue;
363 }
364 watcher->OnChange(origin, fields, (remain != 0) ? ChangeData(datas) : std::move(datas));
365 }
366 return Error::E_OK;
367 }
368
PostDataChange(const StoreMetaData & meta,const std::vector<std::string> & tables)369 void AutoCache::Delegate::PostDataChange(const StoreMetaData &meta, const std::vector<std::string> &tables)
370 {
371 RemoteChangeEvent::DataInfo info;
372 info.userId = meta.user;
373 info.storeId = meta.storeId;
374 info.deviceId = meta.deviceId;
375 info.bundleName = meta.bundleName;
376 info.tables = tables;
377 auto evt = std::make_unique<RemoteChangeEvent>(RemoteChangeEvent::DATA_CHANGE, std::move(info));
378 EventCenter::GetInstance().PostEvent(std::move(evt));
379 }
380 } // namespace OHOS::DistributedData