android - How to setup ad mediation sources to use GDPR and COPPA (US) user choices, got from Google's UMP SDK? - Stack

admin2025-04-23  4

Background

I've used Admob for showing ads for many years on my apps. At some point I had to add the new GDPR consent dialog so that it could be used according to the new EU rules, and Admob SDK automatically uses its results.

This worked fine as I used the SDK by Google and Google already owns Admob to use this SDK.

The problem

Recently I've added mediation support, to include various ad-sources in order to have some competition between them:

Sadly, as opposed to Admob, very few ad-sources automatically get the value out of what the user has chosen. In fact I've found only very few that do it: AppLovin and probably also Mintegral (needs extra code before initialization, but that's it).

For most of the ad-sources, it says to pass the data of whether the user has accepted or not, in code, to the SDK.

Not only that, but in addition to the GDPR rules, there are new ones for the US itself (COPPA):

Thing is, I can't find what is the value I need to set for them, not of GDPR for the various ad-networks, and also not of the new US rule.

Even Applovin (and probably Mintegral too) doesn't check the US part of the SDK.

What I've found

In the past, just for Analytics to see the situation, I've used these:

For example, I created this:

@WorkerThread
fun isGDPR(context: Context): Boolean? {
    val prefs = PreferenceUtil.getDefaultSharedPreferences(context)
    val gdpr =
            if (prefs.contains("IABTCF_gdprApplies"))
                prefs.getInt("IABTCF_gdprApplies", 0)
            else return null
    return gdpr == 1
}

@WorkerThread
fun canShowPersonalizedAds(context: Context): Boolean {
    val prefs = PreferenceUtil.getDefaultSharedPreferences(context)
    //.md#in-app-details
    //;ref_topic=9756841
    val purposeConsent = prefs.getString("IABTCF_PurposeConsents", "") ?: ""
    val vendorConsent = prefs.getString("IABTCF_VendorConsents", "") ?: ""
    val vendorLI = prefs.getString("IABTCF_VendorLegitimateInterests", "") ?: ""
    val purposeLI = prefs.getString("IABTCF_PurposeLegitimateInterests", "") ?: ""
    val googleId = 755
    val hasGoogleVendorConsent = hasAttribute(vendorConsent, index = googleId)
    val hasGoogleVendorLI = hasAttribute(vendorLI, index = googleId)

    return hasConsentFor(listOf(1, 3, 4), purposeConsent, hasGoogleVendorConsent)
            && hasConsentOrLegitimateInterestFor(listOf(2, 7, 9, 10), purposeConsent, purposeLI, hasGoogleVendorConsent, hasGoogleVendorLI)
}

// Check if a binary string has a "1" at position "index" (1-based)
private fun hasAttribute(input: String, index: Int): Boolean {
    return input.length >= index && input[index - 1] == '1'
}

// Check if consent is given for a list of purposes
private fun hasConsentFor(purposes: List<Int>, purposeConsent: String, hasVendorConsent: Boolean): Boolean {
    return purposes.all { p -> hasAttribute(purposeConsent, p) } && hasVendorConsent
}

// Check if a vendor either has consent or legitimate interest for a list of purposes
private fun hasConsentOrLegitimateInterestFor(purposes: List<Int>, purposeConsent: String, purposeLI: String, hasVendorConsent: Boolean, hasVendorLI: Boolean): Boolean {
    return purposes.all { p ->
        (hasVendorLI && hasAttribute(purposeLI, p)) ||
                (hasVendorConsent && hasAttribute(purposeConsent, p))
    }
}

And also this:

/**
 * Checks the stored IABTCF configuration and returns one of the values defined in [AdConfiguration],
 * based on the necessary minimum consent/interest defined here: 
 */
