Few things are more frustrating in Android development than an SDK integration that works perfectly on a test device running Android 8.0 but fails silently on a Pixel running Android 14.
If you are integrating the Smaato SDK (or any programmatic ad network) and noticing that ad requests return "Success" but the ad container remains empty, you are likely hitting a security wall.
On Android 9 (API level 28) and above, the operating system blocks Cleartext HTTP traffic by default. While the Smaato SDK communicates securely over HTTPS, the actual ad creatives—banners, videos, or tracking pixels—often originate from third-party Demand Side Platforms (DSPs) that still rely on unencrypted HTTP.
When the WebView attempts to render these assets, the OS kills the connection. This guide details the root cause and the specific Network Security Configuration required to restore revenue flow.
The Root Cause: Android's Default Security Policy
Prior to Android 9 (Pie), apps could make network requests to any destination, regardless of whether the protocol was HTTP or HTTPS.
With the release of API 28, Google enforced a "secure by default" policy. The usesCleartextTraffic attribute in the application manifest defaults to false.
The Logcat Evidence
If you inspect your Logcat output while the ad fails to render, you will see a java.io.IOException or a console message from the Chromium network stack:
W/System.err: java.io.IOException: Cleartext HTTP traffic to creative-assets.some-ad-server.com not permitted
E/Chromium: [ERROR:network_delegate.cc(30)] FinalRequest: Cleartext HTTP traffic to http://media.adnetwork.com/banner.jpg not permitted
Because programmatic advertising (RTB) involves real-time bidding from thousands of sources, you cannot guarantee that every winning bidder serves their creative assets via HTTPS. To maximize fill rates, you must explicitly permit cleartext traffic for these ad rendering processes.
The Fix: Implementing a Network Security Configuration
While you can force a global bypass in the AndroidManifest.xml, the professional, architectural approach is to use a Network Security Configuration file. This provides granular control and adheres to Android best practices.
Step 1: Create the Configuration File
Navigate to your application's resource directory. You need to create a new XML file in the xml folder.
Path: app/src/main/res/xml/network_security_config.xml
If the xml directory does not exist, create it. Then, paste the following configuration:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--
Base configuration for the entire app.
Smaato and other ad networks require cleartext for
creatives that are served via HTTP.
-->
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!--
OPTIONAL: Domain-specific hardening (Recommended if you know specific endpoints)
If you want to allow cleartext ONLY for Smaato, use <domain-config> instead
of <base-config>, but be warned: dynamic creatives come from random domains.
Therefore, the base-config above is the industry standard for ad-supported apps.
-->
</network-security-config>
Step 2: Register the Configuration in the Manifest
Ideally, the configuration file sits dormant until referenced. Open your AndroidManifest.xml and modify the <application> tag to include the android:networkSecurityConfig attribute.
Path: app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.app">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
<!-- ADD THIS LINE BELOW -->
android:networkSecurityConfig="@xml/network_security_config"
<!--
Legacy Support:
Some developers add 'usesCleartextTraffic="true"' here directly.
While functional, the XML config method (above) is preferred
as it allows for debug-overrides and certificate pinning features.
-->
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Note: We include android:usesCleartextTraffic="true" in the Manifest tag as a fallback to ensure older build tools or specific OEM variations respect the intent, but the networkSecurityConfig is the authoritative source for API 24+.
Deep Dive: Why This Solves the Issue
When the Smaato SDK initializes a WebView to display a rich media ad (MRAID, VAST video, or standard Banner), that WebView runs within your application's process. It inherits the network security policies of your app.
By defining <base-config cleartextTrafficPermitted="true">, you are instructing the Android Network Security subsystem to bypass the strict HTTPS check for all URLs requested by your application.
Why not just use specific domains?
A common question is: "Can I just allowlist smaato.com?"
Technically, yes, using <domain-config>. However, practically, no.
The URL smaato.com is used for the ad request (the auction). The creative (the image or video) is hosted on the DSP's server (e.g., ads.agency-x.com). Since Smaato connects with hundreds of DSPs, the domain list is dynamic and infinite. You cannot predict where the image asset will come from, necessitating the broader base-config permission.
Common Pitfalls and Edge Cases
1. The WebView Cache Issue
If you implement this fix and immediately run the app, you might still see a blank space if the SDK has cached a failed response or if the WebView state is dirty.
Solution: Uninstall the application from your test device and do a clean install. This clears the application data and WebView cache.
2. Google Play Data Safety
When submitting your app to the Play Store, you will fill out the Data Safety form. Using cleartext traffic does not inherently violate Play Store policies, provided you are not transmitting sensitive user data (like passwords or credit cards) over HTTP.
Since this traffic is strictly for public advertising assets, it is generally compliant. However, ensure your own API calls to your backend remain HTTPS.
3. Debug vs. Release Builds
If you want to restrict cleartext traffic to debug builds only (useful for testing local servers) but force HTTPS in production (accepting lower ad fill rates), you can create a debug-specific manifest or config.
File: app/src/debug/res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
File: app/src/release/res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false" />
</network-security-config>
Warning: Doing this in Release mode will cause Smaato ads to fail again for HTTP-based creatives. Only use this strategy if your security requirements strictly outweigh ad revenue.
Conclusion
The transition to HTTPS-everywhere is a positive step for user security, but the fragmented nature of the ad-tech ecosystem requires legacy support. By implementing a network_security_config.xml, you grant the necessary permissions for the Smaato SDK to render creatives from mixed sources without compromising the architectural integrity of your Android application.