Entwicklerleitfaden für die Attribution Reporting API

Wähle beim Lesen der Dokumentation zur Privacy Sandbox für Android über die Schaltfläche Entwicklervorschau oder Beta die Programmversion aus, mit der du arbeitest. Die Anleitung kann davon abweichen.


Feedback geben

Die Attribution Reporting API wurde entwickelt, um den Datenschutz für Nutzer zu verbessern, indem die Abhängigkeit von parteiübergreifenden Nutzerkennungen entfernt wird. Außerdem unterstützt sie wichtige Anwendungsfälle für die Attribution und Conversion-Messung in verschiedenen Apps. In diesem Entwicklerleitfaden wird beschrieben, wie Sie die Attribution Reporting APIs konfigurieren und testen, um Anzeigenklicks, Aufrufe und Conversions zu registrieren. Dazu werden Methoden aufgerufen, die die relevanten Trigger und Quellen für solche Ereignisse registrieren.

In diesem Leitfaden erfahren Sie, wie Sie Serverendpunkte einrichten und eine Clientanwendung erstellen, die diese Dienste aufruft. Weitere Informationen zum Gesamtdesign der Attribution Reporting API finden Sie im Designvorschlag.

Wichtige Begriffe

  • Attributionsquellen beziehen sich auf Klicks oder Aufrufe.
  • Trigger 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 in den folgenden Abschnitten aufgeführten server- und clientseitigen Aufgaben ausführen.

Attribution Reporting API-Endpunkte einrichten

Für die Attribution Reporting API ist eine Reihe von Endpunkten erforderlich, auf die Sie über ein Testgerät oder einen Emulator zugreifen können. Erstellen Sie für jede der folgenden serverseitigen Aufgaben einen Endpunkt:

Es gibt mehrere Methoden, die erforderlichen Endpunkte einzurichten:

  • Der schnellste Einstiegsmöglichkeit ist die Bereitstellung der OpenAPI v3-Dienstdefinitionen aus unserem Beispielcode-Repository auf einer Simulations- oder Mikrodienstplattform. Sie können Postman, Prism oder eine andere fiktive Serverplattform verwenden, die dieses Format akzeptiert. Stellen Sie jeden Endpunkt bereit und verfolgen Sie die URIs zur Verwendung in Ihrer Anwendung. Informationen zum Überprüfen der Berichtszustellung finden Sie in den Aufrufen der fiktiven oder serverlosen Plattform.
  • Führen Sie mit dem Spring Boot-basierten Kotlin-Beispiel einen eigenen eigenständigen Server 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 vorhandenes System einzubinden.

Registrierung der Quelle akzeptieren

Dieser Endpunkt sollte über einen URI ähnlich dem folgenden adressierbar sein:

https://adtech.example/attribution_source

Wenn eine Clientanwendung eine Attributionsquelle registriert, stellt sie den URI für diesen Serverendpunkt bereit. Die Attribution Reporting API stellt dann eine Anfrage und enthält einen der folgenden Header:

  • Für Klickereignisse:

    Attribution-Reporting-Source-Info: navigation
    
  • Für das Ansehen von Ereignissen:

    Attribution-Reporting-Source-Info: event
    

Konfigurieren Sie den 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 zusätzlichen 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 AdTech-Partnern enthält, wird über die Attribution Reporting API an jeden URI eine ähnliche Anfrage gesendet. Jeder AdTech-Partner muss einen Server konfigurieren, der mit folgenden 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 für Conversion-Trigger akzeptieren

Dieser Endpunkt sollte über einen URI ähnlich dem folgenden adressierbar sein:

https://adtech.example/attribution_trigger

Wenn eine Clientanwendung ein Triggerereignis registriert, stellt sie den URI für diesen Serverendpunkt bereit. Die Attribution Reporting API sendet dann eine Anfrage und enthält einen der folgenden Header:

Konfigurieren Sie den 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 zusätzlichen 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 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

