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