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