The Google User Messaging Platform (UMP) SDK is a privacy and messaging tool to
help you manage privacy choices. For more information, see

[About Privacy \& messaging](https://support.google.com/admob/answer/10107561).


## Prerequisites

- Android API level 21 or higher (for Android)


## Create a message type

Create user messages with one of the

[Available user message types](https://support.google.com/admob/answer/10114020)

under the **Privacy \& messaging** tab of your

AdMob

account. The UMP SDK attempts to display a
privacy message created from the AdMob Application ID
set in your project.
For more details, see [About privacy and messaging](https://support.google.com/admob/answer/10107561).

## Install the SDK

1. Follow the steps to [install the Firebase C++
   SDK](https://developers.google.com/admob/cpp/quick-start#install_the_google_mobile_ads_c_sdk). The UMP C++
   SDK is included in the Firebase C++ SDK.

2. Ensure that you [configure your app's AdMob app
   ID](https://developers.google.com/admob/cpp/quick-start#configure_your_apps_admob_app_id) in the project
   before continuing.

3. In your code, initialize the UMP SDK by calling
   [`ConsentInfo::GetInstance()`](https://developers.google.com/admob/cpp/reference/class/firebase/gma/ump/consent-info.md.txt#public-static-functions_1).

   - On Android, you need to pass in the `JNIEnv` and `Activity` provided by the NDK. You only need to do this the first time you call `GetInstance()`.
   - Alternatively, if you're already using the [Firebase C++
     SDK](https://firebase.google.com/docs/cpp/setup) in your app, you can pass in a `firebase::App` the first time you call `GetInstance()`.

   ```c++
   #include "firebase/ump/ump.h"

   namespace ump = ::firebase::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
   ```

Subsequent calls to `ConsentInfo::GetInstance()` all return the same instance.

> [!NOTE]
> **Note:** The `GetInstance()` methods have an optional [`firebase::InitResult`](https://firebase.google.com/docs/reference/cpp/namespace/firebase#initresult) output parameter, which returns `firebase::kInitResultSuccess` if the SDK was initialized successfully, or `firebase::kInitResultFailedMissingDependency` if any required dependencies are missing from your application. Ensure you have followed all of the [installation
> steps](https://developers.google.com/admob/cpp/quick-start#install_the_google_mobile_ads_c_sdk) for your platform if this error occurs.

If you're finished using the UMP SDK, you can shut down the SDK by deleting the
`ConsentInfo` instance:

```c++
void ShutdownUserMessagingPlatform() {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
  delete consent_info;
}
```

> [!NOTE]
> **Note:** You don't need to initialize the Firebase C++ SDK prior to initializing the User Messaging Platform SDK.

## Use a `Future` to monitor asynchronous operations

A
[`firebase::Future`](https://firebase.google.com/docs/reference/cpp/class/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`:

1. Call [`OnCompletion()`](https://firebase.google.com/docs/reference/cpp/class/firebase/future#oncompletion), passing in your own callback function, which is called when the operation completes.
2. Periodically check the `Future`'s `status()`. When the [status](https://firebase.google.com/docs/reference/cpp/namespace/firebase#futurestatus) changes from `kFutureStatusPending` to `kFutureStatusCompleted`, 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`](https://developers.google.com/admob/cpp/reference/namespace/firebase/ump.md.txt#consentrequesterror)
or [`kConsentFormSuccess`](https://developers.google.com/admob/cpp/reference/namespace/firebase/ump.md.txt#consentformerror)),
the operation completed successfully; otherwise, check the error code and
`error_message()` to determine what went wrong.

> [!NOTE]
> **Note:** The error code returned by `RequestConsentInfoUpdate()` uses the enum [`ConsentRequestError`](https://developers.google.com/admob/cpp/reference/namespace/firebase/ump.md.txt#consentrequesterror); all other operations use [`ConsentFormError`](https://developers.google.com/admob/cpp/reference/namespace/firebase/ump.md.txt#consentformerror).

### Completion callback

Here is an example of how to use `OnCompletion` to set a completion callback,
which is called when the asynchronous operation completes.

```c++
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).

```c++
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](https://firebase.google.com/docs/reference/cpp/class/firebase/future)
and the [GMA C++ SDK documentation](https://developers.google.com/admob/cpp/quick-start.md.txt#futures).

> [!NOTE]
> **Note:** In the UMP C++ SDK, all asynchronous operations return `firebase::Future&lt;void&gt;`.

## Get the user's consent information

You should request an update of the user's consent information at every app
launch, using `

RequestConsentInfoUpdate()`. This request checks the following:

- **Whether consent is required**. For example, consent is required for the first time, or the previous consent decision expired.
- **Whether a privacy options entry point is required**. Some privacy messages require apps to allow users to modify their privacy options at any time.

```c++
#include "firebase/ump/ump.h"

namespace ump = ::firebase::ump;

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());
      }
      // Consent information is successfully updated.
    });
}
```

> [!WARNING]
> **Warning:** We don't recommend that you check consent status in another way, such as the cache in your app or a previously saved consent string. These methods might return outdated user consent data.

## Load and present the privacy message form

After you have received the most up-to-date consent status, call
`

LoadAndShowConsentFormIfRequired()` to load any forms required to
collect user consent. After loading, the forms present immediately.

> [!IMPORTANT]
> **Key Point:** If no privacy message forms require collection of user consent prior to requesting ads, the callback is invoked immediately.

```c++
#include "firebase/ump/ump.h"

namespace ump = ::firebase::ump;

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 privacy message form: %s", form_result.error_message());
          } else {
            // Either the form was shown and completed by the user, or consent was not required.
          }
        });
      }
    });
}
```

See [above](https://developers.google.com/admob/cpp/privacy.md.txt#futures) for an example of checking for completion using
update loop polling rather than a completion callback.

> [!NOTE]
> **Note:** Depending on the platform, [`FormParent`](https://developers.google.com/admob/cpp/reference/namespace/firebase/ump.md.txt#formparent) is either a JNI `jobject` reference to an Android [`Activity`](https://developer.android.com/reference/android/app/Activity), or a pointer to an iOS [`UIViewController`](https://developer.apple.com/documentation/uikit/uiviewcontroller), cast to an `FormParent` type.

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()`.

## Privacy options

Some privacy message forms are presented from a publisher-rendered privacy
options entry point, letting users manage their privacy options at any time.
To learn more about which message your users see at the privacy options
entry point, see

[Available user message types](https://support.google.com/admob/answer/10114020).


## Request ads with user consent

Before requesting ads, use `
ConsentInfo::GetInstance()‑>
CanRequestAds()` to check if you've
obtained consent from the user:

Listed are the following places to check if you can request ads while gathering
consent:

- After the UMP SDK gathers consent in the current session.
- Immediately after you have called `

  RequestConsentInfoUpdate()`. The UMP SDK might have obtained consent in the previous app session.

> [!IMPORTANT]
> **Important:** `
> ConsentInfo::GetInstance()‑>
> CanRequestAds()` always returns `false` until you have called `
>
> RequestConsentInfoUpdate()`

If an error occurs during the consent gathering process, check if you can
request ads. The UMP SDK uses the consent status from the previous app session.

#### Prevent redundant ad request work

As you check `
ConsentInfo::GetInstance()‑>
CanRequestAds()` after gathering consent and after calling
`

RequestConsentInfoUpdate()`, ensure your logic prevents redundant ad requests that
might result in both checks returning `true`. For example, with a boolean
variable.

The following complete example uses update loop polling, but you can [also use
`OnCompletion` callbacks](https://developers.google.com/admob/cpp/privacy#futures) to monitor asynchronous operations. Use
whichever technique fits your code structure better.

```c++
#include "firebase/future.h"
#include "firebase/gma/gma.h"
#include "firebase/ump/ump.h"

namespace gma = ::firebase::gma;
namespace ump = ::firebase::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 privacy message 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;
      }
    }
  }
}
```

## Testing

If you want to test the integration in your app as you're developing, follow
these steps to programmatically register your test device. Be sure to remove the
code that sets these test device IDs before you release your app.

1. Call `

   RequestConsentInfoUpdate()`.
2. Check the log output for a message similar to the following example, which
   shows 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]

3. Copy your test device ID to your clipboard.

4. Modify your code to set
   `ConsentRequestParameters.debug_settings.debug_device_ids` to a list of your test device IDs.

   ```c++
   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
were located in various regions, such as the European Economic Area (EEA), the
United Kingdom (UK), and Switzerland using
`debug_settings.debug_geography`. Note that
debug settings only work on test devices.

```c++
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

When 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.

```c++
  ConsentInfo::GetInstance()->Reset();
```

> [!CAUTION]
> **Caution:** This method is intended to be used for testing purposes only. You shouldn't call `Reset()` in production code.

<br />