Technical Postmortem: Android SDK Consent Management Misconfiguration
Summary
This postmortem documents a common architectural misunderstanding in Android SDK development regarding Google User Messaging Platform (UMP) integration and consent management. The incident occurs when SDK developers incorrectly assume that relying on the host app to provide GAID/ASID absolves them from UMP integration requirements.
Key Takeaway: The location where identifiers are obtained is irrelevant to consent requirements—what matters is whether the SDK uses identifiers that require consent under Google’s policies.
Root Cause
The fundamental misunderstanding stems from a conceptual error in interpreting Google’s consent requirements:
- Developers assume: “If I don’t fetch GAID myself, I’m not responsible for consent”
- Reality: Consent is required based on data usage, not data acquisition method
When an SDK receives GAID/ASID from a host app and uses these identifiers for:
- Advertising purposes
- User profiling
- Cross-app tracking
- Personalization based on advertising identifiers
…then the SDK is subject to the same consent requirements as if it had retrieved the identifiers directly from the device.
Why This Happens in Real Systems
This misconfiguration occurs due to several recurring patterns:
- Delegation misconception: Developers believe “the host app handles consent, so I’m covered”
- Parameter passing confusion: Passing GAID as an optional parameter creates ambiguity about responsibility
- Policy ambiguity: Google’s documentation doesn’t explicitly state “SDKs using received identifiers need UMP”
- Scope limitation: Teams only analyze their own code’s identifier retrieval, not identifier consumption
Real-World Impact
The consequences of incorrect consent handling include:
- Policy violations: Google Play Store rejections or warnings
- Account termination risk: Repeated violations lead to developer account suspension
- Legal exposure: GDPR/CCPA non-compliance penalties
- Functional breakage: Devices without proper consent may receive null/zero identifiers, breaking SDK functionality
- User trust erosion: Improper consent flows create poor user experiences
Example or Code
The following demonstrates the incorrect versus correct implementation patterns:
// INCORRECT: Assuming no UMP needed when GAID is received from host
class SdkInitializer {
fun initialize(context: Context, gaid: String?, asid: String?) {
// SDK uses GAID without any consent check
val advertisingId = gaid ?: "unknown"
sendToAnalytics(advertisingId) // ❌ Violation: using ad ID without consent
}
}
// CORRECT: Implementing proper consent checks regardless of GAID source
class SdkInitializer {
fun initialize(context: Context, gaid: String?, asid: String?) {
val consentInfo = UserMessagingPlatform.getConsentInformation(context)
if (consentInfo.isConsentRequired) {
// Show consent form before using any identifiers
showConsentForm(context) {
proceedWithInitialization(gaid, asid)
}
} else {
proceedWithInitialization(gaid, asid)
}
}
private fun proceedWithInitialization(gaid: String?, asid: String?) {
val canUseAdId = ConsentInformation.getInstance(context)
.canUseGoogleIdentifierForAnalytics()
val advertisingId = if (canUseAdId) gaid else null
initializeSdk(advertisingId)
}
}
How Senior Engineers Fix It
Senior engineers address this issue through comprehensive consent architecture:
- Unified consent layer: Implement consent checks regardless of identifier source
- Defense in depth: Assume no identifiers are available until consent is confirmed
- Contract clarity: Document in SDK contracts that host apps must ensure consent before passing identifiers
- UMP integration: Include Google UMP SDK as a required dependency
- Runtime verification: Check consent status at runtime before every identifier use
Best practice implementation:
class ConsentManager(private val context: Context) {
private val consentInformation = UserMessagingPlatform.getConsentInformation(context)
fun canUseAdvertisingIdentifiers(): Boolean {
return consentInformation.canUseGoogleIdentifierForAnalytics()
}
fun requestConsent(activity: Activity, onComplete: () -> Unit) {
if (consentInformation.isConsentRequired) {
val consentForm = UserMessagingPlatform.getConsentForm(activity) { form, error ->
if (error != null) {
handleConsentError(error)
}
onComplete()
}
consentForm.show()
} else {
onComplete()
}
}
}
Why Juniors Miss It
Junior engineers and even mid-level developers frequently overlook this issue because:
- Focus on happy paths: Code works during development when consent is granted
- Limited policy knowledge: Google’s advertising policies aren’t always required reading
- Test environment gaps: Development devices often have identifiers pre-enabled
- Delegation assumption: “The host app handles this” is a reasonable-sounding but incorrect assumption
- Missing integration experience: Developers without prior advertising SDK work lack the context for proper consent handling
Preventive measures include: mandatory consent management training, security review checklists, and explicit documentation of consent responsibilities in SDK contracts.