Skip to content

Commit f91b56f

Browse files
committed
refactor: Refactor 'flash' CLI and add sparse flashing
Rework the Fastboot CLI "flash" command to support --disable-verity and --disable-verification flags, improved argument parsing, ANDROID_PRODUCT_OUT fallback, slot "other" handling, VBMeta special-case flashing, and delegate raw/sparse flashing to driver methods. Introduce FastbootDriver implementations for flashing: FlashUnsparseImage, FlashRawImage and FlashSparseFile (new files), replacing older monolithic/raw-to-sparse logic. Sparse images are now handled via FirmwareKit.Sparse helpers and large raw images are converted/resparsed into sparse chunks when needed. Refactor Windows USB backend (LegacyUsbDevice) for clearer structure and robust async read/write with timeouts, interface checks, serial retrieval and proper disposal. Minor using cleanup in WinUSBDevice and several test files to remove unused usings. Also add FastbootCLI/Properties/launchSettings.json to .gitignore.
1 parent 69fe9df commit f91b56f

File tree

16 files changed

+276
-343
lines changed

16 files changed

+276
-343
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,4 +368,5 @@ MigrationBackup/
368368
.ionide/
369369

370370
# Fody - auto-generated XML schema
371-
FodyWeavers.xsd
371+
FodyWeavers.xsd
372+
FastbootCLI/Properties/launchSettings.json

FastbootCLI/Program.cs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -244,39 +244,56 @@ string GetPartition(string baseName)
244244
break;
245245

246246
case "flash":
247-
bool disableVerity = args.Contains("--disable-verity");
248-
bool disableVerification = args.Contains("--disable-verification");
249-
var flashArgs = args.Where(a => !a.StartsWith("--")).ToList();
250-
if (flashArgs.Count < 1) throw new Exception("usage: fastboot flash <partition> [filename]");
251-
string part = GetPartition(flashArgs[0]);
252-
string? file = flashArgs.Count > 1 ? flashArgs[1] : null;
253-
254-
if (file == null)
247+
if (args.Count == 0) throw new Exception("usage: fastboot flash [--disable-verity] [--disable-verification] <partition> [filename]");
248+
249+
bool disableVerity = false;
250+
bool disableVerification = false;
251+
var flashArgs = new List<string>();
252+
foreach (var a in args)
255253
{
256-
string? envOut = Environment.GetEnvironmentVariable("ANDROID_PRODUCT_OUT");
257-
if (!string.IsNullOrEmpty(envOut))
258-
{
259-
string imgName = $"{flashArgs[0]}.img";
260-
string candidate = Path.Combine(envOut, imgName);
261-
if (File.Exists(candidate)) file = candidate;
262-
}
254+
if (a == "--disable-verity") disableVerity = true;
255+
else if (a == "--disable-verification") disableVerification = true;
256+
else flashArgs.Add(a);
263257
}
264258

265-
if (file == null) throw new Exception("Could not find image. Please specify filename or set $ANDROID_PRODUCT_OUT.");
266-
if (!File.Exists(file)) throw new Exception($"File not found: {file}");
259+
if (flashArgs.Count == 0) throw new Exception("usage: fastboot flash [--disable-verity] [--disable-verification] <partition> [filename]");
267260

268-
if (part.StartsWith("vbmeta") && (disableVerity || disableVerification))
269-
util.FlashVbmeta(part, file, disableVerity, disableVerification).ThrowIfError();
261+
string flashPartition = flashArgs[0];
262+
string flashFile;
263+
if (flashArgs.Count > 1)
264+
{
265+
flashFile = flashArgs[1];
266+
}
270267
else
271268
{
272-
if (force)
269+
string? productOutForFlash = Environment.GetEnvironmentVariable("ANDROID_PRODUCT_OUT");
270+
if (string.IsNullOrEmpty(productOutForFlash))
273271
{
274-
util.OemCommand("snapshot-update cancel").ThrowIfError();
272+
throw new Exception("filename is required when ANDROID_PRODUCT_OUT is not set");
275273
}
274+
flashFile = Path.Combine(productOutForFlash, flashPartition + ".img");
275+
}
276276

277-
using var fs = File.OpenRead(file);
278-
util.FlashUnsparseImage(part, fs, fs.Length).ThrowIfError();
277+
if (!File.Exists(flashFile)) throw new FileNotFoundException(flashFile);
278+
279+
string? slotOverride = slot;
280+
if (slotOverride == "other")
281+
{
282+
string currentSlot = util.GetCurrentSlot();
283+
slotOverride = currentSlot == "a" ? "b" : "a";
279284
}
285+
286+
bool isVbmeta = flashPartition.StartsWith("vbmeta", StringComparison.OrdinalIgnoreCase);
287+
if (isVbmeta && (disableVerity || disableVerification))
288+
{
289+
string vbmetaTarget = GetPartition(flashPartition);
290+
util.FlashVbmeta(vbmetaTarget, flashFile, disableVerity, disableVerification).ThrowIfError();
291+
}
292+
else
293+
{
294+
util.FlashImage(flashPartition, flashFile, slotOverride);
295+
}
296+
280297
break;
281298

