Skip to content
Tolinku
Tolinku
Sign In Start Free
Analytics & Attribution · · 5 min read

Click Injection Detection for Mobile Apps

By Tolinku Staff
|
Tolinku mobile attribution dashboard screenshot for analytics blog posts

Click injection is a mobile ad fraud technique specific to Android. A malicious app on the user's device listens for app installs via a broadcast receiver, then fires a fake ad click just before the app opens for the first time. The attribution system sees the click and credits the install to the fraudulent ad network, which collects the CPI payout for an install it had nothing to do with.

This guide explains how click injection works, how to detect it, and how to prevent it. For attribution fraud broadly, see attribution fraud: detection and prevention guide. For mobile attribution, see mobile attribution: a developer's guide.

How Click Injection Works

The Android Broadcast

On Android, the system sends a com.android.vending.INSTALL_REFERRER broadcast when an app is installed from the Google Play Store. Before Google restricted this broadcast, any app could listen for it:

1. User sees a legitimate ad and decides to install the app
2. User downloads from Google Play Store
3. Android broadcasts INSTALL_REFERRER
4. Malicious app on the device intercepts the broadcast
5. Malicious app fires a fake ad click with matching device ID
6. App opens for the first time
7. Attribution provider sees the fake click (most recent) and attributes the install to it
8. Fraudulent network gets paid for the install

The key exploit is the timing: the malicious app knows an install is happening (from the broadcast) and fires the click before the app opens. Since attribution systems use last-click, the fake click wins.

Why It Only Affects Android

iOS does not broadcast install events to other apps. Each iOS app runs in its own sandbox and cannot observe other apps being installed. Click injection is an Android-specific problem.

Detection Signals

Click-to-Install Time (CTIT)

The most reliable detection signal. Click injection clicks are fired after the download starts but before the first open, resulting in abnormally short click-to-install times:

CTIT Interpretation
< 5 seconds Almost certainly click injection
5-10 seconds Highly suspicious
10-30 seconds Suspicious (depends on app size and connection speed)
30+ seconds Normal range for legitimate clicks

A legitimate click happens before the user decides to install. A click injection click happens after the install has already started. The time difference is the giveaway.

def is_click_injection(click_time, install_time, download_start_time):
    ctit = install_time - click_time

    # Click happened after download started
    if click_time > download_start_time:
        return True  # Definitive click injection

    # Abnormally short CTIT
    if ctit < 10:  # seconds
        return True  # Very likely click injection

    return False

Google Play Install Referrer API

The Google Play Install Referrer API provides two timestamps:

  • referrerClickTimestampSeconds: When the referrer click happened.
  • installBeginTimestampSeconds: When the download began.

If the click timestamp is after the install begin timestamp, the click is fraudulent:

fun checkClickInjection(referrerDetails: ReferrerDetails): Boolean {
    val clickTime = referrerDetails.referrerClickTimestampSeconds
    val installBeginTime = referrerDetails.installBeginTimestampSeconds

    // Click happened after download started = click injection
    if (clickTime > installBeginTime) {
        return true
    }

    // Click happened suspiciously close to install begin
    val ctit = installBeginTime - clickTime
    if (ctit < 5) {
        return true
    }

    return false
}

Distribution Analysis

Analyze CTIT distributions across ad networks. Legitimate networks have a natural distribution curve (most installs have CTIT of 30 seconds to several hours). Click injection networks show a spike at very low CTIT values:

Network Mean CTIT Median CTIT % Under 10s
Legitimate Network A 2.5 hours 45 min 0.3%
Legitimate Network B 1.8 hours 30 min 0.5%
Suspicious Network C 12 min 4 sec 68%

Network C's distribution is clearly abnormal. 68% of installs have a CTIT under 10 seconds, which is not possible with legitimate clicks.

New Device Ratio

Click injection often targets specific device profiles. Check if a network has an unusual concentration of specific device models or OS versions:

