Android SDK - Data Privacy

Complying with Data Privacy Laws

Implement privacy-compliant data collection by notifying Singular of user consent choices for GDPR, CCPA, and other consumer privacy regulations.

When users consent or decline to share their information with third parties, use Singular's privacy methods to communicate their choice. This ensures compliance with regulations like California Consumer Privacy Act (CCPA) and enables partners to respect user privacy preferences.

Learn More: See User Privacy and Limit Data Sharing for detailed information on how Singular processes privacy consent.


Limit Data Sharing

Control Third-Party Data Sharing

Notify Singular whether users have consented to share their personal data with third-party partners using the limitDataSharing method.

Method Signature:

public static void limitDataSharing(boolean shouldLimitDataSharing);

Parameters:

  • false: User has opted in and consented to share their data
  • true: User has opted out and does not consent to share their data

Important: While optional, this method affects attribution data sharing. Some partners only share complete attribution information when explicitly notified that users have opted in.

Usage Examples

Kotlin Java
// User has opted in to share their data
Singular.limitDataSharing(false)

// User has opted out and declined to share their data
Singular.limitDataSharing(true)

// Example: Set based on user preference
fun handlePrivacyConsent(userConsented: Boolean) {
    // Pass inverse: false = opted in, true = opted out
    Singular.limitDataSharing(!userConsented)
    
    Log.d("Privacy", "Data sharing: ${if (userConsented) "Enabled" else "Limited"}")
}

How It Works:

Singular uses this setting in User Privacy Postbacks and passes it to partners who require it for regulatory compliance.


Per-Event Limit Data Sharing

Override Data Sharing on a Single Event

You can override the global limitDataSharing setting for a single event or custom revenue call by passing the Attributes.sngAttrLimitDataSharing attribute alongside your other event arguments. This is useful when a user updates their consent inline with an action and you want that specific event to reflect the new choice immediately, without changing the SDK-wide setting for subsequent events.

Accepted Values:

  • false: User has opted in for this event only
  • true: User has opted out for this event only

Important: The attribute must be a Boolean . Non-boolean inputs are ignored and the request falls back to the global limitDataSharing setting. Either way, the attribute is removed from the event's arguments before the request is sent, so it never appears in your event payload.

Usage Examples

Kotlin Java
// Example 1: Standard event, opt out only for this event
val args = JSONObject()
    .put(Attributes.sngAttrLimitDataSharing.toString(), true)
    .put("order_id", "12345")

Singular.event("checkout_completed", args.toString())

// Example 2: Custom revenue, opt in only for this event
val attributes = mutableMapOf<String, Any>().apply {
    put(Attributes.sngAttrLimitDataSharing.toString(), false)
    put("sku", "premium_monthly")
}

Singular.customRevenue("premium_purchase", "USD", 9.99, attributes)

How It Works:

When a valid boolean is supplied, the SDK removes the attribute from the event's arguments and writes it to the request's data_sharing_options.limit_data_sharing field — overriding the global limitDataSharing setting for that single request only. The global setting and the result of getLimitDataSharing are not modified, so any subsequent events that do not supply the attribute continue to use the global choice.


GDPR Compliance Methods

Manage user tracking consent and control SDK functionality to comply with GDPR (General Data Protection Regulation) and other privacy regulations.

Tracking Consent Management

trackingOptIn

Record explicit user consent for tracking by sending a GDPR opt-in event to Singular servers.

Method Signature:

Singular.trackingOptIn()

When to Use:

  • GDPR Compliance: Call when users explicitly consent to tracking in GDPR-regulated regions
  • Consent Recording: Marks users as having provided GDPR consent in Singular's systems
  • Default Behavior: Without this call, SDK continues tracking but doesn't specifically record consent
Kotlin Java
// User accepted tracking consent
Singular.trackingOptIn()

// Example: Call after consent dialog
fun onUserAcceptedTracking() {
    Singular.trackingOptIn()
    Log.d("GDPR", "User opted in to tracking")
}

Tracking Control Methods

stopAllTracking

Completely disable all SDK tracking activities for the current user on this device.

Method Signature:

Singular.stopAllTracking()

Critical Warning: This method permanently disables the SDK until resumeAllTracking() is called. The disabled state persists across app restarts and can only be reversed programmatically.

