1/* 2 * Copyright (c) 2023 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 16import type common from '@ohos.app.ability.common'; 17import update from '@ohos.update'; 18import type Want from '@ohos.app.ability.Want'; 19import { ErrorCode } from '@ohos/common/src/main/ets/const/update_const'; 20import type { OtaStatus, UpgradeData, } from '@ohos/common/src/main/ets/const/update_const'; 21import { UpdateState, UpgradeCallResult, } from '@ohos/common/src/main/ets/const/update_const'; 22import type { Message } from '@ohos/common/src/main/ets/manager/UpdateManager'; 23import { 24 UpdateManager, 25 MessageQueue, 26 OtaStatusHolder, 27} from '@ohos/common/src/main/ets/manager/UpdateManager'; 28import { DeviceUtils } from '@ohos/common/src/main/ets/util/DeviceUtils'; 29import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; 30import type { BaseState } from '../manager/StateManager'; 31import { StateManager } from '../manager/StateManager'; 32import { NotificationManager } from '../notify/NotificationManager'; 33import VersionUtils from '../util/VersionUtils'; 34import { UpgradeAdapter } from '../UpgradeAdapter'; 35import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; 36 37/** 38 * 升级接口管理类 39 * 40 * @since 2022-06-05 41 */ 42export class OtaUpdateManager { 43 private static readonly KEY = 'EventInfo'; 44 private _updateStatus: number; 45 private _downloadProgress: number; 46 private lastStatus: number; 47 private stateObj: BaseState; 48 private otaStatusHolder: OtaStatusHolder; 49 private updateManager: UpdateManager; 50 private messageQueue: MessageQueue; 51 52 /** 53 * 单例--升级管理类对象实例 54 * 55 * @return 升级管理类对象实例 56 */ 57 static getInstance(): OtaUpdateManager { 58 return globalThis.otaUpdateManager ?? new OtaUpdateManager(); 59 } 60 61 private constructor() { 62 this.log('OtaUpdateManager init.'); 63 globalThis.otaUpdateManager = this; 64 this.otaStatusHolder = new OtaStatusHolder(); 65 this.messageQueue = new MessageQueue(this.handleMessage.bind(this)); 66 67 this.updateManager = new UpdateManager(); 68 this.updateManager.bind(update.BusinessSubType.FIRMWARE, this.notifyUpdateStatusRemote.bind(this)); 69 } 70 71 /** 72 * 取升级状态 73 * 74 * @return resolve 状态/reject 错误信息 75 */ 76 async getOtaStatus(): Promise<UpgradeData<OtaStatus>> { 77 return new Promise((resolve, reject) => { 78 this.updateManager.getOtaStatus().then((result: UpgradeData<OtaStatus>) => { 79 if (result?.callResult === UpgradeCallResult.OK) { 80 this.refreshState(result?.data); 81 } 82 resolve(result); 83 }); 84 }); 85 } 86 87 /** 88 * 从due数据库取新版本信息 89 * 90 * @return resolve 新版本信息/reject 错误信息 91 */ 92 async getNewVersion(): Promise<UpgradeData<update.NewVersionInfo>> { 93 return new Promise((resolve, reject) => { 94 this.updateManager.getNewVersion().then((result: UpgradeData<update.NewVersionInfo>) => { 95 if (result?.callResult === UpgradeCallResult.OK) { 96 globalThis.cachedNewVersionInfo = result?.data; 97 resolve(result); 98 } else { 99 resolve(result); 100 } 101 }); 102 }); 103 } 104 105 /** 106 * 获取新版本描述文件 107 * 108 * @return 新版本描述文件 109 */ 110 async getNewVersionDescription(): Promise<UpgradeData<Array<update.ComponentDescription>>> { 111 let versionDigest: string = await VersionUtils.getNewVersionDigest(); 112 return this.updateManager.getNewVersionDescription(versionDigest, update.DescriptionFormat.STANDARD, 113 DeviceUtils.getSystemLanguage()); 114 } 115 116 /** 117 * 获取当前版本升级日志 118 * 119 * @return 当前版本描述文件 120 */ 121 async getCurrentVersionDescription(): Promise<UpgradeData<Array<update.ComponentDescription>>> { 122 return this.updateManager.getCurrentVersionDescription(update.DescriptionFormat.STANDARD, 123 DeviceUtils.getSystemLanguage()); 124 } 125 126 /** 127 * 从服务器取搜索新版本 128 * 129 * @return resolve 新版本信息/reject 错误信息 130 */ 131 async checkNewVersion(): Promise<UpgradeData<update.CheckResult>> { 132 return new Promise((resolve, reject) => { 133 this.updateManager.checkNewVersion().then((result: UpgradeData<update.CheckResult>) => { 134 if (result?.callResult === UpgradeCallResult.OK) { 135 globalThis.cachedNewVersionInfo = result?.data?.newVersionInfo; 136 resolve(result); 137 } else { 138 resolve(result); 139 } 140 }); 141 }); 142 } 143 144 /** 145 * 升级 146 * 147 * @param order 安装指令 148 */ 149 async upgrade(order: update.Order = update.Order.INSTALL_AND_APPLY): Promise<void> { 150 let versionDigest: string = await VersionUtils.getNewVersionDigest(); 151 return new Promise((resolve, reject) => { 152 this.updateManager.upgrade(versionDigest, order).then(()=> { 153 resolve(); 154 }).catch(err => { 155 let status: OtaStatus = { 156 status: order === update.Order.APPLY ? UpdateState.INSTALL_SUCCESS : UpdateState.DOWNLOAD_SUCCESS, 157 percent: 100, 158 endReason: err?.data?.[0]?.errorCode?.toString() || ErrorCode.DEFAULT_ERROR, 159 }; 160 this.notifyUpdateStatus(status, globalThis.abilityContext); 161 this.logError('upgrade err:' + JSON.stringify(err)); 162 reject(err); 163 }); 164 }); 165 } 166 167 /** 168 * 下载 169 * 170 * @param downloadNetwork 下载网络类型,默认为wifi 171 */ 172 async download(downloadNetwork: update.NetType = update.NetType.WIFI): Promise<void> { 173 UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 174 let versionDigest: string = await VersionUtils.getNewVersionDigest(); 175 this.setDownloadProgress(0); 176 this.updateManager.download(versionDigest, downloadNetwork, update.Order.DOWNLOAD) 177 .catch(err => { 178 let status: OtaStatus = { 179 status: UpdateState.CHECK_SUCCESS, 180 percent: 0, 181 endReason: err?.data?.[0]?.errorCode?.toString() || '', 182 }; 183 this.notifyUpdateStatus(status, globalThis.abilityContext); 184 }); 185 } 186 187 /** 188 * 继续下载 189 */ 190 async resumeDownload(): Promise<void> { 191 let versionDigest: string = await VersionUtils.getNewVersionDigest(); 192 this.setUpdateState(UpdateState.DOWNLOADING); 193 this.updateManager.resumeDownload(versionDigest, update.NetType.WIFI).then(result => { 194 this.log('resumeDownload result:' + JSON.stringify(result)); 195 }).catch(err => { 196 let status: OtaStatus = { 197 status: UpdateState.DOWNLOAD_PAUSE, 198 percent: this.getDownloadProgress(), 199 endReason: err?.data?.[0]?.errorCode?.toString() || '', 200 }; 201 this.notifyUpdateStatus(status, globalThis.abilityContext); 202 }); 203 } 204 205 /** 206 * 取消升级 207 */ 208 async cancel(): Promise<void> { 209 this.setUpdateState(UpdateState.CHECK_SUCCESS); 210 this.setDownloadProgress(0); 211 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 212 this.updateManager.cancel(); 213 } 214 215 /** 216 * 取当前版本数据 217 * 218 * @return resolve 当前版本信息/reject 错误信息 219 */ 220 async getCurrentVersionInfo(): Promise<UpgradeData<update.CurrentVersionInfo>> { 221 return this.updateManager.getCurrentVersionInfo(); 222 } 223 224 /** 225 * 取升级状态缓存数据 226 * 227 * @return 升级状态 228 */ 229 getUpdateState(): number { 230 return this._updateStatus; 231 } 232 233 /** 234 * 设置升级状态缓存数据 235 * 236 * @param value 状态 237 */ 238 setUpdateState(value): void { 239 if (this._updateStatus !== Number(value) && value !== undefined && value !== null) { 240 this._updateStatus = Number(value); 241 AppStorage.Set('updateStatus', this._updateStatus); 242 } 243 } 244 245 /** 246 * 取升级进度 247 * 248 * @return 升级进度 249 */ 250 getDownloadProgress(): number { 251 return this._downloadProgress; 252 } 253 254 /** 255 * 设置进度 256 * 257 * @param value 进度 258 */ 259 setDownloadProgress(value): void { 260 if (this._downloadProgress !== value && value !== undefined && value !== null) { 261 this._downloadProgress = value; 262 AppStorage.Set('downloadProgress', this._downloadProgress); 263 } 264 } 265 266 /** 267 * 取状态对象 268 * 269 * @param status 状态 270 */ 271 getStateObj(status: number): BaseState { 272 if (this.stateObj?.state === status) { 273 return this.stateObj; 274 } else { 275 return StateManager.createInstance(status); 276 } 277 } 278 279 private refreshState(otaStatus: OtaStatus): void { 280 if (!this.stateObj || this.lastStatus !== otaStatus.status) { 281 this.stateObj = StateManager.createInstance(otaStatus); 282 } 283 this.stateObj.refresh(otaStatus); 284 this.lastStatus = otaStatus.status; 285 this.setUpdateState(this.stateObj.state); 286 this.setDownloadProgress(this.stateObj.percent); 287 } 288 289 /** 290 * 状态刷新 291 * 292 * @param otaStatus 状态 293 */ 294 private async notifyUpdateStatusRemote(eventInfo: update.EventInfo): Promise<void> { 295 this.log(`notifyUpdateStatusRemote ${JSON.stringify(eventInfo)}`); 296 let message: Message = { 297 context: globalThis.extensionContext || globalThis.abilityContext, 298 eventInfo: eventInfo, 299 }; 300 301 this.messageQueue.execute(message); 302 } 303 304 private async handleMessage(context: common.Context, eventInfo: update.EventInfo): Promise<void> { 305 let otaStatus: OtaStatus = this.getFormattedOtaStatus(eventInfo); 306 if (this.isTerminalState(otaStatus)) { 307 globalThis.lastVersionName = await VersionUtils.obtainNewVersionName(eventInfo?.taskBody); 308 } 309 let versionDigest: string = eventInfo?.taskBody?.versionDigestInfo?.versionDigest ?? ''; 310 await this.notifyUpdateStatus(otaStatus, context, versionDigest, eventInfo?.eventId); 311 } 312 313 private async notifyUpdateStatus(otaStatus: OtaStatus, context: common.Context, verDigest?: string, 314 eventId?: update.EventId): Promise<void> { 315 this.log('notifyUpdateStatus:' + JSON.stringify(otaStatus)); 316 this.refreshState(otaStatus); 317 318 if (!this.otaStatusHolder.isStatusChangedAndRefresh(otaStatus, eventId)) { 319 LogUtils.warn('UpdateManager', 'notifyUpdateStatus is repeating, abandon.'); 320 return; 321 } 322 if (!globalThis.cachedNewVersionInfo && !this.isTerminalState(otaStatus)) { 323 await this.getNewVersion(); 324 } 325 326 await StateManager.createInstance(otaStatus).notify(context, eventId); 327 } 328 329 private isTerminalState(otaStatus: OtaStatus): boolean { 330 let status = otaStatus?.status ?? UpdateState.INIT; 331 if (status === UpdateState.INIT || status === UpdateState.DOWNLOAD_FAILED || 332 status === UpdateState.INSTALL_FAILED || status === UpdateState.UPGRADE_SUCCESS || 333 status === UpdateState.UPGRADE_FAILED) { 334 return true; 335 } 336 return false; 337 } 338 339 /** 340 * 收到推送消息 341 * 342 * @param otaStatus 状态数据 343 */ 344 async onReceivedUpdateServiceMessage(eventInfo: update.EventInfo): Promise<void> { 345 this.log('receives from onReceivedUpdateServiceMessage:' + JSON.stringify(eventInfo)); 346 let message: Message = { 347 context: globalThis.extensionContext, 348 eventInfo: eventInfo, 349 }; 350 await this.messageQueue.execute(message); 351 } 352 353 /** 354 * 收到page推送消息 355 * 356 * @param otaStatus 状态数据 357 */ 358 async onReceivedUpdatePageMessage(otaStatus: OtaStatus): Promise<void> { 359 this.log('receives from onReceivedUpdatePageMessage:' + JSON.stringify(otaStatus)); 360 this.notifyUpdateStatus(otaStatus, globalThis.abilityContext); 361 } 362 363 /** 364 * 处理推送消息 365 * 366 * @param want 推送数据 367 * @param context 上下文 368 */ 369 public async handleWant(want: Want, context: common.Context): Promise<void> { 370 let action: string = want?.action ?? ''; 371 if (await NotificationManager.handleAction(action, context)) { 372 this.log('handleWant:' + FormatUtils.stringify(want)); 373 return; 374 } 375 let eventInfo = this.wantParser(want); 376 this.log('handleWant: eventInfo is ' + FormatUtils.stringify(eventInfo)); 377 if (!eventInfo?.eventId) { 378 this.log('eventInfo?.eventId is null'); 379 return; 380 } 381 await this.onReceivedUpdateServiceMessage(eventInfo); 382 } 383 384 /** 385 * 是否升级终止 386 * 387 * @return 是否升级终止 388 */ 389 public isTerminal(): boolean { 390 return this.isTerminalState(this.stateObj?.otaStatus); 391 } 392 393 private wantParser(want: Want): update.EventInfo { 394 let eventInfo: update.EventInfo = want?.parameters?.[OtaUpdateManager.KEY] as update.EventInfo; 395 if (typeof eventInfo === 'string') { 396 eventInfo = FormatUtils.parseJson(eventInfo); 397 } 398 return eventInfo; 399 } 400 401 private log(message: string): void { 402 LogUtils.log('UpdateManager', message); 403 } 404 405 private logError(message: string): void { 406 LogUtils.error('UpdateManager', message); 407 } 408 409 /** 410 * 通过eventInfo获取OtaStatus 411 * 同时对status、percent数据进行调整 412 * 413 * @param eventInfo 事件 414 * @return OtaStatus 实例 415 */ 416 private getFormattedOtaStatus(eventInfo: update.EventInfo): OtaStatus { 417 let endReason: string = eventInfo.taskBody?.errorMessages?.[0]?.errorCode?.toString(); 418 let otaStatus: OtaStatus = { 419 status: eventInfo.taskBody?.status, 420 percent: eventInfo.taskBody?.progress, 421 endReason: !endReason || endReason === '0' ? null : endReason, 422 }; 423 if (!otaStatus.status) { 424 otaStatus.status = this.getUpdateStateFromEventId(eventInfo.eventId); 425 } 426 return otaStatus; 427 } 428 429 private getUpdateStateFromEventId(eventId: update.EventId): UpdateState { 430 let status: UpdateState; 431 switch (eventId) { 432 case update.EventId.EVENT_TASK_RECEIVE: 433 status = UpdateState.CHECK_SUCCESS; 434 break; 435 case update.EventId.EVENT_TASK_CANCEL: 436 status = UpdateState.INIT; 437 break; 438 case update.EventId.EVENT_DOWNLOAD_START: 439 status = UpdateState.DOWNLOADING; 440 break; 441 case update.EventId.EVENT_DOWNLOAD_SUCCESS: 442 status = UpdateState.DOWNLOAD_SUCCESS; 443 break; 444 case update.EventId.EVENT_DOWNLOAD_FAIL: 445 status = UpdateState.DOWNLOAD_FAILED; 446 break; 447 case update.EventId.EVENT_UPGRADE_SUCCESS: 448 status = UpdateState.UPGRADE_SUCCESS; 449 break; 450 case update.EventId.EVENT_UPGRADE_FAIL: 451 status = UpdateState.UPGRADE_FAILED; 452 break; 453 default: 454 break; 455 } 456 return status; 457 } 458}