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