1/* 2 * Copyright (c) 2024 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 "mission/dsched_sync_e2e.h" 17 18#include <filesystem> 19#include <fstream> 20#include <iostream> 21#include <parameter.h> 22 23#include "config_policy_utils.h" 24#include "parameters.h" 25#include "securec.h" 26 27namespace OHOS { 28namespace DistributedSchedule { 29namespace { 30const std::string TAG = "DmsKvSyncE2E"; 31const std::string BMS_KV_BASE_DIR = "/data/service/el1/public/database/DistributedSchedule"; 32const int32_t SLEEP_INTERVAL = 100 * 1000; // 100ms 33const int32_t EL1 = 1; 34const int32_t MAX_TIMES = 600; // 1min 35const char DETERMINE_DEVICE_TYPE_KEY[] = "persist.distributed_scene.sys_settings_data_sync"; 36static const int32_t FORBID_SEND_FORBID_RECV = 0; 37static const int32_t ALLOW_SEND_ALLOW_RECV = 1; 38const std::string PARAM_DISTRIBUTED_DATAFILES_TRANS_CTRL = "persist.distributed_scene.datafiles_trans_ctrl"; 39const std::string CONTINUE_CONFIG_RELATIVE_PATH = "etc/distributedhardware/dms/continue_config.json"; 40const std::string ALLOW_APP_LIST_KEY = "allow_applist"; 41constexpr int32_t MAX_CONFIG_PATH_LEN = 1024; 42} // namespace 43 44std::shared_ptr<DmsKvSyncE2E> DmsKvSyncE2E::instance_ = nullptr; 45std::mutex DmsKvSyncE2E::mutex_; 46 47DmsKvSyncE2E::DmsKvSyncE2E() 48{ 49 HILOGD("called."); 50 TryTwice([this] { return GetKvStore(); }); 51 HILOGD("end."); 52} 53 54DmsKvSyncE2E::~DmsKvSyncE2E() 55{ 56 HILOGD("called."); 57 dataManager_.CloseKvStore(appId_, storeId_); 58 HILOGD("end."); 59} 60 61std::shared_ptr<DmsKvSyncE2E> DmsKvSyncE2E::GetInstance() 62{ 63 HILOGD("called."); 64 std::lock_guard<std::mutex> lock(mutex_); 65 if (instance_ == nullptr) { 66 instance_ = std::make_shared<DmsKvSyncE2E>(); 67 } 68 HILOGD("end."); 69 return instance_; 70} 71 72void DmsKvSyncE2E::SetDeviceCfg() 73{ 74 HILOGD("called."); 75 const char *syncType = "1"; 76 const int bufferLen = 10; 77 char paramOutBuf[bufferLen] = {0}; 78 int ret = GetParameter(DETERMINE_DEVICE_TYPE_KEY, "", paramOutBuf, bufferLen); 79 HILOGD("paramOutBuf: %{public}s, ret: %{public}d", paramOutBuf, ret); 80 if (ret > 0 && strncmp(paramOutBuf, syncType, strlen(syncType)) == 0) { 81 HILOGI("Determining the e2e device succeeded."); 82 isCfgDevices_ = true; 83 } 84 85 auto contralType = OHOS::system::GetIntParameter(PARAM_DISTRIBUTED_DATAFILES_TRANS_CTRL, 86 ALLOW_SEND_ALLOW_RECV); 87 HILOGI("contralType=%{public}d", contralType); 88 if (contralType == FORBID_SEND_FORBID_RECV) { 89 isForbidSendAndRecv_ = true; 90 } 91 92 int32_t result = LoadContinueConfig(); 93 if (result != ERR_OK) { 94 HILOGE("Load continue config fail, result %{public}d.", result); 95 } 96} 97 98bool DmsKvSyncE2E::CheckDeviceCfg() 99{ 100 HILOGD("called."); 101 return isCfgDevices_; 102} 103 104bool DmsKvSyncE2E::CheckCtrlRule() 105{ 106 HILOGD("called."); 107 if (isCfgDevices_ && isForbidSendAndRecv_) { 108 HILOGE("The device is a special device and checkCtrlRule fail"); 109 return false; 110 } 111 return true; 112} 113 114bool DmsKvSyncE2E::IsValidPath(const std::string &inFilePath, std::string &realFilePath) 115{ 116 char path[PATH_MAX + 1] = { 0 }; 117 if (inFilePath.empty() || inFilePath.length() > PATH_MAX || inFilePath.length() + 1 > MAX_CONFIG_PATH_LEN || 118 realpath(inFilePath.c_str(), path) == nullptr) { 119 HILOGE("Get continue config file real path fail, inFilePath %{public}s.", GetAnonymStr(inFilePath).c_str()); 120 return false; 121 } 122 123 realFilePath = std::string(path); 124 if (realFilePath.empty()) { 125 HILOGE("Real file path is empty."); 126 return false; 127 } 128 if (!std::filesystem::exists(realFilePath)) { 129 HILOGE("The real file path %{public}s does not exist in the file system.", GetAnonymStr(realFilePath).c_str()); 130 realFilePath = ""; 131 return false; 132 } 133 HILOGD("The real file path %{public}s exist in the file system.", GetAnonymStr(realFilePath).c_str()); 134 return true; 135} 136 137bool DmsKvSyncE2E::UpdateWhiteList(const std::string &cfgJsonStr) 138{ 139 cJSON *inJson = nullptr; 140 cJSON *allowList = nullptr; 141 bool isSuccess = false; 142 do { 143 inJson = cJSON_Parse(cfgJsonStr.c_str()); 144 if (inJson == nullptr) { 145 HILOGE("parse continue config json file stream to json fail."); 146 break; 147 } 148 149 allowList = cJSON_GetObjectItem(inJson, ALLOW_APP_LIST_KEY.c_str()); 150 if (allowList == nullptr || !cJSON_IsArray(allowList)) { 151 HILOGE("allow app list array is not in continue config json file."); 152 break; 153 } 154 155 std::lock_guard<std::mutex> lock(kvStorePtrMutex_); 156 for (int32_t i = 0; i < cJSON_GetArraySize(allowList); i++) { 157 cJSON *iAllowAppJson = cJSON_GetArrayItem(allowList, i); 158 if (!cJSON_IsString(iAllowAppJson)) { 159 HILOGE("allow app list [%{public}d] is not string.", i); 160 continue; 161 } 162 163 std::string iAllowAppStr = std::string(cJSON_GetStringValue(iAllowAppJson)); 164 HILOGI("allow app list show [%{public}d] : [%{public}s].", i, iAllowAppStr.c_str()); 165 whiteList_.push_back(iAllowAppStr); 166 } 167 isSuccess = true; 168 } while (false); 169 170 if (inJson != nullptr) { 171 cJSON_Delete(inJson); 172 inJson = nullptr; 173 } 174 return isSuccess; 175} 176 177int32_t DmsKvSyncE2E::LoadContinueConfig() 178{ 179 HILOGD("Load continue config, continueCfgFullPath %{public}s.", GetAnonymStr(continueCfgFullPath_).c_str()); 180 std::string tempPath = continueCfgFullPath_; 181 if ((continueCfgFullPath_.empty() || !IsValidPath(tempPath, continueCfgFullPath_))) { 182 char cfgPathBuf[MAX_CONFIG_PATH_LEN] = { 0 }; 183 char *filePath = GetOneCfgFile(CONTINUE_CONFIG_RELATIVE_PATH.c_str(), cfgPathBuf, MAX_CONFIG_PATH_LEN); 184 if (filePath == nullptr || filePath != cfgPathBuf) { 185 HILOGE("Not find continue config file, relative path %{public}s.", 186 GetAnonymStr(CONTINUE_CONFIG_RELATIVE_PATH).c_str()); 187 continueCfgFullPath_ = ""; 188 return ERR_OK; 189 } 190 continueCfgFullPath_ = std::string(filePath); 191 HILOGD("Get Continue config file full path success, cfgFullPath %{public}s.", 192 GetAnonymStr(continueCfgFullPath_).c_str()); 193 } 194 195 tempPath = continueCfgFullPath_; 196 if (!IsValidPath(tempPath, continueCfgFullPath_)) { 197 HILOGE("Continue config full path is invalid, cfgFullPath %{public}s.", 198 GetAnonymStr(continueCfgFullPath_).c_str()); 199 return DMS_PERMISSION_DENIED; 200 } 201 std::ifstream in; 202 in.open(continueCfgFullPath_.c_str(), std::ios::binary | std::ios::in); 203 if (!in.is_open()) { 204 HILOGE("Open continue config json file fail, cfgFullPath %{public}s.", 205 GetAnonymStr(continueCfgFullPath_).c_str()); 206 return DMS_PERMISSION_DENIED; 207 } 208 209 std::string cfgFileContent; 210 in.seekg(0, std::ios::end); 211 cfgFileContent.resize(in.tellg()); 212 in.seekg(0, std::ios::beg); 213 in.rdbuf()->sgetn(&cfgFileContent[0], cfgFileContent.size()); 214 in.close(); 215 216 if (!UpdateWhiteList(cfgFileContent)) { 217 HILOGE("Update allow app list fail, cfgFullPath %{public}s.", 218 GetAnonymStr(continueCfgFullPath_).c_str()); 219 return DMS_PERMISSION_DENIED; 220 } 221 HILOGD("Load continue config success, cfgFullPath %{public}s.", GetAnonymStr(continueCfgFullPath_).c_str()); 222 return ERR_OK; 223} 224 225bool DmsKvSyncE2E::CheckBundleContinueConfig(const std::string &bundleName) 226{ 227 if (!isCfgDevices_) { 228 HILOGD("The device is a normal device"); 229 return true; 230 } 231 232 std::lock_guard<std::mutex> lock(kvStorePtrMutex_); 233 auto it = std::find(whiteList_.begin(), whiteList_.end(), bundleName); 234 if (it == whiteList_.end()) { 235 HILOGE("Current app is not allow to continue in config file, bundleName %{public}s, cfgPath %{public}s.", 236 bundleName.c_str(), GetAnonymStr(continueCfgFullPath_).c_str()); 237 return false; 238 } 239 240 HILOGD("Current app is allow to continue in config file, bundleName %{public}s, cfgPath %{public}s.", 241 bundleName.c_str(), GetAnonymStr(continueCfgFullPath_).c_str()); 242 return true; 243} 244 245bool DmsKvSyncE2E::PushAndPullData() 246{ 247 HILOGI("called."); 248 std::vector<std::string> networkIdList = DtbschedmgrDeviceInfoStorage::GetInstance().GetNetworkIdList(); 249 if (networkIdList.empty()) { 250 HILOGE("GetNetworkIdList failed"); 251 return false; 252 } 253 if (!CheckKvStore()) { 254 HILOGE("kvStore is nullptr"); 255 return false; 256 } 257 DistributedKv::DataQuery dataQuery; 258 std::shared_ptr<DmsKvSyncCB> syncCallback = std::make_shared<DmsKvSyncCB>(); 259 Status status = kvStorePtr_->Sync(networkIdList, DistributedKv::SyncMode::PUSH_PULL, dataQuery, syncCallback); 260 if (status != Status::SUCCESS) { 261 HILOGE("sync error: %{public}d", status); 262 return false; 263 } 264 HILOGI("Synchronizing"); 265 return true; 266} 267 268bool DmsKvSyncE2E::PushAndPullData(const std::string &networkId) 269{ 270 HILOGI("called."); 271 if (!CheckDeviceCfg() && IsSynchronized(networkId)) { 272 HILOGW("The normal device been synchronized : %{public}s", GetAnonymStr(networkId).c_str()); 273 return false; 274 } 275 std::vector<std::string> networkIdList = {networkId}; 276 if (!CheckKvStore()) { 277 HILOGE("kvStore is nullptr"); 278 return false; 279 } 280 281 SetSyncRecord(networkId); 282 DistributedKv::DataQuery dataQuery; 283 std::shared_ptr<DmsKvSyncCB> syncCallback = std::make_shared<DmsKvSyncCB>(); 284 Status status = kvStorePtr_->Sync(networkIdList, DistributedKv::SyncMode::PUSH_PULL, dataQuery, syncCallback); 285 if (status != Status::SUCCESS) { 286 HILOGE("sync error: %{public}d", status); 287 return false; 288 } 289 HILOGI("Synchronizing"); 290 return true; 291} 292 293void DmsKvSyncE2E::SetSyncRecord(const std::string &networkId) 294{ 295 std::lock_guard<std::mutex> lock(kvStorePtrMutex_); 296 deviceSyncRecord_.insert(std::make_pair(networkId, true)); 297} 298 299void DmsKvSyncE2E::ClearSyncRecord(const std::string &networkId) 300{ 301 std::lock_guard<std::mutex> lock(kvStorePtrMutex_); 302 auto it = deviceSyncRecord_.find(networkId); 303 if (it != deviceSyncRecord_.end()) { 304 deviceSyncRecord_.erase(it); 305 HILOGI("Successfully cleared synchronization records for: %{public}s", GetAnonymStr(networkId).c_str()); 306 } else { 307 HILOGI("No need to clean up for: %{public}s", GetAnonymStr(networkId).c_str()); 308 } 309} 310 311bool DmsKvSyncE2E::IsSynchronized(const std::string &networkId) 312{ 313 std::lock_guard<std::mutex> lock(kvStorePtrMutex_); 314 if (deviceSyncRecord_.find(networkId) != deviceSyncRecord_.end() && deviceSyncRecord_[networkId]) { 315 return true; 316 } 317 return false; 318} 319 320bool DmsKvSyncE2E::CheckKvStore() 321{ 322 HILOGD("called."); 323 std::lock_guard<std::mutex> lock(kvStorePtrMutex_); 324 if (kvStorePtr_ != nullptr) { 325 return true; 326 } 327 int32_t tryTimes = MAX_TIMES; 328 while (tryTimes > 0) { 329 Status status = GetKvStore(); 330 if (status == Status::SUCCESS && kvStorePtr_ != nullptr) { 331 return true; 332 } 333 HILOGW("CheckKvStore, Times: %{public}d", tryTimes); 334 usleep(SLEEP_INTERVAL); 335 tryTimes--; 336 } 337 HILOGD("end."); 338 return kvStorePtr_ != nullptr; 339} 340 341Status DmsKvSyncE2E::GetKvStore() 342{ 343 HILOGD("called."); 344 Options options = { 345 .createIfMissing = true, 346 .encrypt = false, 347 .autoSync = false, 348 .isPublic = true, 349 .securityLevel = SecurityLevel::S1, 350 .area = EL1, 351 .kvStoreType = KvStoreType::SINGLE_VERSION, 352 .baseDir = BMS_KV_BASE_DIR, 353 .dataType = DataType::TYPE_DYNAMICAL, 354 .cloudConfig = { 355 .enableCloud = true, 356 .autoSync = true 357 }, 358 }; 359 Status status = dataManager_.GetSingleKvStore(options, appId_, storeId_, kvStorePtr_); 360 if (status == Status::SUCCESS) { 361 HILOGD("get kvStore success"); 362 } else if (status == DistributedKv::Status::STORE_META_CHANGED) { 363 HILOGE("This db meta changed, remove and rebuild it"); 364 dataManager_.DeleteKvStore(appId_, storeId_, BMS_KV_BASE_DIR + appId_.appId); 365 } 366 HILOGD("end."); 367 return status; 368} 369 370void DmsKvSyncE2E::TryTwice(const std::function<Status()> &func) const 371{ 372 HILOGD("called."); 373 Status status = func(); 374 if (status != Status::SUCCESS) { 375 status = func(); 376 HILOGW("error and try to call again, result = %{public}d", status); 377 } 378 HILOGD("end."); 379} 380 381DmsKvSyncCB::DmsKvSyncCB() 382{ 383 HILOGD("create"); 384} 385 386DmsKvSyncCB::~DmsKvSyncCB() 387{ 388 HILOGD("destroy"); 389} 390 391void DmsKvSyncCB::SyncCompleted(const std::map<std::string, DistributedKv::Status> &result) 392{ 393 HILOGI("kvstore sync completed."); 394 for (auto ele : result) { 395 HILOGI("uuid: %{public}s , result: %{public}d", GetAnonymStr(ele.first).c_str(), ele.second); 396 } 397} 398} // namespace DistributedSchedule 399} // namespace OHOS 400