Split selection and merge
Google Play delivers modern apps as a set of split APKs: a base plus per-ABI, per-density, per-language, and per-feature pieces. Morphe patches a single APK, so the verified splits have to be merged first.
This stage consumes only split sets that already passed verification, and it never modifies them. APKEditor receives byte-verified copies in a temporary directory.
Selection policy
From a verified split set, the builder selects:
| Split kind | Rule |
|---|---|
| Base | Always included. |
| ABI | Only the requested ABI. If ABI splits exist but the requested one is missing, the set is rejected. |
| Density | The requested density. With no density requested, only an unambiguous single density split is accepted. |
| Language | All verified language splits are included. |
| Feature / neutral | Included, together with their compatible configuration splits, by dependency closure. |
| Required shared libraries | Rejected. |
| Play Asset Delivery asset packs | Rejected. |
Language filtering is deliberately deferred until configuration exposes a language preference: shipping every language is larger but cannot silently break a locale.
Shared libraries and asset packs are rejected rather than merged because current merge validation cannot prove they survive the merge intact. Refusing to build is the honest outcome; a silently incomplete app is not.
Selection is recorded in a deterministic selected-splits.json including the source hash of every selected split.
Merge
APKEditor V1.4.9 runs as an argument array using its documented merge command:
java -jar APKEditor.jar m -i <copied-splits-directory> -o <merged.apk>All of the following fail closed with exit code 6:
- non-zero exit or timeout
- missing output file
- malformed ZIP or APK structure
- package identity mismatch
- version mismatch
- architecture mismatch
- unresolved split requirements in the merged manifest
Signing the merged stock
APKEditor output is unsigned. It is recorded as the raw merge output, then signed immediately by uber-apk-signer with the builder key, before Morphe ever sees it.
The builder then verifies the signed stock's package, version, architecture, ZIP structure, and certificate. Only that verified signed stock is passed to patching, and the same signer handles both signed stock and the final patched APK. See build.md.
Signing merged stock before patching means the patched output is derived from an artifact whose identity was already confirmed, rather than from an unsigned intermediate whose provenance would end at APKEditor.
Merge provenance
merge-provenance.json records the raw APKEditor output hash plus the signed-stock hash and its certificate SHA-256.
google_signature_retained = false always refers to the raw APKEditor output. Merging necessarily discards Google's signature, so Google signer identity applies only to the verified original splits, never to merged output. Those original split hashes stay in source provenance, which is what preserves the chain back to Google. See google-play-integrity.md.
