A library for sending ad beacons on Android devices. Library is currently integrated with Google PAL & OM SDK. The project also includes a demo app that hosts an ExoPlayer that allows users to test their custom DAI assets with Google PAL & OM SDK signaling.
- Harmonic VOS metadata parsing
- Google Programmatic Access Libraries (PAL)
- Fire ad beacons via Open Measurement SDK (OMSDK)
- Google AdChoices (Why this ad?)
- Android TV ads library (AT&C rendering)
- An overlay showing ad break parameters (ID, duration, fired beacons)
Android 8.0 (API 26) or above
- Min SDK 26
- Target SDK 33
- Compile SDK 34
-
Include this library in your project
Groovy:
dependencies { implementation 'com.github.harmonicinc-com:client-side-ad-tracking-android:0.1.4' }
Kotlin:
dependencies { implementation("com.github.harmonicinc-com:client-side-ad-tracking-android:0.1.4") }
Change version to the latest available. You may find the latest version here.
-
Include the OMSDK library as dependency
[!NOTE]
As of now (Oct 2023), Google still has no support on bundling local modules into a single AAR (Fat AAR). That blocks us from shipping the OMSDK AAR together with the library.- Create a directory on your app root (we use
libs
as an example) - Download the AAR that we've included in this repo: omsdk-android-1.4.5-release.aar
- Place it under
libs
- Add the following lines in your dependency block
Kotlin:
dependencies { implementation fileTree(include: ['*.aar'], dir: 'libs') ... }
dependencies { implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar")))) // or just include everything implementation(fileTree("libs")) ... }
- Create a directory on your app root (we use
-
Declare
AD_ID
permission- To enable Google WTA, add the following line to your
AndroidManifest.xml
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
- To enable Google WTA, add the following line to your
-
Initialize parameters & interfaces
- Create an instance of
AdTrackingManager
in activity (preferably, could be somewhere else).class PlayerActivity: FragmentActivity() { private val adTrackingManager = AdTrackingManager(this) }
- Create an instance of
AdTrackingManagerParams
. Fill in all the required parameters:val adTrackingParams = AdTrackingManagerParams( descriptionUrl, // String: Description URL of video being played iconSupported, // Boolean: True if WTA (Why this ad?) icon is supported playerType, // String: Name of partner player used to play the ad playerVersion, // String: Version of partner player used ppid, // String: Publisher Provided ID supportedApiFrameworks, // Set<Int>: Supported API frameworks. See appendix for details playerHeight, // Int: Player height playerWidth, // Int: Player width willAdAutoplay, // Boolean: Will ad begin playback automatically? willAdPlayMuted, // Boolean: Will ad being played while muted? continuousPlayback, // Boolean: Will the player continues to play content videos after ad? omidPartnerVersion?, // String?: OMID partner version omidPartnerName?, // String: OMID partner name omidCustomReferenceData? // String?: OMID custom reference data in JSON string )
- Create a class that implements
PlayerAdapter
. Override all mandatory methods and return appropriate values from your player. The demo project includes an example ExoPlayerAdapter.kt for your reference.class YourPlayerAdapter(private val player: YourPlayer): PlayerAdapter { override fun getCurrentPositionMs(): Long { // TO BE IMPLEMENTED } override fun getPresentationStartTimeMs(): Long { // TO BE IMPLEMENTED } override fun getPlaybackRate(): Float { // TO BE IMPLEMENTED } override fun getDuration(): Long { // TO BE IMPLEMENTED } override fun getAudioVolume(): Float { // TO BE IMPLEMENTED } override fun isPaused(): Boolean { // TO BE IMPLEMENTED } }
- Fire the events in
PlayerAdapter
when specific conditions are met. You might need to listen events emitted from your player.val playerAdapter = YourPlayerAdapter(player) // Call when player starts to buffer playerAdapter.onBufferStart() // Call when playback resumes after buffering playerAdapter.onBufferEnd() // Call when user clicks pause (no need to check if ad is playing. The lib will handle it) playerAdapter.onPause() // Call when user clicks play (no need to check if ad is playing. The lib will handle it) playerAdapter.onResume() // Call when the user clicks an ad playerAdapter.onVideoAdClick() // Call when the user clicks somewhere other than an ad (e.g. skip, mute, etc.) playerAdapter.onVideoAdViewTouch() // Call when user changes the audio volume (no need to check if ad is playing. The lib will handle it) playerAdapter.onVolumeChanged()
- Create an instance of
-
Using the library
- Before loading the asset, call
prepareBeforeLoad
to preload the library. Note that it should be called within a coroutine scope.val manifestUrl = "https://www.example.com" // put your URL here CoroutineScope(Dispatchers.Main).launch { adTrackingManager.prepareBeforeLoad(manifestUrl, adTrackingParams) }
- After preloading, check if the asset supports Harmonic SSAI using
isSSAISupported
. Make sure the asset is supported by the library before continuing.val isSSAISupported = adTrackingManager.isSSAISupported()
- To obtain the generated nonce, call
appendNonceToUrl
val manifestUrls = listOf("https://www.example.com") val urlsWithNonce = adTrackingManager.appendNonceToUrl(manifestUrls)
- Finally, call
onPlay
to start the libraryadTrackingManager.onPlay( playerContext, // context in your player view playerAdapter, // adapter created above overlayFrameLayout?, // Optional. A frame layout for showing overlays (e.g. WTA icon, tracking debug overlay) playerView // Your player view. Fallback to this if no overlay frame layout is provided )
- Before loading the asset, call
-
Stop the library after playback
- Remember to clean the library after unloading the asset. Otherwise it will keep querying ads metadata.
adTrackingManager.cleanupAfterStop()
- Remember to clean the library after unloading the asset. Otherwise it will keep querying ads metadata.
Follow below steps so traffic (especially HTTPS) can be proxied & decrypted by Charles
- Open Charles Proxy, go to Help > SSL Proxying > Save Charles Root Certificate
- Save as
<your_android_app_project_root>/src/main/res/raw/charles_ssl_cert.pem
- Create an XML under
<root>/src/main/res/xml/network_security_config.xml
with the following content:<network-security-config> <debug-overrides> <trust-anchors> <!-- Trust user added CAs while debuggable only --> <certificates src="user" /> <certificates src="@raw/charles_ssl_cert" /> </trust-anchors> </debug-overrides> </network-security-config>
- Reference to the new config in your app's manifest (i.e.
AndroidManifest.xml
)<?xml version="1.0" encoding="utf-8"?> <manifest> <application android:networkSecurityConfig="@xml/network_security_config"> </application> </manifest>
- Configure the proxy settings on your Android device/emulator. Please refer to your device documentation for instructions.
- You should now be able to capture SSL traffic in Charles. Look for segments/manifest/metadata requests and see if you can view the response body.
Note
Allowing insecure traffic is not recommended in production environment. Remember to undo the changes before publishing.
Android apps by default blocks plain text traffic. If you would like to play insecure streams (HTTP):
- In your app's manifest (i.e.
AndroidManifest.xml
), add an extra flag<?xml version="1.0" encoding="utf-8"?> <manifest> <application android:usesCleartextTraffic="true"> </application> </manifest>
- Create an XML under
<root>/src/main/res/xml/network_security_config.xml
with the following content:<network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
- Reference to the new config in your app's manifest (i.e.
AndroidManifest.xml
)<?xml version="1.0" encoding="utf-8"?> <manifest> <application android:networkSecurityConfig="@xml/network_security_config"> </application> </manifest>
To show/hide the tracking overlay, call showTrackingOverlay
adTrackingManager.showTrackingOverlay(state)
Reference: IAB AdCOM v1.0 FINAL
The following table is a list of API frameworks either supported by a placement or required by an ad.
Value | Definition |
1 | VPAID 1.0 |
2 | VPAID 2.0 |
3 | MRAID 1.0 |
4 | ORMMA |
5 | MRAID 2.0 |
6 | MRAID 3.0 |
7 | OMID 1.0 |
8 | SIMID 1.0 |
9 | SIMID 1.1 |
500+ | Vendor-specific codes. |