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