Implement a Custom GMA Adapter for Android
LiveRamp's integration with Google's Secure Signals lets publishers pass RampID envelopes as signals to Google Ad Manager's (GAM) Open Bidding. On mobile applications, the Google Mobile Ads (GMA) SDK collects those signals through classes that extend Google's RtbAdapter.
This guide uses the following placeholders in code examples, which you should replace with your own names as part of the adapter creation process:
YourRtbAdapter: Replace with your own adapter class name (for example,AcmeMediationAdapter,PublisherRtbAdapter).com.yourcompany.yourapp:Replace with your application package name or application ID.YourEnvelopeManager: Replace with your envelope helper.
Prerequisites
Before starting, confirm the following are in place:
- ATS API integration is complete. Your app retrieves, refreshes, and caches RampID envelopes independently of the SDK. The adapter only reads from this layer — it does not call LiveRamp APIs itself. For instructions, see "ATS API Implementation Guide for Mobile Publishers".
- Google Mobile Ads (GMA) SDK is integrated. Your app already initializes
MobileAdsand loads ads. - GAM access is available. You or your ad ops team can edit yield groups and custom events to register the adapter class name and enable Secure Signals for LiveRamp. See Google's article "Share secure signals with bidders".
- Agree on a class name. The adapter class name registered in the Google Ad Manager UI must match with what's implemented in your code. Work with the ad ops team to be aligned on a class name before implementation.
Overall Steps
- Choose and register your adapter class name in GAM.
- Update your app dependencies.
- Implement the adapter class in your app.
- Make sure the envelope is present at ad time.
- Validate the adapter on device.
Migrating from the ATS Mobile SDK
If you previously used com.liveramp.ats.LRAtsMediationAdapter as part of the ATS Mobile SDK, you must:
- Replace the adapter with your own
RtbAdaptersubclass. - Replace the
com.liveramp.ats.LRAtsMediationAdapterclass name in your GAM configuration with the fully qualified class name (FQCN) of your new adapter.
When you used the ATS Mobile SDK, GAM loaded LRAtsMediationAdapter from the AAR to collect RTB (real-time bidding) signals—the identity envelope string attached to your ad bid request. After you remove the SDK, you provide that adapter yourself as a normal class in your application.
See the table below for comparison:
SDK (LRAtsMediationAdapter) | Your adapter | |
|---|---|---|
| Method | LRAtsManager.getSdkStatus() == READY check in initialize() | Call onInitializationSucceeded() only without checking SDK ready state |
| Envelope supply | Envelope retrieval via LRAtsManager.getEnvelope(callback) | From your ATS API integration in YourEnvelopeManager.getEnvelope(callback) |
| Signal priority | envelope27 , then envelope (type 19), then "" | Same priority in collectSignals() |
| Error | AdError(101,..) | AdError(101,..) |
| Where it's shipped | LiveRamp AAR | Your app module; FQCN (Fully Qualified Class Name) registered in GAM |
1. Choose and Register Your Adapter Class Name in GAM
GAM discovers your adapter by the fully qualified class name (FQCN) you register in the Ad Manager UI. Engineering and ad ops must agree on this name before implementation — the string in GAM and the class name in your code must match exactly (case-sensitive).
Work with your ad ops team to complete these steps in Google Ad Manager:
- In GAM, select Admin → Yield groups (or your existing yield group setup for these ad units).
- Add or edit the Custom event used for this RTB/identity signal.
- In the Class name field, enter your adapter FQCN exactly as you will define it in code. The FQCN also includes your application ID, e.g.
com.yourcompany.yourapp.ads.YourRtbAdapter(replaceYourRtbAdapterwith the class name your team chose). - Enter a Label that identifies this signal for your team.
- Leave Parameter empty unless your implementation requires it.
- Save and publish.
See Google's article "Create and manage yield groups" for more information.
If you've used the ATS Mobile SDK, update every yield group, mediation, or custom event setting that referenced
com.liveramp.ats.LRAtsMediationAdapterand replace it with your new FQCN (e.gcom.yourcompany.yourapp.ads.YourRtbAdapter) .
| Handoff | Owner | Value |
|---|---|---|
| Adapter FQCN | Engineering | com.yourcompany.yourapp.ads.YourRtbAdapter |
| GAM Class name field | Ad ops | Same string as FQCN |
| Ad units | Ad ops | Units tied to this yield group |
2. Implement the Adapter Class
Create a new class in your app module — not in a third-party library. The class must extend Google's RtbAdapter and use the exact FQCN you registered in GAM.
At runtime, GAM calls your adapter in two stages:
- Initialization: When
MobileAds.initialize()is called, the GMA SDK loads your adapter by FQCN and callsinitialize()once. - Per-bid signal collection: When an ad loads, the GMA SDK calls
collectSignals(). Your adapter reads the current envelope from your envelope manager and returns it via the signal callbacks. GAM attaches the signal to the outgoing bid request.
If collectSignals() is called before an envelope is available, return signalCallbacks.onFailure(). Do not rely on initialize() to validate envelope state.
| Method | Purpose |
|---|---|
initialize() | Called once when the Mobile Ads SDK starts. Call onInitializationSucceeded() to report success. No SDK ready-state check required. |
collectSignals() | Called before each bid. Returns envelope27, or envelope (type 19), or "" via onSuccess. Call onFailure if no envelope is available. |
getVersionInfo() / getSDKVersionInfo() | Return a VersionInfo used for mediation reporting and debugging. |
Signal priority
When returning the envelope string, apply this priority order:
envelope27if presentenvelope(type 19) ifenvelope27is absent- Empty string
""if neither is present - On error, call
signalCallbacks.onFailure(AdError(101, ...))
Kotlin
Create YourRtbAdapter.kt in your app module. Rename YourRtbAdapter, the package, and YourEnvelopeManager to match your project before use.
package com.yourcompany.yourapp.ads
import android.content.Context
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.VersionInfo
import com.google.android.gms.ads.mediation.InitializationCompleteCallback
import com.google.android.gms.ads.mediation.MediationConfiguration
import com.google.android.gms.ads.mediation.rtb.RtbAdapter
import com.google.android.gms.ads.mediation.rtb.RtbSignalData
import com.google.android.gms.ads.mediation.rtb.SignalCallbacks
import com.yourcompany.yourapp.envelope.YourEnvelopeManager
class YourRtbAdapter : RtbAdapter() { // rename class per your requirements
override fun initialize(
context: Context,
initializationCompleteCallback: InitializationCompleteCallback,
mediationConfigurations: List<MediationConfiguration>
) {
// No LRAtsManager / SDK ready check — always succeed
initializationCompleteCallback.onInitializationSucceeded()
}
override fun collectSignals(
rtbSignalData: RtbSignalData,
signalCallbacks: SignalCallbacks
) {
// Same callback pattern as LRAtsManager.getEnvelope { envelope, error -> }
YourEnvelopeManager.getEnvelope { envelope, error ->
if (envelope != null) {
if (envelope.envelope27 != null) {
signalCallbacks.onSuccess(envelope.envelope27!!)
} else {
signalCallbacks.onSuccess(envelope.envelope ?: "")
}
} else {
val errorMsg = error?.message ?: "Unable to return envelope."
signalCallbacks.onFailure(
AdError(101, errorMsg, "com.yourcompany.yourapp")
)
}
}
}
override fun getSDKVersionInfo(): VersionInfo = adapterVersionInfo()
override fun getVersionInfo(): VersionInfo = adapterVersionInfo()
private fun adapterVersionInfo(): VersionInfo {
// Return your adapter or app version, e.g. VersionInfo(1, 0, 0)
return VersionInfo(1, 0, 0)
}Java
Create YourRtbAdapter.java in your app module. Rename the class, package, and YourEnvelopeManager to match your project before use.
package com.yourcompany.yourapp.ads;
import android.content.Context;
import androidx.annotation.NonNull;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.VersionInfo;
import com.google.android.gms.ads.mediation.InitializationCompleteCallback;
import com.google.android.gms.ads.mediation.MediationConfiguration;
import com.google.android.gms.ads.mediation.rtb.RtbAdapter;
import com.google.android.gms.ads.mediation.rtb.RtbSignalData;
import com.google.android.gms.ads.mediation.rtb.SignalCallbacks;
import com.yourcompany.yourapp.envelope.YourEnvelopeManager;
import java.util.List;
public class YourRtbAdapter extends RtbAdapter {
@Override
public void initialize(
@NonNull Context context,
@NonNull InitializationCompleteCallback initializationCompleteCallback,
@NonNull List<MediationConfiguration> mediationConfigurations) {
initializationCompleteCallback.onInitializationSucceeded();
}
@Override
public void collectSignals(
@NonNull RtbSignalData rtbSignalData,
@NonNull SignalCallbacks signalCallbacks) {
YourEnvelopeManager.getEnvelope((envelope, error) -> {
if (envelope != null) {
if (envelope.getEnvelope27() != null) {
signalCallbacks.onSuccess(envelope.getEnvelope27());
} else {
signalCallbacks.onSuccess(
envelope.getEnvelope() != null ? envelope.getEnvelope() : "");
}
} else {
String errorMsg = error != null ? error.getMessage() : "Unable to return envelope.";
signalCallbacks.onFailure(new AdError(101, errorMsg, "com.yourcompany.yourapp"));
}
});
}
@NonNull
@Override
public VersionInfo getSDKVersionInfo() {
return new VersionInfo(1, 0, 0);
}
@NonNull
@Override
public VersionInfo getVersionInfo() {
return getSDKVersionInfo();
}
}
3. Update App Dependencies
Remove the LiveRamp ATS SDK and keep only the Google Mobile Ads SDK.
Gradle
// Keep
implementation "com.google.android.gms:play-services-ads:<your-version>"
// Remove
// implementation "com.liveramp:lrats:<version>"
// implementation "com.liveramp:lrats-mediation-adapter:<version>"
ProGuard
Add a keep rule using your adapter FQCN to prevent it from being stripped in a production build:
-keep class com.yourcompany.yourapp.ads.YourRtbAdapter { *; }
Do not declare your adapter in AndroidManifest.xml or reference it in your application code. GAM loads the class by FQCN at runtime when the Mobile Ads SDK initializes and when an ad is requested.
Do not declare your adapter in
AndroidManifest.xmlor reference it in your application code. GAM loads the class by FQCN at runtime when the Mobile Ads SDK initializes and when an ad is requested.
4. Make Sure Envelope is Present at Ad Time
The adapter does not call LiveRamp APIs itself to retrieve or refresh envelopes. It only reads whatever your envelope manager provides at the time GAM calls collectSignals().
Because of this, your ATS API implementation should be configured to:
- Retrieve envelopes for consented identifiers.
- Refresh envelopes when needed before an ad load.
- Supply the envelope string at
collectSignals().
| Action | Endpoint |
|---|---|
| Retrieve | https://api.rlcdn.com/api/identity/v2/envelope/ |
| Refresh | https://api.rlcdn.com/api/identity/v2/envelope/refresh/ |
For more information, see "ATS API Implementation Guide for Mobile Publishers".
5. Verify the Adapter on Device
After implementing the class, confirm that GAM recognizes your adapter at runtime.
MobileAds.initialize(context) { status ->
val fqcn = "com.yourcompany.yourapp.ads.YourRtbAdapter" // your actual FQCN
Log.d("Ads", "Adapter status: ${status.adapterStatusMap[fqcn]}")
}
// After an ad loads:
Log.d("Ads", "Mediation class: ${ad.responseInfo?.mediationAdapterClassName}")You should see your FQCN in the output. If you see com.liveramp.ats.LRAtsMediationAdapter instead, the old SDK is still linked or GAM still points to the old class name.
Validation checklist
- [ ] Adapter FQCN agreed on and registered in the GAM Class name field
- [ ] Old
com.liveramp.ats.LRAtsMediationAdapterremoved from GAM configuration - [ ] LiveRamp SDK (
LRAts) removed from Gradle - [ ] Your
RtbAdapterclass added to your app module with the same FQCN as registered in GAM - [ ]
initialize()always callsonInitializationSucceeded()— no SDK ready-state check - [ ]
collectSignals()passesenvelope27→envelope19→""in that priority order - [ ]
adapterStatusMapshows your FQCN afterMobileAds.initialize() - [ ] Envelope is available from your ATS API flow before or during ad load
- [ ] ProGuard keep rule added for your adapter FQCN