fun detectAdConfiguration(context: Context): AdConfiguration {
    // default string for "no consent", used in cases where no configuration has previously been stored
    val defaultPurposeString = "0000000000"
    // IABTCF strings are stored in SharedPreferences
    val sharedPrefs = PreferenceUtil.getDefaultSharedPreferences(context)
    // 
    //limited ads: nothing for 1, consent/legitimate:  2,7,9,10
    //non personalized ads: Consent: 1  , consent/legitimate:  2,7,9,10
    //personalized: consent: 1,3,4 consent/legitimate:  2,7,9,10
    //
    // relevant strings are those for purpose consent or legitimate interest, as well as vendors
    val tcConsentString = sharedPrefs
            .getString("IABTCF_PurposeConsents", defaultPurposeString) ?: defaultPurposeString
    val tcInterestString = sharedPrefs
            .getString("IABTCF_PurposeLegitimateInterests", defaultPurposeString)
            ?: defaultPurposeString
//                Log.d("AppLog", "detectAdConfiguration tcConsentString:$tcConsentString tcInterestString:$tcInterestString tcVendorString:$tcVendorString ")
    // in any case we need at least legitimate interest for purposes N = 2, 7, 9 and 10,
    // stored in positions N-1 of either purpose string:
    val sufficientInterest =
            (tcConsentString.getOrNull(1) == '1' || tcInterestString.getOrNull(1) == '1') &&
                    (tcConsentString.getOrNull(6) == '1' || tcInterestString.getOrNull(6) == '1') &&
                    (tcConsentString.getOrNull(8) == '1' || tcInterestString.getOrNull(8) == '1') &&
                    (tcConsentString.getOrNull(9) == '1' || tcInterestString.getOrNull(9) == '1')
    if (!sufficientInterest) {
        return AdConfiguration.None
    }
    val tcVendorString = sharedPrefs.getString("IABTCF_VendorConsents", "0") ?: "0"
//        Log.d("AppLog", "tcVendorString:$tcVendorString")
    // TODO vendor configuration is variable, so needs to be defined by the individual developer
    //   - run app and make sure that a valid configuration is stored
    //   - have the app log the value of [tcVendorString], then copy that value to the following line
    //   - repeat if ad configuration changes, perhaps make this value available via remote configuration instead
    val goodVendorConfiguration = context.getString(R.string.ad_consent_expected_good_vendor_configuration)
    // if the stored string is shorter than what is necessary, at least some vendors will not be
    // configured properly.
    if (tcVendorString.length < goodVendorConfiguration.length) {
        return AdConfiguration.Unclear
    }
    // we need consent for the following purposes N, stored in positions N-1 of the consent string:
    //   1, 3 and 4 to show all ads
    //   1 to show non-personalized ads
    //   no consent to show limited ads
    val maxAdDisplayConfiguration = when {
        tcConsentString.getOrNull(0) != '1' -> AdConfiguration.Limited
        tcConsentString.getOrNull(2) == '1' && tcConsentString.getOrNull(3) == '1' -> AdConfiguration.All
        else -> AdConfiguration.NonPersonalized
    }
    // build a regex that must match all '1' but not the '0' characters in goodVendorConfiguration,
    // and allows this configuration to be shorter than the string it is compared with
    val vendorRegex = Regex(goodVendorConfiguration.replace("0", ".").plus(".*"))
    //if the regex matches, at least some ads should be served; if not, vendor string is unclear
    return if (vendorRegex.matches(tcVendorString)) {
        maxAdDisplayConfiguration
    } else {
        return AdConfiguration.Unclear
    }
}

But I think I can only use these for detection for Admob (because "755" is of Admob, as shown here, but there are others, and maybe they have other rules) or just accepting all, and not specifically for the various ad-sources. Also it's only for GDPR and not also of US rules.

I also tried to ask Admob and ad-sourecs companies for help about this, but they just pass the responsibility for this to the other side, and I'm in the middle...

Later I've tried to check what happens in the sharedPreferences (using registerOnSharedPreferenceChangeListener) . Seems that when IABTCF_gdprApplies key points to integer 1, it means it's subject to GDPR rules.

As for US regulations, I can see that "IABGPP_GppSID and IABGPP_HDR_GppString keys" are mentioned in the docs:

But I don't understand how to read the details about them. I can see that if I do choose "Don't sell or share my data", I get these added to the default SharedPreferences:

<string name="IABGPP_GppSID">7</string>
<string name="IABGPP_HDR_GppString">DBABL~BVQVAAAAAg</string>

When I choose to allow to share, it changes the value of "IABGPP_HDR_GppString" to "DBABL~BVQqAAAAAg" , so it's a difference of just one letter, from "V" (don't share) to "q" (allow sharing).

What I don't understand is how reliable it is, if I can just compare to one of these strings and that's it, or maybe I need to check a single character or bitwise operation instead.

I could make it as simple as this function, for example:

