summaryrefslogtreecommitdiff
path: root/static/js/web-install.js
diff options
context:
space:
mode:
authorDanny Lin <danny@kdrag0n.dev>2021-01-22 20:14:00 -0800
committerDaniel Micay <danielmicay@gmail.com>2021-01-23 03:28:02 -0500
commit713b4ef564ad41d42b1d1d8a83753a9684f26001 (patch)
treea274c0b9125273f6ab511a7ecc0f40fd9509a3f5 /static/js/web-install.js
parentbd2626b41e5fde8a057a61dd504c11468e2b497a (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.js129
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");
}