Pro Aggregationsschlüssel-ID und Filterstring gilt ein Limit von 25 Byte. Die IDs für Aggregationsschlüssel und Filterstrings dürfen also nicht länger als 25 Zeichen sein. In diesem Beispiel ist campaignCounts 14 Zeichen lang und damit eine gültige Aggregationsschlüssel-ID. 1234 ist 4 Zeichen 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 AdTech-Partnern enthält, wird über die Attribution Reporting API an jeden URI eine ähnliche Anfrage gesendet. Jeder AdTech-Partner muss einen Server konfigurieren, der mit folgenden 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 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.

Berichte auf Ereignisebene akzeptieren

Dieser Endpunkt sollte über einen URI adressierbar sein. Weitere Informationen zum Registrieren von URIs finden Sie unter Für ein Privacy Sandbox-Konto registrieren. Der URI wird vom Ursprung von Servern abgeleitet, die zum Akzeptieren der Quellregistrierung und zum Auslösen der Registrierung verwendet werden. Bei den Beispiel-URIs für Endpunkte, die die Quellregistrierung akzeptieren und die Triggerregistrierung akzeptieren, lautet der URI dieses Endpunkts:

https://adtech.example/.well-known/attribution-reporting/report-event-attribution

Konfiguriere 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 Fehlerbehebungsschlüsseln erhalten Sie zusätzliche Informationen zu Ihren Attributionsberichten. Weitere Informationen zu ihrer Konfiguration

Aggregierbare Berichte akzeptieren

Dieser Endpunkt sollte über einen URI adressierbar sein. Weitere Informationen zum Registrieren von URIs finden Sie unter Für ein Privacy Sandbox-Konto registrieren. Der URI wird vom Ursprung von Servern abgeleitet, die zum Akzeptieren der Quellregistrierung und zum Auslösen der Registrierung verwendet werden. Bei den Beispiel-URIs für Endpunkte, die die Quellregistrierung akzeptieren und die Triggerregistrierung akzeptieren, lautet der URI dieses Endpunkts:

https://adtech.example/.well-known/attribution-reporting/report-aggregate-attribution

Für aggregierte Berichte werden sowohl die verschlüsselten als auch die unverschlüsselten Felder ausgefüllt. Mit den verschlüsselten Berichten können Sie Tests mit dem Aggregationsdienst starten, während das unverschlüsselte Feld einen Einblick in die Strukturierung der Daten durch festgelegte Schlüssel/Wert-Paare liefert.

Konfiguriere 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 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]"
}

Mit Fehlerbehebungsschlüsseln erhalten Sie zusätzliche Informationen zu Ihren Attributionsberichten. Weitere Informationen zu ihrer Konfiguration

Android-Client einrichten