fun isDataShareAllowed(context: Context): Boolean {
    val defaultSharedPreferences = PreferenceUtil.getDefaultSharedPreferences(context)
    val gppString = defaultSharedPreferences.getString("IABGPP_HDR_GppString", null) ?: return true
    //        DBABL~BVQVAAAAAg - don't share
    //        DBABL~BVQqAAAAAg - allow share
    return gppString == "DBABL~BVQqAAAAAg"
}

The questions

  1. Given any ad-source X from the list of supported ad-sources of Admob medaition, is it ok to use one of the functions I've mentioned above for GDPR ? Should I just change the "755" for them based on the website? What should I do for companies that aren't on the list on the website?

  2. What should I do about the US rules? Is it ok to just compare to one of the strings, as I did? Or should I make it work differently, such as checking a single letter or perform a special bitwise operation?

  3. If I choose to be strict there (meaning assume all users chose not to share data), would it affect all countries in the world, or only those that are affected by the US regulations?


EDIT: at least for CCPA, we can probably use something like that:

gradle file:

// .iabgpp/iabgpp-encoder   
implementation("com.iabgpp:iabgpp-encoder:3.2.3")
//         
//    
//    
/** @return returns null if not initialized yet, or initialized and it's ok to share data.
 * When returned true, it means all kinds of data sharing is allowed:SALE_OPT_OUT, SHARING_OPT_OUT, TARGETED_ADVERTISING_OPT_OUT.
 * Otherwise returns false*/
fun isDataShareAllowed(context: Context): Boolean? {
    val defaultSharedPreferences = PreferenceUtil.getDefaultSharedPreferences(context)
    val gppString = defaultSharedPreferences.getString("IABGPP_HDR_GppString", null) ?: return null
    //        Log.d("AppLog", "trying to parse CCPA data of: $encodedString (expected allow sharing?${gppString != "DBABL~BVQVAAAAAg"})")
    //        DBABL~BVQVAAAAAg - don't share
    //        DBABL~BVQqAAAAAg - allow share
    when(gppString){
        "DBABL~BVQqAAAAAg" -> return true
        "DBABL~BVQVAAAAAg" -> return false
    }
    CrashlyticsUtil.logException("AdConsentUtil isDataShareAllowed unknown gppString detected:$gppString, so using fallback")
    val model = GppModel(gppString)
    val usNatSection1 = model.getSection(UsNat.ID)
    val fieldsToCheck = arrayOf(UsNatField.SALE_OPT_OUT, UsNatField.SHARING_OPT_OUT, UsNatField.TARGETED_ADVERTISING_OPT_OUT)
    for (fieldName in fieldsToCheck) {
        if (usNatSection1.hasField(fieldName)) {
            val fieldValue = usNatSection1.getFieldValue(fieldName) ?: continue
            if (fieldValue == 2)
                continue
            return false
        }
    }
    return true
}

Background

I've used Admob for showing ads for many years on my apps. At some point I had to add the new GDPR consent dialog so that it could be used according to the new EU rules, and Admob SDK automatically uses its results.

https://developers.google.com/admob/android/privacy

This worked fine as I used the SDK by Google and Google already owns Admob to use this SDK.

The problem

Recently I've added mediation support, to include various ad-sources in order to have some competition between them:

https://developers.google.com/admob/android/choose-networks

Sadly, as opposed to Admob, very few ad-sources automatically get the value out of what the user has chosen. In fact I've found only very few that do it: AppLovin and probably also Mintegral (needs extra code before initialization, but that's it).

For most of the ad-sources, it says to pass the data of whether the user has accepted or not, in code, to the SDK.

Not only that, but in addition to the GDPR rules, there are new ones for the US itself (COPPA):

https://developers.google.com/admob/android/privacy/us-iab-support

Thing is, I can't find what is the value I need to set for them, not of GDPR for the various ad-networks, and also not of the new US rule.

Even Applovin (and probably Mintegral too) doesn't check the US part of the SDK.

What I've found

In the past, just for Analytics to see the situation, I've used these:

  • https://github.com/bocops/UMP-workarounds
  • https://stackoverflow.com/a/68310602/878126

For example, I created this:

@WorkerThread
fun isGDPR(context: Context): Boolean? {
    val prefs = PreferenceUtil.getDefaultSharedPreferences(context)
    val gdpr =
            if (prefs.contains("IABTCF_gdprApplies"))
                prefs.getInt("IABTCF_gdprApplies", 0)
            else return null
    return gdpr == 1
}

