Skip to content

Commit d6a0264

Browse files
committed
refactor: Refactor CLI and improve transport/USB reliability
Refactors FastbootCLI to use a FastbootUtil instance per command, adds ExecuteDeviceList, and resets transports before commands. Adds FastbootUtil.ResetTransport and improves GetVar/error handling. Hardened transports: UDP init/receive logic tweaks and WinUSB fixes (longer timeouts, RAW_IO policy, short-packet/ZLP handling, robust Read/Write loops and error handling). Implement chunked writes in DownloadDataBytes, improve HandleResponse timeout and parsing behavior, and adjust Flash/partition handling logic to be more AOSP-aligned. Cleans up test usings and reduces library targets in the project file to net10.0.
1 parent 666f2d4 commit d6a0264

21 files changed

+217
-154
lines changed

FastbootCLI/Program.cs

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ static void Main(string[] args)
5151
{
5252
// This is a command (like 'devices', 'flash', 'getvar')
5353
string command = arg;
54-
54+
5555
// Collect arguments for this specific command until next arg starting with '-'
5656
List<string> commandArgs = new List<string>();
5757
while (i < args.Length && !args[i].StartsWith("-"))
@@ -68,11 +68,35 @@ static void Main(string[] args)
6868
return;
6969
}
7070

71+
if (pendingCommands.Count == 1 && pendingCommands[0].Command == "devices")
72+
{
73+
ExecuteDeviceList(pendingCommands[0].Args);
74+
return;
75+
}
76+
77+
var devices = UsbManager.GetAllDevices();
78+
UsbDevice? target = serial != null ? devices.FirstOrDefault(d => d.SerialNumber == serial) : (devices.Count > 0 ? devices[0] : null);
79+
80+
if (target == null)
81+
{
82+
Console.Error.WriteLine("fastboot: error: no devices/found");
83+
Environment.Exit(1);
84+
}
85+
86+
using FastbootUtil util = new FastbootUtil(target);
87+
if (sparseLimit.HasValue) FastbootUtil.SparseMaxDownloadSize = (int)Math.Min(int.MaxValue, sparseLimit.Value);
88+
89+
util.ReceivedFromDevice += (s, e) =>
90+
{
91+
if (e.NewInfo != null) Console.Error.WriteLine("(bootloader) " + e.NewInfo);
92+
};
93+
7194
foreach (var cmd in pendingCommands)
7295
{
7396
try
7497
{
75-
ExecuteCommand(cmd.Command, cmd.Args);
98+
util.ResetTransport();
99+
ExecuteCommand(util, cmd.Command, cmd.Args);
76100
}
77101
catch (Exception ex)
78102
{
@@ -83,6 +107,16 @@ static void Main(string[] args)
83107
}
84108
}
85109

110+
static void ExecuteDeviceList(List<string> args)
111+
{
112+
bool verbose = args.Contains("-l");
113+
foreach (var dev in UsbManager.GetAllDevices())
114+
{
115+
if (verbose) Console.WriteLine($"{dev.SerialNumber}\tfastboot {dev.GetType().Name}");
116+
else Console.WriteLine($"{dev.SerialNumber}\tfastboot");
117+
}
118+
}
119+
86120
static long ParseSize(string sizeStr)
87121
{
88122
long multiplier = 1;
@@ -93,31 +127,14 @@ static long ParseSize(string sizeStr)
93127
return long.Parse(sizeStr) * multiplier;
94128
}
95129