Die Client-App registriert Attributionsquellen und Trigger und aktiviert die Berichterstellung auf Ereignisebene und aggregierte Berichte. So bereiten Sie ein Android-Clientgerät oder einen Emulator für die Verwendung der Attribution Reporting API vor:

  1. Richte deine Entwicklungsumgebung für die Privacy Sandbox für Android ein.
  2. Installiere ein System-Image auf einem unterstützten Gerät oder richte einen Emulator ein, der die Privacy Sandbox für Android unterstützt.
  3. 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 \"\*\"
    
  4. Wenn Sie die Attribution Reporting API lokal testen (z. B. Tests 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"
    
  5. Fügen Sie die Berechtigung ACCESS_ADSERVICES_ATTRIBUTION in Ihre Android-Manifestdatei ein und erstellen Sie eine Anzeigendienstkonfiguration für Ihre App, um die Attribution Reporting APIs zu verwenden:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
    
  6. Optional: Wenn Sie Berichte zur Fehlerbehebung erhalten möchten, fügen Sie der Android-Manifestdatei die Berechtigung ACCESS_ADSERVICES_AD_ID hinzu:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
    
  7. Verweisen Sie im <application>-Element Ihres Manifests auf eine Anzeigendienstkonfiguration:

    <property android:name="android.adservices.AD_SERVICES_CONFIG"
              android:resource="@xml/ad_services_config" />
    
  8. Geben Sie die XML-Ressource für Anzeigendienste an, auf die im Manifest verwiesen wird, 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 in dem Moment registrieren, in dem sie auftreten, um sicherzustellen, dass sie ordnungsgemäß gemeldet werden. Die MeasurementManager-Klasse bietet Methoden zum Registrieren von Attributionsquellenereignissen und Conversion-Triggern.

Ereignis mit Attributionsquelle registrieren

Wenn eine Anzeige angesehen 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 Ereignissen aus Attributionsquellen:

  • Klicks, die Sie in der Regel mit einer Callback-Methode wie onClick() erfassen. Das entsprechende Triggerereignis tritt normalerweise kurz nach dem Klickereignis ein. Diese Art von Ereignis liefert mehr Informationen zur Nutzerinteraktion und ist daher eine gute Art von Attributionsquelle, um eine hohe Priorität zu erhalten.
  • Ansichten, die Sie in der Regel mit einer Callback-Methode wie onAdShown() registrieren. Das entsprechende Triggerereignis kann Stunden oder Tage nach dem Aufrufereignis auftreten.

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 mit attributionSourceUri angegebenen Adresse. Die Antwort des Endpunkts enthält Werte für destination, source_event_id, expiry und source_priority.

Wenn die ursprüngliche Anzeigentechnologie Quellenregistrierungen teilen möchte, kann der ursprüngliche URI der Attributionsquelle Weiterleitungen zu anderen AdTech-Endpunkten enthalten. Die für Weiterleitungen geltenden Beschränkungen und Regeln werden im technischen Vorschlag ausführlich beschrieben.

Für registerSource und registerTrigger werden Daisy-Chain-Weiterleitungen unterstützt. Zusätzlich zum Registrierungsheader kann der API-Nutzer jetzt eine HTTP-Weiterleitung als Serverantwort bereitstellen, die einen 302-Statuscode und einen „Location“-Header mit der nächsten URL für eine zusätzliche Registrierung enthält.

Nur das Feld "Ziel", das beim ersten Besuch angegeben wurde, wird in der Verkettung verwendet. Für die Anzahl der Besuche gilt dasselbe Limit wie für die Header "Attribution-Reporting-Weiterleitung". Diese Weiterleitungsunterstützung gilt zusätzlich zur bisherigen Unterstützung für „Attribution-Reporting-Weiterleitung“. Sofern beide vorhanden sind, wird „Attribution-Reporting-Weiterleitung“ bevorzugt.

Conversion-Trigger-Ereignis registrieren

Um ein Conversion-Trigger-Ereignis zu registrieren, rufen Sie registerTrigger() in Ihrer App auf:

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 mit attributionTriggerUri angegebenen Adresse. Die Antwort des Endpunkts enthält Werte für Ereignisberichte und zusammengefasste Berichte.

Wenn die ursprüngliche AdTech-Plattform die Freigabe von Trigger-Registrierungen zulässt, kann der URI Weiterleitungen zu URIs enthalten, die zu anderen AdTech-Plattformen gehören. Die für die Weiterleitungen geltenden Beschränkungen und Regeln werden im technischen Vorschlag ausführlich beschrieben.

App- und Web-Messung erfassen

Falls sowohl eine App als auch ein Browser auf dem Weg des Nutzers von der Quelle zum Trigger eine Rolle spielen, gibt es kleine Unterschiede bei der Implementierung der Registrierung von Anzeigenereignissen. Wenn ein Nutzer eine Anzeige in einer App sieht und für eine Conversion an einen Browser weitergeleitet wird, wird die Quelle von der App und die Conversion vom Webbrowser registriert. Wenn ein Nutzer in einem Webbrowser startet und zur Konvertierung zu einer Anwendung weitergeleitet wird, registriert der Browser die Quelle und die Anwendung die Conversion.

Da Anzeigentechnologien im Web und unter Android unterschiedlich organisiert sind, haben wir neue APIs hinzugefügt, um Quellen und Trigger zu registrieren, wenn sie in Browsern ausgeführt werden. Der Hauptunterschied zwischen diesen APIs und den entsprechenden app-basierten APIs besteht darin, dass wir erwarten, dass der Browser den Weiterleitungen folgt, browserspezifische Filter anwendet und die gültigen Registrierungen durch Aufrufen von registerWebSource() oder registerWebTrigger() an die Plattform übergibt.

Das folgende Code-Snippet zeigt ein Beispiel für den API-Aufruf, mit dem der Browser eine Attributionsquelle registriert, 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 von 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);

