GDPR and CCPA
The General Data Protection Regulation, better known as GDPR, took effect on May 25, 2018. It’s a set of rules designed to give EU citizens more control over their personal data. Any businesses established in the EU or with users based in Europe are required to comply with GDPR or risk facing heavy fines. The California Consumer Privacy Act (CCPA) went into effect on January 1, 2020.
We have put together some resources below to help publishers understand better the steps they need to take to be GDPR compliant.
Step 1. Update Privacy Policy
1.1 Make Sure Your Privacy Policy Includes Information About Advertising ID Collection.
Don’t forget to add information about IP address and advertising ID collection, as well as the link to Appodeal’s privacy policy to your app’s privacy policy in Google Play.
To speed up the process, you could use privacy policy generators —just insert advertising ID, IP address, and location (if you collect a user’s location) in the "Personally Identifiable Information you collect" field (in line with other information about your app) and the link to Appodeal’s privacy policy in "Link to the privacy policy of third party service providers used by the app".
1.2 Add A Privacy Policy To Your Mobile App.
You must add your explicit privacy policies in two places: your app’s Store Listing page and within your app.
You can find detailed instructions on adding your privacy policy to your app on legal service websites. For example, Iubenda, the solution tailored to legal compliance, provides a comprehensive guide on including a privacy policy in your app.
Make sure that your privacy policy website has an SSL-certificate—this point might seem to be obvious, but it’s still essential.
Here’s are two useful resources that you can utilize while working on your app compliance:
- Privacy, Security and Deception regulations (by Google Play)
- Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)
Please note that although we’re always eager to back you up with valuable information, we’re not authorised to provide any legal advice. It’s important to address your questions to lawyers who work specifically in this area.
Step 2. Stack Consent Manager
In order for Appodeal and our ad providers to deliver ads that are more relevant to your users, as a mobile app publisher, you need to collect explicit user consent in the regions covered by GDPR and CCPA.
To get consent for collecting personal data of your users, we suggest you use a ready-made solution - Stack Consent Manager.
Stack Consent Manager comes with a pre-made consent window that you can easily present to your users. That means you no longer need to create your own consent window.
Starting from Appodeal SDK 3.0, Stack Consent Manager is included by default.
Consent will be requested automatically on SDK initialization, and consent form will shown if it is necessary without any additional calls.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Appodeal.initialize(activity, appKey, adTypes, new ApdInitializationCallback() { @Override public void onInitializationFinished(List<? extends ApdInitializationError> list) { //Appodeal initialization finished } }); }
Advanced
Custom Consent Logic
Stack Consent Manager is included in Appodeal SDK by default. Consent will be requested automatically on SDK initialization, and consent form will shown if it is necessary without any additional calls.
If you would like to customize consent managing logic, call the Appodeal initialize method and pass the value of your received consent.
1. If you use the Stack Consent Manager API to process and pass the user's consent:
Publishers need to pass the Consent result object from Stack Consent Manager SDK to the initialize
method of our SDK.
Consent consent = ConsentManager.getInstance(this).getConsent(); Appodeal.initialize(this, "YOUR_APP_KEY", adTypes, consent);
2. If you don't want to use the Stack Content Manager SDK, you can use the old version to pass the user's consent:
Publishers need to pass the boolean consent flag(with 'false' meaning that the user declined to give the consent) to the initialize
method of our SDK.
boolean consent; //Obtain user's consent Appodeal.initialize(activity, "YOUR_APP_KEY", adType, consent); //or Appodeal.updateConsent(consent);
This method is deprecated and will be removed soon.
3. If you want the user to be able to change their mind about the value of Consent, then you can update it using the method:
Appodeal.updateConsent(...);
You can use this method to provide the GDPR/CCPA user consent for ad networks in Appodeal SDK anywhere in your application. You can read more info about GDPR/CCPA here.
Manage Consent Manually
If you wish, you can manage and update consent manually using Stack Consent Manager calls.
Update Consent Status
To update consent call requestConsentInfoUpdate()
on an instance of ConsentManager
.
@Override protected void onCreate(Bundle savedInstanceState) { ConsentManager.getInstance(this) .requestConsentInfoUpdate("YOUR_APP_KEY", new ConsentInfoUpdateListener() { @Override public void onConsentInfoUpdated(Consent consent) { // User's consent status successfully updated. // Initialize the Appodeal SDK with the received Consent object here or show consent window. } @Override public void onFailedToUpdateConsentInfo(ConsentManagerException exception) { // User's consent status failed to update. int errorCode = exception.getCode(); String reason = exception.getReason(); // Initialize the Appodeal SDK with default params. } }); }
Required parameters:YOUR_APP_KEY
- Appodeal app key, you can get it in your personal account ;
ConsentInfoUpdateListener
- listener for result request.
All returned errors are Exception instances with custom codes:
Code | Description |
---|---|
INTERNAL(1) | Error on the SDK side. Includes JS-bridge or encoding/decoding errors |
NETWORKING(2) | HTTP errors, parse request/response |
INCONSISTENT(3) | Incorrect SDK API usage |
requestConsentInfoUpdate
can be requested at any moment of the application lifecycle. We recommend call request it at the application launch. Multiple request calls are allowed.
If the consent information is successfully updated, the updated consent
is provided via the onConsentInfoUpdated()
method of the ConsentInfoUpdateListener
.
Now you can receive information about the previous user consent and regulation zone. Before request these parameters are undefined.
// Get consent status Consent.Status consentStatus = consent.getConsentStatus();
Consent Status | Definition |
---|---|
UNKNOWN | The user has neither granted nor declined consent for personalized or non-personalized ads. |
NON_PERSONALIZED | The user has granted consent for non-personalized ads. |
PARTLY_PERSONALIZED | The user has granted partly(for a few Ad networks) consent for personalized ads. |
PERSONALIZED | The user has granted consent for personalized ads. |
Necessity Of Showing The Consent Window
After the onConsentInfoUpdated method was called, you need to determine if your users are subject to the GDPR and CCPA and whether you should show the consent window for the collection of personal data.
You can check whether to show a Consent Dialog or not. Before request these parameters are undefined(Unknown status)
// Get current ShouldShow status Consent.ShouldShow consentShouldShow = consentManager.shouldShowConsentDialog(); if (consentShouldShow == Consent.ShouldShow.TRUE) { // show dialog }
ShouldShow | Definition |
---|---|
TRUE | The user is within the scope of the GDPR or CCPA laws, the consent window should be displayed. |
FALSE | The user is not within the scope of the GDPR or CCPA laws OR the consent window has already been shown. |
UNKNOWN | The value is undefined(the requestConsentInfoUpdate method was not called). |
Show Consent Window
SDK allows calling consent window API only after request.
After the SDK requests, you can build and load the consent window. Loading allowed in any regulation zone and independent from previous consent.
The required parameter is Context
.
Optional parameter is ConsentFormListener
.
// Create new Consent form listener ConsentFormListener consentFormListener = new ConsentFormListener() { @Override public void onConsentFormLoaded() { // Consent form was loaded. Now you can display consent form as activity or as dialog } @Override public void onConsentFormError(ConsentManagerException error) { // Consent form loading or showing failed. More info can be found in 'error' object // Initialize the Appodeal SDK with default params. } @Override public void onConsentFormOpened() { // Consent form was shown } @Override public void onConsentFormClosed(Consent consent) { // Consent form was closed, you may initialize Appodeal SDK here } }; // Create new Consent form instance ConsentForm consentForm = new ConsentForm.Builder(this) .withListener(consentFormListener) .build(); consentForm.load();
You can check that the consent window is ready or not.
// Indicates that consent window ready to present boolean loaded = consentForm.isLoaded();
You can check that the consent window is showing or not.
// Indicates that consent window is showing boolean showing = consentForm.isShowing();
After the consent window Is ready you can show it as Activity or Dialog.
// Show consent dialog as Activity consentForm.showAsActivity(); // Show consent dialog as Dialog consentForm.showAsDialog();
After the first display of the consent window, the shouldShowConsentDialog method will return the value of Consent.ShouldShow.FALSE in the next sessions.
Advanced
- You can force consent manager to write iAB keys in
SharedPreference
by setting up storage property before the request toConsentManager.Storage
SDK does not remove iAB keys from SharedPreference
and only overrides them.
// Get manager ConsentManager consentManager = ConsentManager.getInstance(this); // Set storage consentManager.setStorage(ConsentManager.Storage.SHARED_PREFERENCE); // Get storage ConsentManager.Storage iabStorage = consentManager.getStorage();
- You can register yourself as a vendor before request. Should be called before request.
Parameter | Type | Description |
---|---|---|
name | String | Display name. Will be displayed in the consent window |
bundle | String | Custom string to check consent result for the vendor |
policyUrl | String | Policy URL |
purposeIds | Array of integers | iAB purposes ids array |
featureIds | Array of integers | iAB features ids array |
legitimateInterestPurposeIds | Array of integers | iAB leg int purposes ids array |
// Get manager ConsentManager consentManager = ConsentManager.getInstance(this); // Create custom vendor Vendor.Builder builder = new Vendor.Builder("Custom Vendor", "customvendor", "https://customvendor.com") .setPurposeIds(Arrays.asList(1, 3, 3)) .setLegitimateInterestPurposeIds(Arrays.asList(1, 2, 4)) .setFeatureIds(Arrays.asList(1, 2)); // Set custom vendor consentManager.setCustomVendor(builder.build()); // Get custom vendor Vendor customVendor = consentManager.getCustomVendor("customvendor"); // Get consent status for vendor Consent.HasConsent vendorStatus = consentManager.hasConsentForVendor("customvendor");
HasConsent | Definition |
---|---|
TRUE | The vendor's consent value is true. |
FALSE | The vendor's consent value is false. |
UNKNOWN | The value is undefined(the requestConsentInfoUpdate method was not called). |
Stack Consent Form callback + Appodeal SDK initialize.
new ConsentFormListener() { @Override public void onConsentFormLoaded() { // Consent form was loaded. Now you can display consent form as activity or as dialog } @Override public void onConsentFormError(ConsentManagerException exception) { // Consent form loading or showing failed. More info can be found in 'error' object // Initialize the Appodeal SDK with default params. Appodeal.initialize(activity, "YOUR_APP_KEY", adTypes); } @Override public void onConsentFormOpened() { // Consent form was shown } @Override public void onConsentFormClosed(Consent consent) { // Consent form was closed, you may initialize Appodeal SDK here Appodeal.initialize(activity, "YOUR_APP_KEY", adTypes, consent); } }