1 /*
2 * Copyright (c) 2021-2022 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
16 #include "module_usage_data_storage.h"
17
18 #include <cinttypes>
19 #include <unistd.h>
20 #include "datetime_ex.h"
21 #include "string_ex.h"
22
23 #include "bundle_util.h"
24 #include "json_util.h"
25 #include "kvstore_death_recipient_callback.h"
26 #include "nlohmann/json.hpp"
27
28 using namespace OHOS::DistributedKv;
29
30 namespace OHOS {
31 namespace AppExecFwk {
32 namespace {
33 const int32_t MAX_TIMES = 6000; // tem min
34 const int32_t SLEEP_INTERVAL = 100 * 1000; // 100ms
35 const std::string POUND_KEY_SEPARATOR = "#";
36 const std::string SCHEMA_DEFINE = "{\"SCHEMA_VERSION\":\"1.0\","
37 "\"SCHEMA_MODE\":\"COMPATIBLE\","
38 "\"SCHEMA_SKIPSIZE\":0,"
39 "\"SCHEMA_DEFINE\":{"
40 "\"launchedCount\":\"INTEGER, NOT NULL\","
41 "\"lastLaunchTime\":\"LONG, NOT NULL\","
42 "\"isRemoved\":\"BOOL, NOT NULL\""
43 "},"
44 "\"SCHEMA_INDEXES\":[\"$.lastLaunchTime\"]}";
45 } // namespace
46
ModuleUsageRecordStorage()47 ModuleUsageRecordStorage::ModuleUsageRecordStorage()
48 {
49 APP_LOGI("usage instance is created");
50 TryTwice([this] { return GetKvStore(); });
51 }
52
~ModuleUsageRecordStorage()53 ModuleUsageRecordStorage::~ModuleUsageRecordStorage()
54 {
55 APP_LOGI("usage instance is destroyed");
56 dataManager_.CloseKvStore(appId_, kvStorePtr_);
57 }
58
RegisterKvStoreDeathListener()59 void ModuleUsageRecordStorage::RegisterKvStoreDeathListener()
60 {}
61
ParseKey(const std::string & key,ModuleUsageRecord & record) const62 bool ModuleUsageRecordStorage::ParseKey(const std::string &key, ModuleUsageRecord &record) const
63 {
64 std::vector<std::string> splitKeys;
65 SplitStr(key, POUND_KEY_SEPARATOR, splitKeys);
66 if (splitKeys.size() != DATABASE_KEY_INDEX_MAX_LENGTH) {
67 APP_LOGE("error key, parsed failed!");
68 return false;
69 }
70
71 record.bundleName = (splitKeys[DATABASE_KEY_INDEX_BUNDLE_NAME]);
72 record.name = (splitKeys[DATABASE_KEY_INDEX_MODULE_NAME]);
73 APP_LOGD(
74 "parseKey::bundleName = %{public}s, moduleName = %{public}s", record.bundleName.c_str(), record.name.c_str());
75 return true;
76 }
77
AbilityRecordToKey(const std::string & userId,const std::string & deviceId,const std::string & bundleName,const std::string & moduleName,std::string & key) const78 void ModuleUsageRecordStorage::AbilityRecordToKey(const std::string &userId, const std::string &deviceId,
79 const std::string &bundleName, const std::string &moduleName, std::string &key) const
80 {
81 // deviceId_bundleName_moduleName
82 key.append(userId);
83 key.append(POUND_KEY_SEPARATOR);
84 key.append(deviceId);
85 key.append(POUND_KEY_SEPARATOR);
86 key.append(bundleName);
87 key.append(POUND_KEY_SEPARATOR);
88 key.append(moduleName);
89 APP_LOGD("userId = %{public}s, bundleName = %{public}s, moduleName = %{public}s",
90 userId.c_str(),
91 bundleName.c_str(),
92 moduleName.c_str());
93 }
94
UpdateUsageRecord(const std::string & jsonString,ModuleUsageRecord & data)95 void ModuleUsageRecordStorage::UpdateUsageRecord(const std::string &jsonString, ModuleUsageRecord &data)
96 {
97 nlohmann::json jsonObject = nlohmann::json::parse(jsonString);
98 if (jsonObject.is_discarded()) {
99 APP_LOGE("failed to parse existing usage record: %{private}s.", jsonString.c_str());
100 return;
101 }
102
103 const auto &jsonObjectEnd = jsonObject.end();
104 int32_t parseResult = ERR_OK;
105 uint32_t launchedCount;
106 GetValueIfFindKey<uint32_t>(jsonObject,
107 jsonObjectEnd,
108 UsageRecordKey::LAUNCHED_COUNT,
109 launchedCount,
110 JsonType::NUMBER,
111 true,
112 parseResult,
113 ArrayType::NOT_ARRAY);
114 if (parseResult != ERR_OK) {
115 APP_LOGE("parsing failed: %{public}d.", parseResult);
116 return;
117 }
118 data.launchedCount = launchedCount + 1;
119 APP_LOGD("launchedCount = %{public}u", data.launchedCount);
120 return;
121 }
122
AddOrUpdateRecord(ModuleUsageRecord & data,const std::string & deviceId,int32_t userId)123 bool ModuleUsageRecordStorage::AddOrUpdateRecord(ModuleUsageRecord &data, const std::string &deviceId, int32_t userId)
124 {
125 APP_LOGI("add usage record data %{public}s", data.bundleName.c_str());
126 {
127 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
128 if (!CheckKvStore()) {
129 APP_LOGE("kvStore is nullptr");
130 return false;
131 }
132 }
133 Status status;
134 bool isExist = false;
135 {
136 std::string keyOfData;
137 AbilityRecordToKey(std::to_string(userId), deviceId, data.bundleName, data.name, keyOfData);
138 Key key(keyOfData);
139 Value oldValue;
140 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
141 status = kvStorePtr_->Get(key, oldValue);
142 if (status == Status::SUCCESS) {
143 APP_LOGD("get old value %{public}s", oldValue.ToString().c_str());
144 // already isExist, update
145 UpdateUsageRecord(oldValue.ToString(), data);
146 isExist = true;
147 }
148 if (status == Status::KEY_NOT_FOUND || isExist) {
149 Value value(data.ToString());
150 APP_LOGD("add to DB::value %{public}s", value.ToString().c_str());
151 status = kvStorePtr_->Put(key, value);
152 APP_LOGW("add result = %{public}d", status);
153 if (status == Status::IPC_ERROR) {
154 status = kvStorePtr_->Put(key, value);
155 APP_LOGW("distribute database ipc error and try to call again, result = %{public}d", status);
156 }
157 }
158 }
159
160 if (status != Status::SUCCESS) {
161 APP_LOGE("put value to kvStore error: %{public}d", status);
162 return false;
163 }
164
165 APP_LOGD("update success");
166 return true;
167 }
168
DeleteRecordByKeys(const std::string & bundleName,std::vector<DistributedKv::Key> & keys)169 bool ModuleUsageRecordStorage::DeleteRecordByKeys(const std::string &bundleName, std::vector<DistributedKv::Key> &keys)
170 {
171 if (keys.size() == 0) {
172 APP_LOGE("delete error: empty key");
173 return false;
174 }
175 Status status;
176 {
177 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
178 status = kvStorePtr_->DeleteBatch(keys);
179 if (status == Status::IPC_ERROR) {
180 status = kvStorePtr_->DeleteBatch(keys);
181 APP_LOGW("distribute database ipc error and try to call again, result = %{public}d", status);
182 }
183 }
184
185 if (status != Status::SUCCESS) {
186 APP_LOGE("delete keys error: %{public}d", status);
187 return false;
188 }
189 APP_LOGD("delete success");
190 return true;
191 }
192
DeleteUsageRecord(const InnerBundleInfo & data,int32_t userId)193 bool ModuleUsageRecordStorage::DeleteUsageRecord(const InnerBundleInfo &data, int32_t userId)
194 {
195 APP_LOGD("delete usage data");
196 {
197 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
198 if (!CheckKvStore()) {
199 APP_LOGE("kvStore is nullptr");
200 return false;
201 }
202 }
203
204 std::vector<Key> keys;
205 InnerBundleInfoToKeys(data, userId, keys);
206 APP_LOGD("delete key %{public}zu", keys.size());
207 return DeleteRecordByKeys(data.GetBundleName(), keys);
208 }
209
MarkUsageRecordRemoved(const InnerBundleInfo & data,int32_t userId)210 bool ModuleUsageRecordStorage::MarkUsageRecordRemoved(const InnerBundleInfo &data, int32_t userId)
211 {
212 {
213 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
214 if (!CheckKvStore()) {
215 APP_LOGE("kvStore is nullptr");
216 return false;
217 }
218 }
219 ModuleUsageRecord record;
220 Value value;
221 std::string jsonString;
222 std::vector<Key> keys;
223 InnerBundleInfoToKeys(data, userId, keys);
224 {
225 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
226 for (const Key &key : keys) {
227 Status status = kvStorePtr_->Get(key, value);
228 if (status != Status::SUCCESS) {
229 APP_LOGD("database query by key error, result = %{public}d", status);
230 return false;
231 }
232 if (!record.FromJsonString(value.ToString())) {
233 APP_LOGW("database parse entry failed");
234 return false;
235 }
236 if (!record.removed) {
237 record.removed = true;
238 jsonString = record.ToString();
239 APP_LOGD("new value %{public}s", jsonString.c_str());
240 value = jsonString;
241 status = kvStorePtr_->Put(key, value);
242 APP_LOGI("value update result: %{public}d", status);
243 }
244 }
245 }
246 return true;
247 }
248
InnerBundleInfoToKeys(const InnerBundleInfo & data,int32_t userId,std::vector<DistributedKv::Key> & keys) const249 void ModuleUsageRecordStorage::InnerBundleInfoToKeys(
250 const InnerBundleInfo &data, int32_t userId, std::vector<DistributedKv::Key> &keys) const
251 {
252 std::vector<std::string> mouduleNames;
253 data.GetModuleNames(mouduleNames);
254 const std::string &bundleName = data.GetBundleName();
255 for (const auto &moduleName : mouduleNames) {
256 FillDataStorageKeys(std::to_string(userId), bundleName, moduleName, keys);
257 }
258 }
259
FillDataStorageKeys(const std::string & userId,const std::string & bundleName,const std::string & moduleName,std::vector<DistributedKv::Key> & keys) const260 void ModuleUsageRecordStorage::FillDataStorageKeys(const std::string &userId, const std::string &bundleName,
261 const std::string &moduleName, std::vector<DistributedKv::Key> &keys) const
262 {
263 std::string keyOfData;
264 AbilityRecordToKey(userId, Constants::CURRENT_DEVICE_ID, bundleName, moduleName, keyOfData);
265 Key key(keyOfData);
266 keys.push_back(key);
267 }
268
SaveEntries(const std::vector<Entry> & allEntries,std::vector<ModuleUsageRecord> & records) const269 void ModuleUsageRecordStorage::SaveEntries(
270 const std::vector<Entry> &allEntries, std::vector<ModuleUsageRecord> &records) const
271 {
272 APP_LOGD("SaveEntries %{public}zu", allEntries.size());
273 for (const auto &item : allEntries) {
274 APP_LOGD("SaveEntries %{public}s", item.value.ToString().c_str());
275 ModuleUsageRecord record;
276 if (!record.FromJsonString(item.value.ToString())) {
277 APP_LOGE("error entry: %{private}s", item.value.ToString().c_str());
278 continue;
279 }
280
281 if (!ParseKey(item.key.ToString(), record)) {
282 APP_LOGE("error key");
283 continue;
284 }
285 records.emplace_back(record);
286 }
287 }
288
QueryRecordByNum(int32_t maxNum,std::vector<ModuleUsageRecord> & records,int32_t userId)289 bool ModuleUsageRecordStorage::QueryRecordByNum(int32_t maxNum, std::vector<ModuleUsageRecord> &records, int32_t userId)
290 {
291 APP_LOGI("query record by num %{public}d userId %{public}d", maxNum, userId);
292 DataQuery query;
293 query.KeyPrefix(std::to_string(userId) + POUND_KEY_SEPARATOR + Constants::CURRENT_DEVICE_ID + POUND_KEY_SEPARATOR);
294 query.OrderByDesc(UsageRecordKey::SCHEMA_LAST_LAUNCH_TIME);
295 query.Limit(maxNum, 0);
296 std::vector<Entry> allEntries;
297 bool queryResult = QueryRecordByCondition(query, allEntries);
298 if (!queryResult || static_cast<int>(allEntries.size()) > maxNum) {
299 APP_LOGE("query record error");
300 return queryResult;
301 }
302 APP_LOGD("query record success");
303 SaveEntries(allEntries, records);
304 return true;
305 }
306
QueryRecordByCondition(DataQuery & query,std::vector<Entry> & records)307 bool ModuleUsageRecordStorage::QueryRecordByCondition(DataQuery &query, std::vector<Entry> &records)
308 {
309 Status status;
310 {
311 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
312 if (!CheckKvStore()) {
313 APP_LOGE("kvStore is nullptr");
314 return false;
315 }
316 }
317
318 status = kvStorePtr_->GetEntriesWithQuery(query, records);
319 APP_LOGI("query record by condition %{public}d", status);
320 if (status == Status::IPC_ERROR) {
321 status = kvStorePtr_->GetEntriesWithQuery(query, records);
322 APP_LOGW("distribute database ipc error and try to call again, result = %{public}d", status);
323 }
324
325 if (status != Status::SUCCESS) {
326 APP_LOGE("query key error: %{public}d", status);
327 return false;
328 }
329 return true;
330 }
331
GetKvStore()332 Status ModuleUsageRecordStorage::GetKvStore()
333 {
334 Options options = {
335 .createIfMissing = true,
336 .encrypt = false,
337 .autoSync = false,
338 .kvStoreType = KvStoreType::SINGLE_VERSION
339 };
340
341 options.schema = SCHEMA_DEFINE;
342 Status status = dataManager_.GetSingleKvStore(options, appId_, storeId_, kvStorePtr_);
343 if (status != Status::SUCCESS) {
344 APP_LOGE("usage get kvStore error: %{public}d", status);
345 } else {
346 APP_LOGI("usage get kvStore success");
347 }
348 return status;
349 }
350
TryTwice(const std::function<Status ()> & func) const351 void ModuleUsageRecordStorage::TryTwice(const std::function<Status()> &func) const
352 {
353 Status status = func();
354 if (status == Status::IPC_ERROR) {
355 status = func();
356 APP_LOGW("distribute database ipc error and try to call again, result = %{public}d", status);
357 }
358 }
359
CheckKvStore()360 bool ModuleUsageRecordStorage::CheckKvStore()
361 {
362 if (kvStorePtr_ != nullptr) {
363 return true;
364 }
365 int32_t tryTimes = MAX_TIMES;
366 while (tryTimes > 0) {
367 Status status = GetKvStore();
368 if (status == Status::SUCCESS && kvStorePtr_ != nullptr) {
369 return true;
370 }
371 APP_LOGD("usage CheckKvStore, Times: %{public}d", tryTimes);
372 usleep(SLEEP_INTERVAL);
373 tryTimes--;
374 }
375 return kvStorePtr_ != nullptr;
376 }
377
ResetKvStore()378 bool ModuleUsageRecordStorage::ResetKvStore()
379 {
380 std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
381 kvStorePtr_ = nullptr;
382 Status status = GetKvStore();
383 if (status == Status::SUCCESS && kvStorePtr_ != nullptr) {
384 return true;
385 }
386 APP_LOGW("usage reset failed");
387 return false;
388 }
389 } // namespace AppExecFwk
390 } // namespace OHOS
391