Hinzufügen von Geräuschen zum Schutz der Privatsphäre

Berichte auf Ereignisebene enthalten Daten zum Ziel, zur Attributionsquellen-ID und zu Triggerdaten. Sie werden im ursprünglichen (unverschlüsselten) Format an die Quelle 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 in Übereinstimmung mit dem Differential-Privacy-Framework erstellt und gesendet. Dies sind die Standardwerte für den Rauschanteil in verschiedenen Szenarien:

Quelltyp

Quellzielwert

Wahrscheinlichkeit des Berichts mit Rauschen 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 Attributionsmessung von App zu Web, bei der Quellen zu Conversions für App- und Webziele führen, können Sie in Berichten auf Ereignisebene angeben, ob der Trigger in der App oder im Web ausgelöst wurde. Um diese zusätzlichen Details auszugleichen, werden in den generierten Berichten etwa bis zu 7-mal Klicks und Aufrufe um das 1,7-Fache erzielt.

Für einige AdTech-Technologien sind keine Berichte auf Ereignisebene erforderlich, um anzugeben, ob der Trigger in der App oder im Webziel aufgetreten ist. Anzeigentechnologie-Anbieter können das Feld coarse_event_report_destinations unter der Überschrift Attribution-Reporting-Register-Source verwenden, um Bildrauschen zu reduzieren. Wenn eine Quelle mit dem Feld coarse_event_report_destinations die Attribution gewinnt, enthält der resultierende Bericht sowohl App- als auch Webziele, ohne zu unterscheiden, wo der tatsächliche Trigger aufgetreten ist.

In den folgenden Beispielen klickt ein Nutzer auf eine Anzeige und die entsprechende 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 durch. Beide Conversions werden als Trigger registriert und dem ersten Klick zugeordnet.

HTTP-Header für 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 von der App mit dem Paketnamen com.advertiser.example registriert:

Attribution-Reporting-Register-Trigger: {
    "event_trigger_data": [{
    "trigger_data": "1",
    "priority": "1"
    }],
}

Ein Trigger wird von 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 daraus resultierenden Berichte auf Ereignisebene werden generiert. Unter der Voraussetzung, dass beide Trigger der Quelle zugeordnet werden, 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 Berichterstellungsjobs erzwingen

Nachdem Sie ein Ereignis der Attributionsquelle registriert oder ein Triggerereignis 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 Berichterstellungsjobs erzwingen oder die Intervalle zwischen den Jobs verkürzen.

Ausführung des Attributionsjobs erzwingen:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 5

Ausführung des Berichterstellungsjobs auf Ereignisebene erzwingen:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 3

Ausführung des aggregierbaren Berichterstellungsjobs erzwingen:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 7

Prüfen Sie die Ausgabe in logcat, 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 die Ausführung des Berichterstellungsjobs erzwungen wird, sendet das System Berichte gemäß den für geplanten Lieferzeiten, die zwischen einigen Stunden und mehreren Tagen liegen. Zu Testzwecken können Sie die Zeit für das Gerätesystem nach den geplanten Verzögerungen für die Zustellung des Berichts verschieben.

Berichte auf Ihrem Server überprüfen

Prüfen Sie nach dem Senden der Berichte die Zustellung, indem Sie die empfangenen Berichte, entsprechende Serverprotokolle wie den simulierten Serververlauf oder Ihr benutzerdefiniertes System prüfen.