@WorkerThread
fun canShowPersonalizedAds(context: Context): Boolean {
    val prefs = PreferenceUtil.getDefaultSharedPreferences(context)
    //https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
    //https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
    val purposeConsent = prefs.getString("IABTCF_PurposeConsents", "") ?: ""
    val vendorConsent = prefs.getString("IABTCF_VendorConsents", "") ?: ""
    val vendorLI = prefs.getString("IABTCF_VendorLegitimateInterests", "") ?: ""
    val purposeLI = prefs.getString("IABTCF_PurposeLegitimateInterests", "") ?: ""
    val googleId = 755
    val hasGoogleVendorConsent = hasAttribute(vendorConsent, index = googleId)
    val hasGoogleVendorLI = hasAttribute(vendorLI, index = googleId)

    return hasConsentFor(listOf(1, 3, 4), purposeConsent, hasGoogleVendorConsent)
            && hasConsentOrLegitimateInterestFor(listOf(2, 7, 9, 10), purposeConsent, purposeLI, hasGoogleVendorConsent, hasGoogleVendorLI)
}

// Check if a binary string has a "1" at position "index" (1-based)
private fun hasAttribute(input: String, index: Int): Boolean {
    return input.length >= index && input[index - 1] == '1'
}

// Check if consent is given for a list of purposes
private fun hasConsentFor(purposes: List<Int>, purposeConsent: String, hasVendorConsent: Boolean): Boolean {
    return purposes.all { p -> hasAttribute(purposeConsent, p) } && hasVendorConsent
}

// Check if a vendor either has consent or legitimate interest for a list of purposes
private fun hasConsentOrLegitimateInterestFor(purposes: List<Int>, purposeConsent: String, purposeLI: String, hasVendorConsent: Boolean, hasVendorLI: Boolean): Boolean {
    return purposes.all { p ->
        (hasVendorLI && hasAttribute(purposeLI, p)) ||
                (hasVendorConsent && hasAttribute(purposeConsent, p))
    }
}

And also this:

/**
 * Checks the stored IABTCF configuration and returns one of the values defined in [AdConfiguration],
 * based on the necessary minimum consent/interest defined here: https://support.google.com/admob/answer/9760862
 */
fun detectAdConfiguration(context: Context): AdConfiguration {
    // default string for "no consent", used in cases where no configuration has previously been stored
    val defaultPurposeString = "0000000000"
    // IABTCF strings are stored in SharedPreferences
    val sharedPrefs = PreferenceUtil.getDefaultSharedPreferences(context)
    // https://developers.google.com/admob/android/privacy/ad-serving-modes
    //limited ads: nothing for 1, consent/legitimate:  2,7,9,10
    //non personalized ads: Consent: 1  , consent/legitimate:  2,7,9,10
    //personalized: consent: 1,3,4 consent/legitimate:  2,7,9,10
    //
    // relevant strings are those for purpose consent or legitimate interest, as well as vendors
    val tcConsentString = sharedPrefs
            .getString("IABTCF_PurposeConsents", defaultPurposeString) ?: defaultPurposeString
    val tcInterestString = sharedPrefs
            .getString("IABTCF_PurposeLegitimateInterests", defaultPurposeString)
            ?: defaultPurposeString
//                Log.d("AppLog", "detectAdConfiguration tcConsentString:$tcConsentString tcInterestString:$tcInterestString tcVendorString:$tcVendorString ")
    // in any case we need at least legitimate interest for purposes N = 2, 7, 9 and 10,
    // stored in positions N-1 of either purpose string:
    val sufficientInterest =
            (tcConsentString.getOrNull(1) == '1' || tcInterestString.getOrNull(1) == '1') &&
                    (tcConsentString.getOrNull(6) == '1' || tcInterestString.getOrNull(6) == '1') &&
                    (tcConsentString.getOrNull(8) == '1' || tcInterestString.getOrNull(8) == '1') &&
                    (tcConsentString.getOrNull(9) == '1' || tcInterestString.getOrNull(9) == '1')
    if (!sufficientInterest) {
        return AdConfiguration.None
    }
    val tcVendorString = sharedPrefs.getString("IABTCF_VendorConsents", "0") ?: "0"
//        Log.d("AppLog", "tcVendorString:$tcVendorString")
    // TODO vendor configuration is variable, so needs to be defined by the individual developer
    //   - run app and make sure that a valid configuration is stored
    //   - have the app log the value of [tcVendorString], then copy that value to the following line
    //   - repeat if ad configuration changes, perhaps make this value available via remote configuration instead
    val goodVendorConfiguration = context.getString(R.string.ad_consent_expected_good_vendor_configuration)
    // if the stored string is shorter than what is necessary, at least some vendors will not be
    // configured properly.
    if (tcVendorString.length < goodVendorConfiguration.length) {
        return AdConfiguration.Unclear
    }
    // we need consent for the following purposes N, stored in positions N-1 of the consent string:
    //   1, 3 and 4 to show all ads
    //   1 to show non-personalized ads
    //   no consent to show limited ads
    val maxAdDisplayConfiguration = when {
        tcConsentString.getOrNull(0) != '1' -> AdConfiguration.Limited
        tcConsentString.getOrNull(2) == '1' && tcConsentString.getOrNull(3) == '1' -> AdConfiguration.All
        else -> AdConfiguration.NonPersonalized
    }
    // build a regex that must match all '1' but not the '0' characters in goodVendorConfiguration,
    // and allows this configuration to be shorter than the string it is compared with
    val vendorRegex = Regex(goodVendorConfiguration.replace("0", ".").plus(".*"))
    //if the regex matches, at least some ads should be served; if not, vendor string is unclear
    return if (vendorRegex.matches(tcVendorString)) {
        maxAdDisplayConfiguration
    } else {
        return AdConfiguration.Unclear
    }
}

