From ab0a49c45c1e1b3acdfda3992e9fd372d41ffd68 Mon Sep 17 00:00:00 2001 From: Mobile Ads Developer Relations Date: Wed, 27 May 2026 14:02:22 -0700 Subject: [PATCH] Filter age-restricted requests for Pangle SDK when new `AgeRestrictedTreatment` is set to `CHILD`. PiperOrigin-RevId: 922333598 --- ThirdPartyAdapters/pangle/CHANGELOG.md | 3 + ThirdPartyAdapters/pangle/build.gradle | 2 +- ThirdPartyAdapters/pangle/gradlew | 0 ThirdPartyAdapters/pangle/pangle/build.gradle | 6 +- .../ads/mediation/pangle/PangleConstants.java | 4 +- .../pangle/PangleMediationAdapterTest.kt | 83 +++++++++++++++++++ 6 files changed, 93 insertions(+), 5 deletions(-) mode change 100644 => 100755 ThirdPartyAdapters/pangle/gradlew diff --git a/ThirdPartyAdapters/pangle/CHANGELOG.md b/ThirdPartyAdapters/pangle/CHANGELOG.md index ab41e5125..4b99cc69d 100644 --- a/ThirdPartyAdapters/pangle/CHANGELOG.md +++ b/ThirdPartyAdapters/pangle/CHANGELOG.md @@ -1,5 +1,8 @@ ## Pangle Android Mediation Adapter Changelog +#### Next Version +- Filter age-restricted requests for Pangle SDK when new `AgeRestrictedTreatment` is set to `CHILD`. + #### Version 8.0.0.5.0 - Verified compatibility with Pangle SDK version 8.0.0.5. diff --git a/ThirdPartyAdapters/pangle/build.gradle b/ThirdPartyAdapters/pangle/build.gradle index 98c05e4ba..04be82319 100644 --- a/ThirdPartyAdapters/pangle/build.gradle +++ b/ThirdPartyAdapters/pangle/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { - kotlinVersion = '2.1.10' + kotlinVersion = '2.3.0' } repositories { google() diff --git a/ThirdPartyAdapters/pangle/gradlew b/ThirdPartyAdapters/pangle/gradlew old mode 100644 new mode 100755 diff --git a/ThirdPartyAdapters/pangle/pangle/build.gradle b/ThirdPartyAdapters/pangle/pangle/build.gradle index 118866b22..e5b56b74c 100644 --- a/ThirdPartyAdapters/pangle/pangle/build.gradle +++ b/ThirdPartyAdapters/pangle/pangle/build.gradle @@ -122,9 +122,9 @@ dependencies { implementation 'androidx.annotation:annotation:1.5.0' // Check for a 'useNextGenGma' flag to use the next generation GMA SDK. if (project.hasProperty('useNextGenGma')) { - implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.0.1' + implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.1.0' } else { - implementation 'com.google.android.gms:play-services-ads:25.2.0' + implementation 'com.google.android.gms:play-services-ads:25.3.0' } testImplementation 'androidx.core:core-ktx:1.8.0' @@ -135,7 +135,7 @@ dependencies { testImplementation "com.google.truth:truth:1.2.0" testImplementation 'junit:junit:4.13.2' testImplementation 'com.google.testparameterinjector:test-parameter-injector:1.18' - testImplementation 'org.jetbrains.kotlin:kotlin-stdlib:2.1.10' + testImplementation 'org.jetbrains.kotlin:kotlin-stdlib:2.3.0' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.1.0' testImplementation 'org.robolectric:robolectric:4.14' } diff --git a/ThirdPartyAdapters/pangle/pangle/src/main/java/com/google/ads/mediation/pangle/PangleConstants.java b/ThirdPartyAdapters/pangle/pangle/src/main/java/com/google/ads/mediation/pangle/PangleConstants.java index 434fa4e8f..58c0d636c 100644 --- a/ThirdPartyAdapters/pangle/pangle/src/main/java/com/google/ads/mediation/pangle/PangleConstants.java +++ b/ThirdPartyAdapters/pangle/pangle/src/main/java/com/google/ads/mediation/pangle/PangleConstants.java @@ -17,6 +17,7 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import com.google.android.gms.ads.AdError; +import com.google.android.gms.ads.AgeRestrictedTreatment; import com.google.android.gms.ads.MobileAds; import com.google.android.gms.ads.RequestConfiguration; import java.lang.annotation.Retention; @@ -94,7 +95,8 @@ public static boolean isChildUser() { return requestConfiguration.getTagForChildDirectedTreatment() == RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE || requestConfiguration.getTagForUnderAgeOfConsent() - == RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_TRUE; + == RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_TRUE + || requestConfiguration.getAgeRestrictedTreatment() == AgeRestrictedTreatment.CHILD; } /** A private constructor since this is a utility class which should not be instantiated. */ diff --git a/ThirdPartyAdapters/pangle/pangle/src/test/kotlin/com/google/ads/mediation/pangle/PangleMediationAdapterTest.kt b/ThirdPartyAdapters/pangle/pangle/src/test/kotlin/com/google/ads/mediation/pangle/PangleMediationAdapterTest.kt index 8ef3d23e2..d9c26dd1b 100644 --- a/ThirdPartyAdapters/pangle/pangle/src/test/kotlin/com/google/ads/mediation/pangle/PangleMediationAdapterTest.kt +++ b/ThirdPartyAdapters/pangle/pangle/src/test/kotlin/com/google/ads/mediation/pangle/PangleMediationAdapterTest.kt @@ -21,6 +21,9 @@ import com.google.ads.mediation.pangle.utils.mockPangleSdkInitializationSuccess import com.google.android.gms.ads.AdError import com.google.android.gms.ads.AdFormat import com.google.android.gms.ads.AdSize +import com.google.android.gms.ads.AgeRestrictedTreatment +import com.google.android.gms.ads.MobileAds +import com.google.android.gms.ads.RequestConfiguration import com.google.android.gms.ads.mediation.InitializationCompleteCallback import com.google.android.gms.ads.mediation.MediationAdLoadCallback import com.google.android.gms.ads.mediation.MediationAppOpenAd @@ -104,6 +107,16 @@ class PangleMediationAdapterTest { // Resetting the PA Consent Information to their default value. PangleMediationAdapter.setPAConsent(PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT) + val requestConfiguration = + RequestConfiguration.Builder() + .setTagForChildDirectedTreatment( + RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_UNSPECIFIED + ) + .setTagForUnderAgeOfConsent(RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_UNSPECIFIED) + .setAgeRestrictedTreatment(AgeRestrictedTreatment.UNSPECIFIED) + .build() + MobileAds.setRequestConfiguration(requestConfiguration) + pangleMediationAdapter = PangleMediationAdapter(pangleInitializer, pangleSdkWrapper, pangleFactory) } @@ -161,6 +174,32 @@ class PangleMediationAdapterTest { assertThat(error.domain).isEqualTo(PANGLE_SDK_ERROR_DOMAIN) } + @Test + fun collectSignals_withAgeRestrictedTreatmentChild_callsOnFailure() { + val requestConfiguration = + RequestConfiguration.Builder() + .setTagForChildDirectedTreatment( + RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_UNSPECIFIED + ) + .setTagForUnderAgeOfConsent(RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_UNSPECIFIED) + .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD) + .build() + MobileAds.setRequestConfiguration(requestConfiguration) + val signalCallbacks: SignalCallbacks = mock() + + pangleMediationAdapter.collectSignals( + RtbSignalData(context, emptyList(), bundleOf(), AdSize(1, 1)), + signalCallbacks, + ) + + val errorCaptor = argumentCaptor() + verify(signalCallbacks).onFailure(errorCaptor.capture()) + val error = errorCaptor.firstValue + assertThat(error.code).isEqualTo(PangleConstants.ERROR_CHILD_USER) + assertThat(error.domain).isEqualTo(PangleConstants.ERROR_DOMAIN) + assertThat(error.message).isEqualTo(PangleConstants.ERROR_MSG_CHILD_USER) + } + @Test fun initialize_ifAppIdsAreMissing_callsFailureCallback() { // Create server parameters without app ID. @@ -220,6 +259,28 @@ class PangleMediationAdapterTest { verify(initializationCompleteCallback).onInitializationFailed(PANGLE_INIT_FAILURE_MESSAGE) } + @Test + fun initialize_withAgeRestrictedTreatmentChild_callsFailureCallback() { + val requestConfiguration = + RequestConfiguration.Builder() + .setTagForChildDirectedTreatment( + RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_UNSPECIFIED + ) + .setTagForUnderAgeOfConsent(RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_UNSPECIFIED) + .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD) + .build() + MobileAds.setRequestConfiguration(requestConfiguration) + + pangleMediationAdapter.initialize( + context, + initializationCompleteCallback, + listOf(buildProperMediationConfig()), + ) + + verify(initializationCompleteCallback) + .onInitializationFailed(PangleConstants.ERROR_MSG_CHILD_USER) + } + @Test fun getVersionInfo_ifAdapterVersionHasLessThanFourParts_returnsZeros() { // "3.1.4" is invalid because adapter version should contain at least four parts delimited by @@ -285,6 +346,28 @@ class PangleMediationAdapterTest { verify(bannerAd).render(bannerAdConfig) } + @Test + fun loadBannerAd_withAgeRestrictedTreatmentChild_callsOnFailure() { + val requestConfiguration = + RequestConfiguration.Builder() + .setTagForChildDirectedTreatment( + RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_UNSPECIFIED + ) + .setTagForUnderAgeOfConsent(RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_UNSPECIFIED) + .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD) + .build() + MobileAds.setRequestConfiguration(requestConfiguration) + + pangleMediationAdapter.loadBannerAd(bannerAdConfig, bannerAdLoadCallback) + + val errorCaptor = argumentCaptor() + verify(bannerAdLoadCallback).onFailure(errorCaptor.capture()) + val error = errorCaptor.firstValue + assertThat(error.code).isEqualTo(PangleConstants.ERROR_CHILD_USER) + assertThat(error.domain).isEqualTo(PangleConstants.ERROR_DOMAIN) + assertThat(error.message).isEqualTo(PangleConstants.ERROR_MSG_CHILD_USER) + } + @Test fun loadInterstitialAd_rendersInterstitialAd() { pangleMediationAdapter.loadInterstitialAd(interstitialAdConfig, interstitialAdLoadCallback)