Glossary/Android platform
Sideloading
Definition
Sideloading is installing an app onto a device from a source other than the platform's official app store, such as an APK file, a debug build pushed over ADB, an enterprise distribution channel, or an alternative app marketplace.
Sideloading is usually discussed as a security topic, but its sharpest practical effect is on measurement: the official store is also the component that carries the install referrer, so removing the store removes the deterministic link between a click and an install. This is why deferred deep linking appears broken in QA on almost every Android team — the test build was sideloaded, and the signal it depends on was never sent.
What counts as a sideload, on each platform
| Source | Platform | Store install? |
|---|---|---|
| Google Play | Android | Yes |
adb install, or an APK from a browser or file manager | Android | No |
| Third-party store such as F-Droid or Aptoide | Android | No |
| Samsung Galaxy Store, Amazon Appstore | Android | Its own store, not Play |
| MDM or enterprise push | Both | No |
| App Store | iOS | Yes |
| TestFlight | iOS | Partly — no ad attribution |
| Alternative marketplace or web distribution, EU only | iOS | No |
| Development or enterprise provisioning profile | iOS | No |
Android has always permitted this behind a per-source "install unknown apps" permission. iOS did not, until the Digital Markets Act obliged Apple to allow alternative app marketplaces and web distribution for users in the EU — which is the reason a previously iOS-only assumption, that every install came from the App Store, no longer holds even there.
Which attribution signals survive
| Signal | Store install | Sideloaded |
|---|---|---|
| Play install referrer | Available | Gone |
| Meta install referrer | Available | Gone |
| SKAdNetwork postbacks | Available | Gone |
| AdAttributionKit | Available | Marketplaces only |
| App Link verification | Works | Works — if signed the same |
| Universal Links and the AASA file | Works | Works |
| First-party click ID in the link | Works | Works |
| Server-to-server conversions | Works | Works |
The pattern in that table is the useful takeaway: everything the *store* mediates disappears, and everything the *link* or *your own server* mediates survives. Verified links keep working because verification is a handshake between assetlinks.json on your domain and the signing certificate of the installed package — Play is not party to it.
val client = InstallReferrerClient.newBuilder(context).build()
client.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
val details = client.installReferrer
// A sideload can still reach OK on a device that has
// Play installed — with an empty referrer string. Treat
// blank as "no attribution", not as an organic install
// you can report with confidence.
if (details.installReferrer.isBlank()) {
markUnattributed(reason = "referrer_empty")
} else {
attribute(details.installReferrer)
}
}
// Play Store app absent, or too old to serve the API.
InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED ->
markUnattributed(reason = "no_play_store")
InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE ->
retryLater()
InstallReferrerClient.InstallReferrerResponse.DEVELOPER_ERROR ->
logConfigurationBug()
}
client.endConnection()
}
override fun onInstallReferrerServiceDisconnected() = retryLater()
})An empty referrer is not an organic install
The two get collapsed constantly, and the effect is to move paid installs into the organic column. A sideloaded install has *no information* about its origin; a genuinely organic install has been observed arriving with no campaign. Record them as separate states or your organic baseline will absorb every install your QA team, your enterprise channel and your alternative marketplace produce.
The QA trap, and the signing key that breaks verification
Two failures dominate real reports, and both are artefacts of sideloading rather than bugs in the link setup.
The first is deferred deep linking that never resolves in testing. Every build QA installs — over ADB, from a CI artefact, from a shared APK — is a sideload with no install referrer, so a deferred link has nothing to match on and the app opens cold on its home screen. The setup is correct; the test is invalid. Validate deferred behaviour from an internal testing track on Play, where the install genuinely comes from the store, and treat ADB builds as suitable only for testing the routing half.
The second is App Links that verify from Play and fail when sideloaded. Verification compares the SHA-256 fingerprint of the installed package's signing certificate against the fingerprints listed in your assetlinks.json. A Play-distributed build is signed by Play App Signing; a locally built APK is signed by your debug or upload key. Different certificate, different fingerprint, no match — so the domain fails to verify and the link opens in the browser.
# Where did this package actually come from?
# "com.android.vending" means Play. Anything else is a sideload.
adb shell pm list packages -i | grep com.example.app
# Why the domain will not verify: compare this fingerprint against
# every sha256_cert_fingerprints entry in your assetlinks.json.
# List the debug key, the upload key AND the Play App Signing key.
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android | grep 'SHA256:'
# The device's own verdict on each declared host.
adb shell pm get-app-links com.example.app
# Legacy only: some SDKs still listen for the old broadcast, which
# Play stopped sending in 2020. Useful to exercise your handler on
# a sideloaded build; it proves nothing about production.
adb shell am broadcast -a com.android.vending.INSTALL_REFERRER -n com.example.app/.InstallReferrerReceiver --es "referrer" "utm_source=test&utm_campaign=local"List every fingerprint, not just the current one
sha256_cert_fingerprints is an array. Putting the debug key, the upload key and the Play App Signing key in it makes verification succeed for local builds, internal tracks and production alike, with no per-environment file to keep in sync. It is the one change that stops this class of bug recurring — see why Android App Links are not working for the full checklist.
For the EU marketplace case on iOS, the thing to know is that AdAttributionKit was designed with alternative distribution in mind and can attribute installs originating from an alternative app marketplace, whereas SKAdNetwork cannot. If any meaningful share of your installs will arrive that way, that difference is a concrete reason to move off SKAdNetwork rather than a theoretical one.
Android SDK documentation
The Android SDK distinguishes an empty install referrer from a missing one and reports them separately, so sideloaded builds do not quietly inflate your organic column. The documentation covers the fallbacks it uses when no store referrer exists.
Open the android sdk documentation →Frequently asked questions
- What does sideloading mean?
- Sideloading means installing an app from somewhere other than the platform's official store — an APK opened from a browser or file manager, a build pushed over ADB, an enterprise or MDM distribution, a third-party Android store, or on iOS in the EU an alternative app marketplace or web distribution. The defining characteristic is that the official store never mediates the install.
- Does sideloading break install attribution?
- It breaks the store-mediated parts of it. The Play install referrer, the Meta install referrer and SKAdNetwork postbacks all depend on the official store being the installer, so a sideloaded install produces none of them. Signals that do not involve the store — verified link routing, a first-party click identifier you generate, and server-to-server conversion reporting — continue to work unchanged.
- Why does deferred deep linking never work in QA on Android?
- Because QA builds are sideloads. A build installed over ADB or from a CI artefact has no Play install referrer, so there is no signal for the deferred match to use and the app opens on its default screen. The configuration is usually correct and the test is invalid. Validate deferred behaviour through a Play internal testing track, where the install genuinely comes from the store.
- Do Android App Links still verify on a sideloaded build?
- Yes, provided the package is signed with a certificate whose SHA-256 fingerprint is listed in your assetlinks.json. Verification is a handshake between your domain and the installed package's signing certificate and does not involve Play. The usual failure is that the sideloaded build is debug-signed or upload-signed while the file only lists the Play App Signing key, so the fingerprints do not match.
- Can a sideloaded install ever be attributed to a campaign?
- Only through a first-party mechanism. Put a click identifier in the link, record it server-side when the click happens, and have the app report the same identifier once it launches — that path never touches the store. What you cannot recover is the store-mediated referrer, so treat those installs as a distinct unattributed state rather than folding them into your organic count.
Related terms
- Play Install Referrer — The Play Install Referrer is a Google Play API that lets a newly installed Android app read the referrer string and click timestamps recorded when the user arrived at its Play Store listing.
- Deferred deep link not working — A deferred deep link fails when the signal meant to carry the pre-install destination across the app store — an install referrer, a stored token, or a server-side match — is absent, expired, or never read on first launch.
- SHA-256 Certificate Fingerprint — A SHA-256 certificate fingerprint is the SHA-256 hash of an app signing certificate, written as 32 colon-separated hexadecimal bytes, used to prove that a specific build was signed by a specific key.
- Android App Links not working — Android App Links fail when the system cannot verify that a domain and an app belong to the same owner, at which point tapped links open in the browser instead of the app with no error shown to the user.
- Unattributed installs — An install is unattributed when no signal linking it to a prior ad click or link tap survived the journey through the app store, which can mean the install was organic or that the signal existed and was lost.