• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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([&current, 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