Zusammengefassten Bericht decodieren

Wenn Sie einen zusammengefassten Bericht erhalten, enthält das Feld debug_cleartext_payload eine unverschlüsselte Version des aggregierten Berichts. Diese Version Ihres Berichts ist zwar unverschlüsselt, muss aber noch decodiert werden.

Im Folgenden finden Sie ein Beispiel für die Decodierung des Inhalts des Felds debug_cleartext_payload in zwei Schritten: in dem ersten mit Base64-Decodierung und in dem zweiten 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);
});

Testen

Für den Einstieg in die Attribution Reporting API können Sie das MeasurementSampleApp-Projekt auf GitHub verwenden. Diese Beispiel-App veranschaulicht die Registrierung von Attributionsquellen und löst die Registrierung aus.

Für Serverendpunkte sollten Sie die folgenden Referenzressourcen oder Ihre benutzerdefinierte Lösung in Betracht ziehen:

  • MeasurementAdTechServerSpec enthält OpenAPI-Dienstdefinitionen, die auf unterstützten Mock- oder Mikrodienstplattformen bereitgestellt werden können.
  • MeasurementAdTechServer enthält eine Referenzimplementierung eines Pseudoservers, der auf der Spring Boot-Anwendung für Google App Engine basiert.

Voraussetzungen

Stellen Sie simulierte APIs auf Remote-Endpunkten bereit, auf die Sie über Ihr Testgerät oder Ihren Emulator zugreifen können. Einfache Tests finden Sie in den Beispielprojekten MeasurementAdTechServerSpec und MeasurementAdTechServer.

Zu testende Funktionalität

Geplante Funktionen

Flexible Konfiguration auf Ereignisebene

Die Standardkonfiguration für Berichte auf Ereignisebene wird zu Beginn von Dienstprogrammtests empfohlen, ist aber möglicherweise nicht für alle Anwendungsfälle ideal. Die Attribution Reporting API unterstützt optionale, flexiblere Konfigurationen. AdTech-Unternehmen haben so mehr Kontrolle über die Struktur ihrer Berichte auf Ereignisebene und können den Nutzen der Daten maximieren. Diese zusätzliche Flexibilität wird in zwei Phasen in die Attribution Reporting API eingeführt:

  • Phase 1: Flexible Lite-Konfiguration auf Ereignisebene; ein Teil von Phase 2.
  • Phase 2: Vollversion der flexiblen Konfiguration auf Ereignisebene.

Phase 1: Flexible Lite-Ereignisebene

Wir fügen dem JSON-Code in Attribution-Reporting-Register-Source die beiden folgenden 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 für den Empfang von Berichten zu früheren Berichterstellungszeiträumen optimieren 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 in Phase 1 hinzugefügten Parametern fügen wir dem JSON-Code in Attribution-Reporting-Register-Source den zusätzlichen 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 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>, ...]
  }
}

Diese Konfiguration gibt den Ausgabebereich der Berichte auf Ereignisebene pro Quellenregistrierung vollständig an. Für jede Triggerspezifikation geben wir Folgendes vollständig an:

  • Eine Reihe von Abgleichskriterien:
    • Für welche spezifischen Triggerdaten diese Spezifikation gilt. Diese Quelle kann nur mit Triggern abgeglichen werden, die einen der angegebenen trigger_data-Werte im trigger_specs haben. Mit anderen Worten: Wenn der Trigger mit dieser Quelle übereinstimmt, sein trigger_data aber keiner der Werte in der Konfiguration der Quelle ist, wird der Trigger ignoriert.
    • Wenn ein bestimmter Trigger dieser Spezifikation entspricht (mithilfe von event_report_windows), kann er trotzdem einer Quelle für aggregierte Berichte zugeordnet werden, auch wenn die beiden zuvor genannten Übereinstimmungskriterien nicht erfüllt werden.
  • Einen spezifischen Algorithmus zum Zusammenfassen und Einteilen aller Trigger innerhalb eines Attributionsfensters. Dadurch können Trigger einen value-Parameter angeben, der für eine bestimmte Spezifikation summiert, aber als gruppierter Wert gemeldet wird.

