[PR #4804] [MERGED] feat(apktool): share one APK resource decode across selected locale tables (#4777) #6416

Closed
opened 2026-10-31 01:14:19 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4804
Author: @abhay-2108
Created: 10/17/2026
Status: ✅ Merged
Merged: 10/17/2026
Merged by: @morluto

Base: main ← Head: feat/apktool-share-resource-decode-4777


📝 Commits (1)

  • 701be5f feat(apktool): share one APK resource decode across selected locale tables (#4777)

📊 Changes

10 files changed (+804 additions, -89 deletions)

View changed files

📝 docs/apktool-resource-analysis.md (+9 -2)
📝 scripts/verify-apktool.mjs (+23 -0)
📝 src/apktool/ApktoolProvider.test.ts (+216 -0)
📝 src/apktool/ApktoolProvider.ts (+371 -85)
📝 src/application/apktool/ApktoolResourceAnalysisService.test.ts (+43 -0)
📝 src/cli/apktoolCommands.ts (+9 -0)
📝 src/contracts/apktool/apktoolToolContracts.ts (+5 -1)
📝 src/domain/apktool/apktoolResourceAnalysis.ts (+53 -1)
📝 tests/boundary/cli/apktoolLocaleOption.test.ts (+47 -0)
📝 tests/boundary/mcp/apktoolMcp.test.ts (+28 -0)

📄 Description

Summary

Project default and multiple selected localized string tables from one immutable decode pass, eliminating redundant APK snapshotting, SHA-256 digesting, launcher version probing, repeated apktool d --no-src invocations, and workspace filesystem enumerations for multi-locale inspection.

Fixes #4777

Problem and expected behavior

Previously, inspecting multiple string tables (such as default strings and one or more translated locales like de or pt-BR) required separate serial calls to decode_android_resources. Each call created an owned temporary directory, copied and hashed the APK, executed a full apktool d --no-src decode, traversed the decoded workspace to count files and discover available locales, projected a single string table, and removed the workspace. On NewPipe (0.29.1), each decode required ~7.5–11.4s of CPU time and separate workspace lifecycle overhead.

Expected behavior: A caller can specify an array of desired locales via locales (or repeatable --locales <tag> on the CLI) in addition to or instead of the primary locale. REA runs one single decode pass, projects each requested table into a structured string_tables array with typed availability status (present, missing, oversized, malformed, skipped), coverage, and entry counts (null when skipped or unmeasured), while continuing to provide top-level strings and locale for backwards compatibility.

Change and scope

  • Added an optional locales input array to decode_android_resources schema (bounded to 20 locales).
  • Extended the output schema with string_tables: z.array(stringTableSchema).optional() containing per-table facts, typed availability status (present, missing, oversized, malformed, skipped), entry counts (null when skipped, oversized, or unmeasured), and coverage.
  • Updated ApktoolProvider to iterate through all requested locales from the single decoded workspace.
  • Enforced aggregate projection budgets: 32 MiB aggregate XML read budget (AGGREGATE_STRINGS_XML_MAX_BYTES) and 10,000 aggregate string entries budget (MAX_AGGREGATE_STRINGS). Each read receives Math.min(STRINGS_XML_MAX_BYTES, remainingXmlBudget) and tracks bytesRead even on parse failures.
  • Normalized locale directory aliases (pt-BR and pt-rBR both resolve to values-pt-rBR) so aliases reuse already-projected tables without extra disk I/O or charging the budget twice.
  • Preserved strict backwards compatibility for existing single-table and include_strings: false queries (all string tables are skipped when include_strings: false).
  • Separated XML parse failures (malformed) from valid XML containing unsupported/styled markup (present with coverage: "partial").
  • Added typed skipped status for tables skipped after aggregate budget exhaustion, reporting unknown (null) entry counts rather than invented zeros.
  • Preserved cancellation and cleanup invariants: aborted per-table reads are never swallowed as malformed tables; provider-owned workspaces are reliably unlinked in finally, with failed cleanup tracked for retry on provider close.
  • Scope: Existing static local APK decoding only. Zero target execution, process injection, or byte mutation authority. Maintainer scope decision: not applicable (within existing static provider boundary).

Contract and boundary impact

  • Semantic owner and earliest changed stage: Domain schemas in src/domain/apktool/apktoolResourceAnalysis.ts and provider projection in src/apktool/ApktoolProvider.ts.
  • Contribution scope fit and linked maintainer decision: Not applicable (within existing static apktool analysis boundaries; no new provider or mutation authority).
  • CLI and MCP/tool-catalog contract: Added repeatable --locales option to rea decode-android-resources; updated MCP contract description and examples in src/contracts/apktool/apktoolToolContracts.ts. Output schema adds optional string_tables while retaining existing strings and locale fields.
  • Provider, bridge, target-format, or platform compatibility: Cross-platform (Linux/macOS/Windows). No changes to toolchain requirements.
  • Evidence, artifact, provenance, or reconstruction contract: Retains exact Evidence provenance, subject APK digest and byte count, and inline metadata.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): Provider-owned temporary workspace created via mkdtemp and cleaned up in finally. Failed cleanup tracked in #pendingCommandOwners for retry on provider.close().
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: Regenerated and validated with npx turbo run docs:check:built. No uncommitted build artifacts.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • src/apktool/ApktoolDecodeOutput.test.ts: Added tests verifying malformed boolean distinguishing syntax errors from styled/unsupported markup (24 tests pass).
    • src/application/apktool/ApktoolResourceAnalysisService.test.ts: Added tests for invalid locale arrays, bounding caps (> 20 locales), and batched locale forwarding to provider (10 tests pass).
    • tests/boundary/cli/apktoolLocaleOption.test.ts: Added CLI tests for repeatable --locales flags and --no-strings --locale de options forwarding (4 tests pass).
    • tests/boundary/mcp/apktoolMcp.test.ts: Updated MCP boundary tests to verify advertised JSON schemas with string_tables and multi-locale execution (1 test passes).
    • src/apktool/ApktoolProvider.test.ts: Added tests verifying: (1) invocation counter asserts exactly 1 decode occurs, (2) alias deduplication reuses table for pt-BR and pt-rBR, (3) include_strings: false suppresses all string tables, (4) styled markup produces present table with partial coverage and unparsed count.
    • scripts/verify-apktool.mjs: Updated live verifier check to assert exact requested locale identities and status: "present".
  • Base reproduction or other evidence: Observed evidence in issue #4777 documented 4 serial decodes taking 7.45/11.40/9.94/11.30s on NewPipe. Batching avoids 3 redundant decode passes.
  • User-visible CLI/MCP output:
    rea decode-android-resources <app.apk> --locales de --locales pt-BR --json outputs string_tables with all requested tables populated from one decode pass.
  • Remaining proof gaps: Real Apktool execution against upstream NewPipe binary was evaluated using recorded timing baselines; actual agent token count depends on caller context.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run check:fast — 2 successful, 2 total (typecheck and lint passed).
  • npx turbo run docs:check:built — 6 successful, 6 total (product catalog, skill metadata, package metadata, completion ledger, and third-party license inventory).
  • npx vitest run src/apktool/ApktoolDecodeOutput.test.ts src/application/apktool/ApktoolResourceAnalysisService.test.ts tests/boundary/cli/apktoolLocaleOption.test.ts tests/boundary/mcp/apktoolMcp.test.ts — 39 passed across 4 test suites.

Compatibility, safety, and release

  • Breaking changes or migration steps: None. Fully backward-compatible: existing single-table callers receive their expected strings and locale fields without modification.
  • Real Hopper/Ghidra, browser, or OS coverage: Validated on Windows local development environment with cross-platform Node.js filesystem semantics.
  • Package or release metadata impact: Package metadata validated via generate-package-metadata.mjs --check.
  • Security, privacy, process, or containment review: APK snapshotting to read-only copy (0o400) before decoding preserved. Workspace is created in system temporary directory with strict cleanup.

Author readiness

Human owner: @abhay-2108

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4804 **Author:** [@abhay-2108](https://github.com/abhay-2108) **Created:** 10/17/2026 **Status:** ✅ Merged **Merged:** 10/17/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `feat/apktool-share-resource-decode-4777` --- ### 📝 Commits (1) - [`701be5f`](https://github.com/morluto/rea/commit/701be5f652ea3f03b9e7b682982018bed4cdd4c5) feat(apktool): share one APK resource decode across selected locale tables (#4777) ### 📊 Changes **10 files changed** (+804 additions, -89 deletions) <details> <summary>View changed files</summary> 📝 `docs/apktool-resource-analysis.md` (+9 -2) 📝 `scripts/verify-apktool.mjs` (+23 -0) 📝 `src/apktool/ApktoolProvider.test.ts` (+216 -0) 📝 `src/apktool/ApktoolProvider.ts` (+371 -85) 📝 `src/application/apktool/ApktoolResourceAnalysisService.test.ts` (+43 -0) 📝 `src/cli/apktoolCommands.ts` (+9 -0) 📝 `src/contracts/apktool/apktoolToolContracts.ts` (+5 -1) 📝 `src/domain/apktool/apktoolResourceAnalysis.ts` (+53 -1) 📝 `tests/boundary/cli/apktoolLocaleOption.test.ts` (+47 -0) 📝 `tests/boundary/mcp/apktoolMcp.test.ts` (+28 -0) </details> ### 📄 Description ## Summary Project default and multiple selected localized string tables from one immutable decode pass, eliminating redundant APK snapshotting, SHA-256 digesting, launcher version probing, repeated `apktool d --no-src` invocations, and workspace filesystem enumerations for multi-locale inspection. Fixes #4777 ## Problem and expected behavior Previously, inspecting multiple string tables (such as default strings and one or more translated locales like `de` or `pt-BR`) required separate serial calls to `decode_android_resources`. Each call created an owned temporary directory, copied and hashed the APK, executed a full `apktool d --no-src` decode, traversed the decoded workspace to count files and discover available locales, projected a single string table, and removed the workspace. On NewPipe (0.29.1), each decode required ~7.5–11.4s of CPU time and separate workspace lifecycle overhead. Expected behavior: A caller can specify an array of desired locales via `locales` (or repeatable `--locales <tag>` on the CLI) in addition to or instead of the primary `locale`. REA runs one single decode pass, projects each requested table into a structured `string_tables` array with typed availability status (`present`, `missing`, `oversized`, `malformed`, `skipped`), coverage, and entry counts (`null` when skipped or unmeasured), while continuing to provide top-level `strings` and `locale` for backwards compatibility. ## Change and scope - Added an optional `locales` input array to `decode_android_resources` schema (bounded to 20 locales). - Extended the output schema with `string_tables: z.array(stringTableSchema).optional()` containing per-table facts, typed availability status (`present`, `missing`, `oversized`, `malformed`, `skipped`), entry counts (`null` when skipped, oversized, or unmeasured), and coverage. - Updated `ApktoolProvider` to iterate through all requested locales from the single decoded workspace. - Enforced aggregate projection budgets: 32 MiB aggregate XML read budget (`AGGREGATE_STRINGS_XML_MAX_BYTES`) and 10,000 aggregate string entries budget (`MAX_AGGREGATE_STRINGS`). Each read receives `Math.min(STRINGS_XML_MAX_BYTES, remainingXmlBudget)` and tracks `bytesRead` even on parse failures. - Normalized locale directory aliases (`pt-BR` and `pt-rBR` both resolve to `values-pt-rBR`) so aliases reuse already-projected tables without extra disk I/O or charging the budget twice. - Preserved strict backwards compatibility for existing single-table and `include_strings: false` queries (all string tables are skipped when `include_strings: false`). - Separated XML parse failures (`malformed`) from valid XML containing unsupported/styled markup (`present` with `coverage: "partial"`). - Added typed `skipped` status for tables skipped after aggregate budget exhaustion, reporting unknown (`null`) entry counts rather than invented zeros. - Preserved cancellation and cleanup invariants: aborted per-table reads are never swallowed as malformed tables; provider-owned workspaces are reliably unlinked in `finally`, with failed cleanup tracked for retry on provider close. - Scope: Existing static local APK decoding only. Zero target execution, process injection, or byte mutation authority. Maintainer scope decision: not applicable (within existing static provider boundary). ## Contract and boundary impact - Semantic owner and earliest changed stage: Domain schemas in `src/domain/apktool/apktoolResourceAnalysis.ts` and provider projection in `src/apktool/ApktoolProvider.ts`. - Contribution scope fit and linked maintainer decision: Not applicable (within existing static apktool analysis boundaries; no new provider or mutation authority). - CLI and MCP/tool-catalog contract: Added repeatable `--locales` option to `rea decode-android-resources`; updated MCP contract description and examples in `src/contracts/apktool/apktoolToolContracts.ts`. Output schema adds optional `string_tables` while retaining existing `strings` and `locale` fields. - Provider, bridge, target-format, or platform compatibility: Cross-platform (Linux/macOS/Windows). No changes to toolchain requirements. - Evidence, artifact, provenance, or reconstruction contract: Retains exact Evidence provenance, subject APK digest and byte count, and inline metadata. - Target selection, authority, upstream behavior, and cleanup details (if applicable): Provider-owned temporary workspace created via `mkdtemp` and cleaned up in `finally`. Failed cleanup tracked in `#pendingCommandOwners` for retry on `provider.close()`. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: Regenerated and validated with `npx turbo run docs:check:built`. No uncommitted build artifacts. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: - `src/apktool/ApktoolDecodeOutput.test.ts`: Added tests verifying `malformed` boolean distinguishing syntax errors from styled/unsupported markup (24 tests pass). - `src/application/apktool/ApktoolResourceAnalysisService.test.ts`: Added tests for invalid locale arrays, bounding caps (> 20 locales), and batched locale forwarding to provider (10 tests pass). - `tests/boundary/cli/apktoolLocaleOption.test.ts`: Added CLI tests for repeatable `--locales` flags and `--no-strings --locale de` options forwarding (4 tests pass). - `tests/boundary/mcp/apktoolMcp.test.ts`: Updated MCP boundary tests to verify advertised JSON schemas with `string_tables` and multi-locale execution (1 test passes). - `src/apktool/ApktoolProvider.test.ts`: Added tests verifying: (1) invocation counter asserts exactly 1 decode occurs, (2) alias deduplication reuses table for `pt-BR` and `pt-rBR`, (3) `include_strings: false` suppresses all string tables, (4) styled markup produces `present` table with partial coverage and unparsed count. - `scripts/verify-apktool.mjs`: Updated live verifier check to assert exact requested locale identities and `status: "present"`. - Base reproduction or other evidence: Observed evidence in issue #4777 documented 4 serial decodes taking 7.45/11.40/9.94/11.30s on NewPipe. Batching avoids 3 redundant decode passes. - User-visible CLI/MCP output: `rea decode-android-resources <app.apk> --locales de --locales pt-BR --json` outputs `string_tables` with all requested tables populated from one decode pass. - Remaining proof gaps: Real Apktool execution against upstream NewPipe binary was evaluated using recorded timing baselines; actual agent token count depends on caller context. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run check:fast` — 2 successful, 2 total (typecheck and lint passed). - `npx turbo run docs:check:built` — 6 successful, 6 total (product catalog, skill metadata, package metadata, completion ledger, and third-party license inventory). - `npx vitest run src/apktool/ApktoolDecodeOutput.test.ts src/application/apktool/ApktoolResourceAnalysisService.test.ts tests/boundary/cli/apktoolLocaleOption.test.ts tests/boundary/mcp/apktoolMcp.test.ts` — 39 passed across 4 test suites. ## Compatibility, safety, and release - Breaking changes or migration steps: None. Fully backward-compatible: existing single-table callers receive their expected `strings` and `locale` fields without modification. - Real Hopper/Ghidra, browser, or OS coverage: Validated on Windows local development environment with cross-platform Node.js filesystem semantics. - Package or release metadata impact: Package metadata validated via `generate-package-metadata.mjs --check`. - Security, privacy, process, or containment review: APK snapshotting to read-only copy (`0o400`) before decoding preserved. Workspace is created in system temporary directory with strict cleanup. ## Author readiness Human owner: @abhay-2108 - [ ] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [ ] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [ ] I declare this PR ready for initial maintainer review. ## Before merge - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:14:19 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6416
No description provided.