Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 60 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,49 +119,76 @@ jobs:
for value in (os.environ.get("USERPROFILE"), str(Path.home()))
if value
}
text_suffixes = {
".bat", ".java", ".json", ".md", ".py", ".qss", ".spec",
".txt", ".xml", ".yml", ".yaml",
}
private_windows_path = re.compile(
r"[a-z]:[/\\]users[/\\]([^/\\\s<>\"']+)",
re.IGNORECASE,
)
private_posix_path = re.compile(
r"/(?:home|users)/([a-z0-9._-]{3,})(?:[/\\]|$)",
re.IGNORECASE,
)
private_ipv4 = re.compile(
r"(?<![\w.])(?:10(?:\.\d{1,3}){3}|"
r"192\.168(?:\.\d{1,3}){2}|"
r"172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})(?![\w.])",
)
private_ipv6 = re.compile(
r"(?<![0-9a-f:])(?:f[cd][0-9a-f]{2}|fe[89ab][0-9a-f]):"
r"[0-9a-f:]{2,39}(?![0-9a-f:])",
re.IGNORECASE,
)
documented_private_ipv4_placeholders = {"192.168.1.42"}
findings: list[str] = []
placeholder_accounts = {"example", "public", "runner", "runneradmin"}
forbidden_cache_names = {"androguard.db", "androguard.db-shm", "androguard.db-wal"}
forbidden_secret_suffixes = {".p12", ".p8", ".pem", ".pfx", ".key"}

