UNPKG

@ledgerhq/live-common

Version:
156 lines • 7.42 kB
import { log } from "@ledgerhq/logs"; import { MCUNotGenuineToDashboard } from "@ledgerhq/errors"; import { from, of, EMPTY, concat, throwError } from "rxjs"; import { concatMap, delay, filter, map, mergeMap, throttleTime } from "rxjs/operators"; import semver from "semver"; import ManagerAPI from "../manager/api"; import { withDevicePolling, withDevice } from "./deviceAccess"; import { getProviderId } from "../manager/provider"; import getDeviceInfo from "./getDeviceInfo"; import { mcuOutdated, mcuNotGenuine, followDeviceRepair, followDeviceUpdate, } from "../deviceWordings"; import { getDeviceRunningMode } from "./getDeviceRunningMode"; import { fetchMcusUseCase } from "../device/use-cases/fetchMcusUseCase"; const wait2s = of({ type: "wait", }).pipe(delay(2000)); export const repairChoices = [ { id: "mcuOutdated", label: mcuOutdated, forceMCU: "0.7", }, { id: "mcuNotGenuine", label: mcuNotGenuine, forceMCU: "0.7", }, { id: "followDeviceRepair", label: followDeviceRepair, forceMCU: "0.9", }, { id: "followDeviceUpdate", label: followDeviceUpdate, forceMCU: "0.9", }, ]; const repair = (deviceId, forceMCU_) => { log("hw", "firmwareUpdate-repair"); const mcusPromise = fetchMcusUseCase(); const withDeviceInfo = withDevicePolling(deviceId)(transport => from(getDeviceInfo(transport)), () => true); const waitForBootloader = withDeviceInfo.pipe(concatMap(deviceInfo => (deviceInfo.isBootloader ? EMPTY : concat(wait2s, waitForBootloader)))); const loop = (forceMCU) => concat(withDeviceInfo.pipe(concatMap(deviceInfo => { const installMcu = (version) => withDevice(deviceId)(transport => ManagerAPI.installMcu(transport, "mcu", { targetId: deviceInfo.targetId, version, })); if (!deviceInfo.isBootloader) { // finish earlier return EMPTY; } // This is a special case where user is in firmware 1.3.1 // and the device shows MCU Not Genuine. // User needs to press both keys three times to go back to dashboard // and continue the update process if (forceMCU && forceMCU === "0.7" && (deviceInfo.majMin === "0.6" || deviceInfo.majMin === "0.7")) { // finish earlier return throwError(() => new MCUNotGenuineToDashboard()); } if (forceMCU) { return concat(installMcu(forceMCU), wait2s, loop()); } switch (deviceInfo.majMin) { case "0.0": return concat(installMcu("0.6"), wait2s, loop()); case "0.6": return installMcu("1.5"); case "0.7": return installMcu("1.6"); case "0.9": return installMcu("1.7"); default: return from(mcusPromise).pipe(concatMap(mcus => { let next; const { seVersion, seTargetId, mcuBlVersion } = deviceInfo; // This is a special case where a user with LNX version >= 2.0.0 // comes back with a broken updated device. We need to be able // to patch MCU or Bootloader if needed if (seVersion && seTargetId) { log("hw", "firmwareUpdate-repair seVersion and seTargetId found", { seVersion, seTargetId, }); const provider = getProviderId(deviceInfo); /** * filter the MCUs that are available on the provider and * have a "from_bootloader_version" different from "none" * */ const availableMcus = mcus.filter(mcu => mcu.providers.includes(provider) && mcu.from_bootloader_version !== "none"); log("hw", `firmwareUpdate-repair available mcus on provider ${provider}`, { availableMcus, }); return from(ManagerAPI.getDeviceVersion(seTargetId, getProviderId(deviceInfo))).pipe(mergeMap((deviceVersion) => from(ManagerAPI.getCurrentFirmware({ deviceId: deviceVersion.id, version: seVersion, provider: getProviderId(deviceInfo), }))), mergeMap((finalFirmware) => { log("hw", "firmwareUpdate-repair got final firmware", { finalFirmware, }); const mcu = ManagerAPI.findBestMCU(availableMcus.filter(({ id }) => finalFirmware.mcu_versions.includes(id))); log("hw", "firmwareUpdate-repair got mcu", { mcu }); if (!mcu) return EMPTY; const expectedBootloaderVersion = semver.coerce(mcu.from_bootloader_version)?.version; const currentBootloaderVersion = semver.coerce(mcuBlVersion)?.version; log("hw", "firmwareUpdate-repair bootloader versions", { currentBootloaderVersion, expectedBootloaderVersion, }); if (expectedBootloaderVersion === currentBootloaderVersion) { next = mcu; log("hw", "firmwareUpdate-repair bootloader versions are the same", { next, }); } else { next = { name: mcu.from_bootloader_version, }; log("hw", "firmwareUpdate-repair bootloader versions are different", { next, }); } return installMcu(next.name); })); } else { next = ManagerAPI.findBestMCU(ManagerAPI.compatibleMCUForDeviceInfo(mcus, deviceInfo, getProviderId(deviceInfo))); if (next) return installMcu(next.name); } return EMPTY; })); } })), from(getDeviceRunningMode({ deviceId, unresponsiveTimeoutMs: 4000, cantOpenDeviceRetryLimit: 2, })).pipe(mergeMap(result => { if (result.type === "bootloaderMode") { return loop(forceMCU); } else { return EMPTY; } }))); // TODO ideally we should race waitForBootloader with an event "display-bootloader-reboot", it should be a delayed event that is not emitted if waitForBootloader is fast enough.. return concat(waitForBootloader, loop(forceMCU_)).pipe(filter((e) => e.type === "bulk-progress"), map(e => ({ progress: e.progress, })), throttleTime(100)); }; export default repair; //# sourceMappingURL=firmwareUpdate-repair.js.map