• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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