282299
case "flashall":

FirmwareKit.Comm.Fastboot.Tests/AospDriverTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Text;
2-
using FirmwareKit.Comm.Fastboot;
32

43
namespace FirmwareKit.Comm.Fastboot.Tests
54
{

FirmwareKit.Comm.Fastboot.Tests/AospFastbootDriverTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Text;
2-
using FirmwareKit.Comm.Fastboot;
32

43
namespace FirmwareKit.Comm.Fastboot.Tests
54
{

FirmwareKit.Comm.Fastboot.Tests/FastbootInfoTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Globalization;
22
using System.Text;
3-
using FirmwareKit.Comm.Fastboot;
43

54
namespace FirmwareKit.Comm.Fastboot.Tests
65
{

FirmwareKit.Comm.Fastboot.Tests/FastbootProtocolTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
using FirmwareKit.Comm.Fastboot.Usb;
12
using System.Globalization;
23
using System.Text;
3-
using FirmwareKit.Comm.Fastboot.Usb;
4-
using FirmwareKit.Comm.Fastboot;
54

65
namespace FirmwareKit.Comm.Fastboot.Tests
76
{

FirmwareKit.Comm.Fastboot.Tests/TcpTransportTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
using FirmwareKit.Comm.Fastboot.Network;
12
using System.Buffers.Binary;
23
using System.Net;
34
using System.Net.Sockets;
45
using System.Text;
5-
using FirmwareKit.Comm.Fastboot.Network;
6-
using FirmwareKit.Comm.Fastboot;
76

87
namespace FirmwareKit.Comm.Fastboot.Tests
98
{

FirmwareKit.Comm.Fastboot.Tests/UdpTransportTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
using FirmwareKit.Comm.Fastboot.Network;
12
using System.Buffers.Binary;
23
using System.Net;
34
using System.Net.Sockets;
45
using System.Text;
5-
using FirmwareKit.Comm.Fastboot.Network;
6-
using FirmwareKit.Comm.Fastboot;
76

87
namespace FirmwareKit.Comm.Fastboot.Tests
98
{

FirmwareKit.Comm.Fastboot/Backend/Usb/Windows/LegacyUsbDevice.cs

Lines changed: 78 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3,110 +3,111 @@
33
using static FirmwareKit.Comm.Fastboot.Usb.Windows.Win32API;
44

55
namespace FirmwareKit.Comm.Fastboot.Usb.Windows;
6-
public class LegacyUsbDevice : UsbDevice
7-
{
8-
private const int IoTimeoutMs = 30000;
9-
public static uint IoGetSerialCode => CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS);
10-
public static uint IoGetDescriptorCode => CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS);
11-
12-
private IntPtr fileHandle = INVALID_HANDLE_VALUE;
136

14-
public IntPtr Handle => fileHandle;
7+
public class LegacyUsbDevice : UsbDevice
8+
{
9+
private const int IoTimeoutMs = 30000;
10+
public static uint IoGetSerialCode => CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS);
11+
public static uint IoGetDescriptorCode => CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS);
1512

16-
public override int CreateHandle()
17-
{
18-
fileHandle = SimpleCreateHandle(DevicePath);
19-
if (fileHandle == INVALID_HANDLE_VALUE)
20-
return Marshal.GetLastWin32Error();
13+
private IntPtr fileHandle = INVALID_HANDLE_VALUE;
2114

22-
if (!CheckInterface())
23-
{
24-
CloseHandle(fileHandle);
25-
fileHandle = INVALID_HANDLE_VALUE;
26-
return -1;
27-
}
15+
public IntPtr Handle => fileHandle;
2816

29-
GetSerialNumber();
30-
return 0;
31-
}
17+
public override int CreateHandle()
18+
{
19+
fileHandle = SimpleCreateHandle(DevicePath);
20+
if (fileHandle == INVALID_HANDLE_VALUE)
21+
return Marshal.GetLastWin32Error();
3222

33-
private bool CheckInterface()
23+
if (!CheckInterface())
3424
{
35-
byte[] buffer = new byte[256];
36-
int returned;
37-
return DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero);
25+
CloseHandle(fileHandle);
26+
fileHandle = INVALID_HANDLE_VALUE;
27+
return -1;
3828
}
3929

40-
public override int GetSerialNumber()
30+
GetSerialNumber();
31+
return 0;
32+
}
33+
34+
private bool CheckInterface()
35+
{
36+
byte[] buffer = new byte[256];
37+
int returned;
38+
return DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero);
39+
}
40+
41+
public override int GetSerialNumber()
42+
{
43+
byte[] buffer = new byte[256];
44+
int returned;
45+
if (DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero))
4146
{
42-
byte[] buffer = new byte[256];
43-
int returned;
44-
if (DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero))
45-
{
46-
SerialNumber = System.Text.Encoding.Unicode.GetString(buffer, 0, returned).TrimEnd('\0');
47-
return 0;
48-
}
49-
return Marshal.GetLastWin32Error();
47+
SerialNumber = System.Text.Encoding.Unicode.GetString(buffer, 0, returned).TrimEnd('\0');
48+
return 0;
5049
}
50+
return Marshal.GetLastWin32Error();
51+
}
5152