def check_device_distribution(network_installs):
    device_counts = Counter(i.device_model for i in network_installs)
    total = len(network_installs)

    for device, count in device_counts.most_common(5):
        ratio = count / total
        if ratio > 0.3:  # One device model > 30% of installs
            flag_for_review(network_installs, f"Device concentration: {device} at {ratio:.0%}")

Prevention

Use the Install Referrer API

The Play Install Referrer API provides server-validated timestamps that cannot be spoofed:

class InstallReferrerHelper(private val context: Context) {
    fun getInstallReferrer(callback: (ReferrerDetails?) -> Unit) {
        val client = InstallReferrerClient.newBuilder(context).build()

        client.startConnection(object : InstallReferrerStateListener {
            override fun onInstallReferrerSetupFinished(responseCode: Int) {
                if (responseCode == InstallReferrerClient.InstallReferrerResponse.OK) {
                    val details = client.installReferrer
                    callback(details)
                } else {
                    callback(null)
                }
                client.endConnection()
            }

            override fun onInstallReferrerServiceDisconnected() {
                callback(null)
            }
        })
    }
}

Server-Side Validation

Validate installs on your server, not just on the device:

interface InstallEvent {
  clickTime: number;
  installBeginTime: number;
  firstOpenTime: number;
  networkId: string;
  deviceId: string;
}

function validateInstall(event: InstallEvent): { valid: boolean; reason?: string } {
  // Check 1: Click before install begin
  if (event.clickTime > event.installBeginTime) {
    return { valid: false, reason: 'click_after_download' };
  }

  // Check 2: CTIT minimum
  const ctit = event.installBeginTime - event.clickTime;
  if (ctit < 5) {
    return { valid: false, reason: 'ctit_too_short' };
  }

  // Check 3: First open within reasonable time of install
  const installToOpen = event.firstOpenTime - event.installBeginTime;
  if (installToOpen < 2) {
    return { valid: false, reason: 'impossible_open_time' };
  }

  return { valid: true };
}

Rejecting Fraudulent Installs

When you detect click injection, reject the install attribution and send a rejection postback:

async function handleFraudulentInstall(event: InstallEvent) {
  // 1. Reject the attribution
  await attributionDB.rejectInstall(event.deviceId, event.networkId, 'click_injection');

  // 2. Re-attribute to the correct source (if there was a legitimate click before)
  const legitimateClick = await findLegitimateClick(event.deviceId);
  if (legitimateClick) {
    await attributionDB.attribute(event.deviceId, legitimateClick.networkId);
  } else {
    await attributionDB.attributeOrganic(event.deviceId);
  }

  // 3. Send rejection postback to the fraudulent network
  await sendRejectionPostback(event.networkId, event, 'click_injection');
}

Impact of Click Injection

Click injection does not generate fake installs. It steals attribution from legitimate sources (organic, other paid campaigns). The effects:

  • Inflated CPI costs. You pay for installs that would have happened anyway.
  • Distorted channel mix. The fraudulent network appears to perform well, so you allocate more budget to it.
  • Reduced organic count. Installs that should be counted as organic are attributed to paid campaigns.
  • Incorrect ROAS. Campaign performance metrics are skewed because the attributed installs are not truly driven by the ads.

Google's Protections

Google has implemented protections against click injection:

  • Play Install Referrer API v2 provides installBeginTimestampSeconds, which makes click injection detectable.
  • Play Protect scans apps for malicious behavior, including broadcast receiver abuse.
  • Android 8.0+ restricts implicit broadcast receivers, reducing the attack surface.

Despite these protections, click injection remains prevalent because older Android devices are still in use and some users disable Play Protect.

Tolinku for Attribution

Tolinku's analytics track deep link clicks with server-validated timestamps, providing clean attribution data. Since Tolinku deep links use server-side click tracking, they are not susceptible to client-side click injection. Configure attribution tracking in the Tolinku dashboard.

For attribution fraud, see attribution fraud: detection and prevention guide. For Android deep linking, see Android App Links: complete implementation guide.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.