Skip to main content

Navigating Google Play "User Data Policy" Warnings with Mintegral SDK

 Few notifications spike a developer’s heart rate like a rejection email from the Google Play Console. Recently, a wave of "Data Safety" violations has hit apps integrating third-party ad networks.

One specific vector for these rejections involves the Mintegral SDK. The error usually cites discrepancies between the data your app collects (specifically Persistent Device Identifiers) and what you declared in your Data Safety form.

If you are seeing warnings regarding "Unauthorized collection of device data" or "Inaccurate Data Safety Section," this guide provides the technical root cause analysis and the exact code required to remediate the issue.

The Root Cause: Static Analysis and Device Fingerprinting

To solve this, we must understand how Google detects these violations. When you upload an App Bundle (.aab) to the Play Console, Google runs static analysis on the bytecode.

It scans for specific API calls, such as Settings.Secure.ANDROID_IDBuild.SERIAL, or specific pattern-matching on installed package enumeration.

The Legacy SDK Problem

Older versions of the Mintegral SDK (pre-16.x) utilized broader permissions to maximize ad fill rates (eCPM). This included:

  1. Package Enumeration: Scanning other apps installed on the user's device.
  2. Persistent IDs: collecting non-resettable hardware identifiers.

Google’s "User Data Policy" now strictly forbids using persistent device identifiers for advertising if a resettable identifier (like the GAID - Google Advertising ID) is available. Furthermore, any collection of this data must be disclosed.

If your SDK version is outdated, or if you haven't explicitly configured the SDK to respect privacy flags, the bytecode responsible for this collection remains in your app, triggering the automated rejection.

The Technical Fix

Resolving this requires a two-pronged approach: upgrading the artifact to remove deprecated collection logic and programmatically enforcing privacy constraints during initialization.

Step 1: Force Dependency Resolution

First, ensure you are using a version of Mintegral that is compliant with Google Play’s latest policy updates. Versions below 16.2.x are high-risk.

In your module-level build.gradle.kts (Kotlin DSL), update your implementation. If you use mediation (like AdMob or AppLovin MAX), you must force the underlying adapter to use a compliant SDK version.

dependencies {
    // If using Mintegral directly
    implementation("com.mbridge.msdk.oversea:mbridge_android_sdk:16.5.91")

    // If using AppLovin MAX Mediation, force the Mintegral version 
    // to prevent the adapter from pulling in an older transitive dependency
    implementation("com.applovin.mediation:mintegral-adapter:16.5.91.0")
}

Step 2: Privacy-First Initialization

Simply updating the SDK isn't always enough. You must explicitly signal to the SDK that it operates in a restricted environment regarding user data.

Implement the following configuration in your Application class. This code utilizes the MBridgeSDK configuration builder to disable clipboard access and location services, which are common triggers for "Data Not Disclosed" violations.

import android.app.Application
import android.content.Context
import com.mbridge.msdk.out.MBridgeSDKFactory
import com.mbridge.msdk.out.SDKInitStatusListener
import com.mbridge.msdk.MBridgeConstans

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initializeAdSdk(this)
    }

    private fun initializeAdSdk(context: Context) {
        val mBridgeSDK = MBridgeSDKFactory.getMBridgeSDK()
        
        // CRITICAL: Configure privacy settings before initialization
        // to prevent race conditions where data is grabbed on startup.
        val configuration = mapOf(
            // Disable retrieving location info (GPS/Network)
            MBridgeConstans.AUTHORITY_GENERAL_DATA to MBridgeConstans.IS_SWITCH_OFF,
            
            // Disable retrieving other logic identifiers if not necessary
            MBridgeConstans.AUTHORITY_OTHER to MBridgeConstans.IS_SWITCH_OFF
        )

        // Apply Consent Status (GDPR/CCPA compliant logic)
        // If your Consent Management Platform (CMP) returns negative consent:
        // mBridgeSDK.setConsentStatus(context, MBridgeConstans.IS_SWITCH_OFF)

        // For strict Google Play compliance regarding "Personal Information":
        // Ensure Do Not Track is respected if the user opted out via OS settings.
        mBridgeSDK.setDoNotTrackStatus(false) 

        // Initialize with your specific App ID and App Key
        mBridgeSDK.init(configuration, context)
        
        // Listener is optional but recommended for debugging
        mBridgeSDK.setInitStatusListener(object : SDKInitStatusListener {
            override fun onInitSuccess() {
                // Safe to load ads now
            }

            override fun onInitFail(msg: String?) {
                // Log failure to Crashlytics or backend
                System.err.println("Mintegral Init Failed: $msg")
            }
        })
    }
}