52-
public override byte[] Read(int length)
53+
public override byte[] Read(int length)
54+
{
55+
var readTask = Task.Run(() =>
5356
{
54-
var readTask = Task.Run(() =>
55-
{
56-
byte[] buffer = new byte[length];
57-
uint read;
58-
if (ReadFile(fileHandle, buffer, (uint)length, out read, IntPtr.Zero))
59-
{
60-
byte[] result = new byte[read];
61-
Array.Copy(buffer, result, (int)read);
62-
return result;
63-
}
64-
throw new Win32Exception(Marshal.GetLastWin32Error());
65-
});
66-
67-
if (!readTask.Wait(IoTimeoutMs))
57+
byte[] buffer = new byte[length];
58+
uint read;
59+
if (ReadFile(fileHandle, buffer, (uint)length, out read, IntPtr.Zero))
6860
{
69-
throw new TimeoutException($"Legacy USB read timed out after {IoTimeoutMs} ms.");
61+
byte[] result = new byte[read];
62+
Array.Copy(buffer, result, (int)read);
63+
return result;
7064
}
65+
throw new Win32Exception(Marshal.GetLastWin32Error());
66+
});
7167

72-
return readTask.GetAwaiter().GetResult();
68+
if (!readTask.Wait(IoTimeoutMs))
69+
{
70+
throw new TimeoutException($"Legacy USB read timed out after {IoTimeoutMs} ms.");
7371
}
7472

75-
public override long Write(byte[] data, int length)
73+
return readTask.GetAwaiter().GetResult();
74+
}
75+
76+
public override long Write(byte[] data, int length)
77+
{
78+
var writeTask = Task.Run(() =>
7679
{
77-
var writeTask = Task.Run(() =>
80+
uint written;
81+
if (WriteFile(fileHandle, data, (uint)length, out written, IntPtr.Zero))
7882
{
79-
uint written;
80-
if (WriteFile(fileHandle, data, (uint)length, out written, IntPtr.Zero))
81-
{
82-
return (long)written;
83-
}
84-
throw new Win32Exception(Marshal.GetLastWin32Error());
85-
});
86-
87-
if (!writeTask.Wait(IoTimeoutMs))
88-
{
89-
throw new TimeoutException($"Legacy USB write timed out after {IoTimeoutMs} ms.");
83+
return (long)written;
9084
}
85+
throw new Win32Exception(Marshal.GetLastWin32Error());
86+
});
9187

92-
return writeTask.GetAwaiter().GetResult();
93-
}
94-
95-
public override void Reset()
88+
if (!writeTask.Wait(IoTimeoutMs))
9689
{
97-
// Legacy 驱动通常不支持软重置
90+
throw new TimeoutException($"Legacy USB write timed out after {IoTimeoutMs} ms.");
9891
}
9992

100-
public override void Dispose()
93+
return writeTask.GetAwaiter().GetResult();
94+
}
95+
96+
public override void Reset()
97+
{
98+
// Legacy 驱动通常不支持软重置
99+
}
100+
101+
public override void Dispose()
102+
{
103+
if (fileHandle != INVALID_HANDLE_VALUE)
101104
{
102-
if (fileHandle != INVALID_HANDLE_VALUE)
103-
{
104-
CloseHandle(fileHandle);
105-
fileHandle = INVALID_HANDLE_VALUE;
106-
}
107-
GC.SuppressFinalize(this);
105+
CloseHandle(fileHandle);
106+
fileHandle = INVALID_HANDLE_VALUE;
108107
}
108+
GC.SuppressFinalize(this);
109109
}
110+
}
110111

111112

112113

FirmwareKit.Comm.Fastboot/Backend/Usb/Windows/WinUSBDevice.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.ComponentModel;
22
using System.Runtime.InteropServices;
3-
using static FirmwareKit.Comm.Fastboot.Usb.Windows.WinUSBAPI;
43
using static FirmwareKit.Comm.Fastboot.Usb.Windows.Win32API;
4+
using static FirmwareKit.Comm.Fastboot.Usb.Windows.WinUSBAPI;
55

66
namespace FirmwareKit.Comm.Fastboot.Usb.Windows;
77

0 commit comments

Comments
 (0)