Trigger unterstützen auch das Hinzufügen eines optionalen Wertparameters in die Wörterbücher innerhalb von event_trigger_data.

{
  "event_trigger_data": [
    {
      "trigger_data": "2",
      "value": 100,  // Defaults to 1
      "filters": ...
    },
    ...
  ]
}

Jede Triggerregistrierung stimmt mit höchstens einer Triggerspezifikation überein und aktualisiert den zugehörigen Zusammenfassungswert. Auf übergeordneter Ebene werden zum Zeitpunkt des Triggers folgende Aktionen ausgeführt:

  • Wenden Sie globale Attributionsfilter an.
  • Bewerten Sie für jede Triggerspezifikation den event_trigger_data der Spezifikation, um eine Übereinstimmung mit dem event_reporting_window der Spezifikation zu finden. Das event_reporting_windows der obersten Ebene dient als Standardwert für den Fall, dass eine Triggerspezifikation im fehlenden Unterfeld event_report_windows enthalten ist.
  • Die erste übereinstimmende Spezifikation wird für die Attribution ausgewählt und der Zusammenfassungswert wird um value erhöht.

Wenn die event_report_window für eine Spezifikation abgeschlossen ist, ordnen wir ihren Zusammenfassungswert einem Bucket zu und senden einen Bericht auf Ereignisebene für jede Erhöhung im Zusammenfassungs-Bucket, die durch zugeordnete Triggerwerte verursacht wird. Berichte enthalten ein zusätzliches Feld: trigger_summary_bucket.

{
  ...
  "trigger_summary_bucket": [<bucket start>, <bucket end>],
}

Konfigurationen, die der aktuellen Version entsprechen

Im Folgenden finden Sie entsprechende Konfigurationen für die aktuellen Ereignis- und Navigationsquellen der APIs. Dies zeigt insbesondere bei Navigationsquellen, warum die Rauschpegel im Vergleich zu Ereignisquellen so hoch sind, dass die gleichen Epsilon-Werte beibehalten werden: Navigationsquellen haben einen viel größeren Ausgaberaum.

Es ist möglich, dass mehrere Konfigurationen gleichwertig sind, da einige Parameter als Standard festgelegt oder bereinigt 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 über die Standardeinstellungen hinausgehen. Bei allen diesen Beispielen haben Entwickler folgende Vor- und Nachteile:

  • Reduzieren einer Dimension der Standardkonfiguration (#triggers, Trigger-Datenkardinalität, #windows), um eine weitere zu erhöhen, um den Rauschpegel beizubehalten
  • Reduzieren einer Dimension der Standardkonfiguration (#triggers, Trigger-Datenkardinalität, #windows), um den Rauschpegel zu reduzieren

Triggerwert-Buckets melden

Diese Beispielkonfiguration unterstützt einen Entwickler, der Wertdaten für nur einen Berichtszeitraum (z.B. 7 Tage) optimieren und dabei weniger Berichtsfenster für weniger Rauschen eintauschen möchte. In diesem Beispiel kommt jeder Trigger, bei dem trigger_data auf einen anderen Wert als 0 gesetzt ist, nicht für die Attribution infrage.

{
  "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]
  }],
}

Trigger können mit dem Satz value registriert werden, der addiert und zusammengefasst wird. Beispiel: Es gibt drei Trigger innerhalb von 7 Tagen nach der Quellregistrierung 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 zu 8 addiert und nach 7 Tagen + 1 Stunde in den folgenden Berichten erfasst:

// 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 zu 8 + 50 + 45 = 103 addiert. Dadurch werden nach 14 Tagen + 1 Stunde die folgenden Berichte erstellt:

// Report 2
{
  ...
  "trigger_summary_bucket": [10, 99]
},

// Report 3
{
  ...
  "trigger_summary_bucket": [100, MAX_INT]
}
Anzahl der Trigger für Berichte

