From 95a43ffa6336c16e49abce16580529c5981caa65 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 05:06:53 +0000 Subject: [PATCH 1/2] Batch 2: super space, missing partitions, USB check - Super space (Core SuperSpace): super size from super-partition-name, logical partitions split by slot, COW and unslotted, aligned to 1 MiB with room kept for metadata. A card on the partition tab shows it in fastbootd. Before flashing a logical partition, or an OTA, the images are checked to fit at the fullest point of the writes; when they do not, the app says by how much and what could make room. - Missing partitions (Core MissingPartitions): logical partitions an OTA writes but the phone lacks are created in fastbootd (create-logical-partition NAME 0) after extraction and before flashing, with a note on fastbootd's "default" group. The manual Create window carries the same note. - USB check (Core UsbDiagnosis, app UsbScan over WMI): finds phones without a driver or with a broken one, Qualcomm EDL, MediaTek BROM/preloader, Unisoc and Samsung download modes, and phones in Android, which it offers to reboot to the bootloader. - CI: the stand-in phone has no odm, so the OTA flash creates it; a 12 GiB sparse image is refused for lack of room; the USB check reads a stand-in device list. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018ZnrQUTw6hd6BgdooEqhkR --- .github/scripts/screenshots.ps1 | 121 +++++-- CHANGELOG.md | 19 + FastbootActionWindow.xaml | 1 + FastbootActionWindow.xaml.cs | 3 + FastbootEnhance.csproj | 5 + FastbootUI.cs | 332 ++++++++++++++++-- MainWindow.xaml | 26 +- Properties/Resources.Designer.cs | 279 +++++++++++++++ Properties/Resources.ar.resx | 103 ++++++ Properties/Resources.resx | 103 ++++++ README.ar.md | 11 + README.md | 16 +- UsbScan.cs | 45 +++ .../Fastboot/FastbootData.cs | 7 + .../Fastboot/MissingPartitions.cs | 62 ++++ .../Fastboot/SuperSpace.cs | 215 ++++++++++++ .../Payload/PayloadFile.cs | 15 + src/FastbootEnhance.Core/Usb/UsbDiagnosis.cs | 194 ++++++++++ .../SuperSpaceTests.cs | 163 +++++++++ .../UsbDiagnosisTests.cs | 69 ++++ tools/FastbootEnhance.FakeFastboot/Program.cs | 4 +- tools/FastbootEnhance.SampleGen/Program.cs | 22 ++ 22 files changed, 1748 insertions(+), 67 deletions(-) create mode 100644 UsbScan.cs create mode 100644 src/FastbootEnhance.Core/Fastboot/MissingPartitions.cs create mode 100644 src/FastbootEnhance.Core/Fastboot/SuperSpace.cs create mode 100644 src/FastbootEnhance.Core/Usb/UsbDiagnosis.cs create mode 100644 tests/FastbootEnhance.Core.Tests/SuperSpaceTests.cs create mode 100644 tests/FastbootEnhance.Core.Tests/UsbDiagnosisTests.cs diff --git a/.github/scripts/screenshots.ps1 b/.github/scripts/screenshots.ps1 index 5b73bc0..4ab5ccb 100644 --- a/.github/scripts/screenshots.ps1 +++ b/.github/scripts/screenshots.ps1 @@ -50,6 +50,15 @@ $adbMode = Join-Path $appDir 'fake-adb.mode' $fastbootMode = Join-Path $appDir 'fake-fastboot.mode' $saveRoot = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'Fastboot Studio' Remove-Item $crashLog, $fakeLog, $fakeAdbLog, $adbMode, $fastbootMode -ErrorAction SilentlyContinue + +# What the USB check reads instead of Windows' device list: a phone without a driver and one +# in MediaTek preloader mode (see UsbScan.cs). +$fakeUsb = Join-Path $appDir 'fake-usb.txt' +Set-Content $fakeUsb @( + 'Android|USB\VID_0E8D&PID_201C\0123456789ABCDEF|28|', + 'MediaTek PreLoader USB VCOM (Android) (COM5)|USB\VID_0E8D&PID_2000\5&2B1E&0&1|0|Ports' +) +$env:FASTBOOT_STUDIO_FAKE_USB = $fakeUsb Remove-Item $saveRoot -Recurse -Force -ErrorAction SilentlyContinue function Save-Desktop([string] $name) { @@ -184,6 +193,41 @@ function Go([string] $tabId) { return $tab } +# ---------------------------------------------------------------- helpers +function Press([string] $id) { + $button = Wait-For { By-Id $root $id } 10 $id + Wait-For { $button.Current.IsEnabled } 30 "$id to be enabled" | Out-Null + $button.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern).Invoke() +} + +function Rows($list) { + return @($list.FindAll($Scope::Descendants, $rowCond)) +} + +# Picks a file in the open file dialog, then waits for that dialog (not whatever window +# comes next) to go away. +function Choose-File([string] $path, [string] $what) { + $dialog = Wait-For { Find-Dialog } 20 $what + $fileHwnd = $dialog.Current.NativeWindowHandle + Start-Sleep -Milliseconds 800 + [System.Windows.Forms.SendKeys]::SendWait($path + '{ENTER}') + Wait-For { $d = Find-Dialog; $null -eq $d -or $d.Current.NativeWindowHandle -ne $fileHwnd } 20 "$what to close" | Out-Null +} + +# Presses a dialog button by its label, for questions where the first button is not the answer. +function Answer-Dialog($dialog, [string] $label) { + $cond = New-Object System.Windows.Automation.PropertyCondition($UIA::ControlTypeProperty, $Type::Button) + $button = @($dialog.FindAll($Scope::Descendants, $cond)) | Where-Object { $_.Current.Name -eq $label } | Select-Object -First 1 + if ($null -eq $button) { Fail "the dialog has no '$label' button" } + $button.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern).Invoke() + Wait-For { $null -eq (Find-Dialog) } 10 "the dialog to close" | Out-Null + Start-Sleep -Milliseconds 800 +} + +function Fastboot-Log([string] $pattern) { + return @(Get-Content $fakeLog | Where-Object { $_ -match $pattern }).Count +} + # ---------------------------------------------------------------- payload inspector $payloadTab = Go 'payload_tab' Wait-For { Shown $root 'payload_info' } 60 "the payload to open" | Out-Null @@ -202,6 +246,17 @@ $rowCond = New-Object System.Windows.Automation.OrCondition( $row = Wait-For { $devices.FindFirst($Scope::Descendants, $rowCond) } 20 "a device to appear" Save-Window ('{0:D2}-device-list' -f $i); $i++ +# The USB check explains what Windows sees: here a phone without a driver and one in +# MediaTek preloader mode. It offers Device Manager; answer No. +Press 'fastboot_usb_check' +$usb = Wait-For { Find-Dialog } 20 "the USB check" +Start-Sleep -Milliseconds 600 +Save-Window ('{0:D2}-usb-check' -f $i); $i++ +$usbText = ($usb.FindAll($Scope::Descendants, [System.Windows.Automation.Condition]::TrueCondition) | + ForEach-Object { $_.Current.Name } | Where-Object { $_ }) -join ' | ' +Answer-Dialog $usb 'No' +Write-Host "usb check: $usbText" + Select-Item $row $row.SetFocus() Send '{ENTER}' @@ -236,11 +291,18 @@ if ($ExpectedFlashes -gt 0) { $dialog = Wait-For { Find-Dialog } 20 "the file dialog" Write-Host "file dialog: '$($dialog.Current.Name)'" + $fileHwnd = $dialog.Current.NativeWindowHandle Start-Sleep -Milliseconds 800 [System.Windows.Forms.SendKeys]::SendWait($Payload + '{ENTER}') # The file dialog takes a moment to go away; it must not be mistaken for the result. - Wait-For { $null -eq (Find-Dialog) } 20 "the file dialog to close" | Out-Null + Wait-For { $d = Find-Dialog; $null -eq $d -or $d.Current.NativeWindowHandle -ne $fileHwnd } 20 "the file dialog to close" | Out-Null + + # The stand-in phone has no odm: the app offers to create it in super first. + $create = Wait-For { Find-Dialog } 30 "the missing partition question" + Start-Sleep -Milliseconds 600 + Save-Window ('{0:D2}-create-missing' -f $i); $i++ + Answer-Dialog $create 'Yes' # The app shows "operation completed" (or an error) when it is done. With the file dialog # gone, the next window to appear is that box. A message box does not expose its text to @@ -256,43 +318,20 @@ if ($ExpectedFlashes -gt 0) { $flashes = @(Get-Content $fakeLog | Where-Object { $_ -match ' flash ' }).Count Write-Host "fastboot flash commands issued: $flashes (expected $ExpectedFlashes)" if ($flashes -ne $ExpectedFlashes) { Fail "expected $ExpectedFlashes flash commands, saw $flashes" } + # odm_a is created (empty) before anything is flashed, and odm is then written to it. + $lines = @(Get-Content $fakeLog) + $created = -1; $firstFlash = -1 + for ($n = 0; $n -lt $lines.Count; $n++) { + if ($created -lt 0 -and $lines[$n] -match 'create-logical-partition odm_a 0') { $created = $n } + if ($firstFlash -lt 0 -and $lines[$n] -match ' flash ') { $firstFlash = $n } + } + if ($created -lt 0) { Fail "odm_a was not created before flashing the OTA" } + if ($created -gt $firstFlash) { Fail "odm_a was created after flashing had started" } + if ((Fastboot-Log ' flash odm ') -ne 1) { Fail "odm was not flashed after it was created" } + Write-Host "missing partition: odm_a created, then written" } # ---------------------------------------------------------------- backup over adb -function Press([string] $id) { - $button = Wait-For { By-Id $root $id } 10 $id - Wait-For { $button.Current.IsEnabled } 30 "$id to be enabled" | Out-Null - $button.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern).Invoke() -} - -function Rows($list) { - return @($list.FindAll($Scope::Descendants, $rowCond)) -} - -# Picks a file in the open file dialog, then waits for that dialog (not whatever window -# comes next) to go away. -function Choose-File([string] $path, [string] $what) { - $dialog = Wait-For { Find-Dialog } 20 $what - $fileHwnd = $dialog.Current.NativeWindowHandle - Start-Sleep -Milliseconds 800 - [System.Windows.Forms.SendKeys]::SendWait($path + '{ENTER}') - Wait-For { $d = Find-Dialog; $null -eq $d -or $d.Current.NativeWindowHandle -ne $fileHwnd } 20 "$what to close" | Out-Null -} - -# Presses a dialog button by its label, for questions where the first button is not the answer. -function Answer-Dialog($dialog, [string] $label) { - $cond = New-Object System.Windows.Automation.PropertyCondition($UIA::ControlTypeProperty, $Type::Button) - $button = @($dialog.FindAll($Scope::Descendants, $cond)) | Where-Object { $_.Current.Name -eq $label } | Select-Object -First 1 - if ($null -eq $button) { Fail "the dialog has no '$label' button" } - $button.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern).Invoke() - Wait-For { $null -eq (Find-Dialog) } 10 "the dialog to close" | Out-Null - Start-Sleep -Milliseconds 800 -} - -function Fastboot-Log([string] $pattern) { - return @(Get-Content $fakeLog | Where-Object { $_ -match $pattern }).Count -} - # Hashes every image a backup recorded in SHA256SUMS against the file it wrote. function Check-Backup([string] $what) { $folder = Get-ChildItem (Join-Path $saveRoot 'Backups') -Directory | Sort-Object LastWriteTime | Select-Object -Last 1 @@ -391,6 +430,18 @@ $vbDone = Wait-For { Find-Dialog } 30 "vbmeta to be flashed" Close-Dialog $vbDone if ((Fastboot-Log 'flash --disable-verity --disable-verification vbmeta_a ') -ne 1) { Fail "vbmeta was not flashed with --disable-verity --disable-verification" } Write-Host "vbmeta: flashed with verification disabled" +# Super: the card on the partition tab, and a 12 GiB image (a sparse file of a few bytes) +# that cannot fit; the app says by how much and what could make room. Answer No. +Filter-Partitions 'product_a' +Save-Window ('{0:D2}-super-space' -f $i); $i++ +Press 'fastboot_flash' +Choose-File (Join-Path (Split-Path $Payload) 'big-sparse.img') "the image dialog" +$full = Wait-For { Find-Dialog } 20 "the super space warning" +Start-Sleep -Milliseconds 600 +Save-Window ('{0:D2}-super-full' -f $i); $i++ +Answer-Dialog $full 'No' +if ((Fastboot-Log 'flash product_a .*big-sparse') -gt 0) { Fail "an image too big for super was flashed" } +Write-Host "super: a 12 GiB image was stopped before flashing" (By-Id $root 'fastboot_partition_name_textbox').GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern).SetValue('') # Boot once: init_boot has no kernel and is refused before anything is sent. diff --git a/CHANGELOG.md b/CHANGELOG.md index 268bb44..3ef3f8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ +## v2.2.0 +- بطاقة مساحة super: كم ماخذ كل سلوت وكم الفاضي +- تحذير قبل التفليش إذا الصور ما تدخل في super، مع طرق توفير المساحة +- إنشاء الأقسام الناقصة تلقائياً عند تفليش التحديث +- زر «فحص USB»: يكشف التعريف الناقص ووضع EDL و BROM + ## v2.1.0 - حماية تبديل الخانة: تحذير أو منع إذا الخانة الثانية فاضية أو ما تقلع - الأقسام الحساسة (xbl و abl و modem و persist و preloader…) تطلب كتابة اسم القسم @@ -108,6 +114,19 @@ --- +## Version 2.2 in short +- **Super space** card on the Device page (fastbootd): what slot a, slot b and update copies + (COW) take in super, and about how much is free. +- **Room check before flashing** in fastbootd: when the images are known not to fit in super, + the app says by how much and what could make room (the other slot's partitions, COW + partitions or a pending update) before anything is written. +- **Missing partitions are created**: when an OTA writes logical partitions the phone does not + have, they are created in super (empty) and then flashed. The note explains fastbootd's + `default` group. The manual Create window carries the same note. +- **USB check** on the device list: reads what Windows sees on USB and tells a missing or broken + driver (with the steps to fix it), Qualcomm EDL (9008), MediaTek BROM/preloader, Unisoc and + Samsung download modes, and a phone in Android (offering to reboot it to the bootloader). + ## Version 2.1 in short - **Slot switch protection**: reads `slot-successful`, `slot-unbootable` and `slot-retry-count`. A slot that has not booted yet warns; an unbootable slot or one with no retries left asks in diff --git a/FastbootActionWindow.xaml b/FastbootActionWindow.xaml index c82f937..3a12ba0 100644 --- a/FastbootActionWindow.xaml +++ b/FastbootActionWindow.xaml @@ -14,6 +14,7 @@ +