SDK Runtime viewability design proposal

Ads SDKs in the SDK Runtime are not able to access a publisher's view hierarchy. Instead, SDKs in the Runtime have their own views. The SDK cannot use the same View APIs as they use outside the SDK runtime to determine whether the ad is visible to the user, because the ad view is not attached to the application's window. This includes Android View APIs like getLocationOnScreen, getLocationInWindow, or getVisibility, which don't return the expected values.

Supporting ads viewability measurement is a core SDK Runtime requirement. This design proposal aims to achieve support for Open Measurement and similar measurement services. The solutions discussed here might also be applicable to the Attribution Reporting APIs. Your feedback on this proposal is encouraged.

Capabilities

This design aims to support ads SDKs or measurement partners to compute the following viewability data (names are provisional and subject to change):

Illustration showing how the components of SDK Runtime viewability interoperate
Overview of SDK Runtime Viewability.
  • viewport [Rect]: Represents the device screen or the app window geometry, depending on the capabilities of the platform.
  • uiContainerGeometry [Rect]: The geometry of the SandboxedSdkView being rendered.
  • alpha [float]: The opacity of the SandboxedSdkView being rendered.
  • onScreenGeometry [Rect]: The subset of uiContainerGeometry which is not clipped by parent views, up to and including the viewport).
  • occludedGeometry [Rect]: The parts of the onScreenGeometry which are obstructed by any views in the application's hierarchy. Includes a Rect for each occlusion, corresponding to zero, one, or more app views that intersect with the SandboxedSdkView onScreenGeometry

Requirements

  • Values for uiContainerGeometry, onScreenGeometry, and occludedGeometry are expressed in the coordinate space of the viewport.
  • Visibility change reporting occurs with minimal latency.
  • Visibility is measurable for the full lifecycle of the ad view, from its first appearance to its last.

Design proposal

This proposal builds on how UI presentation works using the client and provider UI libraries. We will extend the UI libraries to allow the SDK to register one or more observers of the UI session. The observer will receive viewability information whenever relevant events that modify the data types in the capabilities section are detected. Measurement SDKs in the SDK runtime (OMID and MRAID implementations) can attach this observer to the UI session, so that this information can be sent to them directly. Measurement partners can combine information obtained from UI libraries with data about content already available (such as when using measurement scripts injected in the ad creative) to generate JavaScript viewability events.

Control flow for viewability.
Control flow for viewability.

The client library listens to changes in the ad UI through event listeners such as ViewTreeObserver. Whenever it determines that the ad UI has changed in a way that might affect viewability measurement, the client library checks when the last notification was sent to the observer. If the last update is greater than the allowed latency (configurable by the SDK, up to a minimum of 200ms on mobile), a fresh AdContainerInfo object is created and a notification is dispatched to the observer. This event-based model is better for system health than the polling done by most OMID implementations on Android today.

API

The following will be added to the privacysandbox.ui.core library:

  • SessionObserver: Typically implemented by the measurement SDK, attached to the session returned by the SDK through the privacysandbox.ui. This interface will also enable the measurement SDK to opt-in to certain categories of viewability signals. This in turn, enables the UI client library to only collect signals the observer is interested in, which is better for system health overall.
  • registerObserver(): Added to the Session class, this method allows anyone with access to the Session to register an observer. If the observer is registered after the UI session has been opened, they will be sent the cached AdContainerInfo right away. If registered before the session is opened, they will be sent AdContainerInfo when the session is opened.
  • AdContainerInfo: A class with getters that enables the observer to obtain read-only ad container information for the data types listed in the capabilites section above. The return values from these getters will correspond, wherever possible, to the parcelable return values from existing getters on View and its subclasses. If the ad container has been created using Jetpack Compose, this exposes the container's semantic properties. This class can be used to compute MRAID and OMID events related to viewability.
  • SessionObserverotifyAdContainerChanged(): Used to notify the observer whenever viewability changes. It passes an AdContainerInfo object. This is called whenever events are detected that affect data types listed in the Capabilities section. Note: This method might be called in addition to methods on Session. For example, Session.notifyResized() is called to request the SDK to resize the ad, and SessionObserver.notifyAdContainerChanged() is also called when this happens.
  • SessionObserverotifySessionClosed(): Notifies the observer that the session has been closed.

Future enhancements

Any code running in the application process, including the code from the privacysandbox.ui.client library, can be modified if the application is compromised. Therefore, any signal collection logic that runs in the application process is prone to tampering by application code. This applies also to SDK code deployed prior to the availability of Privacy Sandbox that runs in the application process. Consequently, signal collection by the UI library does not make the security situation worse.

Additionally, code in the SDK runtime can use a platform API called setTrustedPresentationCallback that can give it stronger guarantees from the framework about the ad UI presentation. setTrustedPresentationCallback works at the Surface level, and can help make assertions about the Surface containing the ad UI by specifying minimum thresholds for presentation, such as the percentage of pixels visible, time on screen, or scale. This data can be checked against viewability data provided by the UI client library, explained above. Since the framework-provided data is more reliable, any events from the UI library whose data are not in agreement with data from the framework can be discarded. For example, if the listener provided to setTrustedPresentationCallback is invoked with a notification that no pixels of the ad UI are being shown on the screen, and the client UI library shows a non-zero number of pixels on screen, data from the latter can be discarded.

Open questions

We invite feedback on the following points:

  1. What viewability signals are you interested in that aren't mentioned in this explainer?
  2. The current proposal is to update viewability no less frequently than every 200 milliseconds, provided there is a relevant change in the UI. Is this frequency acceptable to you? If not, what frequency would you prefer?
  3. Do you prefer to analyze information from setTrustedPresentationCallback yourself, or for the provider UI library to drop data from the client UI library, when it doesn't match setTrustedPresentationCallback data?
  4. How do you consume viewability signals? Help us understand your use cases by filing feedback that answers these questions.