96-
static void ExecuteCommand(string command, List<string> args)
130+
static void ExecuteCommand(FastbootUtil util, string command, List<string> args)
97131
{
98132
if (command == "devices")
99133
{
100-
bool verbose = args.Contains("-l");
101-
foreach (var dev in UsbManager.GetAllDevices())
102-
{
103-
if (verbose) Console.WriteLine($"{dev.SerialNumber}\tfastboot {dev.GetType().Name}");
104-
else Console.WriteLine($"{dev.SerialNumber}\tfastboot");
105-
}
134+
ExecuteDeviceList(args);
106135
return;
107136
}
108137

109-
var devices = UsbManager.GetAllDevices();
110-
UsbDevice? target = serial != null ? devices.FirstOrDefault(d => d.SerialNumber == serial) : (devices.Count > 0 ? devices[0] : null);
111-
112-
if (target == null) throw new Exception("no devices/found");
113-
114-
using FastbootUtil util = new FastbootUtil(target);
115-
if (sparseLimit.HasValue) FastbootUtil.SparseMaxDownloadSize = (int)Math.Min(int.MaxValue, sparseLimit.Value);
116-
117-
util.ReceivedFromDevice += (s, e) =>
118-
{
119-
if (e.NewInfo != null) Console.Error.WriteLine("(bootloader) " + e.NewInfo);
120-
};
121138
util.DataTransferProgressChanged += (s, e) =>
122139
{
123140
int percent = (int)(e.Item1 * 100 / e.Item2);
@@ -134,30 +151,26 @@ static void ExecuteCommand(string command, List<string> args)
134151
util.FormatPartition("cache");
135152
}
136153

137-
// Process partition name with slot suffix (Aligned with AOSP dynamic detection)
138154
string GetPartition(string baseName)
139155
{
140156
if (string.IsNullOrEmpty(slot) || slot == "all")
141157
{
142-
// If A/B device and no slot specified, auto-detect current slot
143158
if (util.HasSlot(baseName))
144159
{
145160
string current = util.GetCurrentSlot();
146161
if (!string.IsNullOrEmpty(current)) return baseName + "_" + current;
147162
}
148163
return baseName;
149164
}
150-
165+
151166
if (slot == "other")
152167
{
153168
string current = util.GetCurrentSlot();
154169
string other = (current == "a") ? "b" : "a";
155170
return baseName + "_" + other;
156171
}
157172

158-
// Explicit slot provided (a/b)
159173
if (util.HasSlot(baseName)) return baseName + "_" + slot;
160-
161174
return baseName;
162175
}
163176

@@ -167,7 +180,6 @@ string GetPartition(string baseName)
167180
string? targetSlot = args.Count > 0 ? args[0] : slot;
168181
if (string.IsNullOrEmpty(targetSlot))
169182
{
170-
// AOSP: if no slot, toggle the current slot
171183
string? current = util.GetVar("current-slot");
172184
targetSlot = (current == "a") ? "b" : "a";
173185
}
@@ -214,7 +226,6 @@ string GetPartition(string baseName)
214226
case "flash":
215227
bool disableVerity = args.Contains("--disable-verity");
216228
bool disableVerification = args.Contains("--disable-verification");
217-
// Filter out flags from args
218229
var flashArgs = args.Where(a => !a.StartsWith("--")).ToList();
219230
if (flashArgs.Count < 1) throw new Exception("usage: fastboot flash <partition> [filename]");
220231
string part = GetPartition(flashArgs[0]);
@@ -240,9 +251,9 @@ string GetPartition(string baseName)
240251
{
241252
if (force)
242253
{
243-
// In a real AOSP fastboot, --force might bypass certain checks
244-
// Here we just acknowledge it to silence the warning
254+
util.OemCommand("snapshot-update cancel").ThrowIfError();
245255
}
256+
246257
using var fs = File.OpenRead(file);
247258
util.FlashUnsparseImage(part, fs, fs.Length).ThrowIfError();
248259
}
@@ -282,10 +293,12 @@ string GetPartition(string baseName)
282293
break;
283294

284295
case "set_active":
285-
// Handled before switch if invoked as 'fastboot set_active'
286-
// This block is for if someone passes 'set_active' but it wasn't caught
287-
string saSlot = args.Count > 0 ? args[0] : "";
288-
if (string.IsNullOrEmpty(saSlot)) throw new Exception("usage: fastboot set_active <slot>");
296+
string? saSlot = args.Count > 0 ? args[0] : slot;
297+
if (string.IsNullOrEmpty(saSlot))
298+
{
299+
string? current = util.GetVar("current-slot");
300+
saSlot = (current == "a") ? "b" : "a";
301+
}
289302
util.SetActiveSlot(saSlot).ThrowIfError();
290303
break;
291304

FirmwareKit.Comm.Fastboot.Tests/AospDriverTests.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
using FirmwareKit.Comm.Fastboot.DataModel;
21
using FirmwareKit.Comm.Fastboot.Backend.Network;
3-
using System.Collections.Generic;
2+
using FirmwareKit.Comm.Fastboot.DataModel;
43
using System.Text;
5-
using System.Linq;
6-
using System;
7-
using Xunit;
84

95
namespace FirmwareKit.Comm.Fastboot.Tests
106
{

FirmwareKit.Comm.Fastboot.Tests/AospFastbootDriverTests.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
using FirmwareKit.Comm.Fastboot.DataModel;
21
using FirmwareKit.Comm.Fastboot.Backend.Network;
3-
using System.Collections.Generic;
2+
using FirmwareKit.Comm.Fastboot.DataModel;
43
using System.Text;
5-
using System;
6-
using Xunit;
74

85
namespace FirmwareKit.Comm.Fastboot.Tests
96
{

FirmwareKit.Comm.Fastboot.Tests/FastbootInfoTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using FirmwareKit.Comm.Fastboot.Backend.Network;
22
using System.Globalization;
33
using System.Text;
4-
using System;
5-
using Xunit;
64

75
namespace FirmwareKit.Comm.Fastboot.Tests
86
{

FirmwareKit.Comm.Fastboot.Tests/FastbootProtocolTests.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
using FirmwareKit.Comm.Fastboot.DataModel;
21
using FirmwareKit.Comm.Fastboot.Backend.Usb;
3-
using System.Collections.Generic;
2+
using FirmwareKit.Comm.Fastboot.DataModel;
43
using System.Text;
5-
using System;
6-
using Xunit;
74

85
namespace FirmwareKit.Comm.Fastboot.Tests
96
{

FirmwareKit.Comm.Fastboot.Tests/TcpTransportTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1+
using FirmwareKit.Comm.Fastboot.Backend.Network;
12
using System.Buffers.Binary;
23
using System.Net;
34
using System.Net.Sockets;
45
using System.Text;
5-
using System.Threading.Tasks;
6-
using Xunit;
7-
using FirmwareKit.Comm.Fastboot.Backend.Network;
86

97
namespace FirmwareKit.Comm.Fastboot.Tests
108
{

FirmwareKit.Comm.Fastboot.Tests/UdpTransportTests.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1+
using FirmwareKit.Comm.Fastboot.Backend.Network;
12
using System.Buffers.Binary;
23
using System.Net;
34
using System.Net.Sockets;
45
using System.Text;
5-
using System.Threading.Tasks;
6-
using Xunit;
7-
using FirmwareKit.Comm.Fastboot.Backend.Network;
86

97
namespace FirmwareKit.Comm.Fastboot.Tests
108
{
@@ -168,7 +166,7 @@ public async Task Udp_Write_ErrorResponse_Fails()
168166

169167
using var transport = new UdpTransport("127.0.0.1", port);
170168
await Assert.ThrowsAsync<Exception>(async () => await Task.Run(() => transport.Write(Encoding.ASCII.GetBytes("foo"), 3)));
171-
169+
172170
await serverTask;
173171
}
174172
}

FirmwareKit.Comm.Fastboot/Backend/Network/UdpTransport.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,13 @@ private void InitializeProtocol()
3939
{
4040
_client.Client.ReceiveTimeout = _timeoutMs;
4141
_client.Client.SendTimeout = _timeoutMs;
42-
43-
// AOSP udp.cpp uses kIdQuery (0x01) to get sequence number
42+
4443
byte[] response = SendSinglePacket(PacketId.DeviceQuery, 0, PacketFlag.None, [], 0, 0, MaxTransmissionAttempts);
4544
if (response.Length < 2) throw new Exception("Invalid query response from target.");
46-
47-
// Sequence number is in the data part of the response for Query
4845
_sequence = BinaryPrimitives.ReadUInt16BigEndian(response.AsSpan(0, 2));
49-
50-
// Version (0x0001), Max Packet Size (0x0200 = 512 for kMinPacketSize)
5146
byte[] initData = new byte[4];
5247
BinaryPrimitives.WriteUInt16BigEndian(initData.AsSpan(0, 2), 0x0001);
53-
BinaryPrimitives.WriteUInt16BigEndian(initData.AsSpan(2, 2), 512);
54-
55-
// Initialization (0x02)
48+
BinaryPrimitives.WriteUInt16BigEndian(initData.AsSpan(2, 2), 512);
5649
response = SendSinglePacket(PacketId.Initialization, (ushort)_sequence, PacketFlag.None, initData, initData.Length, MaxTransmissionAttempts);
5750
if (response.Length < 4) throw new Exception("Invalid initialization response from target.");
5851

@@ -106,10 +99,7 @@ private byte[] SendSinglePacket(PacketId id, ushort seq, PacketFlag flag, byte[]
10699
{
107100
IPEndPoint from = new IPEndPoint(IPAddress.Any, 0);
108101
byte[] rxPacket = _client.Receive(ref from);
109-
110102
if (rxPacket.Length < HeaderSize) continue;
111-
112-
// Match Sequence and ID or Error
113103
if (BinaryPrimitives.ReadUInt16BigEndian(rxPacket.AsSpan(2, 2)) == seq &&
114104
(rxPacket[0] == (byte)id || rxPacket[0] == (byte)PacketId.Error))
115105
{
@@ -133,7 +123,7 @@ private byte[] SendSinglePacket(PacketId id, ushort seq, PacketFlag flag, byte[]
133123

134124
public byte[] Read(int length)
135125
{
136-
if (_readBuffer.Count == 0) // UDPSession handles command-response pairing. Content is buffered in _readBuffer.
126+
if (_readBuffer.Count == 0)
137127
{
138128
return Array.Empty<byte>();
139129
}

FirmwareKit.Comm.Fastboot/Backend/Usb/UsbManager.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ public static List<UsbDevice> GetAllDevices()
3636
{
3737
return MacOSUsbFinder.FindDevice();
3838
}
39-
// Fallback to libusb
4039
try
4140
{
4241
return LibUsbFinder.FindDevice();

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ public override int CreateHandle()
1919
if (fileHandle == INVALID_HANDLE_VALUE)
2020
return Marshal.GetLastWin32Error();
2121

22-
// 仿照谷歌原生逻辑,检查是否匹�Fastboot 接口标准 (0xff, 0x42, 0x03)
23-
// �Legacy 驱动中,我们尝试探测其是否响应特定的 IOCTL
2422
if (!CheckInterface())
2523
{
2624
CloseHandle(fileHandle);
@@ -36,7 +34,6 @@ private bool CheckInterface()
3634
{
3735
byte[] buffer = new byte[256];
3836
int returned;
39-
// 能够响应 IoGetSerialCode,初步认为是兼容�Legacy 驱动
4037
return DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero);
4138
}
4239

0 commit comments

Comments
 (0)