Behavior:

  • Immediate Effect: Stops all tracking, event reporting, and data collection instantly
  • Persistent State: Remains disabled even after app closes and reopens
  • No Automatic Reset: Must explicitly call resumeAllTracking() to re-enable
Kotlin Java
// User declined all tracking
Singular.stopAllTracking()

// Example: Handle user opt-out
fun onUserDeclinedTracking() {
    Singular.stopAllTracking()
    Log.d("Privacy", "All tracking stopped")
    
    // Optionally store preference
    saveUserTrackingPreference(false)
}

resumeAllTracking

Re-enable tracking after it was stopped with stopAllTracking() .

Method Signature:

Singular.resumeAllTracking()

Use Cases:

  • Consent Change: User changes privacy preferences and opts back into tracking
  • Privacy Settings: User updates consent through app settings menu
  • Regional Compliance: Re-enable tracking when user moves to non-regulated regions
Kotlin Java
// User opted back in to tracking
Singular.resumeAllTracking()

// Example: Handle consent update
fun onUserResumedTracking() {
    Singular.resumeAllTracking()
    Log.d("Privacy", "Tracking resumed")
    
    // Optionally update stored preference
    saveUserTrackingPreference(true)
}

isAllTrackingStopped

Check whether tracking has been disabled for the current user.

Method Signature:

public static boolean isAllTrackingStopped();

Returns:

  • true: Tracking is currently stopped via stopAllTracking()
  • false: Tracking is active (either never stopped or resumed)

Returns false if the SDK has not been initialized, regardless of any previous tracking state. Always confirm Singular.init() has completed before relying on this value, and treat false as "not stopped or not yet initialized" rather than "actively tracking."

Kotlin Java
// Check current tracking status
val isTrackingStopped = Singular.isAllTrackingStopped()

// Example: Display privacy status in settings
fun getPrivacyStatusText(): String {
    return if (Singular.isAllTrackingStopped()) {
        "Tracking: Disabled"
    } else {
        "Tracking: Enabled"
    }
}

// Example: Sync UI with tracking state
fun updatePrivacyToggle() {
    val isStopped = Singular.isAllTrackingStopped()
    privacyToggle.isChecked = !isStopped
    Log.d("Privacy", "Current tracking state: ${if (isStopped) "Stopped" else "Active"}")
}

Children's Privacy Protection

trackingUnder13

