Compliant with GDPR, CCPA, COPPA, LGPD, PECR, PDPA, PIPEDA, and more.
UniConsent CMP 是用于在 Android 应用中处理 GDPR IAB TCF 2.4 同意管理的软件包。您可以在 "demo" 目录中找到集成了 UniConsent CMP 的演示应用。
将 UniConsentSDK-release.aar 添加到项目的 libs/ 目录中,并更新 build.gradle:
implementation files('libs/UniConsentSDK-release.aar')
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
implementation 'com.iabtcf:iabtcf-core:2.0.10'
implementation 'com.iabtcf:iabtcf-decoder:2.0.10'
在集成 SDK 之前,请在 UniConsent 控制台 中自定义同意横幅的外观,以匹配您的品牌并优化同意率。
前往 Projects → 选择您的项目 → Settings → Step 5: UI & Style Settings 进行配置:
如需更精确的控制,请在 Step 5 的 CSS Content 字段中添加自定义 CSS。建议使用此功能让横幅呈现原生应用的观感,以获得最佳同意率:
/* Example: Style the accept button to match your brand */
.unic-btn-accept {
background-color: #4CAF50;
border-radius: 8px;
font-weight: 600;
}
/* Example: Adjust banner font */
.unic-banner {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* Example: Make the reject button less prominent */
.unic-btn-reject {
background-color: transparent;
border: 1px solid #ccc;
color: #666;
}
提示: 具有原生应用观感的精心设计的同意界面通常能获得更高的同意率。用户更可能对符合预期外观和风格的横幅做出积极响应。
要在应用中使用 UniConsent CMP,请按照以下步骤操作:
使用客户经理提供的 App ID 初始化 CMP:
UniConsent UniConsentCMP = UniConsent.getInstance();
UniConsentCMP.setAppId("YOUR_APP_ID");
// show the CMP when consent is needed, once the SDK is ready
UniConsentCMP.enableAutoLaunch();
UniConsentCMP.init(this); // this: your Activity
显示 CMP 界面:
// Display CMP as full-screen page (default)
UniConsent.getInstance().launchCMP();
// Display CMP as a modal bottom sheet
UniConsent.getInstance().launchCMP(CMPDisplayMode.BOTTOM_SHEET);
// Display CMP as a center dialog
UniConsent.getInstance().launchCMP(CMPDisplayMode.DIALOG);
您还可以使用指定的阶段和显示模式启动:
// Launch at a specific stage with a display mode
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET);
// Bottom sheet at 70% of the screen height
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET, 0.7);
// Center dialog at 80% width and 60% height
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.DIALOG, 0.8, 0.6);
// Or set a default display mode for all launches
UniConsent.getInstance().setDisplayMode(CMPDisplayMode.BOTTOM_SHEET);
UniConsent.getInstance().launchCMP();
自 SDK 26.8.0 版本起可用。
两个比例参数均为可选,所有显示模式不传它们也能正常工作。仅在需要覆盖默认尺寸时才传入比例(相对屏幕的比例,需大于 0 且不超过 1);无效值会抛出 IllegalArgumentException:
| 参数 | 适用于 | 省略时的默认值 |
|---|---|---|
heightRatio | BOTTOM_SHEET、DIALOG | 面板:屏幕的 90%。对话框:70% |
widthRatio | 仅 DIALOG | 屏幕宽度的 90%,最大 500dp |
不适用于所选显示模式的比例会被忽略(FULL_SCREEN 会忽略两者)。
当 vendorList 更新时自动检查同意是否过期:
// Check if consent should be requested (e.g. first visit, or vendor list updated)
// to check yourself instead of enableAutoLaunch(): subscribe before init(), which runs in the background
// and publishes READY when it finishes (import com.uniconsent.sdk.EventType)
UniConsentCMP.subscribe(event -> {
if (event.getType() == EventType.READY && UniConsentCMP.shouldRequestConsent()) {
UniConsentCMP.launchCMP();
}
});
UniConsentCMP.init(this);
获取 tcString(如需要):
// Get tcString
UniConsent.getInstance().getTCString();
读取同意状态:
// Read consent status
UniConsent.getInstance().hasIABPurposeConsent(1);
UniConsent.getInstance().hasIABVendorConsent(1);
重置同意状态(如需要):
// Reset consent status
UniConsent.getInstance().clearData();
自 SDK 26.7.0 版本起可用。如果您构建自己的首层同意界面而不显示 CMP webview,可以直接保存同意状态:
// Grant consent for all purposes, special features and vendors
boolean saved = UniConsent.getInstance().agreeAll();
// Reject all purposes, special features and vendors
boolean saved = UniConsent.getInstance().rejectAll();
这两个 API 保存与 CMP UI 流程相同的同意值(IAB TCF 键、附加同意和同意模式信号),并向订阅者发送相同的关闭事件。
对于美国和加拿大用户,这两个 API 还会按照与 CMP 界面相同的规则,保存适用于该用户的 CCPA、US State Privacy 和 IAB TCF 加拿大选择,并写入 IAB GPP 和 US Privacy 键。请在 UniConsent 控制台的项目设置中开启 API-Only 模式,这些地区便不会显示 CMP 横幅。
在适用 Cookie 类别横幅的地区,它们还会保存 Cookie 类别选择(全部类别,或仅“绝对必要”),以及项目的自定义目的和自定义供应商。
要确定显示哪个首层界面,请调用 getPolicy()(自 SDK 26.10.0 版本起)。它按照与 CMP 相同的规则返回适用于该用户的政策:gdpr(IAB TCF)、tcfca(IAB TCF 加拿大)、uss(US State Privacy)、ccpa、categories(见下文 Cookie 类别)或 none。
UniConsent cmp = UniConsent.getInstance();
if (cmp.shouldRequestConsent()) {
// 用户需要做出选择:显示该政策对应的首层界面
String policy = cmp.getPolicy(); // gdpr、tcfca 或 categories
}
注意: 请仅在
init()完成后(例如 READY 事件之后)调用这些 API。它们依赖初始化期间获取的供应商列表和项目配置,如果无法保存同意状态则返回false。重要: 您的自定义首层界面仍须满足 IAB TCF 要求(披露目的和供应商,并以同等显著程度提供接受和拒绝选项)。用户仍应能够打开完整的 CMP 界面(
launchCMP())进行精细选择。
如果您的应用打开了加载带有 UniConsent CMP 标签的网页的 WebView,您可以传递原生同意状态,这样 CMP 标签会识别已有的同意记录,不会再次显示横幅。
在页面开始加载时、CMP 标签运行之前注入同意状态:
WebView webView = findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, android.graphics.Bitmap favicon) {
UniConsent.getInstance().syncConsent(view);
}
});
webView.loadUrl("https://example.com");
import com.uniconsent.sdk.CMPDisplayMode;
import com.uniconsent.sdk.Event;
import com.uniconsent.sdk.EventHandler;
import com.uniconsent.sdk.EventType;
import com.uniconsent.sdk.Stage;
import com.uniconsent.sdk.UniConsent;
public class MainActivity extends AppCompatActivity implements EventHandler {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UniConsent UniConsentCMP = UniConsent.getInstance();
UniConsentCMP.setAppId("YOUR_APP_ID");
// show the CMP when consent is needed, once the SDK is ready
UniConsentCMP.enableAutoLaunch();
UniConsentCMP.init(this);
// register callback events
UniConsentCMP.subscribe(this);
}
public void openUniConsentUI(View view) {
// Full screen
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen);
}
public void openAsBottomSheet(View view) {
// Bottom sheet
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET);
}
public void openAsDialog(View view) {
// Center dialog
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.DIALOG);
}
@Override
public void handle(Event event) {
UniConsent.getInstance().hasIABVendorConsent(1);
}
}
package com.example.myapplication
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.MyApplicationTheme
import com.uniconsent.sdk.CMPDisplayMode
import com.uniconsent.sdk.Event
import com.uniconsent.sdk.EventHandler
import com.uniconsent.sdk.Stage
import com.uniconsent.sdk.UniConsent
class MainActivity : ComponentActivity(), EventHandler {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val cmp = UniConsent.getInstance()
cmp.setAppId("YOUR_APP_ID")
// show the CMP when consent is needed, once the SDK is ready
cmp.enableAutoLaunch()
cmp.init(this)
cmp.subscribe(this)
setContent {
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen)
}) {
Text("Privacy Settings")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
UniConsent.getInstance().launchCMP(
Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET
)
}) {
Text("Privacy Settings (Bottom Sheet)")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
UniConsent.getInstance().launchCMP(
Stage.GDPRFirstScreen, CMPDisplayMode.DIALOG
)
}) {
Text("Privacy Settings (Dialog)")
}
}
}
}
}
}
override fun handle(event: Event?) {
Log.d("CMP_EVENT", event.toString())
UniConsent.getInstance().hasIABVendorConsent(1)
}
}
<uses-permission android:name="android.permission.INTERNET" />
可以在指定界面打开 CMP,例如从应用中的“隐私设置”链接打开。请求的界面总会打开。对于美国用户,设置界面会打开适用于他们的 CCPA 或 US State Privacy 设置。加拿大用户看到的是 IAB TCF 界面的 IAB TCF Canada 版本。
| 界面 | 显示内容 |
|---|---|
Stage.GDPRFirstScreen | 首层界面(横幅) |
Stage.GDPRSecondScreen | 设置(目的) |
Stage.GDPRVendorsScreen | 供应商 |
Stage.CCPAFirstScreen | CCPA 首层界面 |
Stage.CCPASecondScreen | CCPA 设置 |
Stage.USStateFirstScreen | US State Privacy 首层界面 |
Stage.USStateSecondScreen | US State Privacy 设置 |
Stage.CategoriesFirstScreen | Cookie 类别首层界面(横幅) |
Stage.CategoriesSecondScreen | Cookie 类别设置 |
UniConsent cmp = UniConsent.getInstance();
// 打开设置界面
cmp.launchCMP(Stage.GDPRSecondScreen);
// 以法语而非设备语言显示 CMP(两个字母的代码)
cmp.setLanguage("FR");
// SDK 和 CMP 页面的调试日志,正式版本请使用 LogLevel.ERROR(默认)
cmp.setLogLevel(LogLevel.DEBUG);
// 仅用于测试:按其他地区的用户显示 CMP,"EU"、"CA"(加拿大)、"CAQC"(魁北克)、"US" 加上州代码(如 "USCA" 或 "USTX")、"BR"、"CN"、"ZA"、"VN" 或其他任何国家代码,或 "N"(以上地区以外)。也适用于 getPolicy() 和 shouldRequestConsent();正式版本中请删除
cmp.setTestRegion("CA");
// 如果 CMP 已打开则关闭
cmp.dismissCMP();
// 用户已做出选择:IAB TCF、CCPA、US State Privacy 或 IAB TCF 加拿大
boolean given = cmp.isConsentGiven();
// IAB TCF 同意创建后的天数,没有同意时为 0
int days = cmp.getConsentAge();
// 用户所在地区,以及美国和加拿大用户的州或省代码(未知时为空)
String geo = cmp.getGEO();
String state = cmp.getGEOState();
CMP 会按照 IAB 规范,将 IAB GPP 键和 US Privacy 键存储到应用的默认 SharedPreferences中。应用内的广告 SDK 会从那里读取,无需修改代码。在 UniConsent 控制台的项目设置中启用 GPP、US State Privacy 或 IAB TCF 加拿大后会写入 GPP 键;只要启用 CCPA,就会写入 US Privacy 键。
| 键 | 值 |
|---|---|
IABGPP_HDR_Version | GPP 版本(1) |
IABGPP_HDR_Sections | GPP 字符串中的部分 ID,以 _ 分隔 |
IABGPP_HDR_GppString | 完整的 GPP 字符串 |
IABGPP_GppSID | 适用于该用户的部分 ID,以 _ 分隔;均不适用时为 -1 |
IABGPP_2_String | IAB TCF EU 字符串(适用 GDPR 时) |
IABGPP_5_String | IAB TCF 加拿大字符串(适用加拿大 TCF 时) |
IABGPP_6_String | US Privacy 字符串(适用 CCPA 时) |
IABGPP_7_String | US National(usnat)字符串(适用 US State Privacy 或 CCPA 时) |
IABGPP_8_String 至 IABGPP_27_String | 用户所在州的部分(8 加利福尼亚至 27 罗得岛),适用 US State Privacy 时写入,或在 CCPA 下写入加利福尼亚,遵循 GPP Sections 设置 |
IABUSPrivacy_String | 只要启用 CCPA 就会写入:在 CCPA 地区为用户的选择,否则为 1--- |
每次加载 CMP 以及每次做出同意选择后,这些键都会更新。哪些用户获得 IABGPP_ 键取决于项目设置中的 GPP Signals:加拿大用户始终获得(IAB TCF Canada),开启 US 时为美国用户写入,开启 EU 时为欧盟用户写入。开启 US 或 EU 时,不属于任何地区的用户会获得不含任何部分的键;否则不会写入这些键。如需自行读取:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String gppString = prefs.getString("IABGPP_HDR_GppString", "");
String gppSid = prefs.getString("IABGPP_GppSID", "");
应用支持与网站相同的美国和加拿大选项:CCPA、US State Privacy(包括州列表、GPP Sections 和 MSPA 设置)以及 IAB TCF 加拿大。对于适用 IAB TCF 加拿大的加拿大用户,在其做出选择之前,shouldRequestConsent() 会返回 true。美国隐私采用退出(opt-out)模式,因此不会主动询问美国用户:当您的应用打开 CMP 时(例如通过隐私设置链接),他们会看到美国横幅或设置。在此之前,启用 CCPA 或 US State Privacy 时,SDK 会为美国用户写入默认的 US Privacy 和 GPP 键,不显示任何内容:未退出;若设备关闭了广告跟踪,则为已退出。新的 Mobile App 项目默认关闭 US State Privacy,请在控制台中开启。
当用户在设备上关闭广告跟踪时,CMP 会将其视为美国用户对出售、共享和定向广告的退出,与网站上的 Global Privacy Control 相同。在 iOS 上指 App Tracking Transparency 中不允许跟踪;在 Android 上指已删除广告 ID 或限制广告跟踪(应用包含 Google Play 服务时通过其读取)。用户之后在横幅中保存的选择优先。
SDK 也会返回已存储的字符串:
String gppString = UniConsent.getInstance().getGPPString();
String gppSid = UniConsent.getInstance().getGPPSID();
String usPrivacy = UniConsent.getInstance().getUSPrivacyString();
自 SDK 26.10.0 版本起可用。当某个地区使用 Cookie 类别而不是 IAB TCF 或美国政策时,CMP 会显示包含四个类别的横幅:绝对必要、性能、功能性、定向或广告。适用于:
选择加入(opt-in)类别初始只开启“绝对必要”,在用户做出选择之前,以及您更改配置版本之后,shouldRequestConsent() 会返回 true。选择退出(opt-out)类别初始开启所有类别(在美国,若设备关闭了广告跟踪,则关闭“定向或广告”),应用不会主动询问:用户可通过您的隐私设置链接更改。选择退出适用于其他国家的 opt-out 规则,以及应用中位于美国的 Consent Categories 项目。
使用 Stage.CategoriesFirstScreen(横幅)和 Stage.CategoriesSecondScreen(设置)打开类别界面。setTestRegion() 也支持 BR、CN、ZA、VN 及其他国家代码,用于测试这些地区。
自 SDK 26.10.0 版本起可用。您在 UniConsent 控制台项目设置中添加的自定义目的和自定义供应商会显示在 CMP 界面中:自定义目的显示在 IAB TCF 首层界面和设置以及 Cookie 类别设置中,自定义供应商显示在 IAB TCF 供应商列表中。在 CCPA 和 US State Privacy 设置中,只列出敏感的自定义目的。在 Cookie 类别设置中,自定义供应商跟随“定向或广告”类别。在不适用 IAB TCF 的地区(Cookie 类别和美国),自定义供应商以只读方式列在菜单的供应商列表中,并在 Cookie 类别设置的“定向或广告”下列出。
要据此启动或停止您自己的 SDK,请询问 SDK 应用现在是否可以使用某个自定义目的或自定义供应商。结果遵循用户适用的政策,规则与 CMP 用于标签的规则相同:
UniConsent cmp = UniConsent.getInstance();
if (cmp.isCustomPurposeConsented(1)) {
// 启动自定义目的 1 涵盖的 SDK
}
boolean vendorAllowed = cmp.isCustomVendorConsented(2);
Map<String, Map<String, Boolean>> all = cmp.getCustomConsents(); // {"purposes": {"1": true}, "vendors": {"2": false}}
在 CMP 关闭或同意变化后请再次调用。项目中不存在的 ID,以及 SDK 就绪前的所有 ID,都返回 false。
CMP 可以列出您应用中的第三方 SDK(广告网络、分析、归因等),包括其公司、用途和隐私政策。用户在供应商列表中查看它们,在所有地区均为只读:没有开关,您的应用仍根据用户对目的或 Cookie 类别的选择来开启或关闭各个 SDK。
要设置列表,请在应用项目的 Step 4: Advanced Features 中开启 Enable Third-party SDKs,在 Third-party SDKs 标签页中选择您的 SDK 或添加自己的 SDK,然后保存并发布。您的应用中无需任何代码。
设置默认同意状态键值对:
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
<meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_storage" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_user_data" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_personalization_signals" android:value="false" />
根据同意标志控制分析:
// <= 25.6.1
@Override
public void handle(Event event) {
Log.d("CMP_EVENT", event.toString())
if(UniConsent.getInstance().hasIABPurposeConsent(1)) {
// Set consent types.
Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentMap = new EnumMap<>(FirebaseAnalytics.ConsentType.class);
consentMap.put(FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE, FirebaseAnalytics.ConsentStatus.GRANTED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_STORAGE, FirebaseAnalytics.ConsentStatus.GRANTED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_USER_DATA, FirebaseAnalytics.ConsentStatus.GRANTED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_PERSONALIZATION, FirebaseAnalytics.ConsentStatus.GRANTED);
mFirebaseAnalytics.setConsent(consentMap);
mFirebaseAnalytics.setAnalyticsCollectionEnabled(true);
} else {
// Set consent types.
Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentMap = new EnumMap<>(FirebaseAnalytics.ConsentType.class);
consentMap.put(FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE, FirebaseAnalytics.ConsentStatus.DENIED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_STORAGE, FirebaseAnalytics.ConsentStatus.DENIED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_USER_DATA, FirebaseAnalytics.ConsentStatus.DENIED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_PERSONALIZATION, FirebaseAnalytics.ConsentStatus.DENIED);
mFirebaseAnalytics.setConsent(consentMap);
mFirebaseAnalytics.setAnalyticsCollectionEnabled(false);
}
// other logics such as send analytics events
}
// > 25.6.1
@Override
public void handle(Event event) {
Log.d("CMP_EVENT", event.toString())
// other logics such as send analytics events
// Example: send Firebase event or other analytics metrics
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.ITEM_ID, "id");
bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, "name");
bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "image");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
}
更多信息请参阅 为应用设置 Consent Mode
SDK 支持三种同意界面显示模式,与 Flutter SDK API 一致:
| 模式 | 枚举值 | 说明 |
|---|---|---|
| 全屏 | CMPDisplayMode.FULL_SCREEN | 在新的全屏 Activity 中打开 CMP(默认) |
| 底部弹出 | CMPDisplayMode.BOTTOM_SHEET | 从底部滑出的模态弹窗(90% 高度) |
| 对话框 | CMPDisplayMode.DIALOG | 居中显示的对话框(90% 宽度,70% 高度) |
注意: 底部弹出和对话框模式要求您的 Activity 继承
AppCompatActivity或FragmentActivity。
setCustomConsent(),从应用自己的界面保存自定义目的的选择,例如邮件打开跟踪复选框;isCustomPurposeConsented() 也接受在控制台中设置的自定义目的 IDemail 用户 ID 以 email_hash(SHA-256)发送,邮箱地址本身不会被存储或发送getGPPString()、getGPPSID() 和 getUSPrivacyString()Stage.USStateSecondScreen 打开 US State Privacy 设置;新增 setLanguage()、dismissCMP()、isConsentGiven()、getConsentAge() 和 getGEOState()Stage.CCPAFirstScreen 和 Stage.USStateFirstScreen 打开 CCPA 和 US State Privacy 首层界面;新增 setTestRegion(),可按其他地区(例如加拿大)的用户测试 CMPStage.CategoriesFirstScreen 和 Stage.CategoriesSecondScreengetPolicy();agreeAll() 和 rejectAll() 还会保存 Cookie 类别、自定义目的和自定义供应商setTestRegion() 也支持 BR、CN、ZA、VN 及其他国家代码,并适用于 getPolicy() 和 shouldRequestConsent()isCustomPurposeConsented()、isCustomVendorConsented() 和 getCustomConsents():应用按照用户适用的政策现在可以使用的自定义目的和自定义供应商隐私法规、同意管理与合规动态,每月几封邮件。