Dieses Beispiel zeigt, wie ein Entwickler eine Quelle so konfigurieren kann, dass die Anzahl der Trigger bis zu zehn ausgelöst wird.

{
  "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, bei denen trigger_data auf 0 gesetzt ist, werden gezählt und auf 10 begrenzt. Der Triggerwert wird ignoriert, da für summary_window_operator „Anzahl“ festgelegt ist. Wenn vier Trigger registriert und der Quelle zugeordnet sind, würde der Bericht so aussehen:

// 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ärcode mit häufigeren Berichterstellung

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 standardmäßig erhalten möchten. Auch in diesem Beispiel kommt jeder Trigger, bei dem trigger_data auf einen anderen Wert als 0 gesetzt ist, nicht für die Attribution infrage. Aus diesem Grund 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 empfehlen Entwicklern, unterschiedliche Anwendungsfälle für diese API-Erweiterung vorzuschlagen. Diese Erläuterung werden wir dann mit Beispielkonfigurationen für diese Anwendungsfälle aktualisieren.

Netzwerkübergreifende Attribution ohne Weiterleitungen

AdTech-Unternehmen sollten Weiterleitungen verwenden, um mehrere Trigger für Attributionsquellen zu registrieren und die netzwerkübergreifende Attribution durchzuführen. Diese Funktion unterstützt die netzwerkübergreifende Attribution, wenn Weiterleitungen nicht netzwerkübergreifend möglich sind. Weitere Informationen

Anzeigentechnologie-Anbieter können die Konfiguration in der Trigger-Registrierungsantwort basierend darauf senden, welche Quellen, die von anderen Anzeigentechnologien registriert wurden, zum Generieren abgeleiteter Quellen ausgewählt werden. Diese abgeleiteten Quellen werden dann für die Attribution verwendet. Zusammengefasste Berichte werden generiert, wenn der Trigger einer abgeleiteten Quelle zugeordnet wird. Das Erstellen von Ereignisberichten für abgeleitete Quellen wird nicht unterstützt.

Anzeigentechnologie-Anbieter können in ihren registrierten Quellen aus den aggregation_keys auswählen, die sie mit Anzeigentechnologie-Partnern teilen möchten. Diese Schlüssel können im optionalen Feld shared_aggregation_keys unter dem Header der Quellregistrierung (Attribution-Reporting-Register-Source) deklariert werden:

"shared_aggregation_keys": ["[key name1]", "[key name2]"]

Abgeleitete Quellen werden anhand der Konfiguration unter dem Triggerregistrierungsheader 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 ist eine Version mit hinzugefügten 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"
    }
  ]

Es werden zwei neue optionale Felder hinzugefügt, um den Registrierungsheader auszulösen. Mit diesen Feldern wird die ID der erfolgreichen Anzeigentechnologie in aggregierbaren Berichtsschlüsseln aktiviert:

  • x_network_bit_mapping: Registrierungs-ID zur Bitzuordnung der Anzeigentechnologie-ID
  • x_network_data: Offset (Links-Umschalttaste) für den x_network_bit_mapping-ODER-Vorgang des erfolgreichen AdTech-Anbieters mit dem Trigger-Schlüsselelement
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 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
  }
  …
}

Beim Erstellen eines Berichts für die AdTechB-Quelle ergibt sich folgende Berechnung des Trigger-Schlüsselelements:

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • enrollment_id-Wert von AdtechB: 2 (010) (von x_network_bit_mapping)
  • Resultierendes Trigger-Schlüsselelement: 0x400 | 0x2 << 12 = 0x2400

Beschränkungen

Eine Liste der in Bearbeitung befindlichen Funktionen für die SDK-Laufzeit finden Sie in den Versionshinweisen.

Fehler und Probleme melden

Dein Feedback ist ein wichtiger Teil der Privacy Sandbox für Android. Informieren Sie uns über Probleme, die Sie finden, oder Vorschläge zur Verbesserung der Privacy Sandbox für Android.