But I think I can only use these for detection for Admob (because "755" is of Admob, as shown here, but there are others, and maybe they have other rules) or just accepting all, and not specifically for the various ad-sources. Also it's only for GDPR and not also of US rules.

I also tried to ask Admob and ad-sourecs companies for help about this, but they just pass the responsibility for this to the other side, and I'm in the middle...

Later I've tried to check what happens in the sharedPreferences (using registerOnSharedPreferenceChangeListener) . Seems that when IABTCF_gdprApplies key points to integer 1, it means it's subject to GDPR rules.

As for US regulations, I can see that "IABGPP_GppSID and IABGPP_HDR_GppString keys" are mentioned in the docs:

https://developers.google.com/admob/android/privacy/us-iab-support#read-consent-choices

But I don't understand how to read the details about them. I can see that if I do choose "Don't sell or share my data", I get these added to the default SharedPreferences:

<string name="IABGPP_GppSID">7</string>
<string name="IABGPP_HDR_GppString">DBABL~BVQVAAAAAg</string>

When I choose to allow to share, it changes the value of "IABGPP_HDR_GppString" to "DBABL~BVQqAAAAAg" , so it's a difference of just one letter, from "V" (don't share) to "q" (allow sharing).

What I don't understand is how reliable it is, if I can just compare to one of these strings and that's it, or maybe I need to check a single character or bitwise operation instead.

I could make it as simple as this function, for example:

fun isDataShareAllowed(context: Context): Boolean {
    val defaultSharedPreferences = PreferenceUtil.getDefaultSharedPreferences(context)
    val gppString = defaultSharedPreferences.getString("IABGPP_HDR_GppString", null) ?: return true
    //        DBABL~BVQVAAAAAg - don't share
    //        DBABL~BVQqAAAAAg - allow share
    return gppString == "DBABL~BVQqAAAAAg"
}

The questions

  1. Given any ad-source X from the list of supported ad-sources of Admob medaition, is it ok to use one of the functions I've mentioned above for GDPR ? Should I just change the "755" for them based on the website? What should I do for companies that aren't on the list on the website?

  2. What should I do about the US rules? Is it ok to just compare to one of the strings, as I did? Or should I make it work differently, such as checking a single letter or perform a special bitwise operation?

  3. If I choose to be strict there (meaning assume all users chose not to share data), would it affect all countries in the world, or only those that are affected by the US regulations?


EDIT: at least for CCPA, we can probably use something like that:

gradle file:

