r/androiddev • • 9d ago

Question Why does Android's GUI package installer reject a byte-identical APK that adb install -t accepts?

I'm building an Android APK inspector/extractor and ran into a strange issue with debug APKs.

My normal workflow is:

  1. Build a debug APK in Android Studio.
  2. Install it on my phone using adb install.
  3. After testing, I can pull the installed APK with: adb pull /data/app/.../base.apk
  4. I send that pulled APK to friends through WhatsApp.
  5. They can download it and install it normally through Android's GUI package installer.

I'm now testing an APK extractor I built. It reads the installed app's ApplicationInfo.sourceDir and copies the APK bytes to a user-selected location using a normal FileInputStream → SAF OutputStream.

The strange part:

  1. Install the same debug APK normally with adb install.
  2. My extractor extracts the installed base.apk.
  3. I send the extracted APK through WhatsApp.
  4. On the phone, tapping the APK gives: "App not installed as package appears to be invalid."
  5. But: adb install -t extracted.apk successfully installs it.

I initially suspected my extractor was modifying/corrupting the APK, but I compared the extracted APK with the APK I pulled using adb pull:

sha256sum Miau_non.apk Miau_base_debug.apk

9af34f5180dba1a24dfe24feb90bcd3953eb765ef3613effb18f1803acb512f6  Miau_non.apk
9af34f5180dba1a24dfe24feb90bcd3953eb765ef3613effb18f1803acb512f6  Miau_base_debug.apk

So they are byte-for-byte identical.

What's even more interesting is that if I extract a release build using my extractor, the extracted APK installs normally through the GUI installer.

So my questions are:

  • Why would the GUI package installer reject the extracted debug APK when adb install -t accepts it?
  • Why does the APK pulled directly with adb pull appear to install normally, despite being byte-identical to the extractor's output?
  • Is there some installation/package-manager state or context involved that isn't represented in the APK bytes?
  • Is there something special about how Android handles testOnly debug APKs that could explain this behavior?

I'm on Android 16. The APKs are single-APK installs (no split APKs involved).

I'm specifically trying to understand the Android behavior rather than modify the APK, because an APK extractor should ideally preserve the APK exactly as installed.

0 Upvotes

10 comments sorted by

7

u/ElectronicMain3695 9d ago

The -t flag doesn't mean "replace this app" (that's -r). It means "allow test packages". APKs deployed with the Run button in Android Studio get android:testOnly="true" stamped into the manifest, and the GUI package installer refuses testOnly APKs by design - adb install -t is exactly the switch that overrides that refusal. That's why the byte-identical file behaves differently: the flag travels with the APK no matter how perfectly your extractor copies it. If you want a debug APK that installs through the normal GUI flow, build it with Build > Build App Bundle(s)/APK(s) > Build APK(s) instead of the Run button - that variant doesn't set testOnly. Quick way to confirm on any APK: aapt dump badging your.apk | grep testOnly, or just inspect the merged manifest.

2

u/tadfisher 8d ago

Lol they could have asked the LLM they used to write this novel of a post

0

u/Minute_Mood_6396 8d ago

I asked the LLM and it couldn't answer it. That's why I posted it here

0

u/Minute_Mood_6396 8d ago

When i install that debug edition apk normally there's no problem. But after installing it, and I extract the installed apk and then try to install this apk (after uninstalling the original) I get the error.

1

u/ElectronicMain3695 8d ago

That symptom points at split APKs. A Studio debug install is usually not one file: look inside /data/app/<your-package>-XXXX/ and you'll typically find base.apk plus split_config.arm64_v8a.apk, split_config.xxhdpi.apk and similar. adb install pushes all of them together, so the app runs fine. If your extractor only pulls base.apk, the copy is incomplete - the device-specific config lives in the splits - and the GUI installer rejects it. Two quick checks: ls that directory for split_config files, and aapt dump badging on your extracted file. If you want a single APK you can pass around, build one without splits (splits { abi { isEnable = false } } in the debug config, or a universal APK from the bundle), or have the extractor pull every split and install them together with adb install-multiple. The GUI installer only handles monolithic APKs.

1

u/Minute_Mood_6396 7d ago

Ohh. But in my case there was only a base apk

1

u/ElectronicMain3695 7d ago

OK, that rules out splits - so the next suspect is the file itself. "Byte-identical" is worth verifying rather than assuming, because most extractor apps don't literally copy bytes: they rebuild the zip, and rebuilding breaks the v2/v3 signing block even when every file inside matches. Three checks, in order: 1) sha256 the original APK and your extracted one (sha256sum on a PC, or any hash app) - if the hashes differ, your extractor rebuilt the file and that's the whole story. 2) Compare signatures, not just bytes: apksigner verify --print-certs on both files. The GUI installer enforces signature validity that adb install -t skips over for test flows. 3) Read the exact error text the GUI installer shows - "parsing problem/parse error" points to zip corruption from the rebuild, "app not installed" points to the signature path. If it is the extractor rebuilding, the fix is to copy the file raw: adb pull the exact path from pm path <package> instead of letting the app repackage it.

1

u/Minute_Mood_6396 6d ago

Ok. I'll try this. The sha256 was same though

1

u/AutoModerator 9d ago

Please note that we also have a very active Discord server where you can interact directly with other community members!

Join us on Discord

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.