Wenn Sie die Dokumentation zu Privacy Sandbox für Android lesen, wählen Sie mit der Schaltfläche Entwicklervorschau oder Beta die Programmversion aus, mit der Sie arbeiten, da die Anleitungen variieren können.
Die Attribution Reporting API wurde entwickelt, um den Datenschutz für Nutzer zu verbessern. Dabei wird die Abhängigkeit von dienstleisterübergreifenden Nutzerkennungen entfernt und gleichzeitig werden wichtige Anwendungsfälle für die Attributions- und Conversion-Analyse in Apps unterstützt. In dieser Entwickleranleitung wird beschrieben, wie Sie die Attribution Reporting APIs konfigurieren und testen, um Anzeigenklicks, ‑aufrufe und ‑conversions zu registrieren. Dazu rufen Sie Methoden auf, mit denen die entsprechenden Trigger und Quellen für solche Ereignisse registriert werden.
In diesem Leitfaden erfahren Sie, wie Sie Serverendpunkte einrichten und eine Client-App erstellen, die diese Dienste aufruft. Weitere Informationen zum allgemeinen Design der Attribution Reporting API finden Sie im Designvorschlag.
Wichtige Begriffe
- Attributionsquellen beziehen sich auf Klicks oder Aufrufe.
- Auslöser sind Ereignisse, die Conversions zugeordnet werden können.
- Berichte enthalten Daten zu einem Trigger und der entsprechenden Attributionsquelle. Diese Berichte werden als Reaktion auf Triggerereignisse gesendet. Die Attribution Reporting API unterstützt Berichte auf Ereignisebene und aggregierbare Berichte.
Hinweis
Wenn Sie die Attribution Reporting API verwenden möchten, müssen Sie die serverseitigen und clientseitigen Aufgaben ausführen, die in den folgenden Abschnitten aufgeführt sind.
Attribution Reporting API-Endpunkte einrichten
Für die Attribution Reporting API sind eine Reihe von Endpunkten erforderlich, auf die Sie von einem Testgerät oder Emulator aus zugreifen können. Erstellen Sie einen Endpunkt für jede der folgenden serverseitigen Aufgaben:
- Attributionsquelle (Aufruf oder Klick) registrieren
- Trigger (Conversion) registrieren
- Berichte auf Ereignisebene akzeptieren
- Zusammenfassbare Berichte akzeptieren
Es gibt mehrere Möglichkeiten, die erforderlichen Endpunkte einzurichten:
- Am schnellsten geht es, wenn Sie die OpenAPI v3-Dienstdefinitionen aus unserem Beispielcoderepository auf einer Mock- oder Microservices-Plattform bereitstellen. Sie können Postman, Prism oder eine andere Mock-Server-Plattform verwenden, die dieses Format akzeptiert. Stellen Sie jeden Endpunkt bereit und notieren Sie sich die URIs für die Verwendung in Ihrer App. Um die Zustellung von Berichten zu überprüfen, sehen Sie sich die zuvor an die Mock- oder serverlose Plattform gesendeten Aufrufe an.
- Führen Sie Ihren eigenen Standalone-Server mit dem Spring Boot-basierten Kotlin-Beispiel aus. Stellen Sie diesen Server bei Ihrem Cloud-Anbieter oder in Ihrer internen Infrastruktur bereit.
- Verwenden Sie die Dienstdefinitionen als Beispiele, um die Endpunkte in Ihr bestehendes System zu integrieren.
Quellenregistrierung akzeptieren
Dieser Endpunkt sollte über einen URI wie den folgenden erreichbar sein:
https://adtech.example/attribution_source
Wenn eine Client-App eine Attributionsquelle registriert, gibt sie den URI für diesen Serverendpunkt an. Die Attribution Reporting API sendet dann eine Anfrage und fügt einen der folgenden Header ein:
Für Klickereignisse:
Attribution-Reporting-Source-Info: navigation
Für Aufrufereignisse:
Attribution-Reporting-Source-Info: event
Konfigurieren Sie Ihren Serverendpunkt so, dass er mit Folgendem antwortet:
// 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>
Hier ein Beispiel mit Beispielwerten:
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
Wenn Attribution-Reporting-Redirects
URIs von Ad-Tech-Partnern enthält, sendet die Attribution Reporting API eine ähnliche Anfrage an jeden URI. Jeder Ad-Tech-Partner muss einen Server konfigurieren, der mit diesen Headern antwortet:
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.
Registrierung von Conversion-Triggern akzeptieren
Dieser Endpunkt sollte über einen URI wie den folgenden erreichbar sein:
https://adtech.example/attribution_trigger
Wenn eine Client-App ein Trigger-Ereignis registriert, gibt sie den URI für diesen Serverendpunkt an. Die Attribution Reporting API sendet dann eine Anfrage mit einem der folgenden Header:
Konfigurieren Sie Ihren Serverendpunkt so, dass er mit Folgendem antwortet:
// 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>
Hier ein Beispiel mit Beispielwerten:
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't 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't 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't 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
Pro Aggregationsschlüssel-ID und Filterstring gilt ein Limit von 25 Byte. Das bedeutet, dass Ihre Aggregationsschlüssel-IDs und Filterstrings nicht länger als 25 Zeichen sein dürfen. In diesem Beispiel ist campaignCounts
14 Zeichen lang und somit eine gültige ID für den Aggregationsschlüssel. 1234
ist 4 Zeichen lang und somit ein gültiger Filterstring.
Wenn eine Aggregationsschlüssel-ID oder ein Filterstring länger als 25 Zeichen ist, wird der Trigger ignoriert.
Wenn Attribution-Reporting-Redirect
URIs von Ad-Tech-Partnern enthält, sendet die Attribution Reporting API eine ähnliche Anfrage an jeden URI. Jeder Ad-Tech-Partner muss einen Server konfigurieren, der mit diesen Headern antwortet:
// 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 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"],
}]
}],
"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.
Berichte auf Ereignisebene akzeptieren
Dieser Endpunkt sollte über einen URI erreichbar sein. Weitere Informationen zum Registrieren von URIs finden Sie unter Privacy Sandbox-Konto registrieren. Der URI wird aus dem Ursprung der Server abgeleitet, die zum Akzeptieren der Quellregistrierung und der Triggerregistrierung verwendet werden. Wenn wir die Beispiel-URIs für Endpunkte verwenden, die Quellregistrierung akzeptieren und Triggerregistrierung akzeptieren, lautet der URI dieses Endpunkts:
https://adtech.example/.well-known/attribution-reporting/report-event-attribution
Konfigurieren Sie diesen Server so, dass er JSON-Anfragen im folgenden Format akzeptiert:
{
"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]",
}
Mit Debug-Schlüsseln erhalten Sie zusätzliche Informationen in Ihren Attributionsberichten. Weitere Informationen zum Konfigurieren von Debug-Schlüsseln
Zusammenfassbare Berichte akzeptieren
Dieser Endpunkt sollte über einen URI erreichbar sein. Weitere Informationen zum Registrieren von URIs finden Sie unter Privacy Sandbox-Konto registrieren. Der URI wird aus dem Ursprung der Server abgeleitet, die zum Akzeptieren der Quellregistrierung und der Triggerregistrierung verwendet werden. Wenn wir die Beispiel-URIs für Endpunkte verwenden, die Quellregistrierung akzeptieren und Triggerregistrierung akzeptieren, lautet der URI dieses Endpunkts:
https://adtech.example/.well-known/attribution-reporting/report-aggregate-attribution
Sowohl die verschlüsselten als auch die unverschlüsselten Felder werden für aggregierbare Berichte ausgefüllt. Mit den verschlüsselten Berichten können Sie mit dem Aggregationsdienst testen. Das unverschlüsselte Feld gibt Aufschluss darüber, wie die Daten durch festgelegte Schlüssel-Wert-Paare strukturiert werden.
Konfigurieren Sie diesen Server so, dass er JSON-Anfragen im folgenden Format akzeptiert:
{
// 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 doesn't yet encrypt aggregate reports.
// 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]"
}
Mit Debug-Schlüsseln erhalten Sie zusätzliche Informationen in Ihren Attributionsberichten. Weitere Informationen zum Konfigurieren von Debug-Schlüsseln
Android-Client einrichten
Die Client-App registriert Attributionsquellen und ‑trigger und ermöglicht die Erstellung von Berichten auf Ereignisebene und aggregierbaren Berichten. So bereiten Sie ein Android-Clientgerät oder einen Emulator für die Verwendung der Attribution Reporting API vor:
- Richten Sie Ihre Entwicklungsumgebung für die Privacy Sandbox für Android ein.
- Installieren Sie ein Systemimage auf einem unterstützten Gerät oder richten Sie einen Emulator ein, der die Privacy Sandbox unter Android unterstützt.
Aktivieren Sie den Zugriff auf die Attribution Reporting API, indem Sie den folgenden ADB-Befehl ausführen. Die API ist standardmäßig deaktiviert.
adb shell device_config put adservices ppapi_app_allow_list \"\*\"
Wenn Sie die Attribution Reporting API lokal testen (z. B. auf einem Gerät, auf das Sie physischen Zugriff haben), führen Sie diesen Befehl aus, um die Registrierung zu deaktivieren:
adb shell device_config put adservices disable_measurement_enrollment_check "true"
Fügen Sie die Berechtigung
ACCESS_ADSERVICES_ATTRIBUTION
in Ihre Android-Manifestdatei ein und erstellen Sie eine Konfiguration für Anzeigendienste für Ihre App, damit die Attribution Reporting APIs verwendet werden können:<uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
Optional: Wenn Sie Debug-Berichte erhalten möchten, fügen Sie die Berechtigung
ACCESS_ADSERVICES_AD_ID
in Ihre Android-Manifestdatei ein:<uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
Verweisen Sie im
<application>
-Element Ihres Manifests auf eine Konfiguration für Anzeigendienste:<property android:name="android.adservices.AD_SERVICES_CONFIG" android:resource="@xml/ad_services_config" />
Geben Sie die im Manifest referenzierte XML-Ressource für Anzeigendienste an, z. B.
res/xml/ad_services_config.xml
. Weitere Informationen zu Berechtigungen für Anzeigendienste und zur SDK-Zugriffssteuerung<ad-services-config> <attribution allowAllToAccess="true" /> </ad-services-config>
Anzeigenereignisse registrieren
Ihre App sollte Quellen und Conversions registrieren, sobald sie auftreten, um zu überprüfen, ob sie richtig erfasst werden. Die Klasse MeasurementManager
bietet Methoden, mit denen Sie Attributionsquellenereignisse und Conversion-Trigger registrieren können.
Attributionsquellenereignis registrieren
Wenn eine Anzeige aufgerufen oder angeklickt wird, ruft eine Publisher-App registerSource()
auf, um eine Attributionsquelle zu registrieren, wie im Code-Snippet gezeigt.
Die Attribution Reporting API unterstützt die folgenden Arten von Attributionsquellenereignissen:
- Klicks, die Sie normalerweise in einer Callback-Methode wie
onClick()
registrieren. Das entsprechende Triggerereignis tritt in der Regel kurz nach dem Klickereignis ein. Diese Art von Ereignis enthält mehr Informationen zur Nutzerinteraktion und ist daher eine gute Attributionsquelle, der eine hohe Priorität eingeräumt werden sollte. Ansichten, die Sie normalerweise in einer Callback-Methode wie
onAdShown()
registrieren. Das entsprechende Triggerereignis kann Stunden oder Tage nach dem Aufrufereignis eintreten.
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);
Nach der Registrierung sendet die API eine HTTP-POST-Anfrage an den Dienstendpunkt unter der von attributionSourceUri
angegebenen Adresse. Die Antwort des Endpunkts enthält Werte für destination, source_event_id, expiry
und source_priority
.
Wenn die ursprüngliche Ad-Tech-Plattform Quellregistrierungen freigeben möchte, kann der ursprüngliche URI der Attributionsquelle Weiterleitungen zu anderen Ad-Tech-Endpunkten enthalten. Die für die Weiterleitungen geltenden Limits und Regeln sind im technischen Vorschlag beschrieben.
Es wurde Unterstützung für Weiterleitungsketten für registerSource
und registerTrigger
hinzugefügt. Zusätzlich zum Registrierungsheader kann der API-Nutzer jetzt eine HTTP-Weiterleitung als Serverantwort bereitstellen, die einen Statuscode 302 und einen „Location“-Header mit der nächsten URL enthält, die für eine zusätzliche Registrierung aufgerufen werden soll.
Nur das Feld „Ziel“ aus dem ersten Besuch wird in der gesamten Kette verwendet. Die Anzahl der Besuche hat dasselbe Limit wie die Header „Attribution-Reporting-Redirect“. Diese Unterstützung für Weiterleitungen erfolgt zusätzlich zur bestehenden Unterstützung für „Attribution-Reporting-Redirect“. Wenn beide vorhanden sind, wird „Attribution-Reporting-Redirect“ bevorzugt.
Conversion-Triggerereignis registrieren
Rufen Sie registerTrigger()
in Ihrer App auf, um ein Conversion-Trigger-Ereignis zu registrieren:
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)
Nach der Registrierung sendet die API eine HTTP-POST-Anfrage an den Dienstendpunkt unter der von attributionTriggerUri
angegebenen Adresse. Die Antwort des Endpunkts enthält Werte für Ereignis- und aggregierte Berichte.
Wenn die ursprüngliche Ad-Tech-Plattform die Weitergabe von Triggerregistrierungen zulässt, kann der URI Weiterleitungen zu URIs enthalten, die zu anderen Ad-Tech-Plattformen gehören. Die für die Weiterleitungen geltenden Limits und Regeln sind im technischen Vorschlag beschrieben.
App- und webübergreifende Analyse registrieren
Wenn sowohl eine App als auch ein Browser eine Rolle auf dem Weg des Nutzers von der Quelle zum Trigger spielen, gibt es geringfügige Unterschiede bei der Implementierung der Registrierung von Werbeereignissen. Wenn ein Nutzer eine Anzeige in einer App sieht und für eine Conversion zu einem Browser weitergeleitet wird, wird die Quelle von der App und die Conversion vom Webbrowser registriert. Wenn ein Nutzer beispielsweise in einem Webbrowser beginnt und zur Conversion zu einer App weitergeleitet wird, wird die Quelle im Browser und die Conversion in der App registriert.
Da sich AdTech-Unternehmen im Web und auf Android-Geräten unterschiedlich organisieren, haben wir neue APIs hinzugefügt, um Quellen und Trigger zu registrieren, wenn sie in Browsern auftreten. Der Hauptunterschied zwischen diesen APIs und den entsprechenden appbasierten APIs besteht darin, dass wir erwarten, dass der Browser den Weiterleitungen folgt, alle browserspezifischen Filter anwendet und die gültigen Registrierungen an die Plattform weiterleitet, indem er registerWebSource()
oder registerWebTrigger()
aufruft.
Das folgende Code-Snippet zeigt ein Beispiel für den API-Aufruf, den der Browser ausführt, um eine Attributionsquelle zu registrieren, bevor der Nutzer zu einer App weitergeleitet wird:
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);
Das folgende Code-Snippet zeigt ein Beispiel für den API-Aufruf, den der Browser ausführt, um eine Conversion zu registrieren, nachdem der Nutzer aus der App weitergeleitet wurde:
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);
Rauschen zum Schutz der Privatsphäre hinzufügen
Berichte auf Ereignisebene enthalten Daten zu Zielvorhaben, Attributionsquellen-IDs und Triggern. Sie werden im ursprünglichen (unverschlüsselten) Format an den Ursprung der Berichterstellung gesendet. Um die Privatsphäre der Nutzer zu schützen, kann Rauschen hinzugefügt werden, um die Identifizierung einzelner Nutzer zu erschweren. Berichte auf Ereignisebene mit Rauschen werden gemäß dem Framework für differenzielle Vertraulichkeit generiert und gesendet. Dies sind die Standardwerte für den Rauschprozentsatz für verschiedene Szenarien:
Quelltyp |
Wert für Quellziel |
Wahrscheinlichkeit für Rauschen im Bericht pro Quellenregistrierung |
Ansehen |
App oder Web |
0,0000025 |
Ansehen |
Apps und Web |
0,0000042 |
Klick |
App oder Web |
0.0024263 |
Klick |
Apps und Web |
0.0170218 |
Bei der App-zu-Web-Attributionsanalyse, bei der Quellen Conversions sowohl für App- als auch für Webziele generieren können, kann in Berichten auf Ereignisebene angegeben werden, ob der Trigger in der App oder im Web ausgelöst wurde. Um diese zusätzlichen Details zu berücksichtigen, werden die Berichte mit Rauschen, die generiert werden, bis zu etwa 7-mal für Klicks und etwa 1,7-mal für Aufrufe.
Bei einigen Ad-Tech-Anbietern ist es nicht erforderlich, in Berichten auf Ereignisebene anzugeben, ob der Trigger in der App oder im Webziel aufgetreten ist. Ad-Tech-Unternehmen können das Feld coarse_event_report_destinations
unter dem Header Attribution-Reporting-Register-Source
verwenden, um Rauschen zu reduzieren. Wenn eine Quelle mit dem angegebenen Feld coarse_event_report_destinations
die Attribution gewinnt, enthält der resultierende Bericht sowohl App- als auch Webziele, ohne dass unterschieden wird, wo der tatsächliche Trigger aufgetreten ist.
In den folgenden Beispielen klickt ein Nutzer auf eine Anzeige und die Quelle wird bei der API registriert. Der Nutzer führt dann sowohl in der App des Werbetreibenden als auch auf der Website des Werbetreibenden eine Conversion aus. Beide Conversions werden als Trigger registriert und dem ursprünglichen Klick zugeordnet.
Ein HTTP-Header für die klickbasierte Quellenregistrierung:
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"
}
Ein Trigger wird über die App mit dem Paketnamen com.advertiser.example
registriert:
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
"trigger_data": "1",
"priority": "1"
}],
}
Ein Trigger wird in einem Browser von der Website mit der eTLD+1-Domain https://advertiser.com
registriert:
Attribution-Reporting-Register-Trigger: {
"event_trigger_data": [{
"trigger_data": "2",
"priority": "2"
}],
}
Die entsprechenden Berichte auf Ereignisebene werden generiert. Angenommen, beide Trigger werden der Quelle zugeordnet. Dann werden die folgenden Berichte auf Ereignisebene generiert:
{
"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
}
Berichte erstellen und bereitstellen
Die Attribution Reporting API sendet Berichte an die Endpunkte auf Ihrem Server, die Berichte auf Ereignisebene und aggregierbare Berichte akzeptieren.
Ausführung von Berichtsjobs erzwingen
Nachdem Sie ein Attributionsquellenereignis oder ein Trigger-Ereignis registriert haben, plant das System die Ausführung des Berichtsjobs. Standardmäßig wird dieser Job alle 4 Stunden ausgeführt. Zu Testzwecken können Sie die Ausführung der Berichtsjobs erzwingen oder die Intervalle zwischen den Jobs verkürzen.
Attributionsjob zur Ausführung zwingen:
adb shell cmd jobscheduler run -f com.google.android.adservices.api 5
Erzwingen, dass ein Job für Berichte auf Ereignisebene ausgeführt wird:
adb shell cmd jobscheduler run -f com.google.android.adservices.api 3
Ausführung eines aggregierbaren Berichtsjobs erzwingen:
adb shell cmd jobscheduler run -f com.google.android.adservices.api 7
Sehen Sie sich die Ausgabe in logcat an, um zu sehen, wann die Jobs ausgeführt wurden. Das sollte in etwa so aussehen:
JobScheduler: executeRunCommand(): com.google.android.adservices.api/0 5 s=false f=true
Bereitstellung von Berichten erzwingen
Auch wenn der Berichtsjob zur Ausführung gezwungen wird, sendet das System Berichte weiterhin gemäß den geplanten Lieferzeiten, die zwischen einigen Stunden und mehreren Tagen liegen. Zu Testzwecken können Sie die Systemzeit des Geräts auf einen Zeitpunkt nach den geplanten Verzögerungen vorverlegen, um die Berichtsübermittlung zu starten.
Meldungen auf Ihrem Server überprüfen
Nachdem Berichte gesendet wurden, können Sie die Zustellung überprüfen, indem Sie die empfangenen Berichte, die entsprechenden Serverlogs wie den Mock-Serververlauf oder Ihr benutzerdefiniertes System prüfen.
Zusammengefassten Bericht analysieren
Wenn Sie einen aggregierten Bericht erhalten, enthält das Feld debug_cleartext_payload
eine unverschlüsselte Version des Berichts. Diese Version Ihres Berichts ist zwar nicht verschlüsselt, muss aber trotzdem decodiert werden.
Im Folgenden sehen Sie ein Beispiel für das Decodieren des Inhalts des Felds debug_cleartext_payload
in zwei Schritten: zuerst mit Base64-Decodierung und dann mit CBOR-Decodierung.
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);
});
Test
Um Ihnen den Einstieg in die Attribution Reporting API zu erleichtern, können Sie das Projekt MeasurementSampleApp auf GitHub verwenden. In dieser Beispiel-App wird die Registrierung von Attributionsquellen und Triggern demonstriert.
Für Serverendpunkte können Sie die folgenden Referenzressourcen oder Ihre benutzerdefinierte Lösung verwenden:
- MeasurementAdTechServerSpec enthält OpenAPI-Dienstdefinitionen, die auf unterstützten Mock- oder Microservices-Plattformen bereitgestellt werden können.
- MeasurementAdTechServer enthält eine Referenzimplementierung eines Mock-Servers, der auf einer Spring Boot-App für Google App Engine basiert.
Vorbereitung
Stellen Sie Mock-APIs auf Remote-Endpunkten bereit, auf die von Ihrem Testgerät oder Emulator aus zugegriffen werden kann. Zur Vereinfachung des Tests können Sie sich die Beispielprojekte MeasurementAdTechServerSpec und MeasurementAdTechServer ansehen.
Zu testende Funktionen
- Attributionsquelle und Registrierungen von Conversion-Triggern testen Prüfen Sie, ob serverseitige Endpunkte im richtigen Format antworten.
- Berichtsjobs ausführen
- Bestätigen Sie die Zustellung von Berichten im Backend oder in der Konsole Ihres Testservers.
Geplante Funktionen
Flexible Konfiguration auf Ereignisebene
Die Standardkonfiguration für Berichte auf Ereignisebene wird für den Beginn von Utility-Tests empfohlen, ist aber möglicherweise nicht für alle Anwendungsfälle ideal. Die Attribution Reporting API unterstützt optionale, flexiblere Konfigurationen, damit Ad-Tech-Unternehmen mehr Kontrolle über die Struktur ihrer Berichte auf Ereignisebene haben und die Nützlichkeit der Daten maximieren können. Diese zusätzliche Flexibilität wird in zwei Phasen in die Attribution Reporting API eingeführt:
- Phase 1: Flexible Konfiguration auf Ereignisebene (Lite), eine Teilmenge von Phase 2.
- Phase 2: Vollständige Version der flexiblen Konfiguration auf Ereignisebene.
Phase 1: Lite-Version der flexiblen Ereignisebene
Wir fügen dem JSON in Attribution-Reporting-Register-Source
die folgenden zwei optionalen Parameter hinzu:
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>, ...]
}
}
Beispiel für eine benutzerdefinierte Konfiguration
Diese Beispielkonfiguration unterstützt einen Entwickler, der Berichte in früheren Berichtszeiträumen erhalten möchte.
{
...
"max_event_level_reports": 2,
"event_report_windows": {
"end_times": [7200, 43200, 86400] // 2 hours, 12 hours, 1 day in seconds
}
}
Phase 2: Vollständig flexible Ereignisebene
Zusätzlich zu den Parametern, die in Phase 1 hinzugefügt wurden, fügen wir dem JSON in Attribution-Reporting-Register-Source
einen weiteren optionalen Parameter trigger_specs
hinzu.
{
// 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 makes sure 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>, ...]
}
}
Mit dieser Konfiguration wird der Ausgabebereich der Berichte auf Ereignisebene für jede Quellregistrierung vollständig angegeben. Für jede Triggerspezifikation geben wir Folgendes an:
- Eine Reihe von Übereinstimmungskriterien:
- Auf welche spezifischen Triggerdaten sich diese Spezifikation bezieht. Diese Quelle kann nur mit Triggern abgeglichen werden, die einen der angegebenen
trigger_data
-Werte in dertrigger_specs
haben. Wenn der Trigger also mit dieser Quelle übereinstimmen würde, aber seintrigger_data
nicht einer der Werte in der Konfiguration der Quelle ist, wird der Trigger ignoriert. - Wenn ein bestimmter Trigger dieser Spezifikation entspricht (mit
event_report_windows
). Der Trigger kann weiterhin mit einer Quelle für aggregierbare Berichte abgeglichen werden, auch wenn er die beiden oben genannten Abgleichskriterien nicht erfüllt.
- Auf welche spezifischen Triggerdaten sich diese Spezifikation bezieht. Diese Quelle kann nur mit Triggern abgeglichen werden, die einen der angegebenen
- Ein bestimmter Algorithmus zum Zusammenfassen und Gruppieren aller Trigger innerhalb eines Attributionszeitraums. So kann für Trigger ein
value
-Parameter angegeben werden, der für eine bestimmte Spezifikation summiert, aber als gruppierter Wert gemeldet wird.
Bei Triggern wird auch das Hinzufügen eines optionalen Wertparameters in den Wörterbüchern innerhalb von event_trigger_data
unterstützt.
{
"event_trigger_data": [
{
"trigger_data": "2",
"value": 100, // Defaults to 1
"filters": ...
},
...
]
}
Jede Triggerregistrierung wird mit höchstens einer Triggerspezifikation abgeglichen und der zugehörige Zusammenfassungswert wird aktualisiert. Auf übergeordneter Ebene führen wir zum Zeitpunkt des Triggers folgende Schritte aus:
- Globale Attributionsfilter anwenden
- Werten Sie für jede Triggerspezifikation die
event_trigger_data
anhand der Spezifikation aus, um eine Übereinstimmung zu finden. Verwenden Sie dazu dieevent_reporting_window
der Spezifikation.event_reporting_windows
auf oberster Ebene dient als Standardwert, falls in einer Triggerspezifikation das Unterfeldevent_report_windows
fehlt. - Die erste übereinstimmende Spezifikation wird für die Zuordnung ausgewählt und der Zusammenfassungswert wird um
value
erhöht.
Wenn die event_report_window
für eine Spezifikation abgeschlossen ist, wird ihr Zusammenfassungswert einem Bucket zugeordnet und für jede Steigerung im Zusammenfassungs-Bucket, die durch zugeordnete Triggerwerte verursacht wird, wird ein Bericht auf Ereignisebene gesendet. Berichte enthalten ein zusätzliches Feld: trigger_summary_bucket
.
{
...
"trigger_summary_bucket": [<bucket start>, <bucket end>],
}
Konfigurationen, die der aktuellen Version entsprechen
Die folgenden Konfigurationen sind für die aktuellen Ereignis- und Navigationsquellen der APIs jeweils gleichwertig. Insbesondere bei Navigationsquellen wird deutlich, warum die Rauschpegel im Vergleich zu Ereignisquellen so hoch sind, um dieselben Epsilon-Werte beizubehalten: Navigationsquellen haben einen viel größeren Ausgabebereich.
Es ist möglich, dass es mehrere gleichwertige Konfigurationen gibt, da einige Parameter als Standard festgelegt oder entfernt werden können.
Äquivalente Ereignisquellen
// 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>,
}
Äquivalente Navigationsquellen
// 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>,
}
Beispiele für benutzerdefinierte Konfigurationen
Im Folgenden finden Sie einige zusätzliche Konfigurationen, die nicht den Standardeinstellungen entsprechen. In allen diesen Beispielen müssen Entwickler folgende Kompromisse eingehen:
- Sie können eine Dimension der Standardkonfiguration reduzieren (Anzahl der Triggers, Kardinalität der Triggerdaten, Anzahl der Zeiträume), um eine andere zu erhöhen und so das Rauschniveau beizubehalten.
- Reduzieren Sie eine Dimension der Standardkonfiguration (Anzahl der Trigger, Kardinalität der Triggerdaten, Anzahl der Fenster), um das Rauschen zu verringern.
Wertgruppen für Berichtstrigger
Diese Beispielkonfiguration unterstützt einen Entwickler, der nur für einen Berichtszeitraum (z. B. 7 Tage) auf Wertdaten optimieren möchte. Dabei werden weniger Berichtszeiträume für weniger Rauschen in Kauf genommen. In diesem Beispiel ist jeder Trigger, bei dem trigger_data
auf einen anderen Wert als 0 festgelegt ist, nicht für die Zuordnung geeignet.
{
"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]
}],
}
Es können Trigger mit dem Feld value
registriert werden, die zusammengefasst und in Buckets eingeteilt werden. Beispiel: Innerhalb von 7 Tagen nach der Registrierung der Quelle gibt es drei Trigger mit den Werten 1, 3 und 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}] }
Die Werte werden auf 8 summiert und nach 7 Tagen + 1 Stunde in den folgenden Berichten angezeigt:
// Report 1
{
...
"trigger_summary_bucket": [5, 9]
}
In den folgenden sieben Tagen werden die folgenden Trigger registriert:
{ "event_trigger_data": [{"trigger_data": "0", "value": 50}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 45}] }
Die Werte werden addiert: 8 + 50 + 45 = 103. Das führt zu den folgenden Berichten nach 14 Tagen und 1 Stunde:
// Report 2
{
...
"trigger_summary_bucket": [10, 99]
},
// Report 3
{
...
"trigger_summary_bucket": [100, MAX_INT]
}
Anzahl der Trigger melden
In diesem Beispiel wird gezeigt, wie ein Entwickler eine Quelle so konfiguriert, dass bis zu 10 Trigger gezählt werden.
{
"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]
}],
}
Zugeordnete Trigger mit trigger_data
, die auf 0 gesetzt ist, werden gezählt und auf 10 begrenzt.
Der Triggerwert wird ignoriert, da summary_window_operator
auf „count“ (Anzahl) festgelegt ist. Wenn vier Trigger registriert und der Quelle zugeordnet werden, sieht der Bericht so aus:
// 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]
}
Binärdatei mit häufigeren Berichten
Diese Beispielkonfiguration unterstützt einen Entwickler, der wissen möchte, ob in den ersten 10 Tagen mindestens eine Conversion erfolgt ist (unabhängig vom Wert), aber Berichte in häufigeren Intervallen als der Standard erhalten möchte. Auch in diesem Beispiel ist jeder Trigger, bei dem trigger_data
auf einen anderen Wert als 0 festgelegt ist, nicht für die Zuordnung geeignet. Daher wird dieser Anwendungsfall als binär bezeichnet.
{
"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]
}],
}
Triggerspezifikationen von Quelle zu Quelle variieren
{
"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
}
Wir bitten Entwickler, uns verschiedene Anwendungsfälle für diese API-Erweiterung vorzuschlagen. Wir werden diese Erklärung dann mit Beispielkonfigurationen für diese Anwendungsfälle aktualisieren.
Netzwerkübergreifende Attribution ohne Weiterleitungen
AdTechs sollten Weiterleitungen verwenden, um mehrere Trigger für Attributionsquellen zu registrieren und netzwerkübergreifende Attribution durchzuführen. Mit dieser Funktion lässt sich die Attribution über Netzwerke hinweg unterstützen, wenn Weiterleitungen nicht netzwerkübergreifend möglich sind. Weitere Informationen
Ad-Tech-Unternehmen können die Konfiguration in der Trigger-Registrierungsantwort senden. Diese Konfiguration basiert darauf, welche Quellen, die von anderen Ad-Tech-Unternehmen registriert wurden, ausgewählt werden, um abgeleitete Quellen zu generieren. Diese abgeleiteten Quellen werden dann für die Attribution verwendet. Zusammengefasste Berichte werden generiert, wenn der Trigger einer abgeleiteten Quelle zugeordnet wird. Die Erstellung von Ereignisberichten für abgeleitete Quellen wird nicht unterstützt.
Anbieter von Anzeigentechnologien können aus den aggregation_keys
in ihren registrierten Quellen auswählen, die sie mit Partnern für Anzeigentechnologien teilen möchten. Diese Schlüssel können im optionalen Feld shared_aggregation_keys
unter der Überschrift Attribution-Reporting-Register-Source
für die Quellenregistrierung deklariert werden:
"shared_aggregation_keys": ["[key name1]", "[key name2]"]
Abgeleitete Quellen werden basierend auf der Konfiguration unter der Überschrift „Triggerregistrierung“ Attribution-Reporting-Register-Trigger
generiert:
// 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]"
}
]
Hier eine Version mit Beispielwerten:
"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"
}
]
Dem Header für die Triggerregistrierung werden zwei neue optionale Felder hinzugefügt. Mit diesen Feldern kann die Kennung der erfolgreichen Ad-Tech in aggregierbaren Berichtsschlüsseln angegeben werden:
x_network_bit_mapping
: Zuordnung von Registrierungs-ID zu Ad-Tech-ID-Bitx_network_data
: Offset (Linksverschiebung) für diex_network_bit_mapping
-ODER-Operation der Gewinner-Ad-Tech mit dem Trigger-Schlüsselteil
Beispiel:
"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 ad tech identifier
// bits. eg. If Ad Tech-A's sources wins the attribution then 0x1 here will be
// OR'd with the trigger key pieces to generate the final key piece.
"Ad-Tech-A-enrollment_id": "0x1", // Identifier bits in hex for A
"Ad-Tech-B-enrollment_id": "0x2" // Identifier bits in hex for B
}
…
}
So wird der Trigger-Schlüssel berechnet, wenn ein Bericht für die Quelle von AdTechB generiert wird:
key_piece
:0x400 (010000000000)
key_offset
:12
- AdtechB-Wert für
enrollment_id
:2 (010)
(ausx_network_bit_mapping
) - Resultierender Trigger-Schlüssel:
0x400 | 0x2 << 12 = 0x2400
Beschränkungen
Eine Liste der Funktionen, die sich in der Entwicklung für die SDK-Laufzeit befinden, finden Sie in den Versionshinweisen.
Fehler und Probleme melden
Ihr Feedback ist ein wichtiger Bestandteil der Privacy Sandbox für Android. Teilen Sie uns mit, wenn Sie Probleme feststellen oder Ideen zur Verbesserung der Privacy Sandbox für Android haben.