for path in repository_files:
if path.suffix.casefold() not in text_suffixes:
continue
relative_path = path.relative_to(root)
if path.name.casefold() in forbidden_cache_names:
findings.append(f"{relative_path} is a generated Androguard cache")
if path.suffix.casefold() in forbidden_secret_suffixes:
findings.append(f"{relative_path} is private signing/key material")
try:
content = path.read_text(encoding="utf-8")
except UnicodeDecodeError:
raw = path.read_bytes()
except OSError as exc:
findings.append(f"{relative_path} could not be privacy-scanned: {type(exc).__name__}")
continue
normalized = content.casefold().replace("\\", "/")
for private_root in private_roots:
if private_root and private_root in normalized:
findings.append(f"{path.relative_to(root)} contains the CI user profile path")
private_usernames = {
match.group(1).casefold()
for match in private_windows_path.finditer(content)
if match.group(1).casefold() != "public"
decoded_variants = {
raw.decode("utf-8", errors="ignore"),
raw.decode("utf-16-le", errors="ignore"),
raw.decode("utf-16-be", errors="ignore"),
}
if private_usernames:
findings.append(
f"{path.relative_to(root)} contains a non-placeholder Windows user path"
for content in decoded_variants:
normalized = content.casefold().replace("\\", "/")
for private_root in private_roots:
if private_root and private_root in normalized:
findings.append(f"{relative_path} contains the CI user profile path")
private_usernames = {
match.group(1).casefold()
for match in private_windows_path.finditer(content)
if match.group(1).casefold() not in placeholder_accounts
}
private_usernames.update(
match.group(1).casefold()
for match in private_posix_path.finditer(content)
if match.group(1).casefold() not in placeholder_accounts
)
if (
(path.suffix.casefold() == ".md" or path.name == "capture_readme_screenshots.py")
and private_ipv4.search(content)
):
findings.append(f"{path.relative_to(root)} contains a private IPv4 address")
if private_usernames:
findings.append(
f"{relative_path} contains a non-placeholder user profile path"
)
private_addresses = {
match.group(0) for match in private_ipv4.finditer(content)
} - documented_private_ipv4_placeholders
if private_addresses:
findings.append(f"{relative_path} contains a private IPv4 address")
if private_ipv6.search(content):
findings.append(f"{relative_path} contains a private IPv6 address")

private_metadata = re.compile(
r"(?:[a-z]:[/\\]users[/\\][^/\\\s]+|"
r"/(?:home|users)/[^/\\\s]+|"
r"(?<![\w.])(?:10(?:\.\d{1,3}){3}|"
r"192\.168(?:\.\d{1,3}){2}|"
r"172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})(?![\w.]))",
Expand All @@ -172,8 +199,17 @@ jobs:
continue
with Image.open(path) as image:
metadata = "\n".join(str(value) for value in image.info.values())
if image.getexif():
findings.append(f"{path.relative_to(root)} contains EXIF metadata")
unexpected_metadata = set(image.info) - {"dpi"}
if unexpected_metadata:
findings.append(
f"{path.relative_to(root)} contains unexpected PNG metadata"
)
if private_metadata.search(metadata):
findings.append(f"{path.relative_to(root)} contains private-looking PNG metadata")
if private_ipv6.search(metadata):
findings.append(f"{path.relative_to(root)} contains private IPv6 PNG metadata")

if findings:
raise SystemExit("Private demo data check failed:\n- " + "\n- ".join(findings))
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ __pycache__/
*.py[cod]
*$py.class
.venv*/
androguard.db
androguard.db-*

build/
dist/
Expand All @@ -16,3 +18,4 @@ OpenADB-data/
device-lab-report*.json
device-lab-report*.xml
device-lab-output/
release-performance*.json
79 changes: 79 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ The format is based on Keep a Changelog. The current public project version is
ACBridge 3.0.0 helper is build 2 and therefore uses `versionCode 30002`.
- The 3.0.0 helper APK is rebuilt from source and is not a renamed older APK.

### Architecture and operation lifecycle

- Long-running device work captures a frozen `DeviceContext` containing the
target transport, profile identity and monotonic generation. Bound ADB and
fastboot clients keep that target even if the active device changes.
- A central `OperationRegistry` owns conflicts, cancellation, generation
invalidation and shutdown cleanup. Late results are rejected before they can
update a different device, profile, path or cache.
- Applications, backups and File Manager workflows are separated into focused
controllers, coordinators, immutable transfer models and transport
strategies while retaining the existing settings, profiles and backup
formats.

### File Manager and P2P

- ADB remains the default upload transport for new device profiles. The first
Expand Down Expand Up @@ -49,6 +62,72 @@ The format is based on Keep a Changelog. The current public project version is
and local APK build/package/alignment/signature checks only; it does not
claim new real-device or real-network verification.

### Interface and Windows integration

- Dashboard actions now follow the active transport state, including guarded
Offline reconnect and direct Fastboot routing, without starting duplicate
refresh work.
- Applications keeps its contextual actions usable at compact Windows window
widths, preserves hidden selections, and exposes the relevant selection and
filter state without expanding the page into a second toolbar.
- The System theme now follows Windows Light/Dark changes while OpenADB is
running. Settings writes are atomic and retain a last-known-good backup;
malformed or interrupted settings are preserved for diagnosis and recovered
without silently mixing device profiles.

### Build and release

- Runtime, build, and development dependencies are now pinned and documented.
Windows CI validates CPython 3.10 through 3.14 with compileall, Ruff, the
complete tracked test suite in isolated processes, version/APK/spec checks,
offscreen GUI smoke tests, privacy guardrails, and failure-only test logs.
- A pinned Windows workflow builds and inspects the one-file executable,
bundles checksum-verified Android Platform Tools and ACBridge 3.0.0, and
smoke-tests a clean temporary profile without device-changing commands.
- Authenticode signing is optional but fail-closed: partial secret setup,
signing failure, or verification failure cannot produce a stable-named
artifact. Builds without a certificate retain the `-unsigned` suffix.
- The release workflow requires successful CI for the exact tag commit,
verifies source/build metadata and SHA-256 again, publishes signed builds as
stable, and limits automatic unsigned output to a clearly labelled draft
preview. The full operator and rollback procedure is documented in
`docs/RELEASE_PROCESS.md`.
- Added a manual-only, approved-environment device-lab workflow and a
fail-closed smoke tool whose default command set is strictly read-only.
Sanitized JSON/JUnit reports exclude serials, IP addresses, usernames, home
paths, filenames, secrets, and raw tool output.
- Added a 77-scenario Windows, Android transport, Applications, File Manager,
and Commands validation matrix. Unavailable physical hardware, Windows 10,
alternate-DPI/multi-monitor coverage, removable storage, controlled network
faults, and signed-build checks remain explicitly unclaimed.

### Validation and release evidence

- Added deterministic release benchmarks for 1,200/3,000 applications, a
5,000-entry File Manager tree, thousands of immutable transfer plans, Auto
streams, stale-result checks, and operation-registry overhead. The report
contains sanitized environment/timing data only and rejects non-finite or
malformed results.
- Regenerated seven 3.0.0 README screenshots from isolated safe demo profiles:
Dashboard Light/Dark, Applications with and without contextual actions, File
Manager Auto P2P, Commands, and Settings. Automated checks require RGB
1280×820 PNGs, empty EXIF, allowlisted metadata, current names, and README
references.
- Hardened the repository privacy gate to scan text and binary content in
UTF-8/UTF-16, Windows/POSIX home paths, private IPv4/IPv6 values, generated
caches, signing containers, and PNG metadata. A pre-existing generated
Androguard cache was removed from the active tree and ignored; its historical
Git blob remains documented for a separately coordinated history rewrite.
- Built and smoke-tested the local unsigned preview
`OpenADB-3.0.0-unsigned.exe` (90,452,041 bytes), SHA-256
`B48BCB48F868581384D68EFAA2DC373317C347E90967AA7F11B393F4B8C01A5B`.
Authenticode status is truthfully `NotSigned`; no signed stable release is
claimed.
- Physical Windows 10, real Android/network/storage/device-lab scenarios,
alternate DPI/multi-monitor coverage, and successful Authenticode signing
remain external release blockers. Full evidence is in
`OPENADB_3_RELEASE_REPORT.md`.

## [2.0.1] — 2026-07-12

### Added
Expand Down
44 changes: 42 additions & 2 deletions CHANGELOG_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ The format is based on Keep a Changelog. The current public project version is
ACBridge 3.0.0 helper is build 2 and therefore uses `versionCode 30002`.
- The 3.0.0 helper APK is rebuilt from source and is not a renamed older APK.

### Architecture and operation lifecycle

- Long-running device work captures a frozen `DeviceContext` containing the
target transport, profile identity and monotonic generation. Bound ADB and
fastboot clients keep that target even if the active device changes.
- A central `OperationRegistry` owns conflicts, cancellation, generation
invalidation and shutdown cleanup. Late results are rejected before they can
update a different device, profile, path or cache.
- Applications, backups and File Manager workflows are separated into focused
controllers, coordinators, immutable transfer models and transport
strategies while retaining the existing settings, profiles and backup
formats.

### File Manager and P2P

- ADB remains the default upload transport for new device profiles. The first
Expand Down Expand Up @@ -66,9 +79,9 @@ The format is based on Keep a Changelog. The current public project version is

- Runtime, build, and development dependencies are now pinned and documented.
Windows CI validates CPython 3.10 through 3.14 with compileall, Ruff, the
complete 543-test suite in isolated processes, version/APK/spec checks,
complete tracked test suite in isolated processes, version/APK/spec checks,
offscreen GUI smoke tests, privacy guardrails, and failure-only test logs.
- A reproducible Windows workflow builds and inspects the one-file executable,
- A pinned Windows workflow builds and inspects the one-file executable,
bundles checksum-verified Android Platform Tools and ACBridge 3.0.0, and
smoke-tests a clean temporary profile without device-changing commands.
- Authenticode signing is optional but fail-closed: partial secret setup,
Expand All @@ -88,6 +101,33 @@ The format is based on Keep a Changelog. The current public project version is
alternate-DPI/multi-monitor coverage, removable storage, controlled network
faults, and signed-build checks remain explicitly unclaimed.

### Validation and release evidence

- Added deterministic release benchmarks for 1,200/3,000 applications, a
5,000-entry File Manager tree, thousands of immutable transfer plans, Auto
streams, stale-result checks, and operation-registry overhead. The report
contains sanitized environment/timing data only and rejects non-finite or
malformed results.
- Regenerated seven 3.0.0 README screenshots from isolated safe demo profiles:
Dashboard Light/Dark, Applications with and without contextual actions, File
Manager Auto P2P, Commands, and Settings. Automated checks require RGB
1280×820 PNGs, empty EXIF, allowlisted metadata, current names, and README
references.
- Hardened the repository privacy gate to scan text and binary content in
UTF-8/UTF-16, Windows/POSIX home paths, private IPv4/IPv6 values, generated
caches, signing containers, and PNG metadata. A pre-existing generated
Androguard cache was removed from the active tree and ignored; its historical
Git blob remains documented for a separately coordinated history rewrite.
- Built and smoke-tested the local unsigned preview
`OpenADB-3.0.0-unsigned.exe` (90,452,041 bytes), SHA-256
`B48BCB48F868581384D68EFAA2DC373317C347E90967AA7F11B393F4B8C01A5B`.
Authenticode status is truthfully `NotSigned`; no signed stable release is
claimed.
- Physical Windows 10, real Android/network/storage/device-lab scenarios,
alternate DPI/multi-monitor coverage, and successful Authenticode signing
remain external release blockers. Full evidence is in
`OPENADB_3_RELEASE_REPORT.md`.

## [2.0.1] — 2026-07-12

### Added
Expand Down
33 changes: 33 additions & 0 deletions OPENADB_3_ARCHITECTURE_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,36 @@ for `DeviceContext`, because contexts are runtime snapshots.
checks, transfer fallbacks, or Windows-only behavior.
- The full test suite, compileall, Ruff, source startup, and shutdown checks
remain green after every migration stage.

## Implementation outcome

The migration above was implemented in stages 2–6 without changing the user
settings, backup, or cache formats. `DeviceContext` is a frozen snapshot of the
serial, mode, transport, profile identity/paths, and monotonic generation.
`ADBClient.for_context()` and `FastbootClient.for_context()` now build commands
from that snapshot instead of temporarily changing a shared serial.

`OperationRegistry` owns conflict groups, cancellation events/reasons,
generation invalidation, shutdown cancellation, and guaranteed token cleanup.
Applications, backups, File Manager listings/transfers, Commands, Dashboard
actions, device status, and Wireless ADB callbacks validate their captured
context or connection-attempt token before applying UI or persistent results.
Global discovery and server operations remain deliberately context-free.

The planned responsibility split produced these focused components:

- Applications data/action workflows, filter and selection state,
metadata/assets loaders, `AppsController`, `AppOperationCoordinator`, and
`BackupOperationCoordinator`;
- File Manager state/listing/action controllers, immutable `TransferPlan`,
shared progress/error models, `FileTransferController`, and separate ADB and
P2P strategies;
- frozen Wireless connection attempts, command safety/bound execution, and
the live `SystemThemeController` lifecycle.

Automated regressions cover bound-target stability, generation transitions,
stale UI/cache/profile rejection, switch-during-operation scenarios, conflict
handling, cancellation, late Qt signals, and empty-registry shutdown. This is
software evidence rather than a hardware claim: real multi-device, Android
transport, Windows 10, and device-lab execution remain recorded separately in
`docs/DEVICE_LAB_MATRIX.md`.
Loading
Loading