1  /*
2   * Copyright (c) 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 "quick_fix_switcher.h"
17  
18  #include "app_log_tag_wrapper.h"
19  #include "app_log_wrapper.h"
20  #include "appexecfwk_errors.h"
21  #include "bundle_mgr_service.h"
22  #include "scope_guard.h"
23  
24  namespace OHOS {
25  namespace AppExecFwk {
QuickFixSwitcher(const std::string & bundleName,bool enable)26  QuickFixSwitcher::QuickFixSwitcher(const std::string &bundleName, bool enable)
27      : bundleName_(bundleName), enable_(enable)
28  {
29      LOG_I(BMS_TAG_DEFAULT, "enter QuickFixSwitcher");
30  }
31  
Execute()32  ErrCode QuickFixSwitcher::Execute()
33  {
34      LOG_I(BMS_TAG_DEFAULT, "start execute");
35      return SwitchQuickFix();
36  }
37  
SwitchQuickFix()38  ErrCode QuickFixSwitcher::SwitchQuickFix()
39  {
40      ErrCode result = enable_ ? EnableQuickFix(bundleName_) : DisableQuickFix(bundleName_);
41      if (result != ERR_OK) {
42          LOG_E(BMS_TAG_DEFAULT, "SwitchQuickFix failed");
43      }
44  
45      return result;
46  }
47  
EnableQuickFix(const std::string & bundleName)48  ErrCode QuickFixSwitcher::EnableQuickFix(const std::string &bundleName)
49  {
50      if (bundleName.empty()) {
51          LOG_E(BMS_TAG_DEFAULT, "EnableQuickFix failed due to empty bundleName");
52          return ERR_BUNDLEMANAGER_QUICK_FIX_PARAM_ERROR;
53      }
54      // enable is true, obtain quick fix info from db to replace the current patch
55      if (GetQuickFixDataMgr() != ERR_OK) {
56          LOG_E(BMS_TAG_DEFAULT, "quickFixDataMgr is nullptr");
57          return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
58      }
59      InnerAppQuickFix innerAppQuickFix;
60      // 1. query quick fix info from db
61      if (!quickFixDataMgr_->QueryInnerAppQuickFix(bundleName, innerAppQuickFix)) {
62          LOG_E(BMS_TAG_DEFAULT, "no patch in the db");
63          return ERR_BUNDLEMANAGER_QUICK_FIX_NO_PATCH_IN_DATABASE;
64      }
65      // 2. update status in db and start to switch patch
66      if (!quickFixDataMgr_->UpdateQuickFixStatus(QuickFixStatus::SWITCH_ENABLE_START, innerAppQuickFix)) {
67          LOG_E(BMS_TAG_DEFAULT, "update quickfix status %{public}d failed", QuickFixStatus::SWITCH_ENABLE_START);
68          return ERR_BUNDLEMANAGER_QUICK_FIX_INVALID_PATCH_STATUS;
69      }
70      // 3. utilize stateGuard to rollback quick fix in db
71      ScopeGuard stateGuard([&] {
72          innerAppQuickFix.SwitchQuickFix();
73          quickFixDataMgr_->UpdateQuickFixStatus(QuickFixStatus::DEPLOY_END, innerAppQuickFix);
74      });
75  
76      innerAppQuickFix.SwitchQuickFix();
77      ErrCode res = InnerSwitchQuickFix(bundleName, innerAppQuickFix, true);
78      if (res != ERR_OK) {
79          LOG_E(BMS_TAG_DEFAULT, "InnerSwitchQuickFix failed");
80          return res;
81      }
82      stateGuard.Dismiss();
83      return ERR_OK;
84  }
85  
DisableQuickFix(const std::string & bundleName)86  ErrCode QuickFixSwitcher::DisableQuickFix(const std::string &bundleName)
87  {
88      if (bundleName.empty()) {
89          LOG_E(BMS_TAG_DEFAULT, "DisableQuickFix failed due to empty bundleName");
90          return ERR_BUNDLEMANAGER_QUICK_FIX_PARAM_ERROR;
91      }
92      if (GetQuickFixDataMgr() != ERR_OK) {
93          LOG_E(BMS_TAG_DEFAULT, "quickFixDataMgr is nullptr");
94          return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
95      }
96      InnerAppQuickFix innerAppQuickFix;
97      // 1. query quick fix info from db, if quick fix info exists, use it to disable quick fix
98      bool isExisted = quickFixDataMgr_->QueryInnerAppQuickFix(bundleName, innerAppQuickFix);
99      if (!isExisted) {
100          LOG_W(BMS_TAG_DEFAULT, "no patch in the db");
101          // 1.1 if quick fix info does not exist, use the temporary quick fix info to mark disable-status in db
102          QuickFixMark fixMark = { .status = QuickFixStatus::DEPLOY_END };
103          AppQuickFix appQuickFix;
104          appQuickFix.bundleName = bundleName;
105          innerAppQuickFix.SetAppQuickFix(appQuickFix);
106          innerAppQuickFix.SetQuickFixMark(fixMark);
107      }
108      // 2. update disable-status in db
109      if (!quickFixDataMgr_->UpdateQuickFixStatus(QuickFixStatus::SWITCH_DISABLE_START, innerAppQuickFix)) {
110          LOG_E(BMS_TAG_DEFAULT, "update quickfix status %{public}d failed", QuickFixStatus::SWITCH_DISABLE_START);
111          return ERR_BUNDLEMANAGER_QUICK_FIX_INVALID_PATCH_STATUS;
112      }
113      // 3. if quick fix exist, stateGuard is utilized to rollback quick fix info in db
114      ScopeGuard stateGuard([&] {
115          if (isExisted) {
116              quickFixDataMgr_->UpdateQuickFixStatus(QuickFixStatus::DEPLOY_END, innerAppQuickFix);
117          } else {
118              quickFixDataMgr_->DeleteInnerAppQuickFix(bundleName);
119          }
120      });
121  
122      ErrCode res = InnerSwitchQuickFix(bundleName, innerAppQuickFix, false);
123      if (res != ERR_OK) {
124          LOG_E(BMS_TAG_DEFAULT, "InnerSwitchQuickFix failed %{public}d", res);
125          return res;
126      }
127      stateGuard.Dismiss();
128      return ERR_OK;
129  }
130  
InnerSwitchQuickFix(const std::string & bundleName,const InnerAppQuickFix & innerAppQuickFix,bool enable)131  ErrCode QuickFixSwitcher::InnerSwitchQuickFix(const std::string &bundleName, const InnerAppQuickFix &innerAppQuickFix,
132      bool enable)
133  {
134      LOG_D(BMS_TAG_DEFAULT, "InnerSwitchQuickFix start with bundleName: %{public}s", bundleName.c_str());
135      if (bundleName.empty()) {
136          LOG_E(BMS_TAG_DEFAULT, "InnerSwitchQuickFix failed due to empty bundleName");
137          return ERR_BUNDLEMANAGER_QUICK_FIX_PARAM_ERROR;
138      }
139  
140      if (GetDataMgr() != ERR_OK) {
141          LOG_E(BMS_TAG_DEFAULT, "GetDataMgr failed");
142          return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
143      }
144      InnerBundleInfo innerBundleInfo;
145      // 4. obtain innerBundleInfo and enableGuard used to enable bundle which is under disable status
146      if (!dataMgr_->GetInnerBundleInfo(bundleName, innerBundleInfo)) {
147          LOG_E(BMS_TAG_DEFAULT, "cannot obtain the innerbundleInfo from data mgr");
148          return ERR_BUNDLEMANAGER_QUICK_FIX_NOT_EXISTED_BUNDLE_INFO;
149      }
150      ScopeGuard enableGuard([&] { dataMgr_->EnableBundle(bundleName_); });
151      // utilize appQuickFix to rollback
152      AppQuickFix oldAppQuickFix = innerBundleInfo.GetAppQuickFix();
153      if (!enable && oldAppQuickFix.deployedAppqfInfo.hqfInfos.empty()) {
154          LOG_E(BMS_TAG_DEFAULT, "no patch info to be disabled");
155          return ERR_BUNDLEMANAGER_QUICK_FIX_NO_PATCH_INFO_IN_BUNDLE_INFO;
156      }
157      InnerAppQuickFix oldInnerAppQuickFix;
158      // 5. to generate old quick fix info which is about to be deleted from db
159      auto errCode = CreateInnerAppqf(innerBundleInfo, enable, oldInnerAppQuickFix);
160      if (errCode != ERR_OK) {
161          LOG_E(BMS_TAG_DEFAULT, "CreateInnerAppqf failed");
162          return errCode;
163      }
164      // 6. update innerBundleInfo in memory cache and db
165      AppQuickFix newAppQuickFix = innerAppQuickFix.GetAppQuickFix();
166      newAppQuickFix.deployingAppqfInfo = enable ? AppqfInfo() : oldAppQuickFix.deployingAppqfInfo;
167      innerBundleInfo.SetAppQuickFix(newAppQuickFix);
168      innerBundleInfo.SetBundleStatus(InnerBundleInfo::BundleStatus::ENABLED);
169      if (!dataMgr_->UpdateQuickFixInnerBundleInfo(bundleName, innerBundleInfo)) {
170          LOG_E(BMS_TAG_DEFAULT, "update quickfix innerbundleInfo failed");
171          return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
172      }
173  
174      // 7. utilize innerBundleInfoGuard to rollback inner bundle info in db and memory cache
175      ScopeGuard innerBundleInfoGuard([&] {
176          innerBundleInfo.SetAppQuickFix(oldAppQuickFix);
177          dataMgr_->UpdateQuickFixInnerBundleInfo(bundleName, innerBundleInfo);
178      });
179  
180      if (GetQuickFixDataMgr() != ERR_OK) {
181          LOG_E(BMS_TAG_DEFAULT, "GetQuickFixDataMgr failed");
182          return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
183      }
184      // 8. update quick fix info status of SWITCH_END
185      auto appQuickFix = oldInnerAppQuickFix.GetAppQuickFix();
186      if (appQuickFix.deployedAppqfInfo.hqfInfos.empty() && appQuickFix.deployingAppqfInfo.hqfInfos.empty()) {
187          quickFixDataMgr_->DeleteInnerAppQuickFix(bundleName);
188      } else {
189          if (!quickFixDataMgr_->UpdateQuickFixStatus(QuickFixStatus::SWITCH_END, oldInnerAppQuickFix)) {
190              LOG_E(BMS_TAG_DEFAULT, "update quickfix status %{public}d failed", QuickFixStatus::SWITCH_END);
191              return ERR_BUNDLEMANAGER_QUICK_FIX_INVALID_PATCH_STATUS;
192          }
193      }
194      innerBundleInfoGuard.Dismiss();
195      return ERR_OK;
196  }
197  
CreateInnerAppqf(const InnerBundleInfo & innerBundleInfo,bool enable,InnerAppQuickFix & innerAppQuickFix)198  ErrCode QuickFixSwitcher::CreateInnerAppqf(const InnerBundleInfo &innerBundleInfo,
199      bool enable, InnerAppQuickFix &innerAppQuickFix)
200  {
201      std::string bundleName = innerBundleInfo.GetBundleName();
202      LOG_D(BMS_TAG_DEFAULT, "CreateInnerAppqf start with bundleName: %{public}s", bundleName.c_str());
203  
204      InnerAppQuickFix innerAppqf;
205      if (GetQuickFixDataMgr() != ERR_OK) {
206          LOG_E(BMS_TAG_DEFAULT, "quickFixDataMgr_ is nullptr");
207          return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
208      }
209      auto res = quickFixDataMgr_->QueryInnerAppQuickFix(bundleName, innerAppqf);
210      // old patch or reload in db will lead failure to create innerAppqf
211      const auto &appQf = innerAppqf.GetAppQuickFix();
212      if (res && !appQf.deployedAppqfInfo.hqfInfos.empty()) {
213          LOG_E(BMS_TAG_DEFAULT, "CreateInnerAppqf failed due to some old patch or hot reload in db");
214          return ERR_BUNDLEMANAGER_QUICK_FIX_OLD_PATCH_OR_HOT_RELOAD_IN_DB;
215      }
216  
217      AppQuickFix appQuickFix;
218      appQuickFix.bundleName = bundleName;
219      appQuickFix.versionName = appQf.versionName;
220      appQuickFix.versionCode = appQf.versionCode;
221      appQuickFix.deployedAppqfInfo = innerBundleInfo.GetAppQuickFix().deployedAppqfInfo;
222  
223      if (!enable && res) {
224          appQuickFix.deployingAppqfInfo = appQf.deployingAppqfInfo;
225      }
226      QuickFixMark fixMark;
227      fixMark.bundleName = bundleName;
228      fixMark.status = enable ? QuickFixStatus::SWITCH_ENABLE_START : QuickFixStatus::SWITCH_DISABLE_START;
229      innerAppQuickFix.SetAppQuickFix(appQuickFix);
230      innerAppQuickFix.SetQuickFixMark(fixMark);
231      return ERR_OK;
232  }
233  
GetQuickFixDataMgr()234  ErrCode QuickFixSwitcher::GetQuickFixDataMgr()
235  {
236      if (quickFixDataMgr_ == nullptr) {
237          quickFixDataMgr_ = DelayedSingleton<QuickFixDataMgr>::GetInstance();
238          if (quickFixDataMgr_ == nullptr) {
239              LOG_E(BMS_TAG_DEFAULT, "quickFixDataMgr_ is nullptr");
240              return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
241          }
242      }
243      return ERR_OK;
244  }
245  
GetDataMgr()246  ErrCode QuickFixSwitcher::GetDataMgr()
247  {
248      if (dataMgr_ == nullptr) {
249          dataMgr_ = DelayedSingleton<BundleMgrService>::GetInstance()->GetDataMgr();
250          if (dataMgr_ == nullptr) {
251              LOG_E(BMS_TAG_DEFAULT, "dataMgr_ is nullptr");
252              return ERR_BUNDLEMANAGER_QUICK_FIX_INTERNAL_ERROR;
253          }
254      }
255      return ERR_OK;
256  }
257  } // AppExecFwk
258  } // OHOS