deepadb 1.0.0
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/LICENSE +21 -0
- package/README.md +631 -0
- package/build/bridge/adb-bridge.d.ts +65 -0
- package/build/bridge/adb-bridge.d.ts.map +1 -0
- package/build/bridge/adb-bridge.js +164 -0
- package/build/bridge/adb-bridge.js.map +1 -0
- package/build/bridge/device-manager.d.ts +41 -0
- package/build/bridge/device-manager.d.ts.map +1 -0
- package/build/bridge/device-manager.js +109 -0
- package/build/bridge/device-manager.js.map +1 -0
- package/build/bridge/local-bridge.d.ts +92 -0
- package/build/bridge/local-bridge.d.ts.map +1 -0
- package/build/bridge/local-bridge.js +345 -0
- package/build/bridge/local-bridge.js.map +1 -0
- package/build/config/config.d.ts +39 -0
- package/build/config/config.d.ts.map +1 -0
- package/build/config/config.js +84 -0
- package/build/config/config.js.map +1 -0
- package/build/graphql-api.d.ts +36 -0
- package/build/graphql-api.d.ts.map +1 -0
- package/build/graphql-api.js +296 -0
- package/build/graphql-api.js.map +1 -0
- package/build/http-transport.d.ts +26 -0
- package/build/http-transport.d.ts.map +1 -0
- package/build/http-transport.js +105 -0
- package/build/http-transport.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +66 -0
- package/build/index.js.map +1 -0
- package/build/middleware/chipset.d.ts +29 -0
- package/build/middleware/chipset.d.ts.map +1 -0
- package/build/middleware/chipset.js +123 -0
- package/build/middleware/chipset.js.map +1 -0
- package/build/middleware/cleanup.d.ts +24 -0
- package/build/middleware/cleanup.d.ts.map +1 -0
- package/build/middleware/cleanup.js +53 -0
- package/build/middleware/cleanup.js.map +1 -0
- package/build/middleware/fetch-utils.d.ts +19 -0
- package/build/middleware/fetch-utils.d.ts.map +1 -0
- package/build/middleware/fetch-utils.js +64 -0
- package/build/middleware/fetch-utils.js.map +1 -0
- package/build/middleware/logger.d.ts +17 -0
- package/build/middleware/logger.d.ts.map +1 -0
- package/build/middleware/logger.js +38 -0
- package/build/middleware/logger.js.map +1 -0
- package/build/middleware/output-processor.d.ts +57 -0
- package/build/middleware/output-processor.d.ts.map +1 -0
- package/build/middleware/output-processor.js +162 -0
- package/build/middleware/output-processor.js.map +1 -0
- package/build/middleware/sanitize.d.ts +30 -0
- package/build/middleware/sanitize.d.ts.map +1 -0
- package/build/middleware/sanitize.js +46 -0
- package/build/middleware/sanitize.js.map +1 -0
- package/build/middleware/security.d.ts +52 -0
- package/build/middleware/security.d.ts.map +1 -0
- package/build/middleware/security.js +123 -0
- package/build/middleware/security.js.map +1 -0
- package/build/middleware/ui-dump.d.ts +23 -0
- package/build/middleware/ui-dump.d.ts.map +1 -0
- package/build/middleware/ui-dump.js +59 -0
- package/build/middleware/ui-dump.js.map +1 -0
- package/build/server.d.ts +18 -0
- package/build/server.d.ts.map +1 -0
- package/build/server.js +133 -0
- package/build/server.js.map +1 -0
- package/build/tool-context.d.ts +22 -0
- package/build/tool-context.d.ts.map +1 -0
- package/build/tool-context.js +9 -0
- package/build/tool-context.js.map +1 -0
- package/build/tools/accessibility.d.ts +10 -0
- package/build/tools/accessibility.d.ts.map +1 -0
- package/build/tools/accessibility.js +259 -0
- package/build/tools/accessibility.js.map +1 -0
- package/build/tools/at-commands.d.ts +20 -0
- package/build/tools/at-commands.d.ts.map +1 -0
- package/build/tools/at-commands.js +378 -0
- package/build/tools/at-commands.js.map +1 -0
- package/build/tools/baseband.d.ts +15 -0
- package/build/tools/baseband.d.ts.map +1 -0
- package/build/tools/baseband.js +323 -0
- package/build/tools/baseband.js.map +1 -0
- package/build/tools/build.d.ts +6 -0
- package/build/tools/build.d.ts.map +1 -0
- package/build/tools/build.js +80 -0
- package/build/tools/build.js.map +1 -0
- package/build/tools/ci.d.ts +9 -0
- package/build/tools/ci.d.ts.map +1 -0
- package/build/tools/ci.js +163 -0
- package/build/tools/ci.js.map +1 -0
- package/build/tools/control.d.ts +10 -0
- package/build/tools/control.d.ts.map +1 -0
- package/build/tools/control.js +197 -0
- package/build/tools/control.js.map +1 -0
- package/build/tools/device-farm.d.ts +10 -0
- package/build/tools/device-farm.d.ts.map +1 -0
- package/build/tools/device-farm.js +140 -0
- package/build/tools/device-farm.js.map +1 -0
- package/build/tools/device-profiles.d.ts +16 -0
- package/build/tools/device-profiles.d.ts.map +1 -0
- package/build/tools/device-profiles.js +272 -0
- package/build/tools/device-profiles.js.map +1 -0
- package/build/tools/device.d.ts +6 -0
- package/build/tools/device.d.ts.map +1 -0
- package/build/tools/device.js +72 -0
- package/build/tools/device.js.map +1 -0
- package/build/tools/diagnostics.d.ts +7 -0
- package/build/tools/diagnostics.d.ts.map +1 -0
- package/build/tools/diagnostics.js +153 -0
- package/build/tools/diagnostics.js.map +1 -0
- package/build/tools/emulator.d.ts +9 -0
- package/build/tools/emulator.d.ts.map +1 -0
- package/build/tools/emulator.js +223 -0
- package/build/tools/emulator.js.map +1 -0
- package/build/tools/files.d.ts +6 -0
- package/build/tools/files.d.ts.map +1 -0
- package/build/tools/files.js +78 -0
- package/build/tools/files.js.map +1 -0
- package/build/tools/firmware-analysis.d.ts +24 -0
- package/build/tools/firmware-analysis.d.ts.map +1 -0
- package/build/tools/firmware-analysis.js +623 -0
- package/build/tools/firmware-analysis.js.map +1 -0
- package/build/tools/forwarding.d.ts +7 -0
- package/build/tools/forwarding.d.ts.map +1 -0
- package/build/tools/forwarding.js +64 -0
- package/build/tools/forwarding.js.map +1 -0
- package/build/tools/health.d.ts +7 -0
- package/build/tools/health.d.ts.map +1 -0
- package/build/tools/health.js +112 -0
- package/build/tools/health.js.map +1 -0
- package/build/tools/logcat-watch.d.ts +11 -0
- package/build/tools/logcat-watch.d.ts.map +1 -0
- package/build/tools/logcat-watch.js +209 -0
- package/build/tools/logcat-watch.js.map +1 -0
- package/build/tools/logs.d.ts +6 -0
- package/build/tools/logs.d.ts.map +1 -0
- package/build/tools/logs.js +83 -0
- package/build/tools/logs.js.map +1 -0
- package/build/tools/mirroring.d.ts +14 -0
- package/build/tools/mirroring.d.ts.map +1 -0
- package/build/tools/mirroring.js +243 -0
- package/build/tools/mirroring.js.map +1 -0
- package/build/tools/multi-device.d.ts +9 -0
- package/build/tools/multi-device.d.ts.map +1 -0
- package/build/tools/multi-device.js +138 -0
- package/build/tools/multi-device.js.map +1 -0
- package/build/tools/network-capture.d.ts +10 -0
- package/build/tools/network-capture.d.ts.map +1 -0
- package/build/tools/network-capture.js +143 -0
- package/build/tools/network-capture.js.map +1 -0
- package/build/tools/network-discovery.d.ts +21 -0
- package/build/tools/network-discovery.d.ts.map +1 -0
- package/build/tools/network-discovery.js +284 -0
- package/build/tools/network-discovery.js.map +1 -0
- package/build/tools/ota-monitor.d.ts +16 -0
- package/build/tools/ota-monitor.d.ts.map +1 -0
- package/build/tools/ota-monitor.js +211 -0
- package/build/tools/ota-monitor.js.map +1 -0
- package/build/tools/packages.d.ts +6 -0
- package/build/tools/packages.d.ts.map +1 -0
- package/build/tools/packages.js +237 -0
- package/build/tools/packages.js.map +1 -0
- package/build/tools/plugins.d.ts +22 -0
- package/build/tools/plugins.d.ts.map +1 -0
- package/build/tools/plugins.js +118 -0
- package/build/tools/plugins.js.map +1 -0
- package/build/tools/prompts.d.ts +9 -0
- package/build/tools/prompts.d.ts.map +1 -0
- package/build/tools/prompts.js +94 -0
- package/build/tools/prompts.js.map +1 -0
- package/build/tools/qemu.d.ts +18 -0
- package/build/tools/qemu.d.ts.map +1 -0
- package/build/tools/qemu.js +791 -0
- package/build/tools/qemu.js.map +1 -0
- package/build/tools/registry.d.ts +13 -0
- package/build/tools/registry.d.ts.map +1 -0
- package/build/tools/registry.js +221 -0
- package/build/tools/registry.js.map +1 -0
- package/build/tools/regression.d.ts +10 -0
- package/build/tools/regression.d.ts.map +1 -0
- package/build/tools/regression.js +215 -0
- package/build/tools/regression.js.map +1 -0
- package/build/tools/resources.d.ts +10 -0
- package/build/tools/resources.d.ts.map +1 -0
- package/build/tools/resources.js +77 -0
- package/build/tools/resources.js.map +1 -0
- package/build/tools/ril-intercept.d.ts +24 -0
- package/build/tools/ril-intercept.d.ts.map +1 -0
- package/build/tools/ril-intercept.js +273 -0
- package/build/tools/ril-intercept.js.map +1 -0
- package/build/tools/screen-record.d.ts +9 -0
- package/build/tools/screen-record.d.ts.map +1 -0
- package/build/tools/screen-record.js +95 -0
- package/build/tools/screen-record.js.map +1 -0
- package/build/tools/screenshot-diff.d.ts +13 -0
- package/build/tools/screenshot-diff.d.ts.map +1 -0
- package/build/tools/screenshot-diff.js +370 -0
- package/build/tools/screenshot-diff.js.map +1 -0
- package/build/tools/selinux-audit.d.ts +17 -0
- package/build/tools/selinux-audit.d.ts.map +1 -0
- package/build/tools/selinux-audit.js +301 -0
- package/build/tools/selinux-audit.js.map +1 -0
- package/build/tools/shell.d.ts +6 -0
- package/build/tools/shell.d.ts.map +1 -0
- package/build/tools/shell.js +63 -0
- package/build/tools/shell.js.map +1 -0
- package/build/tools/snapshot.d.ts +9 -0
- package/build/tools/snapshot.d.ts.map +1 -0
- package/build/tools/snapshot.js +192 -0
- package/build/tools/snapshot.js.map +1 -0
- package/build/tools/split-apk.d.ts +13 -0
- package/build/tools/split-apk.d.ts.map +1 -0
- package/build/tools/split-apk.js +229 -0
- package/build/tools/split-apk.js.map +1 -0
- package/build/tools/test-gen.d.ts +14 -0
- package/build/tools/test-gen.d.ts.map +1 -0
- package/build/tools/test-gen.js +252 -0
- package/build/tools/test-gen.js.map +1 -0
- package/build/tools/testing.d.ts +9 -0
- package/build/tools/testing.d.ts.map +1 -0
- package/build/tools/testing.js +144 -0
- package/build/tools/testing.js.map +1 -0
- package/build/tools/thermal-power.d.ts +19 -0
- package/build/tools/thermal-power.d.ts.map +1 -0
- package/build/tools/thermal-power.js +330 -0
- package/build/tools/thermal-power.js.map +1 -0
- package/build/tools/ui.d.ts +6 -0
- package/build/tools/ui.d.ts.map +1 -0
- package/build/tools/ui.js +266 -0
- package/build/tools/ui.js.map +1 -0
- package/build/tools/wireless.d.ts +7 -0
- package/build/tools/wireless.d.ts.map +1 -0
- package/build/tools/wireless.js +78 -0
- package/build/tools/wireless.js.map +1 -0
- package/build/tools/workflow-market.d.ts +17 -0
- package/build/tools/workflow-market.d.ts.map +1 -0
- package/build/tools/workflow-market.js +237 -0
- package/build/tools/workflow-market.js.map +1 -0
- package/build/tools/workflow.d.ts +32 -0
- package/build/tools/workflow.d.ts.map +1 -0
- package/build/tools/workflow.js +374 -0
- package/build/tools/workflow.js.map +1 -0
- package/build/ws-transport.d.ts +30 -0
- package/build/ws-transport.d.ts.map +1 -0
- package/build/ws-transport.js +133 -0
- package/build/ws-transport.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modem Firmware Analysis — Comprehensive firmware version tracking and diffing.
|
|
3
|
+
*
|
|
4
|
+
* Parses and compares firmware version strings across OTA updates.
|
|
5
|
+
* Understands version numbering conventions for major chipset families:
|
|
6
|
+
*
|
|
7
|
+
* - Shannon/Exynos: Google Pixel/Tensor "g5123b-..." and Samsung "S5123AP_CL..." formats
|
|
8
|
+
* - Qualcomm: "MPSS.xx.yy.zz" (modem protocol software stack)
|
|
9
|
+
* - MediaTek: "MOLY.xxx.yyy" prefixed versions
|
|
10
|
+
* - Unisoc/Spreadtrum: SoC model + branch/version formats
|
|
11
|
+
* - HiSilicon/Kirin: Model + version with carrier codes, and encoded formats
|
|
12
|
+
* - Intel XMM: XMM model + branch/build formats
|
|
13
|
+
* - Generic: semver-like or date-based patterns
|
|
14
|
+
*
|
|
15
|
+
* Also parses bootloader versions (Pixel codename/version/build, Samsung model/carrier/revision)
|
|
16
|
+
* and RIL implementation strings (Samsung S.LSI, Qualcomm, MediaTek).
|
|
17
|
+
*
|
|
18
|
+
* Builds on the OTA fingerprint system — reads saved fingerprints from
|
|
19
|
+
* {tempDir}/ota-fingerprints/ to track firmware progression over time.
|
|
20
|
+
* Can also query the live device for current firmware details.
|
|
21
|
+
*/
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
25
|
+
import { OutputProcessor } from "../middleware/output-processor.js";
|
|
26
|
+
import { detectChipsetFamily } from "../middleware/chipset.js";
|
|
27
|
+
/**
|
|
28
|
+
* Parse a baseband firmware version string into structured components.
|
|
29
|
+
* Different chipset families use different version formats.
|
|
30
|
+
*/
|
|
31
|
+
function parseFirmwareVersion(raw, chipsetFamily) {
|
|
32
|
+
const info = { raw, chipsetFamily, parsed: {} };
|
|
33
|
+
if (!raw || raw === "unknown")
|
|
34
|
+
return info;
|
|
35
|
+
if (chipsetFamily === "shannon") {
|
|
36
|
+
info.parsed.family = "Shannon/Exynos";
|
|
37
|
+
// Google Pixel/Tensor format: "g5123b-145971-251030-B-14356419"
|
|
38
|
+
// Pattern: <modemModel>-<changelist>-<YYMMDD>-<variant>-<buildNumber>
|
|
39
|
+
const pixelMatch = raw.match(/^([a-z]\d{4}\w*)-(\d+)-(\d{6})-([A-Z])-(\d+)$/i);
|
|
40
|
+
if (pixelMatch) {
|
|
41
|
+
info.parsed.modemModel = pixelMatch[1];
|
|
42
|
+
info.parsed.changelist = pixelMatch[2];
|
|
43
|
+
const ds = pixelMatch[3];
|
|
44
|
+
info.parsed.buildDate = `20${ds.substring(0, 2)}-${ds.substring(2, 4)}-${ds.substring(4, 6)}`;
|
|
45
|
+
info.parsed.variant = pixelMatch[4];
|
|
46
|
+
info.parsed.buildNumber = pixelMatch[5];
|
|
47
|
+
return info;
|
|
48
|
+
}
|
|
49
|
+
// Classic Samsung format: "S5123AP_CL1234567_V1.2.3" or "SHANNON_xxxxx"
|
|
50
|
+
const clMatch = raw.match(/CL(\d+)/i);
|
|
51
|
+
if (clMatch)
|
|
52
|
+
info.parsed.changelist = clMatch[1];
|
|
53
|
+
const modelMatch = raw.match(/(S\d{4}\w*)/i);
|
|
54
|
+
if (modelMatch)
|
|
55
|
+
info.parsed.modemModel = modelMatch[1];
|
|
56
|
+
const verMatch = raw.match(/V(\d+\.\d+[\.\d]*)/i);
|
|
57
|
+
if (verMatch)
|
|
58
|
+
info.parsed.version = verMatch[1];
|
|
59
|
+
// Date patterns in Shannon strings (YYYYMMDD)
|
|
60
|
+
const dateMatch = raw.match(/(\d{4})(\d{2})(\d{2})/);
|
|
61
|
+
if (dateMatch && parseInt(dateMatch[1], 10) > 2010) {
|
|
62
|
+
info.parsed.buildDate = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (chipsetFamily === "qualcomm") {
|
|
66
|
+
// Qualcomm format: "MPSS.DE.3.1.1-00135-LAHAINA_GEN_PACK-1"
|
|
67
|
+
info.parsed.family = "Qualcomm";
|
|
68
|
+
const mpssMatch = raw.match(/MPSS\.(\S+)/i);
|
|
69
|
+
if (mpssMatch)
|
|
70
|
+
info.parsed.mpssVersion = mpssMatch[1];
|
|
71
|
+
const genMatch = raw.match(/(\w+)_GEN_PACK/i);
|
|
72
|
+
if (genMatch)
|
|
73
|
+
info.parsed.platform = genMatch[1];
|
|
74
|
+
const buildMatch = raw.match(/-(\d+)-/);
|
|
75
|
+
if (buildMatch)
|
|
76
|
+
info.parsed.buildNumber = buildMatch[1];
|
|
77
|
+
// Extract version segments
|
|
78
|
+
const segMatch = raw.match(/MPSS\.(\w+)\.(\d+)\.(\d+)\.(\d+)/i);
|
|
79
|
+
if (segMatch) {
|
|
80
|
+
info.parsed.branch = segMatch[1];
|
|
81
|
+
info.parsed.major = segMatch[2];
|
|
82
|
+
info.parsed.minor = segMatch[3];
|
|
83
|
+
info.parsed.patch = segMatch[4];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (chipsetFamily === "mediatek") {
|
|
87
|
+
// MediaTek format: "MOLY.LR12A.R3.MP.V123.P45"
|
|
88
|
+
info.parsed.family = "MediaTek";
|
|
89
|
+
const molyMatch = raw.match(/MOLY\.(\S+)/i);
|
|
90
|
+
if (molyMatch)
|
|
91
|
+
info.parsed.molyVersion = molyMatch[1];
|
|
92
|
+
const branchMatch = raw.match(/MOLY\.(\w+)\./i);
|
|
93
|
+
if (branchMatch)
|
|
94
|
+
info.parsed.branch = branchMatch[1];
|
|
95
|
+
// Extract sub-version segments if present
|
|
96
|
+
const segMatch = raw.match(/MOLY\.\w+\.(\w+)\.(\w+)\.(\w+)/i);
|
|
97
|
+
if (segMatch) {
|
|
98
|
+
info.parsed.release = segMatch[1];
|
|
99
|
+
info.parsed.milestone = segMatch[2];
|
|
100
|
+
info.parsed.revision = segMatch[3];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (chipsetFamily === "unisoc") {
|
|
104
|
+
// Unisoc/Spreadtrum formats:
|
|
105
|
+
// "UIS8581A3H00G_W21.50.6" — model + generation_version
|
|
106
|
+
// "S9863A1H10_U04.00.04" — SoC model + version
|
|
107
|
+
// "MOCOR5_Trunk_W24.22.2" — branch + version
|
|
108
|
+
info.parsed.family = "Unisoc/Spreadtrum";
|
|
109
|
+
const unisocMatch = raw.match(/^(\w+?)_(\w)(\d+\.\d+[\.\d]*)/);
|
|
110
|
+
if (unisocMatch) {
|
|
111
|
+
info.parsed.model = unisocMatch[1];
|
|
112
|
+
info.parsed.branch = unisocMatch[2];
|
|
113
|
+
info.parsed.version = unisocMatch[3];
|
|
114
|
+
}
|
|
115
|
+
// Model extraction for SoC-prefixed versions
|
|
116
|
+
const socMatch = raw.match(/^((?:UIS|S|T)\d{4}\w*)/i);
|
|
117
|
+
if (socMatch)
|
|
118
|
+
info.parsed.socModel = socMatch[1];
|
|
119
|
+
}
|
|
120
|
+
else if (chipsetFamily === "hisilicon") {
|
|
121
|
+
// HiSilicon/Kirin formats:
|
|
122
|
+
// "21C20B388S000C000" — encoded version string
|
|
123
|
+
// "Kirin990_11.0.1.168(C00E160R5P1)" — model + version with carrier code
|
|
124
|
+
info.parsed.family = "HiSilicon/Kirin";
|
|
125
|
+
const kirinMatch = raw.match(/(Kirin\d+)/i);
|
|
126
|
+
if (kirinMatch)
|
|
127
|
+
info.parsed.model = kirinMatch[1];
|
|
128
|
+
const verMatch = raw.match(/(\d+\.\d+\.\d+\.\d+)/);
|
|
129
|
+
if (verMatch)
|
|
130
|
+
info.parsed.version = verMatch[1];
|
|
131
|
+
const carrierMatch = raw.match(/\((\w+)\)/);
|
|
132
|
+
if (carrierMatch)
|
|
133
|
+
info.parsed.carrierCode = carrierMatch[1];
|
|
134
|
+
// Encoded format: extract segments
|
|
135
|
+
const encodedMatch = raw.match(/^(\d{2})(\w)(\d{2})(\w)(\d+)/);
|
|
136
|
+
if (encodedMatch && !kirinMatch) {
|
|
137
|
+
info.parsed.majorVersion = encodedMatch[1];
|
|
138
|
+
info.parsed.buildId = raw;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (chipsetFamily === "intel") {
|
|
142
|
+
// Intel XMM format: "XMM7560_LA_PEAR2.1-00115-GEN_PACK-1"
|
|
143
|
+
info.parsed.family = "Intel XMM";
|
|
144
|
+
const xmmMatch = raw.match(/(XMM\d+)/i);
|
|
145
|
+
if (xmmMatch)
|
|
146
|
+
info.parsed.model = xmmMatch[1];
|
|
147
|
+
const verMatch = raw.match(/(\w+)\.(\d+)-(\d+)/);
|
|
148
|
+
if (verMatch) {
|
|
149
|
+
info.parsed.branch = verMatch[1];
|
|
150
|
+
info.parsed.minor = verMatch[2];
|
|
151
|
+
info.parsed.buildNumber = verMatch[3];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Generic: try date patterns, semver, or build numbers
|
|
156
|
+
info.parsed.family = "Generic";
|
|
157
|
+
const semverMatch = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
158
|
+
if (semverMatch) {
|
|
159
|
+
info.parsed.major = semverMatch[1];
|
|
160
|
+
info.parsed.minor = semverMatch[2];
|
|
161
|
+
info.parsed.patch = semverMatch[3];
|
|
162
|
+
}
|
|
163
|
+
const dateMatch = raw.match(/(\d{4})[\-.]?(\d{2})[\-.]?(\d{2})/);
|
|
164
|
+
if (dateMatch && parseInt(dateMatch[1], 10) > 2010) {
|
|
165
|
+
info.parsed.buildDate = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return info;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Parse a bootloader version string into structured components.
|
|
172
|
+
* Common formats:
|
|
173
|
+
* Pixel: "bluejay-16.4-14097577" → codename-version-build
|
|
174
|
+
* Samsung: "G991BXXS9FVL4" → model+carrier+revision
|
|
175
|
+
* Generic: semver or date-based
|
|
176
|
+
*/
|
|
177
|
+
function parseBootloaderVersion(raw) {
|
|
178
|
+
const parsed = {};
|
|
179
|
+
if (!raw || raw === "unknown")
|
|
180
|
+
return parsed;
|
|
181
|
+
// Pixel/Google format: "codename-major.minor-buildnumber"
|
|
182
|
+
const pixelBL = raw.match(/^(\w+)-(\d+\.\d+)-(\d+)$/);
|
|
183
|
+
if (pixelBL) {
|
|
184
|
+
parsed.codename = pixelBL[1];
|
|
185
|
+
parsed.version = pixelBL[2];
|
|
186
|
+
parsed.buildNumber = pixelBL[3];
|
|
187
|
+
return parsed;
|
|
188
|
+
}
|
|
189
|
+
// Samsung format: model + carrier code + revision
|
|
190
|
+
const samsungBL = raw.match(/^([A-Z]\d{3,4}\w?)([A-Z]{3,4})(\d?\w{3,5}\d?)$/);
|
|
191
|
+
if (samsungBL) {
|
|
192
|
+
parsed.model = samsungBL[1];
|
|
193
|
+
parsed.carrierCode = samsungBL[2];
|
|
194
|
+
parsed.revision = samsungBL[3];
|
|
195
|
+
return parsed;
|
|
196
|
+
}
|
|
197
|
+
// Generic: just store the raw value
|
|
198
|
+
parsed.raw = raw;
|
|
199
|
+
return parsed;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Parse the RIL implementation string into structured components.
|
|
203
|
+
* Format: "Samsung S.LSI Vendor RIL 5400 V2.3 Build 2025-09-13 00:48:55"
|
|
204
|
+
* "android samsung-ril 1.0"
|
|
205
|
+
* "libril-qc-hal-qmi"
|
|
206
|
+
*/
|
|
207
|
+
function parseRilImpl(raw) {
|
|
208
|
+
const parsed = {};
|
|
209
|
+
if (!raw || raw === "unknown")
|
|
210
|
+
return parsed;
|
|
211
|
+
// Samsung S.LSI format: "Samsung S.LSI Vendor RIL <id> V<ver> Build <date>"
|
|
212
|
+
const samsungRil = raw.match(/Samsung\s+\S+\s+Vendor\s+RIL\s+(\d+)\s+V([\d.]+)\s+Build\s+([\d-]+\s*[\d:]*)/i);
|
|
213
|
+
if (samsungRil) {
|
|
214
|
+
parsed.vendor = "Samsung S.LSI";
|
|
215
|
+
parsed.rilId = samsungRil[1];
|
|
216
|
+
parsed.apiVersion = samsungRil[2];
|
|
217
|
+
parsed.buildDate = samsungRil[3].trim();
|
|
218
|
+
return parsed;
|
|
219
|
+
}
|
|
220
|
+
// Qualcomm format: "libril-qc-hal-qmi" or "android qualcomm-ril 1.0"
|
|
221
|
+
if (raw.includes("qc") || raw.includes("qualcomm")) {
|
|
222
|
+
parsed.vendor = "Qualcomm";
|
|
223
|
+
const verMatch = raw.match(/([\d.]+)/);
|
|
224
|
+
if (verMatch)
|
|
225
|
+
parsed.version = verMatch[1];
|
|
226
|
+
return parsed;
|
|
227
|
+
}
|
|
228
|
+
// MediaTek format: "mtk-ril" or similar
|
|
229
|
+
if (raw.includes("mtk")) {
|
|
230
|
+
parsed.vendor = "MediaTek";
|
|
231
|
+
return parsed;
|
|
232
|
+
}
|
|
233
|
+
parsed.raw = raw;
|
|
234
|
+
return parsed;
|
|
235
|
+
}
|
|
236
|
+
function getFingerprintDir(tempDir) {
|
|
237
|
+
return join(tempDir, "ota-fingerprints");
|
|
238
|
+
}
|
|
239
|
+
function loadFingerprints(dir, deviceFilter) {
|
|
240
|
+
if (!existsSync(dir))
|
|
241
|
+
return [];
|
|
242
|
+
const files = readdirSync(dir)
|
|
243
|
+
.filter((f) => f.endsWith(".json") && (!deviceFilter || f.startsWith(deviceFilter)))
|
|
244
|
+
.sort();
|
|
245
|
+
const results = [];
|
|
246
|
+
for (const file of files) {
|
|
247
|
+
try {
|
|
248
|
+
results.push(JSON.parse(readFileSync(join(dir, file), "utf-8")));
|
|
249
|
+
}
|
|
250
|
+
catch { /* skip corrupt */ }
|
|
251
|
+
}
|
|
252
|
+
return results;
|
|
253
|
+
}
|
|
254
|
+
export function registerFirmwareAnalysisTools(ctx) {
|
|
255
|
+
ctx.server.tool("adb_firmware_probe", "Comprehensive firmware identification for the connected device. Reports all firmware components: baseband (parsed by chipset family — Shannon, Qualcomm, MediaTek, Unisoc, HiSilicon, Intel), bootloader, RIL implementation, kernel, security patch, A/B slot, verified boot state, and OTA-updatable partitions.", {
|
|
256
|
+
device: z.string().optional().describe("Device serial"),
|
|
257
|
+
}, async ({ device }) => {
|
|
258
|
+
try {
|
|
259
|
+
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
260
|
+
const serial = resolved.serial;
|
|
261
|
+
const props = await ctx.deviceManager.getDeviceProps(serial);
|
|
262
|
+
const family = detectChipsetFamily(props);
|
|
263
|
+
const basebandRaw = props["gsm.version.baseband"] ?? "unknown";
|
|
264
|
+
const rilImpl = props["gsm.version.ril-impl"] ?? "unknown";
|
|
265
|
+
const bootloaderRaw = props["ro.bootloader"] ?? "unknown";
|
|
266
|
+
const firmware = parseFirmwareVersion(basebandRaw, family);
|
|
267
|
+
const bootloaderParsed = parseBootloaderVersion(bootloaderRaw);
|
|
268
|
+
const rilParsed = parseRilImpl(rilImpl);
|
|
269
|
+
const sections = [];
|
|
270
|
+
sections.push("=== Comprehensive Firmware Analysis ===");
|
|
271
|
+
sections.push(`Device: ${props["ro.product.model"] ?? "unknown"} (${serial})`);
|
|
272
|
+
sections.push(`Chipset family: ${family}`);
|
|
273
|
+
// ── Baseband ──
|
|
274
|
+
sections.push(`\n── Baseband Firmware ──`);
|
|
275
|
+
sections.push(`Raw: ${basebandRaw}`);
|
|
276
|
+
if (props["ro.build.expect.baseband"] && props["ro.build.expect.baseband"] !== basebandRaw) {
|
|
277
|
+
sections.push(`Expected: ${props["ro.build.expect.baseband"]}`);
|
|
278
|
+
}
|
|
279
|
+
if (Object.keys(firmware.parsed).length > 0) {
|
|
280
|
+
sections.push("Parsed:");
|
|
281
|
+
for (const [key, value] of Object.entries(firmware.parsed)) {
|
|
282
|
+
sections.push(` ${key}: ${value}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// ── Bootloader ──
|
|
286
|
+
sections.push(`\n── Bootloader ──`);
|
|
287
|
+
sections.push(`Raw: ${bootloaderRaw}`);
|
|
288
|
+
if (props["ro.build.expect.bootloader"] && props["ro.build.expect.bootloader"] !== bootloaderRaw) {
|
|
289
|
+
sections.push(`Expected: ${props["ro.build.expect.bootloader"]}`);
|
|
290
|
+
}
|
|
291
|
+
if (Object.keys(bootloaderParsed).length > 0 && !bootloaderParsed.raw) {
|
|
292
|
+
sections.push("Parsed:");
|
|
293
|
+
for (const [key, value] of Object.entries(bootloaderParsed)) {
|
|
294
|
+
sections.push(` ${key}: ${value}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// ── RIL Implementation ──
|
|
298
|
+
sections.push(`\n── RIL Implementation ──`);
|
|
299
|
+
sections.push(`Raw: ${rilImpl}`);
|
|
300
|
+
if (Object.keys(rilParsed).length > 0 && !rilParsed.raw) {
|
|
301
|
+
sections.push("Parsed:");
|
|
302
|
+
for (const [key, value] of Object.entries(rilParsed)) {
|
|
303
|
+
sections.push(` ${key}: ${value}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ── Kernel ──
|
|
307
|
+
const kernelVersion = props["ro.kernel.version"] ?? "unknown";
|
|
308
|
+
sections.push(`\n── Kernel ──`);
|
|
309
|
+
sections.push(`Version: ${kernelVersion}`);
|
|
310
|
+
// ── Security & Build ──
|
|
311
|
+
sections.push(`\n── Security & Build ──`);
|
|
312
|
+
sections.push(`Android version: ${props["ro.build.version.release"] ?? "unknown"} (SDK ${props["ro.build.version.sdk"] ?? "?"})`);
|
|
313
|
+
sections.push(`Security patch: ${props["ro.build.version.security_patch"] ?? "unknown"}`);
|
|
314
|
+
sections.push(`Vendor security patch: ${props["ro.vendor.build.security_patch"] ?? "unknown"}`);
|
|
315
|
+
sections.push(`Build ID: ${props["ro.build.id"] ?? "unknown"}`);
|
|
316
|
+
sections.push(`Build fingerprint: ${props["ro.build.fingerprint"] ?? "unknown"}`);
|
|
317
|
+
// ── A/B Partition & Boot ──
|
|
318
|
+
const slotSuffix = props["ro.boot.slot_suffix"] ?? "";
|
|
319
|
+
const abUpdate = props["ro.build.ab_update"] ?? "false";
|
|
320
|
+
sections.push(`\n── Partition & Boot ──`);
|
|
321
|
+
sections.push(`A/B update: ${abUpdate}`);
|
|
322
|
+
if (slotSuffix)
|
|
323
|
+
sections.push(`Active slot: ${slotSuffix}`);
|
|
324
|
+
sections.push(`Secure boot: ${props["ro.boot.secure_boot"] ?? "unknown"}`);
|
|
325
|
+
sections.push(`Verified boot state: ${props["ro.boot.verifiedbootstate"] ?? "unknown"}`);
|
|
326
|
+
sections.push(`Flash lock: ${props["ro.boot.flash.locked"] === "1" ? "locked" : props["ro.boot.flash.locked"] === "0" ? "unlocked" : "unknown"}`);
|
|
327
|
+
// ── VBMeta ──
|
|
328
|
+
const vbmetaDigest = props["ro.boot.vbmeta.digest"];
|
|
329
|
+
if (vbmetaDigest) {
|
|
330
|
+
sections.push(`\n── Verified Boot Metadata ──`);
|
|
331
|
+
sections.push(`AVB version: ${props["ro.boot.vbmeta.avb_version"] ?? "?"}`);
|
|
332
|
+
sections.push(`Hash algorithm: ${props["ro.boot.vbmeta.hash_alg"] ?? "?"}`);
|
|
333
|
+
sections.push(`Digest: ${vbmetaDigest.substring(0, 32)}...`);
|
|
334
|
+
}
|
|
335
|
+
// ── OTA Partitions ──
|
|
336
|
+
const otaParts = props["ro.product.ab_ota_partitions"];
|
|
337
|
+
if (otaParts) {
|
|
338
|
+
const parts = otaParts.split(",").map(p => p.trim()).filter(p => p);
|
|
339
|
+
sections.push(`\n── OTA Partitions (${parts.length}) ──`);
|
|
340
|
+
sections.push(parts.join(", "));
|
|
341
|
+
}
|
|
342
|
+
// ── Hypervisor ──
|
|
343
|
+
const hvVersion = props["ro.boot.hypervisor.version"];
|
|
344
|
+
if (hvVersion) {
|
|
345
|
+
sections.push(`\n── Hypervisor ──`);
|
|
346
|
+
sections.push(`Version: ${hvVersion}`);
|
|
347
|
+
sections.push(`VM supported: ${props["ro.boot.hypervisor.vm.supported"] ?? "?"}`);
|
|
348
|
+
sections.push(`Protected VM: ${props["ro.boot.hypervisor.protected_vm.supported"] ?? "?"}`);
|
|
349
|
+
}
|
|
350
|
+
// ── Radio properties ──
|
|
351
|
+
const radioProps = [
|
|
352
|
+
"gsm.nitz.time",
|
|
353
|
+
"gsm.operator.numeric",
|
|
354
|
+
"gsm.sim.operator.numeric",
|
|
355
|
+
"ro.telephony.default_network",
|
|
356
|
+
"persist.radio.multisim.config",
|
|
357
|
+
];
|
|
358
|
+
const extras = [];
|
|
359
|
+
for (const prop of radioProps) {
|
|
360
|
+
const val = props[prop];
|
|
361
|
+
if (val)
|
|
362
|
+
extras.push(` ${prop}: ${val}`);
|
|
363
|
+
}
|
|
364
|
+
if (extras.length > 0) {
|
|
365
|
+
sections.push(`\nRadio properties:`);
|
|
366
|
+
sections.push(...extras);
|
|
367
|
+
}
|
|
368
|
+
return { content: [{ type: "text", text: sections.join("\n") }] };
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
ctx.server.tool("adb_firmware_diff", "Compare all firmware components between two saved OTA fingerprints, or between the current device state and a saved fingerprint. Compares baseband (with chipset-specific parsed component diffs), bootloader, kernel, security patch, build ID, and Android version.", {
|
|
375
|
+
from: z.string().optional().describe("Path to 'from' fingerprint JSON (or 'current' to use live device state). Defaults to the second-most-recent fingerprint."),
|
|
376
|
+
to: z.string().optional().describe("Path to 'to' fingerprint JSON (or 'current' to use live device state). Defaults to the most recent fingerprint."),
|
|
377
|
+
device: z.string().optional().describe("Device serial (used when 'from' or 'to' is 'current', or for auto-selecting fingerprints)"),
|
|
378
|
+
}, async ({ from, to, device }) => {
|
|
379
|
+
try {
|
|
380
|
+
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
381
|
+
const serial = resolved.serial;
|
|
382
|
+
const props = await ctx.deviceManager.getDeviceProps(serial);
|
|
383
|
+
const family = detectChipsetFamily(props);
|
|
384
|
+
const dir = getFingerprintDir(ctx.config.tempDir);
|
|
385
|
+
const safeDevice = serial.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
386
|
+
// Helper: build a firmware component map from live device or saved fingerprint
|
|
387
|
+
// Only includes fields that are stored in both live state and saved fingerprints
|
|
388
|
+
// to avoid misleading "(absent)" diffs. RIL implementation is excluded because
|
|
389
|
+
// saved OTA fingerprints don't capture it.
|
|
390
|
+
function currentComponents() {
|
|
391
|
+
return {
|
|
392
|
+
baseband: props["gsm.version.baseband"] ?? "unknown",
|
|
393
|
+
bootloader: props["ro.bootloader"] ?? "unknown",
|
|
394
|
+
kernel: props["ro.kernel.version"] ?? "unknown",
|
|
395
|
+
securityPatch: props["ro.build.version.security_patch"] ?? "unknown",
|
|
396
|
+
buildId: props["ro.build.id"] ?? "unknown",
|
|
397
|
+
androidVersion: props["ro.build.version.release"] ?? "unknown",
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function fpComponents(fp) {
|
|
401
|
+
return {
|
|
402
|
+
baseband: fp.basebandVersion ?? "unknown",
|
|
403
|
+
bootloader: fp.bootloaderVersion ?? "unknown",
|
|
404
|
+
kernel: fp.kernelVersion ?? "unknown",
|
|
405
|
+
securityPatch: fp.securityPatch ?? "unknown",
|
|
406
|
+
buildId: fp.buildId ?? "unknown",
|
|
407
|
+
androidVersion: fp.androidVersion ?? "unknown",
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
// Resolve 'from'
|
|
411
|
+
let fromComps;
|
|
412
|
+
let fromLabel;
|
|
413
|
+
if (from === "current") {
|
|
414
|
+
fromComps = currentComponents();
|
|
415
|
+
fromLabel = "current device";
|
|
416
|
+
}
|
|
417
|
+
else if (from) {
|
|
418
|
+
if (!existsSync(from))
|
|
419
|
+
return { content: [{ type: "text", text: `File not found: ${from}` }], isError: true };
|
|
420
|
+
try {
|
|
421
|
+
fromComps = fpComponents(JSON.parse(readFileSync(from, "utf-8")));
|
|
422
|
+
fromLabel = from.split(/[\\/]/).pop() ?? from;
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
return { content: [{ type: "text", text: `Corrupt fingerprint: ${from}` }], isError: true };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
const fps = loadFingerprints(dir, safeDevice);
|
|
430
|
+
if (fps.length < 2) {
|
|
431
|
+
return { content: [{ type: "text", text: `Need at least 2 fingerprints to auto-diff. Found ${fps.length}.` }], isError: true };
|
|
432
|
+
}
|
|
433
|
+
fromComps = fpComponents(fps[fps.length - 2]);
|
|
434
|
+
fromLabel = fps[fps.length - 2].timestamp?.substring(0, 19) ?? "older";
|
|
435
|
+
}
|
|
436
|
+
// Resolve 'to'
|
|
437
|
+
let toComps;
|
|
438
|
+
let toLabel;
|
|
439
|
+
if (to === "current") {
|
|
440
|
+
toComps = currentComponents();
|
|
441
|
+
toLabel = "current device";
|
|
442
|
+
}
|
|
443
|
+
else if (to) {
|
|
444
|
+
if (!existsSync(to))
|
|
445
|
+
return { content: [{ type: "text", text: `File not found: ${to}` }], isError: true };
|
|
446
|
+
try {
|
|
447
|
+
toComps = fpComponents(JSON.parse(readFileSync(to, "utf-8")));
|
|
448
|
+
toLabel = to.split(/[\\/]/).pop() ?? to;
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
return { content: [{ type: "text", text: `Corrupt fingerprint: ${to}` }], isError: true };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const fps = loadFingerprints(dir, safeDevice);
|
|
456
|
+
if (fps.length < 1) {
|
|
457
|
+
return { content: [{ type: "text", text: "No fingerprints saved. Use adb_ota_fingerprint first." }], isError: true };
|
|
458
|
+
}
|
|
459
|
+
toComps = fpComponents(fps[fps.length - 1]);
|
|
460
|
+
toLabel = fps[fps.length - 1].timestamp?.substring(0, 19) ?? "newest";
|
|
461
|
+
}
|
|
462
|
+
const sections = [];
|
|
463
|
+
sections.push("=== Comprehensive Firmware Diff ===");
|
|
464
|
+
sections.push(`Chipset: ${family}`);
|
|
465
|
+
sections.push(`From: ${fromLabel}`);
|
|
466
|
+
sections.push(`To: ${toLabel}`);
|
|
467
|
+
// Compare all firmware components
|
|
468
|
+
const componentLabels = {
|
|
469
|
+
baseband: "Baseband",
|
|
470
|
+
bootloader: "Bootloader",
|
|
471
|
+
kernel: "Kernel",
|
|
472
|
+
securityPatch: "Security Patch",
|
|
473
|
+
buildId: "Build ID",
|
|
474
|
+
androidVersion: "Android Version",
|
|
475
|
+
};
|
|
476
|
+
const changed = [];
|
|
477
|
+
const unchanged = [];
|
|
478
|
+
const allKeys = new Set([...Object.keys(fromComps), ...Object.keys(toComps)]);
|
|
479
|
+
for (const key of allKeys) {
|
|
480
|
+
const fv = fromComps[key] ?? "(absent)";
|
|
481
|
+
const tv = toComps[key] ?? "(absent)";
|
|
482
|
+
const label = componentLabels[key] ?? key;
|
|
483
|
+
if (fv !== tv) {
|
|
484
|
+
changed.push(` ✗ ${label}: ${fv} → ${tv}`);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
unchanged.push(` ✓ ${label}: ${fv}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (changed.length === 0) {
|
|
491
|
+
sections.push("\nResult: ✓ IDENTICAL — all firmware components unchanged.");
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
sections.push(`\nResult: ✗ ${changed.length} component(s) changed\n`);
|
|
495
|
+
sections.push("Changed:");
|
|
496
|
+
sections.push(...changed);
|
|
497
|
+
// Deep baseband diff if baseband changed
|
|
498
|
+
if (fromComps.baseband !== toComps.baseband) {
|
|
499
|
+
const fromParsed = parseFirmwareVersion(fromComps.baseband, family);
|
|
500
|
+
const toParsed = parseFirmwareVersion(toComps.baseband, family);
|
|
501
|
+
const parsedKeys = new Set([...Object.keys(fromParsed.parsed), ...Object.keys(toParsed.parsed)]);
|
|
502
|
+
const parsedChanges = [];
|
|
503
|
+
for (const pk of parsedKeys) {
|
|
504
|
+
const fpv = fromParsed.parsed[pk] ?? "(absent)";
|
|
505
|
+
const tpv = toParsed.parsed[pk] ?? "(absent)";
|
|
506
|
+
if (fpv !== tpv && pk !== "family") {
|
|
507
|
+
parsedChanges.push(` ${pk}: "${fpv}" → "${tpv}"`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (parsedChanges.length > 0) {
|
|
511
|
+
sections.push("\n Baseband parsed diffs:");
|
|
512
|
+
sections.push(...parsedChanges);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// Deep bootloader diff if bootloader changed
|
|
516
|
+
if (fromComps.bootloader !== toComps.bootloader) {
|
|
517
|
+
const fromBL = parseBootloaderVersion(fromComps.bootloader);
|
|
518
|
+
const toBL = parseBootloaderVersion(toComps.bootloader);
|
|
519
|
+
const blKeys = new Set([...Object.keys(fromBL), ...Object.keys(toBL)]);
|
|
520
|
+
const blChanges = [];
|
|
521
|
+
for (const bk of blKeys) {
|
|
522
|
+
if (bk === "raw")
|
|
523
|
+
continue;
|
|
524
|
+
const fbv = fromBL[bk] ?? "(absent)";
|
|
525
|
+
const tbv = toBL[bk] ?? "(absent)";
|
|
526
|
+
if (fbv !== tbv)
|
|
527
|
+
blChanges.push(` ${bk}: "${fbv}" → "${tbv}"`);
|
|
528
|
+
}
|
|
529
|
+
if (blChanges.length > 0) {
|
|
530
|
+
sections.push("\n Bootloader parsed diffs:");
|
|
531
|
+
sections.push(...blChanges);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (unchanged.length > 0) {
|
|
536
|
+
sections.push("\nUnchanged:");
|
|
537
|
+
sections.push(...unchanged);
|
|
538
|
+
}
|
|
539
|
+
return { content: [{ type: "text", text: sections.join("\n") }] };
|
|
540
|
+
}
|
|
541
|
+
catch (error) {
|
|
542
|
+
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
ctx.server.tool("adb_firmware_history", "Show firmware version progression across all saved OTA fingerprints for a device. Tracks baseband, bootloader, kernel, security patch, and build ID changes between consecutive snapshots with parsed component diffs.", {
|
|
546
|
+
device: z.string().optional().describe("Device serial (filters fingerprints to this device)"),
|
|
547
|
+
}, async ({ device }) => {
|
|
548
|
+
try {
|
|
549
|
+
let serial;
|
|
550
|
+
let family = "generic";
|
|
551
|
+
if (device) {
|
|
552
|
+
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
553
|
+
serial = resolved.serial;
|
|
554
|
+
const props = await ctx.deviceManager.getDeviceProps(serial);
|
|
555
|
+
family = detectChipsetFamily(props);
|
|
556
|
+
}
|
|
557
|
+
const dir = getFingerprintDir(ctx.config.tempDir);
|
|
558
|
+
const safeDevice = serial?.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
559
|
+
const fps = loadFingerprints(dir, safeDevice);
|
|
560
|
+
if (fps.length === 0) {
|
|
561
|
+
return { content: [{ type: "text", text: "No fingerprints saved. Use adb_ota_fingerprint to start tracking." }], isError: true };
|
|
562
|
+
}
|
|
563
|
+
const sections = [];
|
|
564
|
+
sections.push("=== Firmware Version History ===");
|
|
565
|
+
sections.push(`${fps.length} fingerprint(s)\n`);
|
|
566
|
+
// Tracked fields for change detection
|
|
567
|
+
const trackedFields = [
|
|
568
|
+
{ key: "basebandVersion", label: "Baseband" },
|
|
569
|
+
{ key: "bootloaderVersion", label: "Bootloader" },
|
|
570
|
+
{ key: "kernelVersion", label: "Kernel" },
|
|
571
|
+
{ key: "securityPatch", label: "Security Patch" },
|
|
572
|
+
{ key: "buildId", label: "Build ID" },
|
|
573
|
+
{ key: "androidVersion", label: "Android" },
|
|
574
|
+
];
|
|
575
|
+
let totalChanges = 0;
|
|
576
|
+
for (let i = 0; i < fps.length; i++) {
|
|
577
|
+
const fp = fps[i];
|
|
578
|
+
const prev = i > 0 ? fps[i - 1] : null;
|
|
579
|
+
// Detect changes across all tracked fields
|
|
580
|
+
const fieldChanges = [];
|
|
581
|
+
if (prev) {
|
|
582
|
+
for (const { key, label } of trackedFields) {
|
|
583
|
+
const pv = String(prev[key] ?? "");
|
|
584
|
+
const cv = String(fp[key] ?? "");
|
|
585
|
+
if (pv && cv && pv !== cv) {
|
|
586
|
+
fieldChanges.push(` ${label}: ${pv} → ${cv}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (fieldChanges.length > 0)
|
|
591
|
+
totalChanges++;
|
|
592
|
+
const marker = fieldChanges.length > 0 ? ` ⚠ ${fieldChanges.length} CHANGE(S)` : "";
|
|
593
|
+
sections.push(`[${i + 1}] ${fp.timestamp?.substring(0, 19) ?? "?"} | ${fp.basebandVersion ?? "unknown"}${marker}`);
|
|
594
|
+
// Show deep baseband parsed diffs
|
|
595
|
+
if (prev && prev.basebandVersion !== fp.basebandVersion) {
|
|
596
|
+
const fromParsed = parseFirmwareVersion(prev.basebandVersion, family);
|
|
597
|
+
const toParsed = parseFirmwareVersion(fp.basebandVersion, family);
|
|
598
|
+
const allKeys = new Set([...Object.keys(fromParsed.parsed), ...Object.keys(toParsed.parsed)]);
|
|
599
|
+
for (const key of allKeys) {
|
|
600
|
+
const fv = fromParsed.parsed[key];
|
|
601
|
+
const tv = toParsed.parsed[key];
|
|
602
|
+
if (fv !== tv && key !== "family") {
|
|
603
|
+
sections.push(` baseband.${key}: "${fv ?? "(absent)"}" → "${tv ?? "(absent)"}"`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// Show non-baseband field changes
|
|
608
|
+
if (fieldChanges.length > 0) {
|
|
609
|
+
for (const change of fieldChanges) {
|
|
610
|
+
if (!change.includes("Baseband:"))
|
|
611
|
+
sections.push(change);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
sections.push(`\nSnapshots with firmware changes: ${totalChanges}`);
|
|
616
|
+
return { content: [{ type: "text", text: sections.join("\n") }] };
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
//# sourceMappingURL=firmware-analysis.js.map
|