// https://search.maven.org/artifact/com.iabgpp/iabgpp-encoder   https://github.com/IABTechLab/iabgpp-java
implementation("com.iabgpp:iabgpp-encoder:3.2.3")
//        https://groups.google.com/g/google-admob-ads-sdk/c/cibpBrhtiR4 https://stackoverflow.com/q/79371638/878126
//    https://developers.google.com/admob/android/privacy/us-iab-support#read-consent-choices
//    https://github.com/bocops/UMP-workarounds/issues/14#issuecomment-2736670233
/** @return returns null if not initialized yet, or initialized and it's ok to share data.
 * When returned true, it means all kinds of data sharing is allowed:SALE_OPT_OUT, SHARING_OPT_OUT, TARGETED_ADVERTISING_OPT_OUT.
 * Otherwise returns false*/
fun isDataShareAllowed(context: Context): Boolean? {
    val defaultSharedPreferences = PreferenceUtil.getDefaultSharedPreferences(context)
    val gppString = defaultSharedPreferences.getString("IABGPP_HDR_GppString", null) ?: return null
    //        Log.d("AppLog", "trying to parse CCPA data of: $encodedString (expected allow sharing?${gppString != "DBABL~BVQVAAAAAg"})")
    //        DBABL~BVQVAAAAAg - don't share
    //        DBABL~BVQqAAAAAg - allow share
    when(gppString){
        "DBABL~BVQqAAAAAg" -> return true
        "DBABL~BVQVAAAAAg" -> return false
    }
    CrashlyticsUtil.logException("AdConsentUtil isDataShareAllowed unknown gppString detected:$gppString, so using fallback")
    val model = GppModel(gppString)
    val usNatSection1 = model.getSection(UsNat.ID)
    val fieldsToCheck = arrayOf(UsNatField.SALE_OPT_OUT, UsNatField.SHARING_OPT_OUT, UsNatField.TARGETED_ADVERTISING_OPT_OUT)
    for (fieldName in fieldsToCheck) {
        if (usNatSection1.hasField(fieldName)) {
            val fieldValue = usNatSection1.getFieldValue(fieldName) ?: continue
            if (fieldValue == 2)
                continue
            return false
        }
    }
    return true
}
Share Improve this question edited Apr 17 at 13:31 android developer asked Jan 20 at 14:26 android developerandroid developer 116k162 gold badges781 silver badges1.3k bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2 +250

The questions

Question 1

> 1) Given any ad-source X from the list of supported ad-sources of Admob
> medaition, is it ok to use one of the functions I've mentioned above
> for GDPR ? Should I just change the "755" for them based on the
> website? What should I do for companies that aren't on the list on the
> website?

Replace the vendor ID (e.g., 755 for Google) with the specific ID of the ad network in question.

To access the complete and most up-to-date list of vendor IDs, you can visit the official IAB Europe TCF Vendor List page: see here. See also here: https://www.uniconsent.com/iab-tcf-vendor-list

Question 2

> 2) What should I do about the US rules? Is it ok to just compare to
> one of the strings, as I did? Or should I make it work differently,
> such as checking a single letter or perform a special bitwise
> operation?

I think that it's fine to compare the strings. E.g. you can check for the string if it signifies that the user opted out. But it would actually be better to do a full processing of the string. See: github.com/InteractiveAdvertisingBureau/USPrivacy/blob/master/… . A bitwise operation isn’t required for CCPA string parsing. Etracting characters via usPrivacyString[index] is sufficient.

Question 3

> If I choose to be strict there (meaning assume all users chose not to
> share data), would it affect all countries in the world, or only those
> that are affected by the US regulations?

Applying COPPA settings might affect all users globally. To prevent unintended consequences, might be good to verify the user's location or the presence of "IABGPP_GppSID" before enforcing COPPA restrictions.

Additional references & Information:

  1. Mobile Ads v2.0 - Admob + UMP SDK implementation tutorial - Unity 2023
  2. https://iabeurope.eu/vendor-list-tcf/
  3. How to implement UMP SDK correctly for eu consent?
  4. How do you forward user consent (GDPR) to mediation partner Meta?
  5. https://www.uniconsent.com/iab-tcf-vendor-list

Update (February 24, 2025)

Please have a look at https://github.com/InteractiveAdvertisingBureau/USPrivacy/blob/master/CCPA/US%20Privacy%20String.md in relation to CCPA and the US Privacy String Format.

转载请注明原文地址:http://anycun.com/QandA/1745363373a90638.html