Skip to content

v5.0.0 — consent-safe mediation and production hardening

Choose a tag to compare

@faizahmaddae faizahmaddae released this 20 Jul 13:17
· 8 commits to main since this release
78802a1

A post-release adversarial audit of 4.0.0 plus a mediation-consent lifecycle redesign. The major is driven by mediation-privacy correctness: consent forwarding now runs before MobileAds.initialize() and fails closed by default, and the conceptually-invalid deferMediationInit is removed.

📦 pub.dev: https://pub.dev/packages/ad_flow/versions/5.0.0

Breaking changes

  • Removed AdFlowConfig.deferMediationInit and AdSdk.disableMediationInitialization(). Both drove the plugin's session-wide disable of Google mediation (an A/B-testing tool), not a defer/resume — conceptually invalid and revenue-harming. AdSdk removal affects only direct seam implementers; FakeAdSdk/GmaAdSdk are updated.
  • forwardConsent runs BEFORE MobileAds.initialize() and fails CLOSED by default. A failed/timed-out forward means the GMA SDK is not initialized and mediation-capable loads block with AdBlockReason.consentNotForwarded (retried in the background); init + serving recover when forwarding succeeds. UI is never blocked — initialize() returns immediately. Opt out only via MediationConsentFailurePolicy.unsafeFailOpen.
  • AdBlockReason gained consentNotForwarded — exhaustive switches need the new case (or a wildcard).

Added

  • AdFlow.initialize(forwardConsent:) — the fail-closed, before-init consent-forwarding barrier for networks that don't read the IAB TCF string themselves. Serialized (never invoked concurrently, even across the 15s wait bound — Future.timeout does not cancel its source) and generation-guarded.
  • AdFlowConfig.mediationConsentPolicy + MediationConsentFailurePolicy (failClosed default, unsafeFailOpen = explicit unsafe opt-out).

Fixed (correctness / reward integrity / reliability)

  • Frequency-cap late-hydration overwrite (late store read now merges, never rolls memory back / double-counts).
  • Runtime SSV in-flight race + fail-drop (re-apply on install; drop a warm ad on attach failure).
  • Rewarded-interstitial: re-validate live permission + expiry after the unbounded intro.
  • Async-callback + refreshed-banner-paid isolation; safe teardown (safeUnawaited).
  • validate() mirrors every constructor assert (release builds strip asserts).
  • Forwarder serialization across the timeout boundary (at most one invocation, strictly ordered).
  • Stale-consent ad invalidation folded into recheckGate() via an internal consent-generation stamp (no new public method).
  • MediationNetworkExtras asserts against an empty class name.

Verification

Static/tests: flutter analyze clean · 494 tests · coverage 85.2% · pana 160/160 · publish dry-run 0 warnings.

Runtime (Google-demand only, AdFlowConfig.test(), no Unity/overrides): Android emulator — all six formats shown; iOS simulator — banner, native, interstitial, rewarded, rewarded-interstitial shown, app-open loaded/ready. Physical-device testing intentionally deferred.

Unity mediation — known limitation (not a v5 blocker)

Unity mediation via gma_mediation_unity is temporarily unavailable with 5.0.0: the latest published adapter (1.8.1) requires google_mobile_ads: ^8.0.0 while 5.0.0 requires ^9.0.0. Google's main has an in-progress 1.9.0 for ^9.0.0 but it is unpublished; this release intentionally uses no git dependency, override, or vendored patch. Documented as an optional-integration limitation in doc/MEDIATION_SETUP.md. Unity mediation itself was not runtime-validated.

Upgrading

Most apps compile unchanged — see MIGRATION.md. Delete deferMediationInit if set; add the consentNotForwarded case to exhaustive AdBlockReason switches; if you use forwardConsent, note it now runs before init and fails closed.

Full notes: CHANGELOG.md.