device_data is the object you send on init, attribution-data, and signup. It carries the signals Linkrunner uses to match an install to a click. Most fields are simple lookups. Three need Google Play libraries and do the heavy lifting for attribution.
The full client collects all of these for you. This page explains each one so you can trust, trim, or rebuild that collection.
Dependencies
dependencies {
// Advertising ID (GAID)
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
// App Set ID
implementation 'com.google.android.gms:play-services-appset:16.0.2'
// Google Play Install Referrer
implementation 'com.android.installreferrer:installreferrer:2.2'
}
Going SDK-less does not remove these libraries. They are how Android exposes the advertising ID and the install referrer, so you need them no matter how you call Linkrunner.
Permissions and queries
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
<!-- Lets you see the Meta apps for the Meta install referrer (Android 11+) -->
<queries>
<package android:name="com.facebook.katana" />
<package android:name="com.instagram.android" />
<package android:name="com.facebook.lite" />
</queries>
What to send
| Field | Source on Android | Importance |
|---|
gaid | AdvertisingIdClient | High. Google Ads and device matching. |
install_ref (and related) | Play Install Referrer | High. Carries gclid, fbclid, utm_*, Meta referrer. |
meta_install_ref | Meta content provider | High for Meta ads. |
device_id | Settings.Secure.ANDROID_ID | Recommended. Matching fallback. |
device_ip | Network interfaces | Recommended. IP matching fallback. |
user_agent | WebSettings.getDefaultUserAgent | Recommended. Fingerprint matching. |
app_version, build_number, bundle_id, application_name | PackageManager | Recommended. |
manufacturer, brand, device_name, system_version | Build | Recommended. |
connectivity, carrier | System services | Optional. |
appsetid, appsetid_scope | AppSet | Optional. |
If you send only the basics and skip gaid, install_ref, and meta_install_ref, install registration still works, but paid attribution for Google and Meta will be weak or missing.
Advertising ID (GAID)
The Google Advertising ID is the primary signal for Google Ads and device matching. Read it off the main thread.
fun gaid(context: Context): String? = try {
val info = AdvertisingIdClient.getAdvertisingIdInfo(context)
if (info.isLimitAdTrackingEnabled) null else info.id
} catch (e: Exception) { null }
It returns null when the user has limited ad tracking. If your app targets children, do not collect the GAID and remove the AD_ID permission. See the SDK guidance on AAID.
Google Play Install Referrer
The install referrer is the single most valuable attribution signal on Android. It carries the gclid, fbclid, utm_* parameters, and the Meta encrypted referrer from the Play Store click that led to the install.
suspend fun installReferrer(context: Context): JSONObject? = withContext(Dispatchers.IO) {
suspendCancellableCoroutine { cont ->
val client = InstallReferrerClient.newBuilder(context).build()
client.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(code: Int) {
try {
if (code == InstallReferrerClient.InstallReferrerResponse.OK) {
val r = client.installReferrer
cont.resume(JSONObject().apply {
put("install_ref", r.installReferrer ?: "")
put("install_ref_install_version", r.installVersion ?: "")
put("install_ref_installBeginTimestampSeconds", r.installBeginTimestampSeconds)
put("install_ref_referrerClickTimestampSeconds", r.referrerClickTimestampSeconds)
put("install_ref_googlePlayInstantParam", r.googlePlayInstantParam)
})
} else cont.resume(null)
} catch (e: Exception) {
cont.resume(null)
} finally {
runCatching { client.endConnection() }
}
}
override fun onInstallReferrerServiceDisconnected() {
if (cont.isActive) cont.resume(null)
}
})
cont.invokeOnCancellation { runCatching { client.endConnection() } }
}
}
The install referrer is available right after install. Read it once on first launch, send it with init, and cache it. The Play Store keeps it only briefly, so do not delay the first read.
For Meta (Facebook and Instagram) attribution, query the Meta install referrer from the installed Meta app. It needs your Facebook App ID in the manifest and the <queries> block above.
<application>
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="fb1234567890" />
<!-- If you do not use the Facebook SDK, use this key instead: -->
<!-- <meta-data android:name="com.linkrunner.FacebookApplicationId" android:value="1234567890" /> -->
</application>
fun metaInstallReferrer(context: Context): JSONObject? {
val appId = facebookAppId(context) ?: return null
val providers = listOf(
"com.facebook.katana.provider.InstallReferrerProvider" to "facebook",
"com.instagram.contentprovider.InstallReferrerProvider" to "instagram",
"com.facebook.lite.provider.InstallReferrerProvider" to "facebook_lite",
)
for ((authority, source) in providers) {
if (context.packageManager.resolveContentProvider(authority, 0) == null) continue
val uri = Uri.parse("content://$authority/$appId")
context.contentResolver.query(
uri, arrayOf("install_referrer", "is_ct", "actual_timestamp"), null, null, null
)?.use { c ->
if (c.moveToFirst()) {
val refIdx = c.getColumnIndex("install_referrer")
val ref = if (refIdx >= 0) c.getString(refIdx) else null
if (!ref.isNullOrEmpty()) return JSONObject().apply {
put("install_referrer", ref)
put("source", source)
c.getColumnIndex("is_ct").let { if (it >= 0) put("is_ct", c.getInt(it)) }
c.getColumnIndex("actual_timestamp").let { if (it >= 0) put("actual_timestamp", c.getLong(it)) }
}
}
}
}
return null
}
fun facebookAppId(context: Context): String? = try {
val meta = context.packageManager
.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).metaData
meta?.getString("com.facebook.sdk.ApplicationId")
?: meta?.getString("com.linkrunner.FacebookApplicationId")
} catch (e: Exception) { null }
See Meta Install Referrer for how Linkrunner uses this signal.
Everything else
These are plain system lookups. The full client includes them.
device_id: Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
device_ip: first non-loopback IPv4 from NetworkInterface.getNetworkInterfaces()
user_agent: WebSettings.getDefaultUserAgent(context)
manufacturer, brand, device_name, system_version: Build.MANUFACTURER, Build.BRAND, Build.MODEL, Build.VERSION.RELEASE
- App info:
PackageManager.getPackageInfo for app_version and build_number, context.packageName for bundle_id
Need help? Contact support@linkrunner.io