@aepyornis/fastboot.ts 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -27
- package/package.json +1 -1
- package/src/client.ts +58 -5
- package/src/flasher.ts +23 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# fastboot.ts
|
|
2
2
|
|
|
3
|
+
Android Fastboot implementation for WebUSB
|
|
4
|
+
|
|
3
5
|
```sh
|
|
4
6
|
npm install
|
|
5
7
|
npm run build
|
|
@@ -11,7 +13,11 @@ npm run build
|
|
|
11
13
|
src/sparse.ts sparse image utilities Copyright (c) 2021 Danny Lin <danny@kdrag0n.dev>
|
|
12
14
|
|
|
13
15
|
```js
|
|
14
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
FastbootDevice,
|
|
18
|
+
FastbootClient,
|
|
19
|
+
FastbootFlasher,
|
|
20
|
+
} from "@aepyornis/fastboot.ts"
|
|
15
21
|
|
|
16
22
|
const client = await FastbootClient.create()
|
|
17
23
|
|
|
@@ -21,31 +27,12 @@ await client.getVar("product")
|
|
|
21
27
|
|
|
22
28
|
// flash CalyxOS
|
|
23
29
|
import OpfsBlobStore from "@aepyornis/opfs_blob_store"
|
|
24
|
-
const
|
|
25
|
-
const hash = "
|
|
26
|
-
const url = "https://release.calyxinstitute.org/
|
|
27
|
-
await
|
|
28
|
-
const file = await
|
|
29
|
-
|
|
30
|
-
const instructions = `
|
|
31
|
-
fastboot --set-active=other reboot-bootloader
|
|
32
|
-
sleep 5
|
|
33
|
-
fastboot flash --slot=other bootloader bootloader-lynx-lynx-15.2-12878710.img
|
|
34
|
-
fastboot --set-active=other reboot-bootloader
|
|
35
|
-
sleep 5
|
|
36
|
-
fastboot flash --slot=other radio radio-lynx-g5300q-241205-250127-B-12973597.img
|
|
37
|
-
fastboot --set-active=other reboot-bootloader
|
|
38
|
-
sleep 5
|
|
39
|
-
fastboot flash --slot=other radio radio-lynx-g5300q-241205-250127-B-12973597.img
|
|
40
|
-
fastboot --set-active=other reboot-bootloader
|
|
41
|
-
sleep 5
|
|
42
|
-
fastboot erase avb_custom_key
|
|
43
|
-
fastboot flash avb_custom_key avb_custom_key.img
|
|
44
|
-
fastboot --skip-reboot -w update image-lynx-bp1a.250305.019.zip
|
|
45
|
-
fastboot reboot-bootloader
|
|
46
|
-
`
|
|
47
|
-
|
|
30
|
+
const opfs = await OpfsBlobStore.create()
|
|
31
|
+
const hash = "db9ab330a1b5d5ebf131f378dca8b5f6400337f438a97aef2a09a1ba88f3935c"
|
|
32
|
+
const url = "https://release.calyxinstitute.org/bangkk-factory-25608210.zip"
|
|
33
|
+
await opfs.fetch(hash, url)
|
|
34
|
+
const file = await opfs.get(hash)
|
|
48
35
|
const client = await FastbootClient.create()
|
|
49
|
-
const deviceFlasher = new FastbootFlasher()
|
|
50
|
-
await deviceFlasher.
|
|
36
|
+
const deviceFlasher = new FastbootFlasher(client, file)
|
|
37
|
+
await deviceFlasher.runFlashAll()
|
|
51
38
|
```
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BlobWriter, Entry } from "@zip.js/zip.js"
|
|
2
2
|
import { IMAGES } from "./images"
|
|
3
3
|
import { parseBlobHeader, splitBlob, fromRaw } from "./sparse"
|
|
4
|
-
import { FastbootDevice } from "./device"
|
|
4
|
+
import { FastbootDevice, FastbootDeviceError } from "./device"
|
|
5
5
|
|
|
6
6
|
export class FastbootError extends Error {}
|
|
7
7
|
|
|
@@ -20,16 +20,27 @@ interface Logger {
|
|
|
20
20
|
export class FastbootClient {
|
|
21
21
|
fd: FastbootDevice
|
|
22
22
|
logger: Logger
|
|
23
|
+
var_cache: Object
|
|
23
24
|
|
|
24
25
|
constructor(usb_device: USBDevice, logger: Logger = window.console) {
|
|
25
26
|
this.fd = new FastbootDevice(usb_device, logger)
|
|
26
27
|
this.logger = logger
|
|
28
|
+
this.var_cache = {}
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
async getVar(variable: string) {
|
|
30
32
|
return this.fd.getVar(variable)
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
async getVarCache(variable: string) {
|
|
36
|
+
if (this.var_cache[variable]) {
|
|
37
|
+
return this.var_cache[variable]
|
|
38
|
+
} else {
|
|
39
|
+
this.var_cache[variable] = await this.getVar(variable)
|
|
40
|
+
return this.var_cache[variable]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
async lock() {
|
|
34
45
|
await this.flashing("lock")
|
|
35
46
|
if (await this.unlocked()) {
|
|
@@ -139,10 +150,16 @@ export class FastbootClient {
|
|
|
139
150
|
) {
|
|
140
151
|
return true
|
|
141
152
|
}
|
|
142
|
-
|
|
143
153
|
this.logger.log(`ACTION NEEDED: flashing ${command}`)
|
|
144
154
|
await this.fd.exec(`flashing ${command}`)
|
|
145
|
-
|
|
155
|
+
|
|
156
|
+
// some devices register before we have a chance to call
|
|
157
|
+
// waitForReconnect()
|
|
158
|
+
if ((await navigator.usb.getDevices()).length > 0) {
|
|
159
|
+
await this.fd.reconnect()
|
|
160
|
+
} else {
|
|
161
|
+
await this.fd.waitForReconnect()
|
|
162
|
+
}
|
|
146
163
|
}
|
|
147
164
|
|
|
148
165
|
// run text, typically the contents of fastboot-info.txt
|
|
@@ -273,11 +290,21 @@ export class FastbootClient {
|
|
|
273
290
|
}
|
|
274
291
|
|
|
275
292
|
async unlocked() {
|
|
276
|
-
|
|
293
|
+
const product = await this.getVarCache("product")
|
|
294
|
+
if (product === "bangkk") {
|
|
295
|
+
return (await this.getVar("securestate")) === "flashing_unlocked"
|
|
296
|
+
} else {
|
|
297
|
+
return (await this.getVar("unlocked")) === "yes"
|
|
298
|
+
}
|
|
277
299
|
}
|
|
278
300
|
|
|
279
301
|
async locked() {
|
|
280
|
-
|
|
302
|
+
const product = await this.getVarCache("product")
|
|
303
|
+
if (product === "bangkk") {
|
|
304
|
+
return (await this.getVar("securestate")) === "flashing_locked"
|
|
305
|
+
} else {
|
|
306
|
+
return (await this.getVar("unlocked")) === "no"
|
|
307
|
+
}
|
|
281
308
|
}
|
|
282
309
|
|
|
283
310
|
async currentSlot() {
|
|
@@ -301,6 +328,32 @@ export class FastbootClient {
|
|
|
301
328
|
return (await this.getVar("is-userspace")) === "yes"
|
|
302
329
|
}
|
|
303
330
|
|
|
331
|
+
// tested on bangkk only
|
|
332
|
+
async getUnlockData() {
|
|
333
|
+
await client.fd.exec(`oem get_unlock_data`)
|
|
334
|
+
|
|
335
|
+
let data = ""
|
|
336
|
+
|
|
337
|
+
for (let packet of client.fd.session.packets) {
|
|
338
|
+
if (packet.command) {
|
|
339
|
+
continue
|
|
340
|
+
} else if (packet.status === "INFO") {
|
|
341
|
+
let message = packet.message.replace("(bootloader)", "").trim()
|
|
342
|
+
if (message === "Unlock data:") {
|
|
343
|
+
continue
|
|
344
|
+
} else {
|
|
345
|
+
data += message
|
|
346
|
+
}
|
|
347
|
+
} else if (packet.status === "OKAY") {
|
|
348
|
+
break
|
|
349
|
+
} else {
|
|
350
|
+
throw new Error(`packet status == ${packet.status}`)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return data
|
|
355
|
+
}
|
|
356
|
+
|
|
304
357
|
static async create() {
|
|
305
358
|
return new FastbootClient(await this.requestUsbDevice(), window.console)
|
|
306
359
|
}
|
package/src/flasher.ts
CHANGED
|
@@ -57,6 +57,8 @@ function parseInstruction(text: string): Instruction {
|
|
|
57
57
|
options.wipe = true
|
|
58
58
|
} else if (word === "--set-active=other") {
|
|
59
59
|
options.setActive = "other"
|
|
60
|
+
} else if (word === "--set-active=a" || word === "--set-active=b") {
|
|
61
|
+
options.setActive = word.slice(-1)
|
|
60
62
|
} else if (word === "--slot-other") {
|
|
61
63
|
options.slot = "other"
|
|
62
64
|
} else if (word.slice(0, 6) === "--slot") {
|
|
@@ -105,12 +107,14 @@ export class FastbootFlasher {
|
|
|
105
107
|
// except fastboot or sleep
|
|
106
108
|
async runFlashAll() {
|
|
107
109
|
const entries: Entry[] = await this.reader.getEntries()
|
|
108
|
-
const flashAllSh = await getEntry(entries, "flash-all.sh").getData(
|
|
110
|
+
const flashAllSh = await getEntry(entries, "flash-all.sh").getData(
|
|
111
|
+
new TextWriter(),
|
|
112
|
+
)
|
|
109
113
|
|
|
110
114
|
const instructions = flashAllSh
|
|
111
115
|
.split("\n")
|
|
112
116
|
.map((x) => x.trim())
|
|
113
|
-
.filter(x =>
|
|
117
|
+
.filter((x) => x.slice(0, 9) === "fastboot " || x.slice(0, 5) === "sleep")
|
|
114
118
|
.join("\n")
|
|
115
119
|
|
|
116
120
|
return this.run(instructions)
|
|
@@ -141,6 +145,10 @@ export class FastbootFlasher {
|
|
|
141
145
|
} else if (command.command === "reboot-bootloader") {
|
|
142
146
|
if (command.options.setActive === "other") {
|
|
143
147
|
await this.client.setActiveOtherSlot()
|
|
148
|
+
} else if (command.options.setActive === "a") {
|
|
149
|
+
await this.client.fd.exec("set_active:a")
|
|
150
|
+
} else if (command.options.setActive === "b") {
|
|
151
|
+
await this.client.fd.exec("set_active:b")
|
|
144
152
|
}
|
|
145
153
|
await this.client.rebootBootloader()
|
|
146
154
|
} else if (command.command === "update") {
|
|
@@ -181,6 +189,19 @@ export class FastbootFlasher {
|
|
|
181
189
|
} else if (command.command === "sleep") {
|
|
182
190
|
const ms = command.args[0] ? parseInt(command.args[0]) * 1000 : 5000
|
|
183
191
|
await new Promise((resolve) => setTimeout(resolve, ms))
|
|
192
|
+
// do_oem_command in cpp is raw command?
|
|
193
|
+
} else if (command.command === "oem") {
|
|
194
|
+
// motorola setting that does nothing useful here?
|
|
195
|
+
if (
|
|
196
|
+
command.args[0] === "fb_mode_set" ||
|
|
197
|
+
command.args[0] === "fb_mode_clear"
|
|
198
|
+
) {
|
|
199
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
200
|
+
} else {
|
|
201
|
+
throw new Error(
|
|
202
|
+
`Fastboot oem command ${command.args[0]} not implemented`,
|
|
203
|
+
)
|
|
204
|
+
}
|
|
184
205
|
} else {
|
|
185
206
|
throw new Error(`Fastboot command ${command.command} not implemented`)
|
|
186
207
|
}
|