These are the integration instructions for the Android Open Measurement SDK. If you are porting an existing integration to the newest version of OM SDK, please refer to the migration guide.
OM SDK supports Android API 16, Android 4.1, Jelly Bean and above versions. This includes the Amazon Fire TV and Android TV platforms as long as they meet the above version requirements.
Please implement the following setup steps before moving on to the specific ad format instructions:
The first step is, of course, to add the OM SDK to your app or ads SDK. The SDK artifact to be integrated should be the output of the namespaced build generated on the IAB portal (refer the IAB OMSDK Quickstart Onboarding docs) You can include this [.AAR] artifact into your Android Studio project using any standard method like module import, etc.
You should implement these steps as early as possible in your app or SDK lifecycle. It’s recommended that the SDK is activated on app launch.
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
this.initializeOmsdk();
// Other logic.
}
private void initializeOmsdk() {
// 1. Activate the SDK.
// 2. Publish last activity.
}
}
Note that OM SDK may only be used on the main UI thread. Make sure you are on the main thread when you initialize the SDK, create its objects, and invoke its methods.
The first step is to initialize the OM SDK. Activate the OM SDK by passing in the SDK version, and the Application’s context:
boolean activated = Omid.activate(applicationContext);
if (!activated) {
// SDK failed to activate. Handle appropriately.
}
Note: This method call will throw an IllegalArgumentException
if the parameter is null.
Especially on Android TV, the app should signal to the SDK when a user interacts with app via manual input. This can be done like so:
Omid.updateLastActivity();
Note that the SDK will automatically consider app activation (specifically the onActivityStarted
lifecycle callback triggered on app foregrounding) as an activity event; however, this will only detect cases when the app is backgrounded and then re-foregrounded after the OM SDK is activated. It will not detect the initial launch of the app even if the OM SDK is activated on launch. Therefore, it is recommended to call updateLastActivity
on app launch right after activating the OM SDK.
Only the latest activity timestamp will be sent to measurement scripts, so that they can validate that a user is likely present.
Note that careful thought should be given to implementing calling this API, to ensure that it’s only called on user input. For example, calling this API on the start of media playback would be incorrect, since a future update may trigger via media playback via autoplay.
Load the OM SDK JavaScript library using your preferred method (Volley, Retrofit, etc.). For example, using Volley, we retrieve the resource and save it:
String OMID_JS_SERVICE_URL = context.getString(R.string.omid_js_service);
StringRequest stringRequest = new StringRequest(Request.Method.GET, OMID_JS_SERVICE_URL,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
OMID_JS_SERVICE_CONTENT = response;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(stringRequest);
We recommend that this step is performed right after you have initialized the SDK because you will need the JS library to be available before you create any tracking session.
Note that this step is only necessary if you are injecting the OM SDK JS library client-side, which may not necessarily be true if you use only WebView ad formats. This is because WebView ad formats (not native, however) permit the injection server-side. If you are indeed injecting the JS library server-side (i.e., within the ad response itself), you can skip this step.
Note also that when integrating the native OM SDK, you must use the same or later version of the OM SDK JavaScript — ideally deploying the latest JavaScript patch versions from IAB Tech Lab
Create a Partner
object to identify your integration. The IAB Tech Lab will assign a unique
partner name to you at the time of integration, so this is the value you should use here.
The version string should represent the integration version in semantic versioning format. For an ads SDK, this should be the same as your SDK’s semantic version. For an app publisher, this should be the same as your app version.
try {
Partner partner = Partner.createPartner(PARTNER_NAME, PARTNER_SDK_OR_APP_VERSION);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
The following sections describe how to implement measurement for various ad formats. They assume you have already imported the library and implemented the initialization code.
The steps below describe how to create a tracking session for a WebView (HTML) ad.
Initialize the WebView, and enable JavaScript within the WebView:
WebView adView;
adView = (WebView) findViewById(R.id.webviewad);
adView.getSettings().setJavaScriptEnabled(true);
Retrieve the ad response as you normally would. For the purposes of the subsequent steps, the ad response should be an HTML string.
String adResponseHtmlString = "<html>...</html>";
After retrieving the ad response HTML, inject the OM SDK JS library you retrieved in the previous step into the ad response, and load it in the WebView:
try {
String htmlString = ScriptInjector.injectScriptContentIntoHtml(OMID_JS_SERVICE_CONTENT,
adResponseHtml);
adView.loadDataWithBaseURL("", htmlString, "text/html", "UTF-8", "");
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
Note, as mentioned in Step 2 of Initialization, if you have injected the JS service library server-side, this step is not necessary.
Create the session in the sequence of steps outlined below.
Note: in order to prevent issues with starting the session later, you must wait until the
WebView finishes loading OM SDK JavaScript before creating the AdSession
. Creating the session
sooner than that may result in an inability to signal events (impression, etc.) to verification
scripts inside the WebView. The easiest way to avoid this issue is to create the AdSession
in a
webview callback WebViewClient.onPageFinished()
. Alternatively, if an implementation can receive
an HTML5 DOMContentLoaded event from the WebView, it can create the AdSession
in a message handler
for that event.
// In an implementation of WebViewClient:
public void onPageFinished(WebView webView, String url) {
if (adSession != null) return;
/* Create the session.
* Eliding the steps for brevity. Please reference for full steps below.
*/
adSession = AdSession.createAdSession(adSessionConfiguration, context);
adSession.registerAdView(webView);
// Start the session
adSession.start();
}
First, create a context with a reference to the partner object you created in the setup step and the ad’s WebView:
try {
String customReferenceData = "";
String contentUrl = ...
AdSessionContext context = AdSessionContext.createHtmlAdSessionContext(partner, adView,
contentUrl, customReferenceData);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
OMID-1.3 requires specifying the type of creative & type of impression for the ad session, along with the layer responsible for signaling the impression event. For WebView display ads this is generally the native layer. The mediaEventsOwner value should be Owner.NONE for display ads. We’ll get to the actual signaling of the event in a subsequent step:
try {
AdSessionConfiguration adSessionConfiguration =
AdSessionConfiguration.createAdSessionConfiguration(CreativeType.HTML_DISPLAY, ImpressionType.BEGIN_TO_RENDER,
Owner.NATIVE, Owner.NONE, false);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
Finally, create the session itself:
try {
adSession = AdSession.createAdSession(adSessionConfiguration, context);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
Set the view on which to track viewability. For a WebView ad, this will be the WebView itself. For a native ad, this will be the native view that contains all the relevant elements of the ad:
try {
adSession.registerAdView(adView);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
If there are any native elements which you would consider to be part of the ad, such as a close button, some logo text, or another decoration, you should register them as friendly obstructions to prevent them from counting towards coverage of the ad. This applies to any ancestor or peer views in the view hierarchy (all sub-views of the adView will be automatically treated as part of the ad):
try {
adSession.addFriendlyObstruction(logoView);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
If the view changes at a subsequent time due to a fullscreen expansion or for a similar reason, you
should always update the adView
reference to whatever is appropriate at that time:
try {
adSession.registerAdView(adView);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
Starting the session does not trigger an impression yet – it only prepares the session for tracking. It is important to start the session before dispatching any events.
Generally you should start the session as soon as you’ve completed the previous steps:
adSession.start();
As described in the previous step, this should occur after the WebView has loaded.
Ideally this would occur as early in your ad session as the creative is loaded, but it is ok to send it just before the impression
try {
AdEvents adEvents = AdEvents.createAdEvents(adSession);
adEvents.loaded();
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
The definition of an impression is generally accepted to be on ad render so this is likely when you will want to dispatch the event. The event should be dispatched only once and attempting to trigger it multiple times is an error.
try {
adEvents.impressionOccurred();
} catch (IllegalStateException e) {
e.printStackTrace();
}
Stop the session when the impression has completed and the ad will be destroyed. Note, that after you’ve stopped the session it is an error to attempt to start it again or trigger an impression on the finished session.
Note that ending an OMID ad session sends a message to the verification scripts running inside the webview supplied by the integration. So that the verification scripts have enough time to handle the sessionFinish
event, the integration must maintain a strong reference to the webview for at least 1.0 seconds after ending the session.
adSession.finish();
adSession = null;
The implementation instructions for WebView Video are similar in many respects to WebView Display on the native side. The primary point of difference is that WebView Video will generally require some additional implementation within the JavaScript layer inside the WebView. This additional JavaScript configuration will likely be performed to a large degree in your ad server so that the necessary components are embedded within the ad response by the time your app or SDK receives it.
The following implementation instructions assume that the JavaScript layer is responsible for these actions:
Impression events and playback progress can be handled from the native layer as well. You can
indicate which layer is responsible for event handling at the time that you create the
AdSessionConfiguration
instance by passing through the appropriate owner (Native or JavaScript)
for the respective events. As mentioned previously, this guide assumes you will be implementing the
responsibilities cited above in the JavaScript layer. If you would like instructions on how to do
the same in the native layer, please reference the Native Video implementation instructions.
Within the HTML ad response, please create a SessionClient. You will interact with classes from the SessionClient to signal events and pass metadata:
var sessionClient;
try {
sessionClient = OmidSessionClient[SESSION_CLIENT_VERSION];
} catch (e) {}
if (!sessionClient) {
return;
}
const AdSession = sessionClient.AdSession;
const Partner = sessionClient.Partner;
const Context = sessionClient.Context;
const VerificationScriptResource = sessionClient.VerificationScriptResource;
const AdEvents = sessionClient.AdEvents;
const MediaEvents = sessionClient.MediaEvents;
See this step of WebView Display. This guide assumes that the ad response will contain HTML (which will render the video player) as well as a VAST component.
See this step of WebView Display.
See this step of WebView Display.
Note that you will want to designate which layer is responsible for signaling events differently than for WebView Display. Generally for WebView video the JavaScript layer will be signaling both the impression and video events.
As with WebView display, you should ensure the session set up and creation should happen only after receiving the WebView loaded event. Please reference this step of the WebView Display instructions for further detail.
Furthermore, you should decide on the appropriate value for the isolateVerificationScripts
parameter. The effect of a true
value is that measurement resources will be placed in a sandboxed
iframe where they cannot access the video ad element. If you specify false
, they will be placed
into a same-origin iframe. The FAQ has further detail on this setting.
try {
// The creative and impression types will be defined by the javascript layer
AdSessionConfiguration adSessionConfiguration =
AdSessionConfiguration.createAdSessionConfiguration(CreativeType.DEFINED_BY_JAVASCRIPT, ImpressionType.DEFINED_BY_JAVASCRIPT,
Owner.JAVASCRIPT, Owner.JAVASCRIPT, false);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
See this step of WebView Display.
The following instructions assume the ad response uses the VAST 4.1 Verification node, either per the 4.1 spec exactly or in the Extensions node. The exact details are outside the scope of this document and are addressed by other Tech Lab guidance and documentation.
If you are not able to use VAST or the 4.1 node, you must find an alternative way to embed this information within the ad response and will likely need to work with each measurement vendor individually to determine the correct mechanism for loading their tags.
Using this 4.1 Verification node as an example:
<Verification vendor="vendorKey">
<JavaScriptResource apiFramework="omid">
<![CDATA[https://measurement.domain.com/tag.js]]>
</JavaScriptResource>
<VerificationParameters>
<![CDATA[{...}]]>
</VerificationParameters>
</Verification>
First create an array to hold one or more measurement resources that you will parse in the next step:
var resources = [];
Then, for each Verification node in the ad response, create a VerificationScriptResource
instance
as follows:
var vendorKey = ...; // parsed from "vendor" attribute
var params = ...; // parsed from VerificationParameters as a string
var url = ...; // parsed from JavaScriptResource
var resource = new VerificationScriptResource(url, vendorKey, params);
resources.add(resource);
Note that the vendorKey must match the vendor
attribute from the Verification node in order
for OM SDK to pass any impression specific metadata in the VerificationParameters to the correct
verification script.
Next, create the JS ad session and pass through the measurement resources you parsed from the ad response in the previous step. You will need to use this session instance in order to subscribe to the native session start event as well as to load the resources.
var partner = new Partner(PARTNER_NAME, PARTNER_SDK_OR_APP_VERSION);
var context = new Context(partner, resources);
var adSession = new AdSession(context);
Note that the parameters for Partner
here should match those you’ve passed in the native layer.
In order to ensure the ad is measured correctly, you should provide a reference to the video element when it is available. The right steps will depend on whether the video element is in the top window or inside a cross-domain iframe.
Simply provide the video element reference using the Context
object you created previously:
const videoElement = document.getElementById(...);
context.setVideoElement(videoElement);
There are two possible scenarios when the video element is in a cross-domain iframe:
Session
and the element are inside the cross-domain iframe.Session
in the top window as well as inside the cross-domain iframe
with the ad element.In the first scenario, you should mark up the iframe with a predefined class name: omid-element
.
This will ensure the OM SDK JS service running in the top level is able to locate the iframe. The next
step is to indicate the position of the element within the iframe. This can be done by passing the
element’s offset to the Session
instance you created inside the iframe:
// elementBounds is a rect {x, y, width, height} that indicates the position of the video element
// inside the iframe.
adSession.setElementBounds(elementBounds)
In the second scenario, you should pass the reference to the iframe to the Session
and Context
instance in the top level:
const iframeElement = document.getElementById(...);
context.setSlotElement(iframeElement);
And within the iframe, provide the element’s offset to the Session
instance inside the iframe:
adSession.setElementBounds(elementBounds)
In addition, you should also at this time create the event notification objects which you will use to signal the impression and playback events.
const adEvents = new AdEvents(adSession);
const mediaEvents = new MediaEvents(adSession);
Start the session in the native layer before signaling any events in the JS layer.
adSession.start();
When the video has loaded, signal the load event and pass through the following metadata:
adSession.registerSessionObserver((event) => {
if (event.type === "sessionStart") {
// setup code
...
// Set the impression/creative type here.
if (event.data.creativeType === 'definedByJavaScript') {
adSession.setCreativeType('video');
}
if (event.data.impressionType === 'definedByJavaScript') {
adSession.setImpressionType('beginToRender');
}
// load event
adEvents.loaded({ isSkippable: skippable, skipOffset:offsetForSkip, isAutoPlay: autoplay, position: position });
// other event code
...
} else if (event.type === "sessionError") {
// handle error
} else if (event.type === "sessionFinish") {
// clean up
}
});
Note that we are dispatching the event within the session observer callback. This is to ensure that we do not dispatch any events until we’ve received session start. All events in the JS layer must be dispatched only after the session start event. You should also check the event type to ensure you handle each event type appropriately.
When you are ready, signal the impression event using the events object you created in the previous step. For video, an impression is generally accepted to occur when the first frame of the video successfully plays. It should only be dispatched once, and attempting to trigger it multiple times is an error. As noted in the previous step, you must also ensure that you don’t dispatch the impression event until after you’ve received the session start event.
// this should be within the same callback as the load event from the previous step
adSession.registerSessionObserver((event) => {
if (event.type === "sessionStart") {
...
adEvents.impressionOccurred();
...
} else if (event.type === "sessionError") {
// handle error
} else if (event.type === "sessionFinish") {
// clean up
}
})
Note that we are signaling this event from the JavaScript rather than native layer since we designated the JavaScript layer as the impression event owner.
As playback progresses, dispatch notifications for the playback progress events. You should at a minimum signal the following events, as appropriate:
Monitor video playback to signal the progress events at appropriate times (reference bullet list above). Generally the timing of the events corresponds to the industry defined standards VPAID and VAST.
Note that the start event is distinct from others because it requires the duration of the ad as well as the creative volume. Please ensure that video player duration is available at the time you dispatch this event.
if (!isNaN(player.duration)) {
mediaEvents.start(player.duration, player.volume);
} else {
// wait until duration is available to start
}
Note that the mediaPlayerVolume
parameter should only be the volume of the player element. The
device volume is detected by the SDK automatically. The player volume should be normalized between
0 and 1. If the creative volume only supports muted or unmuted, it is sufficient to use the
following for the mediaPlayerVolume parameter:
mediaEvents.start(player.duration, player.muted ? 0 : player.volume);
With respect to volume changes, you are responsible for notifying only about creative volume
changes. See the note about mediaPlayerVolume
in the section above.
mediaEvents.volumeChange(player.volume);
Finally, you are also responsible for signaling any player state changes. If the player can expand to fullscreen as well as exit fullscreen, you will want to signal these state changes as follows:
// entering fullscreen
mediaEvents.playerStateChange("fullscreen");
// exiting fullscreen
mediaEvents.playerStateChange("normal");
For clarity, when we refer to Native Display, we refer to non-WebView display ad formats where the components of the ad are native (non-HTML) UI elements.
Retrieve the ad response as you normally would. For a native ad the ad response may generally be in the form of a JSON which includes some metadata and URLs to the ad assets.
The steps here are conceptually similar to the same step of Native Video. Unlike video, there is no standard ad response format for display, so you must find an alternative way to determine which measurement resources should be tracking a given ad impression, but, in any case, you will most likely return this information as part of the ad response one way or another.
Create the session in the following sequence of steps:
First, create a context with a reference to the partner object you created in the setup step and the ad’s Native View:
String customReferenceData = "";
String contentUrl = ...
try {
AdSessionContext adSessionContext = AdSessionContext.createNativeAdSessionContext(partner,
OMID_JS_SERVICE_CONTENT, verificationScriptResources, contentUrl, customReferenceData);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
OMID-1.3 requires specifying the type of creative & type of impression for the ad session, along with the layer responsible for signaling the impression event. For native display ads this is the native layer. The mediaEventsOwner value should be Owner.NONE for display ads.
try {
AdSessionConfiguration adSessionConfiguration =
AdSessionConfiguration.createAdSessionConfiguration(CreativeType.NATIVE_DISPLAY, ImpressionType.BEGIN_TO_RENDER,
Owner.NATIVE, Owner.NONE, false);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
Finally, create the session itself:
try {
adSession = AdSession.createAdSession(adSessionConfiguration, adSessionContext);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
See this section of WebView display.
Starting the session does not trigger an impression yet – it only prepares the session for tracking. It is important to start the session before dispatching any events.
Generally you should start the session as soon as you’ve completed the previous steps:
adSession.start();
Ideally this would occur as early in your ad session as the creative is loaded, but it is ok to send it just before the impression
try {
AdEvents adEvents = AdEvents.createAdEvents(adSession);
adEvents.loaded();
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
The definition of an impression is generally accepted to be on ad render so this is likely when you will want to dispatch the event. The event should be dispatched only once and attempting to trigger it multiple times is an error. Note that this should only be done after starting the session.
try {
adEvents.impressionOccurred();
} catch (IllegalStateException e) {
e.printStackTrace();
}
Stop the session when the impression has completed and the ad will be destroyed. Note, that after you’ve stopped the session it is an error to attempt to start it again or trigger an impression on the finished session.
adSession.finish();
adSession = null;
Please follow the instructions below for how to correctly track a native video ad.
Retrieve the ad response as you would normally. Generally this will be a VAST document.
In this step, you will determine which measurement resources should be tracking the ad. Overall the instructions are similar to this step of the WebView Video instructions. As before, we will assume that the ad response contains one or more Verification nodes as specified in VAST 4.1.
First we create an array to hold the scripts:
List<VerificationScriptResource> verificationScriptResources = new ArrayList<>();
Then parse each Verification node and add the associated VerificationScriptResource
to the array
of resources:
final String url = ...;
final String vendorKey = ...;
final String params = ...;
try {
VerificationScriptResource verificationScriptResource =
VerificationScriptResource.createVerificationScriptResourceWithoutParameters(vendorKey,
url, params);
verificationScriptResources.add(verificationScriptResource);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
See this step of the Native Display implementation instructions.
Note that you will want to specify a media event owner that is different from the example provided with native display.
try {
AdSessionConfiguration adSessionConfiguration =
AdSessionConfiguration.createAdSessionConfiguration(CreativeType.VIDEO, ImpressionType.BEGIN_TO_RENDER,
Owner.NATIVE, Owner.NATIVE, false);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
See this step of WebView Display.
You will use these instances to signal impression and playback events, so you will want to hold on to them after you’ve created them.
try {
AdEvents adEvents = AdEvents.createAdEvents(adSession);
MediaEvents mediaEvents = MediaEvents.createMediaEvents(adSession);
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
As noted previously, signaling the start of the session does not yet trigger the impression. This only prepares the session for tracking. You will want to do this as close to completing the previous steps as possible.
adSession.start();
Dispatch the loaded event to signal that the ad has loaded and is ready to play. It is best practice for this (and all other events) to be fired only after the session has started.
final VastProperties vProps = VastProperties.createVastPropertiesForSkippableMedia(...);
try {
adEvents.loaded(vProps);
} catch (Exception e) {
}
Signal the start of the impression. Generally this should occur when the first frame of the video successfully plays.
try {
adEvents.impressionOccurred();
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
As playback progresses, dispatch notifications for the playback progress events. You should at a minimum signal the following events, as appropriate:
Monitor video playback to signal the progress events at appropriate times (reference bullet list above). Generally the timing of the events corresponds to the industry defined standards VPAID and VAST.
Note that the start event is distinct from others because it requires the duration of the ad as well as the creative volume:
try {
mediaEvents.start(getDuration(), PLAYER_VOLUME_0_TO_1);
} catch (IllegalStateException e) {
e.printStackTrace();
}
Device volume is detected by the SDK automatically. With respect to volume changes, you are responsible for notifying only about creative volume changes:
try {
mediaEvents.volumeChange(PLAYER_VOLUME_0_TO_1);
} catch (IllegalArgumentException | IllegalStateException ex) {
ex.printStackTrace();
}
Finally, you are also responsible for signaling any player state changes. If the player can expand to fullscreen as well as exit fullscreen, you will want to signal these state changes as follows:
// enter fullscreen
mediaEvents.playerStateChange(PlayerState.FULLSCREEN);
// exit fullscreen
mediaEvents.playerStateChange(PlayerState.NORMAL);
Note the above methods should be appropriately handled for exceptions.
Stop the session on completion or termination of ad playback.
adSession.finish();
adSession = null;
The setup for an audio ad is similar to the corresponding video ad, with minor exceptions…
Validating your steps is an important part of the integration process. Follow the instructions here.
This section is directed at ads SDKs that will want to distribute their SDK after integrating the OM SDK in order to provide measurement across their network of publishers. While the ads SDK may very well choose to distribute the OM SDK as a separate component, this will generally provide a poorer usability experience compared to embedding the OM SDK within. The following instructions detail how to embed the OM SDK when possible. Note that the OM SDK does use namespacing so that it can be independently included within multiple ads SDKs in a single app without issues.
[TODO]
FAQ available here
Kill switch guidance available here