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([&current, 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