<br />

The web view APIs for ads makes app signals available to the tags in your
[`CustomTabSession`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession),
helping to improve monetization for the web publishers that provided the content
and to protect advertisers from spam.

![](https://developers.google.com/static/ad-manager/mobile-ads-sdk/images/webview/monetization-paths-paw-cct.svg)

## How it works

Communication with the Google Mobile Ads SDK (Legacy) only happens in response to ad
events triggered by any of the following:

- [AdSense code](https://support.google.com/adsense/answer/9274634)
- [Google Publisher Tag](https://support.google.com/admanager/answer/181073)
- [IMA for HTML5](https://support.google.com/adsense/answer/6391192)

The Google Mobile Ads SDK (Legacy) facilitates communication by opening a postMessage
channel with the `CustomTabsSession` provided by the Android framework.

> [!WARNING]
> **Warning:** Don't call `CustomTabsSession.requestPostMessageChannel()` from your `CustomTabsSession` instance as this has unintended side effects. Use [`CustomTabsSession.postMessage()`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#postMessage(java.lang.String,android.os.Bundle)) to send postMessages and [`CustomTabsCallback.onPostMessage()`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onPostMessage(java.lang.String,android.os.Bundle)) to listen for these events.

## Prerequisites

- Google Mobile Ads SDK (Legacy) 23.0.0 or higher.
- An existing [Chrome Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs/guide-get-started) implementation in your mobile app. See [Warm-up and pre-fetch: using the
  Custom Tabs Service](https://developer.chrome.com/docs/android/custom-tabs/guide-warmup-prefetch).
- Associate your mobile app to a website using [Digital Asset Links](https://developers.google.com/digital-asset-links/v1/getting-started.md.txt). For instructions, see [PostMessage for TWA](https://developer.chrome.com/docs/android/post-message-twa).
- Add the following `<meta-data>` tag in your `AndroidManifest.xml` file to
  bypass the check for the `APPLICATION_ID`. If you miss this step and don't
  provide the `<meta-data>` tag, the Google Mobile Ads SDK (Legacy) throws an
  [`IllegalStateException`](https://developer.android.com/reference/java/lang/IllegalStateException)
  on app start.

  ```xml
  <!-- Bypass APPLICATION_ID check for web view APIs for ads -->
   <meta-data
       android:name="com.google.android.gms.ads.INTEGRATION_MANAGER"
       android:value="webview"/>
  ```

## Implementation

Within your Chrome Custom Tabs implementation, modify the code in the
[`onCustomTabsServiceConnected()`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsServiceConnection#onCustomTabsServiceConnected(android.content.ComponentName,androidx.browser.customtabs.CustomTabsClient))
callback. Replace [`newSession()`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsClient#newSession(androidx.browser.customtabs.CustomTabsCallback))
with [`MobileAds.registerCustomTabsSession()`](https://developers.google.com/ad-manager/mobile-ads-sdk/android/reference/com/google/android/gms/ads/MobileAds.md.txt#registerCustomTabsSession(android.content.Context,androidx.browser.customtabs.CustomTabsClient,java.lang.String,androidx.browser.customtabs.CustomTabsCallback)). This establishes a connection with the
postMessage channel to enrich the ad tags with SDK signals. A Digital Asset Link
is needed to connect the postMessage channel. Optionally, you can set the
[CustomTabsCallback](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback)
parameter to listen for Chrome Custom Tab events.

You should still be able to send messages from the `CustomTabsSession` received
from the Google Mobile Ads SDK (Legacy) but the port could change when the SDK requests
a new channel that overrides the existing one.

The following code snippet shows how to register a `CustomTabsSession` using the
Google Mobile Ads SDK (Legacy):

### Kotlin

```kotlin
import com.google.android.gms.ads.MobileAds

class MainActivity : ComponentActivity() {
  private var customTabsClient: CustomTabsClient? = null
  private var customTabsSession: CustomTabsSession? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // Get the default browser package name, this will be null if
    // the default browser does not provide a CustomTabsService.
    val packageName = CustomTabsClient.getPackageName(applicationContext, null);
    if (packageName == null) {
      // Do nothing as service connection is not supported.
      return
    }

    CustomTabsClient.bindCustomTabsService(
      applicationContext,
      packageName,
      object : CustomTabsServiceConnection() {
        override fun onCustomTabsServiceConnected(
          name: ComponentName, client: CustomTabsClient,
          ) {
            customTabsClient = client

            // Warm up the browser process.
            customTabsClient?.warmup(0L)
            // Create a new browser session using the Google Mobile Ads SDK (Legacy).
customTabsSession = MobileAds.registerCustomTabsSession(
this@MainActivity.applicationContext,
client,
// Checks the "Digital Asset Link" to connect the postMessage channel.
ORIGIN,
// Optional parameter to receive the delegated callbacks.
customTabsCallback
)
// Create a new browser session if the Google Mobile Ads SDK (Legacy) is
// unable to create one.
if (customTabsSession == null) {
customTabsSession = client.newSession(customTabsCallback)
}

            // Pass the custom tabs session into the intent.
            val customTabsIntent = CustomTabsIntent.Builder(customTabsSession).build()
            customTabsIntent.launchUrl(this@MainActivity,
                                      Uri.parse("YOUR_URL"))
          }

          override fun onServiceDisconnected(componentName: ComponentName) {
            // Remove the custom tabs client and custom tabs session.
            customTabsClient = null
            customTabsSession = null
          }
      })
  }

  // Listen for events from the CustomTabsSession delegated by the Google Mobile Ads SDK (Legacy).
  private val customTabsCallback: CustomTabsCallback = object : CustomTabsCallback() {
    @Synchronized
    override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
      // Called when a navigation event happens.
    }

    @Synchronized
    override fun onMessageChannelReady(extras: Bundle?) {
      // Called when the channel is ready for sending and receiving messages on both
      // ends.
      // This frequently happens, such as each time the SDK requests a
// new channel.
    }

    @Synchronized
    override fun onPostMessage(message: String, extras: Bundle?) {
      // Called when a tab controlled by this CustomTabsSession has sent a postMessage.
    }

    override fun onRelationshipValidationResult(
      relation: Int, requestedOrigin: Uri, result: Boolean, extras: Bundle?
    ) {
      // Called when a relationship validation result is available.
    }

    override fun onActivityResized(height: Int, width: Int, extras: Bundle) {
      // Called when the tab is resized.
    }

    override fun extraCallback(callbackName: String, args: Bundle?) {

    }

    override fun extraCallbackWithResult(callbackName: String, args: Bundle?): Bundle? {
      return null
    }
  }

  companion object {
    // Replace this URL with an associated website.
const val ORIGIN = "https://www.google.com"
  }
}
```

### Java

```java
import com.google.android.gms.ads.MobileAds;

class MainActivity extends ComponentActivity {
  // Replace this URL with an associated website.
private static final String ORIGIN = "https://www.google.com";
  private CustomTabsClient customTabsClient;
  private CustomTabsSession customTabsSession;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Get the default browser package name, this will be null if
    // the default browser does not provide a CustomTabsService.
    String packageName = CustomTabsClient.getPackageName(getApplicationContext(), null);
    if (packageName == null) {
      // Do nothing as service connection is not supported.
      return;
    }

    CustomTabsClient.bindCustomTabsService(
        getApplicationContext(),
        packageName,
        new CustomTabsServiceConnection() {
          @Override
          public void onCustomTabsServiceConnected(@NonNull ComponentName name,
              @NonNull CustomTabsClient client) {
            customTabsClient = client;

            // Warm up the browser process.
            customTabsClient.warmup(0);
            // Create a new browser session using the Google Mobile Ads SDK (Legacy).
customTabsSession = MobileAds.registerCustomTabsSession(
MainActivity.this.getApplicationContext(),
client,
// Checks the "Digital Asset Link" to connect the postMessage channel.
ORIGIN,
// Optional parameter to receive the delegated callbacks.
customTabsCallback);
// Create a new browser session if the Google Mobile Ads SDK (Legacy) is
// unable to create one.
if (customTabsSession == null) {
customTabsSession = client.newSession(customTabsCallback);
}

            // Pass the custom tabs session into the intent.
            CustomTabsIntent intent = new CustomTabsIntent.Builder(customTabsSession).build();
            intent.launchUrl(MainActivity.this, Uri.parse("YOUR_URL"));
          }

          @Override
          public void onServiceDisconnected(ComponentName componentName) {
            // Remove the custom tabs client and custom tabs session.
            customTabsClient = null;
            customTabsSession = null;
          }
        }

    );
  }

  // Listen for events from the CustomTabsSession delegated by the Google Mobile Ads SDK (Legacy).
  private final CustomTabsCallback customTabsCallback = new CustomTabsCallback() {
    @Override
    public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) {
      // Called when a navigation event happens.
      super.onNavigationEvent(navigationEvent, extras);
    }

    @Override
    public void onMessageChannelReady(@Nullable Bundle extras) {
      // Called when the channel is ready for sending and receiving messages on both
      // ends.
      // This frequently happens, such as each time the SDK requests a
// new channel.
      super.onMessageChannelReady(extras);
    }

    @Override
    public void onPostMessage(@NonNull String message, @Nullable Bundle extras) {
      // Called when a tab controlled by this CustomTabsSession has sent a postMessage.
      super.onPostMessage(message, extras);
    }

    @Override
    public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,
        boolean result, @Nullable Bundle extras) {
      // Called when a relationship validation result is available.
      super.onRelationshipValidationResult(relation, requestedOrigin, result, extras);
    }

    @Override
    public void onActivityResized(int height, int width, @NonNull Bundle extras) {
      // Called when the tab is resized.
      super.onActivityResized(height, width, extras);
    }

    @Override
    public void extraCallback(@NonNull String callbackName, @Nullable Bundle args) {
      super.extraCallback(callbackName, args);
    }

    @Nullable
    @Override
    public Bundle extraCallbackWithResult(@NonNull String callbackName, @Nullable Bundle args) {
      return super.extraCallbackWithResult(callbackName, args);
    }
  };
}
```