One file on disk is not the install set
A universal APK contains DEX, resources, and usually lib/arm64-v8a/*.so (and leftover ABIs if you were sloppy). A split install from bundletool or Play sends base.apk plus split_config.arm64_v8a.apk (and density / language splits). Native libraries live in the ABI split. If you only drop base.apk on the analyzer, the report will say native libs: none. That is true of that ZIP. It is not true of the app.
QA still zips “the APKs” as a single base.apk from an extracted .apks archive. Sideload then crashes in JNI. The next commit disables a feature that was never missing — it was in the other file.
What we do on each slice
Drop base.apk. Confirm DEX count, AndroidManifest.xml present, SHA-256. Drop split_config.arm64_v8a.apk. Confirm lib/arm64-v8a/ entries. If the second file is missing from the folder QA sent you, you do not have a complete install set — do not sideload base alone and call the build “no JNI.”
The analyzer treats any .apk as a ZIP. It does not reconstruct the set. It does not understand .aab. If you rename a bundle to .apk, JSZip may still list entries; the layout is BundleConfig + modules, not an installable package. Use bundletool build-apks then analyze those APKs.
bundletool build-apks --bundle app-release.aab --output app-release.apks --mode=universal bundletool build-apks --bundle app-release.aab --output app-splits.apks # unzip / bundletool extract to get base + config splits
Universal mode is for a local install check. Split mode is what Play-like delivery looks like. Hash each file separately. Do not average hashes.
Worked listings (what to screenshot)
base.apk: DEX ≥ 1, manifest present, lib/ often empty on modern splits. SHA-256 of this file is not the hash of the app.
config.arm64_v8a.apk: little or no DEX, many .so files. Hash this slice separately if a crash is ABI-specific. A crash on armeabi-v7a devices with only an arm64 split in the set is an ABI coverage bug, not “JNI is broken.”
config.xxhdpi.apk / language splits: resources only. Analyzer will look “empty” of DEX. That is expected.
Renamed AAB: look for BundleConfig.pb / base/manifest/ style paths. Stop. You are not looking at an APK.
Density and language false negatives
A base module can omit density-specific drawables that live in split_config.xxhdpi.apk. Screenshot-diff tools that unpack only base will report missing assets. Same for values-es in a language split. If localization “vanished,” check whether QA installed the language split.
Play App Signing and hashes
Each slice has its own SHA-256. Play may also re-sign. Do not compare a local universal APK hash to a testers’ split install. See desk hash vs testers’ hash. Certificate fingerprints from apksigner are the identity that should match Console → App signing for device installs.
Sideload rules we keep in the ticket template
- Name every file:
base.apk,split_config.arm64_v8a.apk, … - Paste analyzer SHA-256 per file.
- Paste
adb install-multiplethe set, notadb install base.apk. - If using a universal APK from bundletool, say so — that is a different artifact than Play’s splits.
The ADB generator will print an adb install line for one path. For splits, type install-multiple yourself. The generator does not talk to the phone either way.
When to leave the browser
bundletool get-size, Play’s App bundle explorer, and a device install from the internal track. The APK analysis guide is the rest of the preflight on whichever APK you actually have. Job table: browser vs apktool.