Notify Singular that the user is under 13 years old to comply with COPPA (Children's Online Privacy Protection Act) and other child privacy regulations.

Method Signature:

Singular.trackingUnder13()

Compliance Requirements:

  • COPPA Compliance: Required for apps that collect data from children under 13 in the United States
  • Age-Gated Content: Use when users identify themselves as under 13 during registration or age verification
  • Restricted Tracking: Limits data collection to comply with children's privacy protection laws
Kotlin Java
// User identified as under 13
Singular.trackingUnder13()

// Example: Call after age verification
fun onAgeVerified(userAge: Int) {
    if (userAge < 13) {
        Singular.trackingUnder13()
        Log.d("Privacy", "COPPA mode enabled for user under 13")
    }
}

Important: Call this method as early as possible after determining the user is under 13, ideally during app initialization or immediately after age verification. This ensures all subsequent tracking respects children's privacy regulations.


setLimitAdvertisingIdentifiers

Restrict the collection and use of advertising identifiers (GAID on Android) after SDK initialization for mixed audience apps.

Method Signature:

public static void setLimitAdvertisingIdentifiers(boolean enabled);

Parameters:

  • enabled: Pass true to limit advertising identifier collection; false to restore normal collection.

Do not confuse this with the config-time variant. The runtime method Singular.setLimitAdvertisingIdentifiers(boolean) requires a boolean argument, while the config-time method SingularConfig.withLimitAdvertisingIdentifiers() takes no arguments and enables the limit unconditionally. Calling the runtime method with no arguments will not compile.

Use Cases:

  • Mixed Audience Apps: Apps that serve both adults and children where age is determined after app launch
  • Dynamic Privacy Controls: Adjust tracking based on user actions or content being accessed
  • Runtime Restrictions: Apply advertising identifier limitations after initial SDK setup
Kotlin Java
// Limit advertising identifiers after initialization
Singular.setLimitAdvertisingIdentifiers(true)

// Example: Mixed audience app with age gate
fun onKidsModeSwitched(isKidsMode: Boolean) {
    if (isKidsMode) {
        Singular.setLimitAdvertisingIdentifiers(true)
        Log.d("Privacy", "Advertising identifiers limited for kids mode")
    }
}

// Example: Content-based restrictions
fun onViewingChildrensContent() {
    Singular.setLimitAdvertisingIdentifiers(true)
    Log.d("Privacy", "Ad identifiers restricted for children's content")
}

Configuration Alternative: You can also limit advertising identifiers during SDK initialization using SingularConfig.withLimitAdvertisingIdentifiers() if you know privacy requirements before the SDK starts.

Configuration Method

Set advertising identifier limitations during SDK initialization for apps that know privacy requirements upfront.

Kotlin Java
// Limit advertising identifiers at initialization
val config = SingularConfig("SDK_KEY", "SDK_SECRET")
    .withLimitAdvertisingIdentifiers()

Singular.init(applicationContext, config)

Implementation Best Practices

Complete Privacy Management Example

Implement comprehensive privacy controls that respect user preferences and comply with regulations.

Kotlin Java
class PrivacyManager(private val context: Context) {
    
    private val prefs = context.getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
    
    // Initialize privacy settings on app start
    fun initializePrivacy() {
        val hasUserConsent = getUserConsent()
        val allowDataSharing = getDataSharingPreference()
        val userAge = getUserAge()
        
        // Apply stored preferences
        if (hasUserConsent) {
            Singular.trackingOptIn()
            Singular.resumeAllTracking()
        } else {
            Singular.stopAllTracking()
        }
        
        // Set data sharing preference
        Singular.limitDataSharing(!allowDataSharing)
        
        // Handle children's privacy
        if (userAge > 0 && userAge < 13) {
            Singular.trackingUnder13()
            Singular.setLimitAdvertisingIdentifiers(true)
        }
        
        Log.d("Privacy", "Initialized: consent=$hasUserConsent, sharing=$allowDataSharing, age=$userAge")
    }
    
    // User accepts tracking via consent dialog
    fun userAcceptedTracking() {
        saveUserConsent(true)
        Singular.trackingOptIn()
        Singular.resumeAllTracking()
        Log.d("Privacy", "User accepted tracking")
    }
    
    // User declines tracking
    fun userDeclinedTracking() {
        saveUserConsent(false)
        Singular.stopAllTracking()
        Log.d("Privacy", "User declined tracking")
    }
    
    // User updates data sharing preference
    fun setDataSharingEnabled(enabled: Boolean) {
        saveDataSharingPreference(enabled)
        // Note: limitDataSharing uses inverse logic
        Singular.limitDataSharing(!enabled)
        Log.d("Privacy", "Data sharing: ${if (enabled) "Enabled" else "Limited"}")
    }
    
    // Handle age verification result
    fun onAgeVerified(age: Int) {
        saveUserAge(age)
        if (age < 13) {
            Singular.trackingUnder13()
            Singular.setLimitAdvertisingIdentifiers(true)
            Log.d("Privacy", "COPPA restrictions applied for user under 13")
        }
    }
    
    // Get current tracking status
    fun isTrackingEnabled(): Boolean {
        return !Singular.isAllTrackingStopped()
    }
    
    // Private helper methods
    private fun getUserConsent(): Boolean {
        return prefs.getBoolean("user_consent", false)
    }
    
    private fun saveUserConsent(consent: Boolean) {
        prefs.edit().putBoolean("user_consent", consent).apply()
    }
    
    private fun getDataSharingPreference(): Boolean {
        return prefs.getBoolean("data_sharing", false)
    }
    
    private fun saveDataSharingPreference(enabled: Boolean) {
        prefs.edit().putBoolean("data_sharing", enabled).apply()
    }
    
    private fun getUserAge(): Int {
        return prefs.getInt("user_age", 0)
    }
    
    private fun saveUserAge(age: Int) {
        prefs.edit().putInt("user_age", age).apply()
    }
}

Best Practices:

  • Persistent Storage: Save user preferences in SharedPreferences or secure storage
  • Early Initialization: Apply privacy settings before SDK initialization when possible
  • UI Sync: Keep settings UI synchronized with actual SDK state using isAllTrackingStopped()
  • Clear Communication: Provide clear, accessible privacy controls in app settings
  • Age Verification: Implement robust age verification for apps targeting children
  • Combined Controls: For users under 13, apply both trackingUnder13() and setLimitAdvertisingIdentifiers(true)