Under the Google EU User Consent Policy, you must make certain disclosures to your users in the European Economic Area (EEA) along with the UK and obtain their consent to use cookies or other local storage, where legally required, and to use personal data (such as AdID) to serve ads. This policy reflects the requirements of the EU ePrivacy Directive and the General Data Protection Regulation (GDPR).
To support publishers in meeting their duties under this policy, Google offers the User Messaging Platform (UMP) SDK. The UMP SDK has been updated to support the latest IAB standards. All of these configurations can now conveniently be handled in AdMob privacy & messaging.
Prerequisites
- Complete the Get Started guide
- Android API level 21 or higher (for Android)
- If you're working on GDPR-related requirements, read How IAB requirements affect EU consent messages
Create a message type
Create user messages with one of the available user message types under the Privacy & messaging tab of your AdMob account. The UMP SDK attempts to display a user message created from the AdMob Application ID set in your project. If no message is configured for your application, the SDK returns an error.
For more details, see About privacy and messaging.
Install the SDK
Follow the steps to install the Google Mobile Ads (GMA) C++ SDK. The UMP C++ SDK is included in the GMA C++ SDK.
Ensure that you configure your app's AdMob app ID in the project before continuing.
In your code, initialize the UMP SDK by calling
ConsentInfo::GetInstance()
.- On Android, you need to pass in the
JNIEnv
andActivity
provided by the NDK. You only need to do this the first time you callGetInstance()
. - Alternatively, if you're already using the Firebase C++
SDK in your app, you can pass
in a
firebase::App
the first time you callGetInstance()
.
#include "firebase/gma/ump.h" namespace ump = ::firebase::gma::ump; // Initialize using a firebase::App void InitializeUserMessagingPlatform(const firebase::App& app) { ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(app); } // Initialize without a firebase::App #ifdef ANDROID void InitializeUserMessagingPlatform(JNIEnv* jni_env, jobject activity) { ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(jni_env, activity); } #else // non-Android void InitializeUserMessagingPlatform() { ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(); } #endif
- On Android, you need to pass in the
Subsequent calls to ConsentInfo::GetInstance()
all return the same instance.
If you're finished using the UMP SDK, you can shut down the SDK by deleting the
ConsentInfo
instance:
void ShutdownUserMessagingPlatform() {
ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
delete consent_info;
}
Use a Future
to monitor asynchronous operations
A
firebase::Future
provides you a way to determine the completion status of asynchronous method
calls.
All UMP C++ functions and method calls that operate asynchronously return a
Future
, and also provide a "last result" function to retrieve the Future
from the most recent operation.
There are two ways to obtain a result from a Future
:
- Call
OnCompletion()
, passing in your own callback function, which is called when the operation completes. - Periodically check the
Future
'sstatus()
. When the status changes fromkFutureStatusPending
tokFutureStatusCompleted
, the operation has been completed.
After the asynchronous operation is completed, you should check the
Future
's error()
to obtain the operation's error
code. If the error code is 0
(kConsentRequestSuccess
or kConsentFormSuccess
),
the operation completed successfully; otherwise, check the error code and
error_message()
to determine what went wrong.
Completion callback
Here is an example of how to use OnCompletion
to set a completion callback,
which is called when the asynchronous operation completes.
void MyApplicationStart() {
// [... other app initialization code ...]
ump::ConsentInfo *consent_info = ump::ConsentInfo::GetInstance();
// See the section below for more information about RequestConsentInfoUpdate.
firebase::Future<void> result = consent_info->RequestConsentInfoUpdate(...);
result.OnCompletion([](const firebase::Future<void>& req_result) {
if (req_result.error() == ump::kConsentRequestSuccess) {
// Operation succeeded. You can now call LoadAndShowConsentFormIfRequired().
} else {
// Operation failed. Check req_result.error_message() for more information.
}
});
}
Update loop polling
In this example, after an asynchronous operation is started at app launch, the results are checked elsewhere, in the game's update loop function (which runs once per frame).
ump::ConsentInfo *g_consent_info = nullptr;
bool g_waiting_for_request = false;
void MyApplicationStart() {
// [... other app initialization code ...]
g_consent_info = ump::ConsentInfo::GetInstance();
// See the section below for more information about RequestConsentInfoUpdate.
g_consent_info->RequestConsentInfoUpdate(...);
g_waiting_for_request = true;
}
// Elsewhere, in the game's update loop, which runs once per frame:
void MyGameUpdateLoop() {
// [... other game logic here ...]
if (g_waiting_for_request) {
// Check whether RequestConsentInfoUpdate() has finished.
// Calling "LastResult" returns the Future for the most recent operation.
firebase::Future<void> result =
g_consent_info->RequestConsentInfoUpdateLastResult();
if (result.status() == firebase::kFutureStatusComplete) {
g_waiting_for_request = false;
if (result.error() == ump::kConsentRequestSuccess) {
// Operation succeeded. You can call LoadAndShowConsentFormIfRequired().
} else {
// Operation failed. Check result.error_message() for more information.
}
}
}
}
For more information about firebase::Future
, see the Firebase C++ SDK
documentation
and the GMA C++ SDK documentation.
Request for consent information
You should request an update of the user's consent information at every app
launch, using RequestConsentInfoUpdate()
. This determines
whether your user needs to provide consent if they haven't done so already, or
if their consent has expired.
#include "firebase/gma/ump.h"
namespace ump = ::firebase::gma::ump;
void MyApplicationStart() {
ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
// Create a ConsentRequestParameters struct.
ump::ConsentRequestParameters params;
// Set tag for under age of consent. False means users are NOT under age
// of consent.
params.tag_for_under_age_of_consent = false;
consent_info->RequestConsentInfoUpdate(params).OnCompletion(
[](const Future<void>& result) {
if (result.error() != ump::kConsentRequestSuccess) {
LogMessage("Error requesting consent update: %s", result.error_message());
} else {
// Consent status is now available.
}
});
}
See above for an example of checking for completion using update loop polling rather than a completion callback.
Load and show a consent form if required
After you have received the most up-to-date consent status, call
LoadAndShowConsentFormIfRequired()
on the
ConsentInfo
class to load a consent form. If the
consent status is required, the SDK loads a form and immediately presents it
from the provided FormParent
. The Future
is completed after the form is dismissed. If consent is not required, the Future
is completed immediately.
void MyApplicationStart(ump::FormParent parent) {
ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
// Create a ConsentRequestParameters struct..
ump::ConsentRequestParameters params;
// Set tag for under age of consent. False means users are NOT under age of consent.
params.tag_for_under_age_of_consent = false;
consent_info->RequestConsentInfoUpdate(params).OnCompletion(
[*](const Future<void>& req_result) {
if (req_result.error() != ump::kConsentRequestSuccess) {
// req_result.error() is a kConsentRequestError enum.
LogMessage("Error requesting consent update: %s", req_result.error_message());
} else {
consent_info->LoadAndShowConsentFormIfRequired(parent).OnCompletion(
[*](const Future<void>& form_result) {
if (form_result.error() != ump::kConsentFormSuccess) {
// form_result.error() is a kConsentFormError enum.
LogMessage("Error showing consent form: %s", form_result.error_message());
} else {
// Either the form was shown and completed by the user, or consent was not required.
}
});
}
});
}
If you need to perform any actions after the user has made a choice or dismissed
the form, place that logic in the code that handles the Future
returned by LoadAndShowConsentFormIfRequired()
.
Request ads
Before requesting ads in your app, check if you have obtained consent
from the user using ConsentInfo::GetInstance()‑>CanRequestAds()
. There are two
places to check while gathering consent:
- Once consent has been gathered in the current session.
- Immediately after you have called
RequestConsentInfoUpdate()
. It is possible consent has been obtained in the previous session. As a latency best practice, we recommend not waiting for the callback to complete so you can start loading ads as soon as possible after your app launches.
If an error occurs during the consent gathering process, you should still attempt to request ads. The UMP SDK uses the consent status from the previous session.
The following complete example uses update loop polling, but you can also use
OnCompletion
callbacks to monitor asynchronous operations. Use
whichever technique fits your code structure better.
#include "firebase/future.h"
#include "firebase/gma/gma.h"
#include "firebase/gma/ump.h"
namespace gma = ::firebase::gma;
namespace ump = ::firebase::gma::ump;
using firebase::Future;
ump::ConsentInfo* g_consent_info = nullptr;
// State variable for tracking the UMP consent flow.
enum { kStart, kRequest, kLoadAndShow, kInitGma, kFinished, kErrorState } g_state = kStart;
bool g_ads_allowed = false;
void MyApplicationStart() {
g_consent_info = ump::ConsentInfo::GetInstance(...);
// Create a ConsentRequestParameters struct..
ump::ConsentRequestParameters params;
// Set tag for under age of consent. False means users are NOT under age of consent.
params.tag_for_under_age_of_consent = false;
g_consent_info->RequestConsentInfoUpdate(params);
// CanRequestAds() can return a cached value from a previous run immediately.
g_ads_allowed = g_consent_info->CanRequestAds();
g_state = kRequest;
}
// This function runs once per frame.
void MyGameUpdateLoop() {
// [... other game logic here ...]
if (g_state == kRequest) {
Future<void> req_result = g_consent_info->RequestConsentInfoUpdateLastResult();
if (req_result.status() == firebase::kFutureStatusComplete) {
g_ads_allowed = g_consent_info->CanRequestAds();
if (req_result.error() == ump::kConsentRequestSuccess) {
// You must provide the FormParent (Android Activity or iOS UIViewController).
ump::FormParent parent = GetMyFormParent();
g_consent_info->LoadAndShowConsentFormIfRequired(parent);
g_state = kLoadAndShow;
} else {
LogMessage("Error requesting consent status: %s", req_result.error_message());
g_state = kErrorState;
}
}
}
if (g_state == kLoadAndShow) {
Future<void> form_result = g_consent_info->LoadAndShowConsentFormIfRequiredLastResult();
if (form_result.status() == firebase::kFutureStatusComplete) {
g_ads_allowed = g_consent_info->CanRequestAds();
if (form_result.error() == ump::kConsentRequestSuccess) {
if (g_ads_allowed) {
// Initialize GMA. This is another asynchronous operation.
firebase::gma::Initialize();
g_state = kInitGma;
} else {
g_state = kFinished;
}
// Optional: shut down the UMP SDK to save memory.
delete g_consent_info;
g_consent_info = nullptr;
} else {
LogMessage("Error displaying consent form: %s", form_result.error_message());
g_state = kErrorState;
}
}
}
if (g_state == kInitGma && g_ads_allowed) {
Future<gma::AdapterInitializationStatus> gma_future = gma::InitializeLastResult();
if (gma_future.status() == firebase::kFutureStatusComplete) {
if (gma_future.error() == gma::kAdErrorCodeNone) {
g_state = kFinished;
// TODO: Request an ad.
} else {
LogMessage("Error initializing GMA: %s", gma_future.error_message());
g_state = kErrorState;
}
}
}
}
Privacy options
Some consent forms require the user to modify their consent at any time. Adhere to the following steps to implement a privacy options button if required.
To accomplish this:
- Implement a UI element, such as a button in your app's settings page, that can trigger a privacy options form.
- Once
LoadAndShowConsentFormIfRequired()
completes, checkgetPrivacyOptionsRequirementStatus()
to determine whether to display the UI element that can present the privacy options form. - When a user interacts with your UI element, call
showPrivacyOptionsForm()
to show the form so the user can update their privacy options at any time.
Testing
If you want to test the integration in your app as you're developing, follow the steps below to programmatically register your test device.
- Call
RequestConsentInfoUpdate()
. Check the log output for a message that looks like the one below, which shows you your device ID and how to add it as a test device:
Android
Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("33BE2250B43518CCDA7DE426D04EE231") to set this as a debug device.
iOS
<UMP SDK>To enable debug mode for this device, set: UMPDebugSettings.testDeviceIdentifiers = @[2077ef9a63d2b398840261c8221a0c9b]
Copy your test device ID to your clipboard.
Modify your code to set
ConsentRequestParameters.debug_settings.debug_device_ids
to a list of your test device IDs.
void MyApplicationStart() {
ump::ConsentInfo consent_info = ump::ConsentInfo::GetInstance(...);
ump::ConsentRequestParameters params;
params.tag_for_under_age_of_consent = false;
params.debug_settings.debug_device_ids = {"TEST-DEVICE-HASHED-ID"};
consent_info->RequestConsentInfoUpdate(params);
}
Force a geography
The UMP SDK provides a way to test your app's behavior as though the device was
located in the EEA or UK using ConsentRequestParameters.debug_settings.debug_geography
. Note that
debug settings only work on test devices.
void MyApplicationStart() {
ump::ConsentInfo consent_info = ump::ConsentInfo::GetInstance(...);
ump::ConsentRequestParameters params;
params.tag_for_under_age_of_consent = false;
params.debug_settings.debug_device_ids = {"TEST-DEVICE-HASHED-ID"};
// Geography appears as EEA for debug devices.
params.debug_settings.debug_geography = ump::kConsentDebugGeographyEEA
consent_info->RequestConsentInfoUpdate(params);
}
Reset consent state
In testing your app with the UMP SDK, you might find it helpful to reset the
state of the SDK so that you can simulate a user's first install experience.
The SDK provides the Reset()
method to do this.
ConsentInfo::GetInstance()->Reset();