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