在阅读 Privacy Sandbox on Android 文档时,请使用开发者预览版或 Beta 版按钮选择您所使用的程序版本,因为不同版本的说明可能会有所不同。
Attribution Reporting API 旨在通过消除对跨方用户标识符的依赖来更好地保护用户隐私,并支持跨应用进行归因和转化衡量的关键用例。本开发者指南将介绍如何配置和测试 Attribution Reporting API,通过调用为广告点击、观看和转化事件注册相关触发器和来源的方法来注册广告点击、观看和转化。
本指南将指导您设置服务器端点并构建调用这些服务的客户端应用。如需详细了解 Attribution Reporting API 的整体设计,请参阅设计方案。
关键术语
- “归因来源”是指点击或观看。
- 触发器是可归因于转化的事件。
- 报告包含有关触发器和相应归因来源的数据。这些报告是响应触发器事件而发送的。Attribution Reporting API 支持事件级报告和可汇总报告。
须知事项
为了使用 Attribution Reporting API,请完成以下几部分中列出的服务器端任务和客户端任务。
设置 Attribution Reporting API 端点
Attribution Reporting API 需要一组可通过测试设备或模拟器进行访问的端点。请为以下每个服务器端任务创建一个端点:
- 注册归因来源(观看或点击)
- 注册触发器(转化)
- 接受事件级报告
- 接受可汇总报告
您可以通过多种方法来设置所需端点:
- 最快上手的方法是,将示例代码库中的 OpenAPI v3 服务定义部署到模拟或微服务平台。您可以使用 Postman、Prism 或接受此格式的任何其他模拟服务器平台。部署每个端点,并跟踪在您的应用中使用的 URI。如需验证报告发送,请参阅之前对模拟或无服务器平台发出的调用。
- 使用基于 Spring Boot 的 Kotlin 示例运行您自己的独立服务器。在云服务提供商的基础架构或内部基础架构上部署此服务器。
- 使用服务定义作为示例,将端点集成到您的现有系统中。
接受来源注册
此端点应可通过如下所示的 URI 寻址:
https://adtech.example/attribution_source
当客户端应用注册归因来源时,会提供此服务器端点的 URI。然后,Attribution Reporting API 会发出请求并在其中包含以下标头之一:
对于点击事件:
Attribution-Reporting-Source-Info: navigation
对于观看事件:
Attribution-Reporting-Source-Info: event
配置服务器端点,让其使用以下内容进行响应:
// Metadata associated with attribution source.
Attribution-Reporting-Register-Source: {
"destination": "[app package name]",
"web_destination": "[eTLD+1]",
"source_event_id": "[64 bit unsigned integer]",
"expiry": "[64 bit signed integer]",
"event_report_window": "[64-bit signed integer]",
"aggregatable_report_window": "[64-bit signed integer]",
"priority": "[64 bit signed integer]",
"filter_data": {
"[key name 1]": ["key1 value 1", "key1 value 2"],
"[key name 2]": ["key2 value 1", "key2 value 2"],
// Note: "source_type" key will be automatically generated as
// one of {"navigation", "event"}.
},
// Attribution source metadata specifying histogram contributions in aggregate
// report.
"aggregation_keys": {
"[key1 name]": "[key1 value]",
"[key2 name]": "[key2 value]",
},
"debug_key": "[64-bit unsigned integer]",
"debug_reporting": [boolean]
}
// Specify additional ad tech URLs to register this source with.
Attribution-Reporting-Redirect: <Ad Tech Partner URI 1>
Attribution-Reporting-Redirect: <Ad Tech Partner URI 2>
以下是一个添加了示例值的示例:
Attribution-Reporting-Register-Source: {
"destination": "android-app://com.example.advertiser",
"source_event_id": "234",
"expiry": "259200",
"event_report_window": "172800",
"aggregatable_report_window": "172800",
"priority": "5",
"filter_data": {
"product_id": ["1234"]
},
"aggregation_keys": {
// Generates a "0x159" key piece named (low order bits of the key) for the key
// named "campaignCounts".
// User saw an ad from campaign 345 (out of 511).
"campaignCounts": "0x159",
// Generates a "0x5" key piece (low order bits of the key) for the key named
// "geoValue".
// Source-side geo region = 5 (US), out of a possible ~100 regions.
"geoValue": "0x5",
},
// Opts in to receiving verbose debug reports
"debug_reporting": true
}
Attribution-Reporting-Redirect:
https://adtechpartner1.example?their_ad_click_id=567
Attribution-Reporting-Redirect:
https://adtechpartner2.example?their_ad_click_id=890
如果 Attribution-Reporting-Redirects
包含广告技术合作伙伴的 URI,Attribution Reporting API 就会向每个 URI 发出类似的请求。每个广告技术合作伙伴都必须配置一台服务器,让其使用以下标头进行响应:
Attribution-Reporting-Register-Source: {
"destination": "[app package name]",
"web_destination": "[eTLD+1]",
"source_event_id": "[64 bit unsigned integer]",
"expiry": "[64 bit signed integer]",
"event_report_window": "[64-bit signed integer]",
"aggregatable_report_window": "[64-bit signed integer]",
"priority": "[64 bit signed integer]",
"filter_data": {
"[key name 1]": ["key1 value 1", "key1 value 2"],
"[key name 2]": ["key2 value 1", "key2 value 2"],
// Note: "source_type" key will be automatically generated as
// one of {"navigation", "event"}.
},
"aggregation_keys": {
"[key1 name]": "[key1 value]",
"[key2 name]": "[key2 value]",
}
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.
接受转化触发器注册
此端点应可通过如下所示的 URI 寻址:
https://adtech.example/attribution_trigger
当客户端应用注册触发器事件时,会提供此服务器端点的 URI。然后,Attribution Reporting API 会发出请求并在其中包含以下标头之一:
配置服务器端点,让其使用以下内容进行响应:
// Metadata associated with trigger.
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
// "trigger_data returned" in event reports is truncated to
// the last 1 or 3 bits, based on conversion type.
"trigger_data": "[unsigned 64-bit integer]",
"priority": "[signed 64-bit integer]",
"deduplication_key": "[signed 64-bit integer]",
// "filter" and "not_filters" are optional fields which allow configuring
// event trigger data based on source's filter_data. They consist of a
// filter set, which is a list of filter maps. An event_trigger_data object
// is ignored if none of the filter maps in the set match the source's
// filter data.
// Note: "source_type" can be used as a key in a filter map to filter based
// on the source's "navigation" or "event" type. The first
// Event-Trigger that matches (based on the filters/not_filters) will be
// used for report generation. If none of the event-triggers match, no
// event report will be generated.
"filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"],
// If a key is missing from filters or source's filter_data, it won't be
// used during matching.
"[key name 2]": ["key2 value 1", "key2 value 2"],
}],
"not_filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"],
// If a key is missing from not_filters or source's filter_data, it won't
// be used during matching.
"[key name 2]": ["key2 value 1", "key2 value 2"],
}]
}],
// Specify a list of dictionaries that generates aggregation keys.
"aggregatable_trigger_data": [
// Each dictionary entry independently adds pieces to multiple source keys.
{
"key_piece": "[key piece value]",
"source_keys": ["[key name the key piece value applies to]",
["list of IDs in source to match. Non-matching IDs are ignored"]]
// filters/not_filters are optional fields similar to event trigger data
// filter fields.
"filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"]
}],
"not_filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"],
"[key name 2]": ["key2 value 1", "key2 value 2"],
}]
},
..
],
// Specify an amount of an abstract value which can be integers in [1, 2^16]
// to contribute to each key that is attached to aggregation keys in the
// order they are generated.
"aggregatable_values": [
// Each source event can contribute a maximum of L1 = 2^16 to the
// aggregate histogram.
{
"[key_name]": [value]
},
..
],
aggregatable_deduplication_keys: [{
deduplication_key": [unsigned 64-bit integer],
"filters": {
"category": [filter_1, …, filter_H]
},
"not_filters": {
"category": [filter_1, …, filter_J]
}
},
...
{
"deduplication_key": [unsigned 64-bit integer],
"filters": {
"category": [filter_1, …, filter_D]
},
"not_filters": {
"category": [filter_1, …, filter_J]
}
}
]
"debug_key": "[64-bit unsigned integer]",
"debug_reporting": [boolean]
}
// Specify additional ad tech URLs to register this trigger with.
// Repeated Header field "Attribution-Reporting-Redirect"
Attribution-Reporting-Redirect: <Ad Tech Partner URI 1>
Attribution-Reporting-Redirect: <Ad Tech Partner URI 2>
以下是一个添加了示例值的示例:
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
"trigger_data": "1122", // Returns 010 for CTCs and 0 for VTCs in reports.
"priority": "3",
"deduplication_key": "3344"
"filters": [{ // Filter strings can not exceed 25 characters
"product_id": ["1234"],
"source_type": ["event"]
}]
},
{
"trigger_data": "4", // Returns 100 for CTCs and 0 for VTCs in reports.
"priority": "3",
"deduplication_key": "3344"
"filters": [{ // Filter strings can not exceed 25 characters
"product_id": ["1234"],
"source_type": ["navigation"]
}]
}],
"aggregatable_trigger_data": [
// Each dictionary independently adds pieces to multiple source keys.
{
// Conversion type purchase = 2 at a 9-bit offset, i.e. 2 << 9.
// A 9-bit offset is needed because there are 511 possible campaigns,
// which takes up 9 bits in the resulting key.
"key_piece": "0x400",// Conversion type purchase = 2
// Apply this key piece to:
"source_keys": ["campaignCounts"]
// Filter strings can not exceed 25 characters
},
{
// Purchase category shirts = 21 at a 7-bit offset, i.e. 21 << 7.
// A 7-bit offset is needed because there are ~100 regions for the geo
// key, which takes up 7 bits of space in the resulting key.
"key_piece": "0xA80",
// Apply this key piece to:
"source_keys": ["geoValue", "nonMatchingIdsAreIgnored"]
// source_key values must not exceed the limit of 25 characters
}
],
"aggregatable_values":
{
// Privacy budget for each key is L1 / 2 = 2^15 (32768).
// Conversion count was 1.
// Scale the count to use the full budget allocated: 1 * 32768 = 32768.
"campaignCounts": 32768,
// Purchase price was $52.
// Purchase values for the app range from $1 to $1,024 (integers only).
// Scaling factor applied is 32768 / 1024 = 32.
// For $52 purchase, scale the value by 32 ($52 * 32 = $1,664).
"geoValue": 1664
}
,
// aggregatable_deduplication_keys is an optional field. Up to 50 “keys”
// can be included in the aggregatable_deduplication_keys list. Filters, not
// filters, and deduplication_key are optional fields. If deduplication_key
// is omitted, it will be treated as a null value. See
// https://wicg.github.io/attribution-reporting-api/#triggering-aggregatable-attribution
aggregatable_deduplication_keys:
[
{
deduplication_key": 3,
"filters": {
"category": [A]
}
},
{
"deduplication_key": 4,
"filters": {
"category": [C, D]
},
"not_filters": {
"category": [F]
}
}
]
// Opts into receiving verbose debug reports
"debug_reporting": true
}
Attribution-Reporting-Redirect:https://adtechpartner.example?app_install=567
每个汇总键 ID 和过滤器字符串不得超过 25 个字节。这意味着汇总键 ID 和过滤字符串不应超过 25 个字符。在此示例中,campaignCounts
包含 14 个字符,因此它是有效的汇总键 ID,而 1234
包含 4 个字符,因此它是有效的过滤字符串。如果汇总键 ID 或过滤器字符串超过 25 个字符,系统会忽略该触发器。
如果 Attribution-Reporting-Redirect
包含广告技术合作伙伴的 URI,Attribution Reporting API 就会向每个 URI 发出类似的请求。每个广告技术合作伙伴都必须配置一台服务器,让其使用以下标头进行响应:
// Metadata associated with trigger.
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
// "trigger_data" returned in event reports is truncated to
// the last 1 or 3 bits, based on conversion type.
"trigger_data": "[unsigned 64-bit integer]",
"priority": "[signed 64-bit integer]",
"deduplication_key": "[signed 64-bit integer]",
// filter and not_filters are optional fields which allow configuring
// different event trigger data based on source's filter_data. They
// consist of a filter set, which is a list of filter maps. An
// event_trigger_data object is ignored if none of the filter maps in the
// set match the source's filter data. Note: "source_type" can be used as
// a key in a filter map to filter based on the source's "navigation" or
// "event" type. The first Event-Trigger that matches (based on the
// filters/not_filters) will be used for report generation. If none of the
// event-triggers match, no report will be generated.
"filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"],
// If a key is missing from filters or source's filter_data, it will not be
// used during matching.
"[key name 2]": ["key2 value 1", "key2 value 2"],
}],
"not_filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"],
// If a key is missing from not_filters or source's filter_data, it will not
// be used during matching.
"[key name 2]": ["key2 value 1", "key2 value 2"],
}]
}],
"aggregatable_trigger_data": [
// Each dictionary entry independently adds pieces to multiple source keys.
{
"key_piece": "[key piece value]",
"source_keys": ["[key name the key piece value applies to]",
["list of IDs in source to match. Non-matching IDs are ignored"]],
// filters/not_filters are optional fields similar to event trigger data
// filter fields.
"filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"]
}],
"not_filters": [{
"[key name 1]": ["key1 value 1", "key1 value 2"],
"[key name 2]": ["key2 value 1", "key2 value 2"],
}]
},
..
],
// Specify an amount of an abstract value which can be integers in [1, 2^16] to
// contribute to each key that is attached to aggregation keys in the order they
// are generated.
"aggregatable_values": [
// Each source event can contribute a maximum of L1 = 2^16 to the aggregate
// histogram.
{
"[key_name]": [value]
}
]
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.
接受事件级报告
此端点应可通过 URI 寻址。如需详细了解如何注册 URI,请参阅注册 Privacy Sandbox 帐号。(系统会根据用于接受来源注册和触发器注册的服务器的 eTLD+1 推断 URI。)使用接受来源注册和接受触发器注册的端点的示例 URI,推断出来的此端点 URI 为:
https://adtech.example/.well-known/attribution-reporting/report-event-attribution
配置此服务器,让其接受使用以下格式的 JSON 请求:
{
"attribution_destination": "android-app://com.advertiser.example",
"source_event_id": "12345678",
"trigger_data": "2",
"report_id": "12324323",
"source_type": "navigation",
"randomized_trigger_rate": "0.02"
[Optional] "source_debug_key": "[64-bit unsigned integer]",
[Optional] "trigger_debug_key": "[64-bit unsigned integer]",
}
调试键可让您进一步了解归因报告;详细了解配置方式。
接受可汇总报告
此端点应可通过 URI 寻址。如需详细了解如何注册 URI,请参阅注册 Privacy Sandbox 帐号。(系统会根据用于接受来源注册和触发器注册的服务器来源推断 URI。)使用接受来源注册和接受触发器注册的端点的示例 URI,推断出来的此端点 URI 为:
https://adtech.example/.well-known/attribution-reporting/report-aggregate-attribution
对于可汇总报告,系统会同时填充加密字段和未加密字段。加密报告可让您开始使用汇总服务进行测试,而未加密的字段则可以让您了解设置的键值对的数据构建方式。
配置此服务器,让其接受使用以下格式的 JSON 请求:
{
// Info that the aggregation services also need encoded in JSON
// for use with AEAD. Line breaks added for readability.
"shared_info": "{
\"api\":\"attribution-reporting\",
\"attribution_destination\": \"android-app://com.advertiser.example.advertiser\",
\"scheduled_report_time\":\"[timestamp in seconds]\",
\"source_registration_time\": \"[timestamp in seconds]\",
\"version\":\"[api version]\",
\"report_id\":\"[UUID]\",
\"reporting_origin\":\"https://reporter.example\" }",
// In the current Developer Preview release, The "payload" and "key_id" fields
// are not used because the platform does not yet encrypt aggregate reports.
// Currently, the "debug_cleartext_payload" field holds unencrypted reports.
"aggregation_service_payloads": [
{
"payload": "[base64 HPKE encrypted data readable only by the aggregation service]",
"key_id": "[string identifying public key used to encrypt payload]",
"debug_cleartext_payload": "[unencrypted payload]"
},
],
"source_debug_key": "[64 bit unsigned integer]",
"trigger_debug_key": "[64 bit unsigned integer]"
}
调试键可让您进一步了解归因报告;详细了解配置方式。
设置 Android 客户端
客户端应用需要注册归因来源和触发器,并允许生成事件级报告和可汇总报告。如需准备 Android 客户端设备或模拟器以使用 Attribution Reporting API,请执行以下操作:
- 为 Privacy Sandbox on Android 设置开发环境。
- 将系统映像安装到受支持的设备上或设置支持 Privacy Sandbox on Android 的模拟器。
通过运行以下 adb 命令启用对 Attribution Reporting API 的访问权限。(此 API 默认处于停用状态)。
adb shell device_config put adservices ppapi_app_allow_list \"\*\"
如果您在本地测试 Attribution Reporting API(例如,在您有权访问的设备上进行测试),请运行以下命令以停用注册:
adb shell device_config put adservices disable_measurement_enrollment_check "true"
在 Android 清单文件中添加
ACCESS_ADSERVICES_ATTRIBUTION
权限,并为您的应用创建广告服务配置,以使用 Attribution Reporting API:<uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
(可选)如果您打算接收调试报告,请在 Android 清单文件中添加
ACCESS_ADSERVICES_AD_ID
权限:<uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
在清单的
<application>
元素中引用广告服务配置:<property android:name="android.adservices.AD_SERVICES_CONFIG" android:resource="@xml/ad_services_config" />
指定清单中引用的广告服务 XML 资源,例如
res/xml/ad_services_config.xml
。详细了解广告服务权限和 SDK 访问权限控制。<ad-services-config> <attribution allowAllToAccess="true" /> </ad-services-config>
注册广告事件
您的应用应在来源和转化发生时进行记录,以确保正确报告来源和转化。
MeasurementManager
类提供相关方法,可帮助您注册归因来源事件和转化触发器。
注册归因来源事件
有人观看或点击广告时,发布商应用会调用 registerSource()
来注册代码段中所示的归因来源。
Attribution Reporting API 支持以下类型的归因来源事件:
- 点击:通常在与
onClick()
类似的回调方法中注册。相应的触发器事件通常紧跟在点击事件之后发生。此类事件提供有关用户互动的详细信息,因此适合作为高优先级的归因来源。 观看:通常在与
onAdShown()
类似的回调方法中注册。相应的触发器事件可能会在观看事件发生几小时或几天后发生。
Kotlin
companion object {
private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}
val measurementManager = context.getSystemService(MeasurementManager::class.java)
var exampleClickEvent: InputEvent? = null
// Use the URI of the server-side endpoint that accepts attribution source
// registration.
val attributionSourceUri: Uri =
Uri.parse("https://adtech.example/attribution_source?AD_TECH_PROVIDED_METADATA")
val future = CompletableFuture<Void>()
adView.setOnTouchListener(_: View?, event: MotionEvent?)) ->
exampleClickEvent = event
true
}
// Register Click Event
measurementManager.registerSource(
attributionSourceUri,
exampleClickEvent,
CALLBACK_EXECUTOR,
future::complete)
// Register View Event
measurementManager.registerSource(
attributionSourceUri,
null,
CALLBACK_EXECUTOR,
future::complete)
Java
private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();
private InputEvent exampleClickEvent;
MeasurementManager measurementManager =
context.getSystemService(MeasurementManager.class);
// Use the URI of the server-side endpoint that accepts attribution source
// registration.
Uri attributionSourceUri =
Uri.parse("https://adtech.example/attribution_source?AD_TECH_PROVIDED_METADATA");
CompletableFuture<Void> future = new CompletableFuture<>();
adView.setOnTouchListener(v, event)) -> {
exampleClickEvent = event;
return true;
}
// Register Click Event
measurementManager.registerSource(attributionSourceUri, exampleClickEvent,
CALLBACK_EXECUTOR, future::complete);
// Register View Event
measurementManager.registerSource(attributionSourceUri, null,
CALLBACK_EXECUTOR, future::complete);
注册后,API 会向位于 attributionSourceUri
指定地址的服务端点发出 HTTP POST 请求。端点的响应包含 destination, source_event_id, expiry
和 source_priority
的值。
如果发起调用的广告技术平台希望共享来源注册信息,那么原始归因来源 URI 可以包含指向其他广告技术端点的重定向。如需详细了解适用于重定向的限制和规则,请参阅技术方案。
已为 registerSource
和 registerTrigger
添加对菊花链重定向的支持。除了注册标头之外,API 使用方现在还可以提供 HTTP 重定向作为服务器响应,其中包含 302 状态代码和“Location”标头,以及用于额外注册的下一个网址。
只有第一次访问中提供的“目的地”字段才会跨菊花链使用。访问次数的限制与“Attribution-Reporting-Redirect”标头相同。此重定向支持是对现有的“Attribution-Reporting-Redirect”支持的补充,如果两者都存在,则优先使用“Attribution-Reporting-Redirect”。
注册转化触发器事件
如需注册转化触发器事件,请在您的应用中调用 registerTrigger()
:
Kotlin
companion object {
private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}
val measurementManager = context.getSystemService(MeasurementManager::class.java)
// Use the URI of the server-side endpoint that accepts trigger registration.
val attributionTriggerUri: Uri =
Uri.parse("https://adtech.example/trigger?AD_TECH_PROVIDED_METADATA")
val future = CompletableFuture<Void>()
// Register trigger (conversion)
measurementManager.registerTrigger(
attributionTriggerUri,
CALLBACK_EXECUTOR,
future::complete)
Java
private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();
MeasurementManager measurementManager =
context.getSystemService(MeasurementManager.class);
// Use the URI of the server-side endpoint that accepts trigger registration.
Uri attributionTriggerUri =
Uri.parse("https://adtech.example/trigger?AD_TECH_PROVIDED_METADATA");
CompletableFuture<Void> future = new CompletableFuture<>();
// Register trigger (conversion)
measurementManager.registerTrigger(
attributionTriggerUri,
CALLBACK_EXECUTOR,
future::complete)
注册后,API 会向位于 attributionTriggerUri
指定地址的服务端点发出 HTTP POST 请求。端点的响应包含事件和汇总报告的值。
如果发起调用的广告技术平台允许共享触发器注册信息,那么该 URI 可以包含指向属于其他广告技术平台的 URI 的重定向。如需详细了解适用于重定向的限制和规则,请参阅技术方案。
注册跨应用和跨网站衡量
如果在从来源到触发器的用户历程中应用和浏览器二者都发挥了作用,那么注册广告事件的实现就略有不同。如果用户在应用中看到广告并被重定向到浏览器中完成转化,那么来源由应用注册,转化则由网络浏览器注册。同理,如果用户先在网络浏览器中看到广告,然后被定向到应用完成转化,那么就由浏览器注册来源,由应用注册转化。
由于广告技术平台在网站和 Android 中的组织方式有所不同,因此我们添加了新的 API,用于注册浏览器中发生的来源和触发器。这些 API 与基于应用的相应 API 之间的主要区别在于,我们希望浏览器跟踪重定向,应用任何浏览器专用过滤器,并通过调用 registerWebSource()
或 registerWebTrigger()
将有效注册传递给平台。
以下代码段展示了将用户定向到应用之前,浏览器为注册归因来源而发出的 API 调用示例:
Kotlin
companion object {
private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}
val measurementManager =
context.getSystemService(MeasurementManager::class.java)
var exampleClickEvent: InputEvent? = null
// Use the URIs of the server-side endpoints that accept attribution source
// registration.
val sourceParam1 = WebSourceParams.Builder(Uri.parse(
"https://adtech1.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
// True, if debugging is allowed for the ad tech.
.setDebugKeyAllowed(true)
.build()
val sourceParam2 = WebSourceParams.Builder(Uri.parse(
"https://adtech2.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
.setDebugKeyAllowed(false)
.build()
val sourceParam3 = WebSourceParams.Builder(Uri.parse(
"https://adtech3.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
.build()
val sourceParams = Arrays.asList(sourceParam1, sourceParam2, sourceParam3)
val publisherOrigin = Uri.parse("https://publisher.example")
val appDestination = Uri.parse("android-app://com.example.store")
val webDestination = Uri.parse("https://example.com")
val future = CompletableFuture<Void>()
adView.setOnTouchListener {_: View?, event: MotionEvent? ->
exampleClickEvent = event
true
}
val clickRegistrationRequest = WebSourceRegistrationRequest.Builder(
sourceParams,
publisherOrigin)
.setAppDestination(appDestination)
.setWebDestination(webDestination)
.setInputEvent(event)
.build()
val viewRegistrationRequest = WebSourceRegistrationRequest.Builder(
sourceParams,
publisherOrigin)
.setAppDestination(appDestination)
.setWebDestination(webDestination)
.setInputEvent(null)
.build()
// Register a web source for a click event.
measurementManager.registerWebSource(
clickRegistrationRequest,
CALLBACK_EXECUTOR,
future::complete)
// Register a web source for a view event.
measurementManager.registerWebSource(
viewRegistrationRequest,
CALLBACK_EXECUTOR,
future::complete)
Java
private static final Executor CALLBACK_EXECUTOR =
Executors.newCachedThreadPool();
private InputEvent exampleClickEvent;
MeasurementManager measurementManager =
context.getSystemService(MeasurementManager.class);
// Use the URIs of the server-side endpoints that accept attribution source
// registration.
WebSourceParams sourceParam1 = WebSourceParams.Builder(Uri.parse(
"https://adtech1.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
// True, if debugging is allowed for the ad tech.
.setDebugKeyAllowed(true)
.build();
WebSourceParams sourceParam2 = WebSourceParams.Builder(Uri.parse(
"https://adtech2.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
.setDebugKeyAllowed(false)
.build();
WebSourceParams sourceParam3 = WebSourceParams.Builder(Uri.parse(
"https://adtech3.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
.build();
List<WebSourceParams> sourceParams =
Arrays.asList(sourceParam1, sourceParam2, sourceParam3);
Uri publisherOrigin = Uri.parse("https://publisher.example");
Uri appDestination = Uri.parse("android-app://com.example.store");
Uri webDestination = Uri.parse("https://example.com");
CompletableFuture<Void> future = new CompletableFuture<>();
adView.setOnTouchListener(v, event) -> {
exampleClickEvent = event;
return true;
}
WebSourceRegistrationRequest clickRegistrationRequest =
new WebSourceRegistrationRequest.Builder(sourceParams, publisherOrigin)
.setAppDestination(appDestination)
.setWebDestination(webDestination)
.setInputEvent(event)
.build();
WebSourceRegistrationRequest viewRegistrationRequest =
new WebSourceRegistrationRequest.Builder(sourceParams, publisherOrigin)
.setAppDestination(appDestination)
.setWebDestination(webDestination)
.setInputEvent(null)
.build();
// Register a web source for a click event.
measurementManager.registerWebSource(clickRegistrationRequest,
CALLBACK_EXECUTOR, future::complete);
// Register a web source for a view event.
measurementManager.registerWebSource(viewRegistrationRequest,
CALLBACK_EXECUTOR, future::complete);
以下代码段展示了用户从应用被定向到浏览器之后,浏览器为注册转化而发出的 API 调用示例:
Kotlin
companion object {
private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}
val measurementManager = context.getSystemService(MeasurementManager::class.java)
// Use the URIs of the server-side endpoints that accept trigger registration.
val triggerParam1 = WebTriggerParams.Builder(Uri.parse(
"https://adtech1.example/trigger?AD_TECH_PROVIDED_METADATA"))
// True, if debugging is allowed for the ad tech.
.setDebugKeyAllowed(true)
.build()
val triggerParam2 = WebTriggerParams.Builder(Uri.parse(
"https://adtech2.example/trigger?AD_TECH_PROVIDED_METADATA"))
.setDebugKeyAllowed(false)
.build()
val triggerParams = Arrays.asList(triggerParam1, triggerParam2)
val advertiserOrigin = Uri.parse("https://advertiser.example")
val future = CompletableFuture<Void>()
val triggerRegistrationRequest = WebTriggerRegistrationRequest.Builder(
triggerParams,
advertiserOrigin)
.build()
// Register the web trigger (conversion).
measurementManager.registerWebTrigger(
triggerRegistrationRequest,
CALLBACK_EXECUTOR,
future::complete)
Java
private static final Executor CALLBACK_EXECUTOR =
Executors.newCachedThreadPool();
MeasurementManager measurementManager =
context.getSystemService(MeasurementManager.class);
// Use the URIs of the server-side endpoints that accept trigger registration.
WebTriggerParams triggerParam1 = WebTriggerParams.Builder(Uri.parse(
"https://adtech1.example/trigger?AD_TECH_PROVIDED_METADATA"))
// True, if debugging is allowed for the ad tech.
.setDebugKeyAllowed(true)
.build();
WebTriggerParams triggerParam2 = WebTriggerParams.Builder(Uri.parse(
"https://adtech2.example/trigger?AD_TECH_PROVIDED_METADATA"))
.setDebugKeyAllowed(false)
.build();
List<WebTriggerParams> triggerParams =
Arrays.asList(triggerParam1, triggerParam2);
Uri advertiserOrigin = Uri.parse("https://advertiser.example");
CompletableFuture<Void> future = new CompletableFuture<>();
WebTriggerRegistrationRequest triggerRegistrationRequest =
new WebTriggerRegistrationRequest.Builder(
triggerParams, advertiserOrigin)
.build();
// Register the web trigger (conversion).
measurementManager.registerWebTrigger( triggerRegistrationRequest,
CALLBACK_EXECUTOR, future::complete);
为保护隐私而增加噪声
事件级报告包含目标位置、归因来源 ID 和触发器数据。它们以原始(未加密)格式发送到报告来源。为了保护用户的隐私,您可以添加噪声,以提高识别单个用户的难度。已根据差分隐私框架生成和发送带有噪声的事件级报告。以下是不同场景的默认噪声百分比值:
来源类型 |
源目的地值 |
每次来源注册的干扰报告概率 |
观看 |
应用或网站 |
0.0000025 |
观看 |
应用 + 网站 |
0.0000042 |
点击 |
应用或网站 |
0.0024263 |
点击 |
应用 + 网站 |
0.0170218 |
在应用到网站的归因衡量中,来源可以推动转化到应用和网站目标位置,事件级报告可以指定触发器是发生在应用上还是网站上。为了弥补这些额外的细节,生成的报告的点击次数最多约为 7 倍,观看次数最多约为 1.7 倍。
某些广告技术平台不需要事件级报告来指定触发器发生在应用目标位置还是网站目标位置。广告技术平台可以使用 Attribution-Reporting-Register-Source
标题下的 coarse_event_report_destinations
字段来减少噪声。如果指定了 coarse_event_report_destinations
字段的来源胜出,则生成的报告将同时包含应用和网站目标位置,而不会区分实际触发器的发生位置。
在以下示例中,用户点击了一个广告,并且该来源向 API 进行了注册。然后,用户在广告主应用和广告主的网站上完成转化。这两个转化都会注册为触发器,并归因于初始点击。
基于点击的来源注册 HTTP 标头:
Attribution-Reporting-Register-Source: {
"destination": "android-app://com.advertiser.example",
"web_destination": "https://advertiser.com",
"source_event_id": "234",
"expiry": "60000",
"priority": "5",
// Ad tech opts out of receiving app-web destination distinction
// in event report, avoids additional noise
"coarse_event_report_destinations": "true"
}
使用软件包名称 com.advertiser.example
从应用中注册触发器:
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
"trigger_data": "1",
"priority": "1"
}],
}
使用 eTLD+1 网域 https://advertiser.com
从网站在浏览器中注册触发器:
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
"trigger_data": "2",
"priority": "2"
}],
}
系统随即会生成事件级报告。假设这两个触发器都归因于该来源,系统会生成以下事件级报告:
{
"attribution_destination": ["android-app://com.advertiser.example,https://advertiser.com"],
"scheduled_report_time": "800176400",
"source_event_id": "53234",
"trigger_data": "1",
// Can be "event" if source were registered by user viewing the ad
"source_type": "navigation",
// Would be 0.0170218 without coarse_event_report_destinations as true in the source
"randomized_trigger_rate": 0.0024263
}
生成并发送报告
Attribution Reporting API 会将报告发送到您的服务器上接受事件级报告和可汇总报告的端点。
强制运行报告作业
注册归因来源事件或注册触发器事件后,系统会安排运行报告作业。默认情况下,此作业每 4 小时运行一次。出于测试目的,您可以强制运行报告作业或缩短两次作业之间的间隔时间。
强制运行归因作业:
adb shell cmd jobscheduler run -f com.google.android.adservices.api 5
强制运行事件级报告作业:
adb shell cmd jobscheduler run -f com.google.android.adservices.api 3
强制运行可汇总报告作业:
adb shell cmd jobscheduler run -f com.google.android.adservices.api 7
查看 logcat 中的输出,了解作业的运行时间。输出应如下所示:
JobScheduler: executeRunCommand(): com.google.android.adservices.api/0 5 s=false f=true
强制发送报告
即使强制运行报告作业,系统仍会按照安排的发送时间(从几小时到几天不等)发送报告。出于测试目的,您可以将设备的系统时间向前调到安排的延迟时间之后,以便立即开始发送报告。
在服务器上验证报告
报告发送后,通过查看收到的报告、适用的服务器日志(如模拟服务器历史记录)或您的自定义系统来验证报告发送。
解码汇总报告
收到汇总报告时,debug_cleartext_payload
字段将包含未加密的汇总报告。虽然此版本的报告未加密,但仍需要解码。
下面是一个分两步解码 debug_cleartext_payload
字段内容的示例:第一次使用 Base64 解码,第二次使用 CBOR 解码。
String base64DebugPayload = "omRkYXRhgqJldmFsdWVEAAAGgGZidWNrZXRQAAAAAAAAAAAAAAAAAAAKhaJldmFsdWVEAACAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAFWWlvcGVyYXRpb25paGlzdG9ncmFt";
byte[] cborEncoded = Base64.getDecoder().decode(base64DebugPayload);
// CbodDecoder comes from this library https://github.com/c-rack/cbor-java
final List<DataItem> dataItems = new CborDecoder(new ByteArrayInputStream(cborEncoded)).decode();
// In here you can see the contents, but the value will be something like:
// Data items: [{ data: [{ value: co.nstant.in.cbor.model.ByteString@a8b5c07a,
// bucket: co.nstant.in.cbor.model.ByteString@f812097d },
// { value: co.nstant.in.cbor.model.ByteString@a8b5dfc0,
// bucket: co.nstant.in.cbor.model.ByteString@f8120934 }], operation: histogram }]
Log.d("Data items : " + dataItems);
// In order to see the value for bucket and value, you can traverse the data
// and get their values, something like this:
final Map payload = (Map) dataItems.get(0);
final Array payloadArray = (Array) payload.get(new UnicodeString("data"));
payloadArray.getDataItems().forEach(i -> {
BigInteger value = new BigInteger(((ByteString) ((Map)i).get(new UnicodeString("value"))).getBytes());
BigInteger bucket = new BigInteger(((ByteString) ((Map)i).get(new UnicodeString("bucket"))).getBytes());
Log.d("value : " + value + " ;bucket : " + bucket);
});
测试
为帮助您开始使用 Attribution Reporting API,您可以使用 GitHub 上的 MeasurementSampleApp 项目。该示例应用演示了归因来源注册和触发器注册。
对于服务器端点,请考虑使用以下参考资源或您的自定义解决方案:
- MeasurementAdTechServerSpec 包含 OpenAPI 服务定义,您可将这些定义部署到受支持的模拟或微服务平台。
- MeasurementAdTechServer 包含一个模拟服务器的参考实现,以用于 Google App Engine 的 Spring Boot 应用为基础。
前提条件
在可通过测试设备或模拟器进行访问的远程端点上部署模拟 API。为便于测试,请参阅 MeasurementAdTechServerSpec 和 MeasurementAdTechServer 示例项目。
需要测试的功能
即将发布的功能
灵活事件级配置
建议在开始实用性测试时使用事件级报告的默认配置,但该配置可能并不适用于所有用例。Attribution Reporting API 将支持更加灵活的可选配置,以便广告技术平台可以更好地控制其事件级报告的结构,并能够最大限度地利用数据。这种额外的灵活性将分两个阶段引入到 Attribution Reporting API 中:
第 1 阶段:精简版灵活事件级别
我们会将以下两个可选参数添加到 Attribution-Reporting-Register-Source
中的 JSON:
max_event_level_reports
event_report_windows
{
...
// Optional. This is a parameter that acts across all trigger types for the
// lifetime of this source. It restricts the total number of event-level
// reports that this source can generate. After this maximum is hit, the
// source is no longer capable of producing any new data. The use of
// priority in the trigger attribution algorithm in the case of multiple
// attributable triggers remains unchanged. Defaults to 3 for navigation
// sources and 1 for event sources
"max_event_level_reports": <int>,
// Optional. Represents a series of time windows, starting at 0. Reports
// for this source will be delivered an hour after the end of each window.
// Time is encoded as seconds after source registration. If
// event_report_windows is omitted, will use the default windows. This
// field is mutually exclusive with the existing `event_report_window` field.
// // End time is exclusive.
"event_report_windows": {
"start_time": <int>,
"end_times": [<int>, ...]
}
}
自定义配置示例
此示例配置支持希望进行优化以在较早的报告期内接收报告的开发者。
{
...
"max_event_level_reports": 2,
"event_report_windows": {
"end_times": [7200, 43200, 86400] // 2 hours, 12 hours, 1 day in seconds
}
}
第 2 阶段:完全灵活的事件级别
除了在第 1 阶段添加的参数之外,我们还将向 Attribution-Reporting-Register-Source
中的 JSON 添加一个额外的可选参数 trigger_specs
。
{
// A trigger spec is a set of matching criteria, along with a scheme to
// generate bucketized output based on accumulated values across multiple
// triggers within the specified event_report_window. There will be a limit on
// the number of specs possible to define for a source.
"trigger_specs": [{
// This spec will only apply to registrations that set one of the given
// trigger data values (non-negative integers) in the list.
// trigger_data will still appear in the event-level report.
"trigger_data": [<int>, ...]
// Represents a series of time windows, starting at the source registration
// time. Reports for this spec will be delivered an hour after the end of
// each window. Time is encoded as seconds after source registration.
// end_times must consist of strictly increasing positive integers.
//
// Note: specs with identical trigger_data cannot have overlapping windows;
// this ensures that triggers match at most one spec. If
// event_report_windows is omitted, will use the "event_report_window" or
// "event_report_windows" field specified at the global level for the source
// (or the default windows if none are specified). End time is exclusive.
"event_report_windows": {
"start_time": <int>,
"end_times": [<int>, ...],
}
// Represents an operator that summarizes the triggers within a window
// count: number of triggers attributed within a window
// value_sum: sum of the value of triggers within a window
// The summary is reported as an index into a bucketization scheme. Defaults
// to "count"
"summary_window_operator": <one of "count" or "value_sum">,
// Represents a bucketization of the integers from [0, MAX_INT], encoded as
// a list of integers where new buckets begin (excluding 0 which is
// implicitly included).
// It must consist of strictly increasing positive integers.
//
// e.g. [5, 10, 100] encodes the following ranges:
// [[0, 4], [5, 9], [10, 99], [100, MAX_INT]]
//
// At the end of each reporting window, triggers will be summarized into an
// integer which slots into one of these ranges. Reports will be sent for
// every new range boundary that is crossed. Reports will never be sent for
// the range that includes 0, as every source is initialized in this range.
//
// If omitted, then represents a trivial mapping
// [1, 2, ... , MAX_INT]
// With MAX_INT being the maximum int value defined by the browser.
"summary_buckets": [<bucket start>, ...]
}, {
// Next trigger_spec
} ...],
// See description in phase 1.
"max_event_level_reports": <int>
// See description in phase 1.
"event_report_windows": {
"start_time": <int>,
"end_times": [<int>, ...]
}
}
此配置按来源注册完全指定了事件级报告的输出空间。对于每个触发器规范,我们都会全面指定:
- 一组匹配条件:
- 此规范适用于哪些特定触发器数据。此来源仅可与具有
trigger_specs
中某个指定trigger_data
值的触发器匹配。换言之,如果触发器本来能匹配此来源,但其trigger_data
不是该来源配置中的值之一,就会忽略该触发器。 - 当特定触发器符合此规范时(使用
event_report_windows
)。请注意,尽管未通过前面提到的两个匹配条件,该触发器仍可能与可汇总报告的来源匹配。
- 此规范适用于哪些特定触发器数据。此来源仅可与具有
- 一种特定算法,用于汇总归因回溯期内的所有触发器并对其进行分桶。这样,触发器就可以指定一个
value
参数,该参数针对特定规范进行求和,但会报告为分桶值。
触发器还支持在 event_trigger_data
内的字典中添加可选的值参数。
{
"event_trigger_data": [
{
"trigger_data": "2",
"value": 100, // Defaults to 1
"filters": ...
},
...
]
}
每个触发器注册最多将与一个触发器规范匹配,并更新其关联的摘要值。概括来讲,在触发时,我们将:
- 应用全局归因过滤器。
- 对于每个触发器规范,请使用规范的
event_reporting_window
评估该规范的event_trigger_data
以查找匹配项。如果任何触发器规范是缺失的event_report_windows
子字段,顶级event_reporting_windows
将充当默认值。 - 系统会选择第一个匹配的规范用于归因,并且摘要值会按
value
递增。
在规范的 event_report_window
完成后,我们会将其摘要值映射到某个存储分区,并针对摘要存储分区中由归因的触发器值引起的每次增量发送事件级报告。报告将包含一个额外字段 trigger_summary_bucket
。
{
...
"trigger_summary_bucket": [<bucket start>, <bucket end>],
}
与当前版本等效的配置
以下是 API 当前事件和导航来源的等效配置。这说明了为什么噪声等级相对于事件来源如此高,可以保持相同的 epsilon 值,因为导航来源的输出空间要大得多。
鉴于某些参数可设置为默认值或删减,可能存在多个等效配置。
等效事件来源
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
"trigger_specs": [
{
"trigger_data": [0, 1],
"event_report_windows": {
"end_times": [<30 days>]
},
"summary_window_operator": "count",
"summary_buckets": [1],
}],
"max_event_level_reports": 1,
...
// expiry must be greater than or equal to the last element of the end_times
"expiry": <30 days>,
}
等效导航来源
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
"trigger_specs": [
{
"trigger_data": [0, 1, 2, 3, 4, 5, 6, 7],
"event_report_windows": {
"end_times": [<2 days>, <7 days>, <30 days>]
},
"summary_window_operator": "count",
"summary_buckets": [1, 2, 3],
}],
"max_event_level_reports": 3,
...
// expiry must be greater than or equal to the last element of the end_times
"expiry": <30 days>,
}
自定义配置示例
以下是除默认值之外的一些其他配置。在所有这些示例中,开发者需要权衡的因素包括:
- 减少默认配置的某个维度(#triggers、触发数据基数、#windows),以增加另一个维度以保持噪声等级
- 减少默认配置的某些维度(#triggers、触发数据基数、#windows)以降低噪声级别
报告触发器值范围
此示例配置支持开发者希望仅针对一个报告期(例如 7 天)的价值数据进行优化,以减少报告期来换取干扰数据。在此示例中,任何将 trigger_data
设置为非 0 值的触发器都不符合归因的条件。
{
"trigger_specs": [
{
"trigger_data": [0],
"event_report_windows": {
"end_times": [604800, 1209600] // 7 days, 14 days represented in seconds
},
"summary_window_operator": "value_sum",
"summary_buckets": [5, 10, 100]
}],
}
触发器可以通过设置 value
字段来注册,这些字段会进行求和并分桶。例如,如果在来源注册后的 7 天内有三个触发器,其值为 1、3 和 4。
{ "event_trigger_data": [{"trigger_data": "0", "value": 1}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 3}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 4}] }
这些值的总和为 8,并在 7 天 + 1 小时后在以下报告中报告:
// Report 1
{
...
"trigger_summary_bucket": [5, 9]
}
在接下来的 7 天内,注册了以下触发器:
{ "event_trigger_data": [{"trigger_data": "0", "value": 50}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 45}] }
这些值的总和为 8 + 50 + 45 = 103。这会在 14 天 + 1 小时时生成以下报告:
// Report 2
{
...
"trigger_summary_bucket": [10, 99]
},
// Report 3
{
...
"trigger_summary_bucket": [100, MAX_INT]
}
报告触发器计数
以下示例展示了开发者如何配置来源,以获取最多 10 个触发器的计数。
{
"trigger_specs": [
{
"trigger_data": [0],
"event_report_windows": {
"end_times": [604800] // 7 days represented in seconds
},
// This field could be omitted to save bandwidth since the default is "count"
"summary_window_operator": "count",
"summary_buckets": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}],
}
trigger_data
设为 0 的归因触发器会统计在内,且上限为 10。
由于 summary_window_operator
设置为计数,因此系统会忽略触发器值。如果注册了 4 个触发器并归因于该来源,报告将如下所示:
// Report 1
{
...
"trigger_summary_bucket": [1, 1]
}
// Report 2
{
...
"trigger_summary_bucket": [2, 2]
}
// Report 3
{
...
"trigger_summary_bucket": [3, 3]
}
// Report 4
{
...
"trigger_summary_bucket": [4, 4]
}
二进制文件,报告频率更高
如果开发者希望了解前 10 天内是否至少发生了一次转化(无论价值是多少),但希望以高于默认间隔的频率接收报告,那么采用此示例配置即可。同样,在此示例中,任何将 trigger_data
设置为非 0 值的触发器都不符合归因的条件。因此,此用例称为二进制文件。
{
"trigger_specs": [
{
"trigger_data": [0],
"event_report_windows": {
// 1 day, 2 days, 3 days, 5 days, 7 days, 10 days represented in seconds
"end_times": [86400, 172800, 259200, 432000, 604800, 864000]
},
// This field could be omitted to save bandwidth since the default is "count"
"summary_window_operator": "count",
"summary_buckets": [1]
}],
}
触发器规范因来源而异
{
"trigger_specs": [
{
"trigger_data": [0, 1, 2, 3],
"event_report_windows": {
"end_times": [172800, 604800, 2592000] // 2 days, 7 days, 30 days represented in seconds
}
}],
"max_event_level_reports": 3
}
{
"trigger_specs": [
{
"trigger_data": [4, 5, 6, 7],
"event_report_windows": {
"end_times": [172800, 604800, 2592000] // 2 days, 7 days, 30 days represented in seconds
}
}],
"max_event_level_reports": 3
}
我们鼓励开发者针对此 API 扩展推荐不同的用例,并且我们将更新此说明性消息,在其中提供这些用例的示例配置。
无重定向的跨广告网络归因
广告技术平台应使用重定向来注册多个归因来源触发器并执行跨广告网络归因。当无法在跨广告网络进行重定向时,此功能有助于支持跨广告网络归因。了解详情。
广告技术平台可以在触发器注册响应中发送配置,具体取决于选择其他广告技术平台注册的来源来生成派生来源;这些派生来源随后用于归因。如果触发器归因于派生来源,系统就会生成汇总报告。不支持为派生来源生成事件报告。
广告技术平台可以从自己打算与合作伙伴广告技术平台共享的已注册来源中的 aggregation_keys
中进行选择。这些键可以在可选 shared_aggregation_keys
字段中声明,该字段位于来源注册标头 Attribution-Reporting-Register-Source
下:
"shared_aggregation_keys": ["[key name1]", "[key name2]"]
派生源代码是根据触发器注册标头 Attribution-Reporting-Register-Trigger
下的配置生成的:
// Specifies the configuration based on which derived sources should be
// generated. Those derived sources will be included for source matching at the
// time of attribution. For example, if adtech2 is registering a trigger with an
// attribution_config with source_network as adtech1, available sources
// registered by adtech1 will be considered with additional filtering criteria
// applied to that set as mentioned in the attribution_config. Derived
// sources can have different values to priority, post_install_exclusivity_window
// etc.
"attribution_config": [
{
// Derived sources are created from this adtech's registered sources
"source_network": "[original source's adtech enrollment ID]",
//(optional) Filter sources whose priority falls in this range
"source_priority_range": {
"start": [priority filter lower bound],
"end": [priority filter upper bound]
},
// (optional) Filter sources whose at least one of filter maps matches these
// filters
"source_filters": {
"key name 1": ["key1 value 1"]
},
// (optional) Filter sources whose none of filter map matches these
// filters
"source_not_filters": {
"key name 1": ["key1 value 1"]
},
// (optional) Apply this priority to the generated derived sources
"priority": "[64 bit signed integer]",
// (optional) The derived source will have expiry set as this or parent
// source's, whichever is earlier
"expiry": "[64 bit signed integer]",
// (optional) set on the derived source
"filter_data": {
"key name 1": ["key1 value 1"]
},
// (optional) set on the derived source
"post_install_exclusivity_window": "[64-bit unsigned integer]"
}
]
下面是一个添加了示例值的版本:
"attribution_config": [
{
"source_network": "adtech1-enrollment-id",
"source_priority_range": {
"start": 50,
"end": 100
},
"source_filters": {
"source_type": ["NAVIGATION"]
},
"source_not_filters": {
"product_id": ["789"]
},
"priority": "30",
"expiry": "78901",
// (optional) set on the derived source
"filter_data": {
"product_id": ["1234"]
},
// (optional) set on the derived source
"post_install_exclusivity_window": "7890"
}
]
向触发器注册标头添加了两个新的选填字段。这些字段用于在可汇总报告键中为胜出的广告技术平台启用标识符:
x_network_bit_mapping
:注册 ID 到广告技术标识符的位映射x_network_data
:胜出的广告技术平台x_network_bit_mapping
OR 运算与触发器键部分之间的偏移(左移)
例如:
"Attribution-Reporting-Register-Trigger": {
"attribution_config": [...],
"aggregatable_trigger_data": [
{
"key_piece": "0x400",
"source_keys": ["campaignCounts"]
"x_network_data" : {
"key_offset" : 12 // [64 bit unsigned integer]
}
}
…
]
…
"x_network_bit_mapping": {
// This mapping is used to generate trigger key pieces with AdTech identifier
// bits. eg. If AdTechA's sources wins the attribution then 0x1 here will be
// OR'd with the trigger key pieces to generate the final key piece.
"AdTechA-enrollment_id": "0x1", // Identifier bits in hex for A
"AdTechB-enrollment_id": "0x2" // Identifier bits in hex for B
}
…
}
以下是针对 AdTechB 的来源生成报告时生成的触发器关键部分计算:
key_piece
:0x400 (010000000000)
key_offset
:12
- AdtechB 的
enrollment_id
值:2 (010)
(来自x_network_bit_mapping
) - 结果触发器键部分:
0x400 | 0x2 << 12 = 0x2400
限制
有关 SDK 运行时的正在开发中的功能列表,请查看版本说明。
报告 bug 和问题
您的反馈对 Privacy Sandbox on Android 至关重要!如果发现任何问题或有任何关于改进 Privacy Sandbox on Android 的想法,欢迎告诉我们。