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