Step 3: Android Manifest Cleanup

Ensure your merged manifest does not contain permissions that Mintegral might have injected in previous versions but are no longer needed.

Open your AndroidManifest.xml and explicitly remove these if you are not using them for core app functionality:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Explicitly remove sensitive permissions if an old library attempts to merge them -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove" />

</manifest>

Configuring the Google Play Data Safety Form

Even with the code fixed, you will get rejected if your Data Safety form does not match the SDK's behavior. The automated review compares your declaration against the SDK's capabilities.

For Mintegral (and most modern ad SDKs), you must declare the following.

1. Data Collection

You must select Yes for "Does your app collect or share any of the required user data types?"

2. Data Types

Drill down into the specific data types. You must check the boxes for:

  • Device or other IDs:
    • Device or other IDs (This covers the Advertising ID).
  • App info and performance:
    • Crash logs (Mintegral collects SDK stability data).
    • Diagnostics.
  • App Activity:
    • App interactions (Data regarding ad clicks and views).

3. Usage and Handling

For each of the selected data types above, configure the details as follows:

  • Is this data collected, shared, or both?
    • Select Collected and Shared. (Ad networks technically "share" data with demand sources/bidders).
  • Is this data processed ephemerally?
    • Generally No (It is stored for analytics/fraud prevention).
  • Why is this data collected?
    • Check Advertising or Marketing.
    • Check Analytics.
    • Check Fraud Prevention, Security, and Compliance.

Crucial Note: Do not claim that "Device or other IDs" are used for "App Functionality" unless the ID is strictly necessary for the app to run (e.g., account management). For ad SDKs, the purpose is always Marketing/Analytics.

Deep Dive: Why This Fix Works

The combination of the code updates and the manifest cleanup resolves the "Persistent Device Identifiers" warning by shifting the SDK's fingerprinting strategy.

By updating to version 16.x+, you replace the underlying compiled classes. The newer Mintegral binaries have stripped out the logic that queries getInstalledPackages (a common red flag for Google).

Furthermore, by setting AUTHORITY_GENERAL_DATA to IS_SWITCH_OFF, you are programmatically short-circuiting the logic inside the SDK that attempts to access LocationManager.

When Google's policy bot re-scans your .aab, it sees:

  1. No bytecode calling prohibited package scanning APIs.
  2. A Data Safety form that explicitly acknowledges the collection of Advertising IDs for Marketing purposes.
  3. Alignment between code capability and policy declaration.

Common Pitfalls and Edge Cases

The "Cached Dependency" Trap

A common issue occurs when Gradle caches older versions of a library despite the build.gradle update. If you update the version but still see the error, perform a clean build: ./gradlew clean build --refresh-dependencies

ProGuard/R8 Obfuscation Rules

If you use R8 (enabled by default in release builds), ensure you aren't stripping out required Mintegral classes, which can cause the SDK to behave unpredictably or crash, potentially triggering "stability" flags.

Add this to your proguard-rules.pro:

-keepattributes Signature
-keepattributes *Annotation*
-keep class com.mbridge.** { *; }
-keep interface com.mbridge.** { *; }
-dontwarn com.mbridge.**

COPPA (Children's Apps)

If your app targets children (under 13 in the US), you cannot use the standard configuration. You must set the COPPA flag.

// In the initialization map
MBridgeConstans.AUTHORITY_ALL_INFO to MBridgeConstans.IS_SWITCH_OFF

If you target children, you must generally avoid collecting ADID entirely. Ensure your ad network settings in the Mintegral dashboard are also set to "Non-Personalized Ads" (NPA).

Conclusion

Google Play's policy enforcement is automated and binary; you are either compliant or you are not. There is no gray area for "legacy code."

By forcing a modern SDK version, cleaning up the Manifest, and accurately mapping the Data Safety form to "Advertising and Marketing" purposes, you satisfy both the static analyzer and the policy requirements. Once these changes are committed, create a new release, update the Data Safety form in the Console, and submit them simultaneously to clear the warning.