最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin5浏览0评论

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 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, 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>

So I guess that just having them is enough for the check, perhaps...

It seems it has a bug though, because when I change again the choice in the dialog, nothing seems to change...

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 ?

  2. What should I do about the US rules, as I couldn't even find where the choice is saved by the user? What I've found about the weird SharedPreferences file seems unreliable...

  3. If I choose to be strict there, would it affect all countries in the world, or only those that are affected by these rules?

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论