diff options
| author | Danny Lin <danny@kdrag0n.dev> | 2021-01-22 20:14:00 -0800 |
|---|---|---|
| committer | Daniel Micay <danielmicay@gmail.com> | 2021-01-23 03:28:02 -0500 |
| commit | 713b4ef564ad41d42b1d1d8a83753a9684f26001 (patch) | |
| tree | a274c0b9125273f6ab511a7ecc0f40fd9509a3f5 /static/js/web-install.js | |
| parent | bd2626b41e5fde8a057a61dd504c11468e2b497a (diff) | |
Implement web install using fastboot.js
This implements the WebUSB-based web installer using fastboot.js to
act as a fastboot client. A bare minimum UI with a plain-text
status/progress caption for each step is included, as well as a plain
button to trigger it and basic error handling.
WebADB has been removed now that we are only using fastboot.js.
Initial features:
- Unlocking and locking the bootloader
- Downloading the latest GrapheneOS release available for the device
- Flashing the factory images zip
- Reusing USB connections
Diffstat (limited to 'static/js/web-install.js')
| -rw-r--r-- | static/js/web-install.js | 129 |
1 files changed, 77 insertions, 52 deletions
diff --git a/static/js/web-install.js b/static/js/web-install.js index eaebe471..f4ee0c8d 100644 --- a/static/js/web-install.js +++ b/static/js/web-install.js @@ -1,77 +1,102 @@ // @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT -async function unlockBootloader() { - const webusb = await Adb.open("WebUSB"); +import { FastbootDevice } from "./fastboot/fastboot.js"; +import * as Factory from "./fastboot/factory.js"; - if (!webusb.isFastboot()) { - console.log("error: not in fastboot mode"); - } +const RELEASES_URL = "https://releases.grapheneos.org"; - console.log("connecting with fastboot"); +let device = new FastbootDevice(); +let lastReleaseZip = null; - const fastboot = await webusb.connectFastboot(); - await fastboot.send("flashing unlock"); - await fastboot.receive(); +async function ensureConnected(setProgress) { + console.log(device.device); + if (!device.isConnected) { + setProgress("Connecting to device..."); + await device.connect(); + } } -async function downloadRelease() { - const webusb = await Adb.open("WebUSB"); +async function unlockBootloader(setProgress) { + await ensureConnected(setProgress); - if (!webusb.isFastboot()) { - console.log("error: not in fastboot mode"); - } + setProgress("Unlocking bootloader..."); + await device.runCommand("flashing unlock"); + return "Bootloader unlocked."; +} - console.log("connecting with fastboot"); +async function downloadRelease(setProgress) { + await ensureConnected(setProgress); - const fastboot = await webusb.connectFastboot(); - await fastboot.send("getvar:product"); - const response = await fastboot.receive(); - if (fastboot.get_cmd(response) == "FAIL") { - throw new Error("getvar product failed"); - } - const decoder = new TextDecoder(); - const product = decoder.decode(fastboot.get_payload(response)); + setProgress("Getting device model..."); + let product = await device.getVariable("product"); + setProgress("Finding latest release..."); + let metadataResp = await fetch(`${RELEASES_URL}/${product}-stable`); + let metadata = await metadataResp.text(); + let releaseId = metadata.split(" ")[0]; + + // Download and cache the zip as a blob + setProgress(`Downloading ${releaseId} release for ${product}...`); + lastReleaseZip = `${product}-factory-${releaseId}.zip`; + await Factory.downloadZip(`${RELEASES_URL}/${lastReleaseZip}`); + return `Downloaded ${releaseId} release for ${product}.`; +} - const baseUrl = "https://releases.grapheneos.org/"; - const metadata = await (await fetch(baseUrl + product + "-stable")).text(); - const buildNumber = metadata.split(" ")[0]; - const downloadUrl = baseUrl + product + "-factory-" + buildNumber + ".zip"; - console.log(downloadUrl); +async function flashRelease(setProgress) { + await ensureConnected(setProgress); - // Download factory images - // - // Need to do this in a way that works well with huge files, particularly since the zip needs - // to be extracted. Could potentially split it up on the server if this works out badly. + setProgress("Flashing release..."); + await Factory.flashZip(device, lastReleaseZip, (action, partition) => { + let userPartition = partition == "avb_custom_key" ? "verified boot key" : partition; + if (action == "unpack") { + setProgress(`Unpacking image: ${userPartition}`); + } else { + setProgress(`Flashing image: ${userPartition}`); + } + }); + return `Flashed ${lastReleaseZip} to device.`; } -async function lockBootloader() { - const webusb = await Adb.open("WebUSB"); +async function lockBootloader(setProgress) { + await ensureConnected(setProgress); - if (!webusb.isFastboot()) { - console.log("error: not in fastboot mode"); - } + setProgress("Locking bootloader..."); + await device.runCommand("flashing lock"); + return "Bootloader locked."; +} - console.log("connecting with fastboot"); +function addButtonHook(id, callback) { + let statusField = document.getElementById(`${id}-status`); + let statusCallback = (status) => { + statusField.textContent = status; + statusField.className = ""; + }; - const fastboot = await webusb.connectFastboot(); - await fastboot.send("flashing lock"); - await fastboot.receive(); + let button = document.getElementById(`${id}-button`); + button.disabled = false; + button.onclick = async () => { + try { + let finalStatus = await callback(statusCallback); + statusCallback(finalStatus); + } catch (error) { + statusCallback(`Error: ${error.message}`); + statusField.className = "error-text"; + } + }; } +// zip.js is loaded separately. +// eslint-disable-next-line no-undef +zip.configure({ + workerScriptsPath: "/js/fastboot/libs/", +}); + if ("usb" in navigator) { console.log("WebUSB available"); - const unlockBootloaderButton = document.getElementById("unlock-bootloader"); - unlockBootloaderButton.disabled = false; - unlockBootloaderButton.onclick = unlockBootloader; - - const downloadReleaseButton = document.getElementById("download-release"); - downloadReleaseButton.disabled = false; - downloadReleaseButton.onclick = downloadRelease; - - const lockBootloaderButton = document.getElementById("lock-bootloader"); - lockBootloaderButton.disabled = false; - lockBootloaderButton.onclick = lockBootloader; + addButtonHook("unlock-bootloader", unlockBootloader); + addButtonHook("download-release", downloadRelease); + addButtonHook("flash-release", flashRelease); + addButtonHook("lock-bootloader", lockBootloader); } else { console.log("WebUSB unavailable"); } |
