Guía para desarrolladores sobre la API de Attribution Reporting

A medida que leas la documentación de Privacy Sandbox en Android, usa el botón Versión preliminar para desarrolladores o Beta para seleccionar la versión del programa con la que estás trabajando, ya que las instrucciones pueden variar.


Enviar comentarios

La API de Attribution Reporting está diseñada para brindar una mayor privacidad del usuario, ya que quita la dependencia de los identificadores de usuario entre varias partes y admite casos de uso clave para la medición de atribución y conversión en apps. En esta guía para desarrolladores, se describe cómo configurar y probar las APIs de Attribution Reporting para registrar clics en anuncios, vistas y conversiones mediante llamadas a métodos que registren los activadores y las fuentes relevantes para esos eventos.

En esta guía, aprenderás a configurar extremos del servidor y a compilar una app del cliente que llame a estos servicios. Obtén más información sobre el diseño general de la API de Attribution Reporting en la propuesta de diseño.

Términos clave

  • Las fuentes de atribución hacen referencia a los clics o las vistas.
  • Los activadores son eventos que se pueden atribuir a las conversiones.
  • Los informes contienen datos sobre un activador y la fuente de atribución correspondiente. Estos informes se envían en respuesta a los eventos de activación. La API de Attribution Reporting admite los informes a nivel del evento y los informes agregables.

Antes de comenzar

Para usar la API de Attribution Reporting, completa las tareas del servidor y del cliente que se enumeran en las siguientes secciones.

Configura los extremos de la API de Attribution Reporting

La API de Attribution Reporting requiere un conjunto de extremos a los que puedes acceder desde un dispositivo de prueba o emulador. Crea un extremo para cada una de las siguientes tareas del servidor:

Existen varios métodos para configurar los extremos necesarios:

  • La forma más rápida de ponerse en marcha es implementar las definiciones del servicio OpenAPI v3 de nuestro repositorio de código de muestra en una plataforma de microservicios o de prueba. Puedes usar Postman, Prism o cualquier otra plataforma de servidor simulado que acepte este formato. Implementa cada extremo y realiza un seguimiento de los URIs que se usarán en tu app. Para verificar la entrega del informe, consulta las llamadas realizadas anteriormente a la plataforma simulada o sin servidores.
  • Ejecuta tu propio servidor independiente con la muestra de Kotlin basada en Spring Boot. Implementa este servidor en tu proveedor de servicios en la nube o en tu infraestructura interna.
  • Usa las definiciones de servicio como ejemplos para integrar los extremos en tu sistema actual.

Acepta el registro de la fuente

Se debe poder acceder a este extremo desde un URI similar al siguiente:

https://adtech.example/attribution_source

Cuando una app del cliente registra una fuente de atribución, proporciona el URI para el extremo del servidor. Luego, la API de Attribution Reporting realiza una solicitud e incluye uno de los siguientes encabezados:

  • Para los eventos de clic:

    Attribution-Reporting-Source-Info: navigation
    
  • Para los eventos de vista:

    Attribution-Reporting-Source-Info: event
    

Configura tu extremo del servidor para que responda de la siguiente manera:

// 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>

Este es un ejemplo con valores de muestra agregados:

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

Si Attribution-Reporting-Redirects contiene URIs de socios de tecnología publicitaria, la API de Attribution Reporting realiza una solicitud similar a cada URI. Cada socio de tecnología publicitaria debe configurar un servidor que responda con los siguientes encabezados:

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.

Acepta el registro del activador de conversión

Se debe poder acceder a este extremo desde un URI similar al siguiente:

https://adtech.example/attribution_trigger

Cuando una app del cliente registra un evento activador, proporciona el URI para el extremo del servidor. Luego, la API de Attribution Reporting realiza una solicitud e incluye uno de los siguientes encabezados:

Configura tu extremo del servidor para que responda de la siguiente manera:

// 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>

Este es un ejemplo con valores de muestra agregados:

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

Existe un límite de 25 bytes por ID de clave de agregación y cadena de filtro. Esto significa que tus IDs de clave de agregación y las cadenas de filtro no deben exceder los 25 caracteres. En este ejemplo, campaignCounts tiene 14 caracteres, por lo que es un ID de clave de agregación válido, y 1234 es de 4 caracteres, por lo que es una cadena de filtro válida. Si un ID de clave de agregación o una cadena de filtro exceden los 25 caracteres, se ignora el activador.

Si Attribution-Reporting-Redirect contiene URIs de socios de tecnología publicitaria, la API de Attribution Reporting realiza una solicitud similar a cada URI. Cada socio de tecnología publicitaria debe configurar un servidor que responda con los siguientes encabezados:

// 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.

Acepta informes a nivel del evento

Se debe poder acceder a este extremo desde un URI. Consulta el artículo Cómo inscribir tu plataforma con Privacy Sandbox y obtén más información sobre la inscripción de URIs. (El URI se infiere del origen de los servidores que se usan para aceptar el registro de la fuente y del activador). Con los URIs de ejemplo para extremos que aceptan el registro de la fuente y aceptan el registro del activador, el URI de este extremo es el siguiente:

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

Configura este servidor para que acepte solicitudes JSON que usan el siguiente formato:

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

Las claves de depuración permiten comprender mejor tus informes de atribución. Obtén más información sobre su configuración.

Acepta informes agregables

Se debe poder acceder a este extremo desde un URI. Consulta el artículo Cómo inscribir tu plataforma con Privacy Sandbox y obtén más información sobre la inscripción de URIs. (El URI se infiere del origen de los servidores que se usan para aceptar el registro de la fuente y del activador). Con los URIs de ejemplo para extremos que aceptan el registro de la fuente y aceptan el registro del activador, el URI de este extremo es el siguiente:

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

Tanto los campos encriptados como los no encriptados se completan para los informes agregables. Los informes encriptados te permiten comenzar a realizar pruebas con el servicio de agregación, mientras que el campo sin encriptar proporciona información sobre la forma en que los pares clave-valor establecidos estructuran los datos.

Configura este servidor para que acepte solicitudes JSON que usan el siguiente formato:

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

Las claves de depuración permiten comprender mejor tus informes de atribución. Obtén más información sobre su configuración.

Configura el cliente de Android

La app del cliente registra los activadores y las fuentes de atribución, y habilita la generación de informes agregables y a nivel del evento. Si deseas preparar un dispositivo o emulador cliente de Android para usar la API de Attribution Reporting, haz lo siguiente:

  1. Configura tu entorno de desarrollo para Privacy Sandbox en Android.
  2. Instala una imagen del sistema en un dispositivo compatible o configura un emulador que incluya compatibilidad con Privacy Sandbox en Android.
  3. Habilita el acceso a la API de Attribution Reporting mediante la ejecución del siguiente comando adb (la API está inhabilitada de forma predeterminada).

    adb shell device_config put adservices ppapi_app_allow_list \"\*\"
    
  4. Si estás probando la API de Attribution Reporting de forma local (por ejemplo, realizas pruebas en un dispositivo al que tienes acceso físico), ejecuta este comando para inhabilitar la inscripción:

    adb shell device_config put adservices disable_measurement_enrollment_check "true"
    
  5. Incluye el permiso ACCESS_ADSERVICES_ATTRIBUTION en tu archivo de manifiesto de Android y crea una configuración de servicios de anuncios para que tu app use las APIs de Attribution Reporting:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
    
  6. (Opcional) Si planeas recibir informes de depuración, incluye el permiso ACCESS_ADSERVICES_AD_ID en el archivo de manifiesto de Android:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
    
  7. Haz referencia a una configuración de servicios de anuncios en el elemento <application> de tu manifiesto:

    <property android:name="android.adservices.AD_SERVICES_CONFIG"
              android:resource="@xml/ad_services_config" />
    
  8. Especifica el recurso XML de servicios de anuncios al que se hace referencia en el manifiesto, como res/xml/ad_services_config.xml. Obtén más información sobre los permisos de los servicios de anuncios y el control de acceso al SDK.

    <ad-services-config>
        <attribution allowAllToAccess="true" />
    </ad-services-config>
    

Registra eventos de anuncios

Tu app debe registrar fuentes y conversiones a medida que ocurren para asegurarse de que se informen de forma correcta. La clase MeasurementManager cuenta con métodos para ayudarte a registrar eventos de fuente de atribución y activadores de conversión.

Registra un evento de fuente de atribución

Cuando se ve un anuncio o se hace clic en este, una app de publicador llama a registerSource() para registrar una fuente de atribución, como se muestra en el fragmento de código.

La API de Attribution Reporting admite los siguientes tipos de eventos de fuentes de atribución:

  • Clics, que suelen registrarse en un método de devolución de llamada similar a onClick(). Por lo general, el evento activador correspondiente ocurre poco después del evento de clic. Este tipo de evento proporciona más información sobre la interacción del usuario y, por lo tanto, representa un buen tipo de fuente de atribución para darle una prioridad alta.
  • Vistas, que suelen registrarse en un método de devolución de llamada similar a onAdShown(). El evento de activación correspondiente puede ocurrir horas o días después del evento de vista.

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);

Después del registro, la API emite una solicitud POST HTTP al extremo del servicio en la dirección que especifica attributionSourceUri. La respuesta del extremo incluye valores para destination, source_event_id, expiry y source_priority.

Si la tecnología publicitaria de origen desea compartir los registros de la fuente, el URI de la fuente de atribución original puede incluir redireccionamientos a otros extremos de tecnología publicitaria. Los límites y las reglas que rigen para los redireccionamientos se detallan en la propuesta técnica.

Se agregó compatibilidad con los redireccionamientos de encadenamiento para registerSource y registerTrigger. Además del encabezado de registro, el consumidor de la API ahora puede proporcionar un redireccionamiento HTTP como la respuesta del servidor que incluye un código de estado 302 y un encabezado "Location" con la siguiente URL para visitar y obtener registro.

Solo el campo "destino" proporcionado en la primera visita se usa en el encadenamiento. La cantidad de visitas tiene el mismo límite que los encabezados de redireccionamientos de informe de atribución. Esta compatibilidad con el redireccionamiento es adicional a la compatibilidad existente con el atributo "Attribution-Reporting-Redirect". Si ambos están presentes, se dará prioridad a "Attribution-Reporting-Redirect".

Registra un evento de activador de conversión

Para registrar un evento de activador de conversión, llama a registerTrigger() en tu app:

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)

Después del registro, la API emite una solicitud POST HTTP al extremo del servicio en la dirección que especifica attributionTriggerUri. La respuesta del extremo incluye valores para los informes agregados y de eventos.

Si la plataforma de tecnología publicitaria de origen permite que se compartan registros de activadores, el URI puede incluir redireccionamientos a URIs que pertenezcan a otras plataformas de tecnología publicitaria. Los límites y las reglas que rigen para los redireccionamientos se detallan en la propuesta técnica.

Registra la medición web y entre apps

En el caso de que una app y un navegador desempeñen un papel en el recorrido que realiza el usuario desde la fuente hasta el activador, hay diferencias sutiles en la implementación del registro de eventos de anuncios. Si un usuario ve un anuncio en una app y se lo redirecciona a un navegador para generar una conversión, la app registra la fuente y el navegador web genera la conversión. Del mismo modo, si un usuario se inicia en un navegador web y se lo dirige a una app para la conversión, el navegador registra la fuente y la app registra la conversión.

Dado que existen diferencias en la forma en que se organizan las tecnologías publicitarias en la Web y en Android, agregamos nuevas APIs para registrar fuentes y activadores cuando se realizan en navegadores. La diferencia clave entre estas APIs y las APIs basadas en apps correspondientes es que esperamos que el navegador siga los redireccionamientos, aplique filtros específicos del navegador y pase los registros válidos a la plataforma con una llamada a registerWebSource() o registerWebTrigger().

En el siguiente fragmento de código, se muestra un ejemplo de la llamada a la API que realiza el navegador para registrar una fuente de atribución antes de dirigir al usuario a una app:

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);

En el siguiente fragmento de código, se muestra un ejemplo de la llamada a la API que realiza el navegador para registrar una conversión después de que se dirige al usuario desde la app:

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);

Agrega ruido para la privacidad

Los informes a nivel del evento contienen datos del destino, el ID de la fuente de atribución y el activador. Se envían en el formato original (sin encriptar) al origen del informe. Para proteger la privacidad del usuario, se puede agregar ruido para dificultar la identificación de un usuario individual. Los informes a nivel del evento con ruido se generan y se envían de acuerdo con el marco de trabajo de privacidad diferencial. Estos son los valores predeterminados de porcentaje de ruido para diferentes situaciones:

Tipo de fuente

Valor de destino de origen

Probabilidad de informe de ruido por registro de origen

Ver

En la app o en la Web

0.0000025

Vista

Aplicación y Web

0.0000042

Clic

En la app o en la Web

0.0024263

Clic

Aplicación y Web

0.0170218

En la medición de atribución de app a la Web, donde las fuentes pueden generar conversiones en los destinos web y de apps, los informes a nivel del evento pueden especificar si el activador se produjo en la app o en la Web. Para compensar este detalle adicional, los informes con ruido generados son de hasta alrededor de 7 veces por los clics y alrededor de 1.7 veces por las vistas.

Algunas tecnologías publicitarias no requieren informes a nivel del evento para especificar si el activador ocurrió en el destino web o de apps. Las tecnologías publicitarias pueden usar el campo coarse_event_report_destinations debajo del encabezado Attribution-Reporting-Register-Source para disminuir el ruido. Si una fuente con el campo coarse_event_report_destinations especificado gana la atribución, el informe resultante incluye destinos web y de apps sin distinguir dónde ocurrió el activador real.

En los siguientes ejemplos, un usuario hace clic en un anuncio, y esa fuente se registra con la API. Luego, el usuario genera una conversión en la app y en el sitio web del anunciante. Ambas conversiones se registran como activadores y se atribuyen al clic inicial.

Un encabezado HTTP de registro de fuente basado en clics:

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

Se registra un activador desde la app con el nombre de paquete com.advertiser.example:

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

Se registra un activador desde un navegador del sitio web con el dominio eTLD+1 https://advertiser.com:

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

Se generan los informes a nivel del evento resultantes. Si suponemos que ambos activadores se atribuyen a la fuente, se generan los siguientes informes a nivel del evento:

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

Genera y publica informes

La API de Attribution Reporting envía informes a los extremos de tu servidor que aceptan informes a nivel del evento y agregables.

Fuerza la ejecución de los trabajos de informes

Después de registrar un evento de fuente de atribución o de un activador, el sistema programa la ejecución de la tarea de informes. De forma predeterminada, esta tarea se ejecuta cada 4 horas. Si deseas realizar pruebas, puedes forzar la ejecución de las tareas de informes o reducir los intervalos entre estas.

Fuerza la ejecución de la tarea de atribución:

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

Fuerza la ejecución de la tarea de informe a nivel del evento:

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

Fuerza la ejecución de la tarea de informe agregable:

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

Verifica el resultado en logcat para comprobar cuándo se ejecutaron las tareas. Debería verse similar a lo siguiente:

JobScheduler: executeRunCommand(): com.google.android.adservices.api/0 5 s=false f=true

Fuerza la entrega de informes

Incluso si se fuerza la ejecución de la tarea de informes, el sistema envía informes según sus tiempos de entrega programados, que varían de un par de horas a varios días. Si deseas realizar pruebas, puedes aplazar el tiempo del sistema del dispositivo para después de las demoras programadas a fin de iniciar la entrega del informe.

Verifica los informes en tu servidor

Una vez que se envían los informes, comprueba la entrega mediante la revisión de los informes recibidos, los registros del servidor aplicables, como el historial de servidor simulado o tu sistema personalizado.

Cómo decodificar tu informe agregado

Cuando recibes un informe agregado, el campo debug_cleartext_payload contiene una versión no encriptada de tu informe agregado. Si bien esta versión del informe no está encriptada, aún debe decodificarse.

A continuación, se muestra un ejemplo de la decodificación del contenido del campo debug_cleartext_payload en dos pasos: el primero usa la decodificación en Base 64 y el segundo usa la decodificación CBOR.

String base64DebugPayload  = "omRkYXRhgqJldmFsdWVEAAAGgGZidWNrZXRQAAAAAAAAAAAAAAAAAAAKhaJldmFsdWVEAACAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAFWWlvcGVyYXRpb25paGlzdG9ncmFt";
byte[] cborEncoded = Base64.getDecoder().decode(base64DebugPayload);

// CbodDecoder comes from this library https://github.com/c-rack/cbor-java
final List<DataItem> dataItems = new CborDecoder(new ByteArrayInputStream(cborEncoded)).decode();

// In here you can see the contents, but the value will be something like:
// Data items: [{ data: [{ value: co.nstant.in.cbor.model.ByteString@a8b5c07a,
//   bucket: co.nstant.in.cbor.model.ByteString@f812097d },
//   { value: co.nstant.in.cbor.model.ByteString@a8b5dfc0,
//   bucket: co.nstant.in.cbor.model.ByteString@f8120934 }], operation: histogram }]
Log.d("Data items : " + dataItems);

// In order to see the value for bucket and value, you can traverse the data
// and get their values, something like this:
final Map payload = (Map) dataItems.get(0);
final Array payloadArray = (Array) payload.get(new UnicodeString("data"));

payloadArray.getDataItems().forEach(i -> {
    BigInteger value = new BigInteger(((ByteString) ((Map)i).get(new UnicodeString("value"))).getBytes());
    BigInteger bucket = new BigInteger(((ByteString) ((Map)i).get(new UnicodeString("bucket"))).getBytes());
    Log.d("value : " + value + " ;bucket : " + bucket);
});

Pruebas

Para ayudarte a comenzar a usar la API de Attribution Reporting, puedes usar el proyecto MeasurementSampleApp en GitHub. Esta app de ejemplo muestra el registro de la fuente de atribución y del activador.

Para los extremos del servidor, considera los siguientes recursos de referencia o tu solución personalizada:

  • MeasurementAdTechServerSpec incluye definiciones del servicio de OpenAPI, que se pueden implementar en una plataforma simulada o de microservicios compatible.
  • MeasurementAdTechServer incluye una implementación de referencia de un servidor simulado basado en la app de Spring Boot para Google App Engine.

Requisitos previos

Implementa APIs de prueba en extremos remotos a los que se puede acceder desde tu dispositivo de prueba o emulador. Para facilitar las pruebas, consulta los proyectos de muestra MeasurementAdTechServerSpec y MeasurementAdTechServer.

Funcionalidad para realizar pruebas

  • Registra la fuente de atribución y el activador de conversión. Verifica que los extremos del servidor respondan con el formato correcto.
  • Ejecuta las tareas de informes
  • Verifica la entrega de informes en el backend o la consola del servidor de prueba.

Próximas funciones

Configuración flexible a nivel del evento

Se recomienda la configuración predeterminada de los informes a nivel del evento para iniciar pruebas de utilidad, pero es posible que no sea ideal en todos los casos de uso. La API de Attribution Reporting admitirá configuraciones opcionales y más flexibles para que las tecnologías publicitarias tengan un mayor control sobre la estructura de sus informes a nivel del evento y puedan maximizar la utilidad de los datos. Esta flexibilidad adicional se introducirá en la API de Attribution Reporting en dos fases:

  • Fase 1: Configuración flexible y liviana al nivel de evento; un subconjunto de la Fase 2
  • Fase 2: Versión completa de la configuración flexible a nivel del evento

Fase 1: Flexible y liviana a nivel del evento

Agregaremos los dos parámetros opcionales siguientes al JSON en Attribution-Reporting-Register-Source:

  • 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>, ...]
  }
}

Ejemplo de configuración personalizada

Esta configuración de ejemplo está orientada a los desarrolladores que desean realizar optimizaciones para recibir informes en ventanas de informes anteriores.

{
  ...
  "max_event_level_reports": 2,
  "event_report_windows": {
    "end_times": [7200, 43200, 86400] // 2 hours, 12 hours, 1 day in seconds
  }
}

Fase 2: Completamente flexible a nivel del evento

Además de los parámetros que se agregaron en la fase 1, agregaremos un parámetro opcional adicional trigger_specs al JSON en Attribution-Reporting-Register-Source.

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

Esta configuración especifica por completo el espacio de salida de los informes a nivel del evento por registro de fuente. Para cada especificación de activador, especificamos por completo lo siguiente:

  • Un conjunto de criterios de coincidencia:
    • A qué datos de activador específicos se aplica esta especificación. Esta fuente es apta para coincidir solo con activadores que tengan uno de los valores trigger_data especificados en trigger_specs. En otras palabras, si el activador hubiera coincidido con esta fuente, pero su trigger_data no es uno de los valores de la configuración de la fuente, se ignora el activador.
    • Cuándo un activador específico coincida con esta especificación (a través de event_report_windows). Ten en cuenta que el activador aún podría coincidir con una fuente para informes agregables a pesar de que fallan los dos criterios de coincidencia mencionados anteriormente.
  • Un algoritmo específico para resumir y agrupar en buckets todos los activadores dentro de una ventana de atribución. Esto permite que los activadores especifiquen un parámetro value que se resume para una especificación en particular, pero que se informa como un valor agrupado.

Los activadores también admitirán la adición de un parámetro de valor opcional en los diccionarios dentro de event_trigger_data.

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

Cada registro de activador coincidirá como máximo con una especificación de activador y actualizará su valor de resumen asociado. A grandes rasgos, en el momento de la activación tomaremos las siguientes medidas:

  • Aplicaremos filtros de atribución global.
  • Para cada especificación de activador, evalúa el event_trigger_data en la especificación con el objetivo de encontrar una coincidencia con el objeto event_reporting_window de la especificación. El event_reporting_windows de nivel superior actúa como un valor predeterminado en caso de que alguna especificación del activador sea el subcampo event_report_windows faltante.
  • Se elige la primera especificación coincidente para la atribución, y el valor del resumen se incrementa en value.

Cuando se complete el event_report_window de una especificación, asignaremos su valor de resumen a un bucket y enviaremos un informe a nivel de evento por cada incremento en el bucket de resumen causado por valores de activador atribuidos. Los informes tendrán un campo adicional, trigger_summary_bucket.

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

Configuraciones equivalentes a la versión actual

Las siguientes son configuraciones equivalentes para los eventos actuales de las APIs y las fuentes de navegación, respectivamente. En el caso de las fuentes de navegación, esto ilustra por qué los niveles de ruido son tan altos en relación con las fuentes de eventos para mantener los mismos valores de épsilon: las fuentes de navegación tienen un espacio de salida mucho mayor.

Es posible que se incluyan varios parámetros de configuración que sean equivalentes, ya que algunos parámetros se pueden establecer como predeterminados o reducir.

Fuentes de eventos equivalentes
// 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>,
}
Fuentes de navegación equivalentes
// 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>,
}

Ejemplos de configuraciones personalizadas

A continuación, se muestran algunas configuraciones adicionales fuera de los valores predeterminados. En todos estos ejemplos, las compensaciones para los desarrolladores incluyen las siguientes acciones:

  • Reducir alguna dimensión de la configuración predeterminada (#triggers, cardinalidad de datos de activador, #windows) para aumentar otra con el objetivo de preservar el nivel de ruido
  • Reducir alguna dimensión de la configuración predeterminada (#triggers, cardinalidad de datos de activador, #windows) para reducir el nivel de ruido

Informa los buckets de valores del activador

Esta configuración de ejemplo es compatible con un desarrollador que desea realizar optimizaciones en función de los datos de valor para una sola ventana de informe (p. ej., 7 días) y cambia menos ventanas de informe por menos ruido. En este ejemplo, cualquier activador que establezca trigger_data en un valor distinto de 0 no es apto para la atribución.

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

Los activadores se podrían registrar con el conjunto de campos value, que se suman y se agrupan. Por ejemplo, si hay tres activadores en un plazo de 7 días desde los registros de origen con valores 1, 3 y 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}] }

Los valores se suman en 8 y se informan en los siguientes informes después de 7 días + 1 hora:

// Report 1
{
  ...
  "trigger_summary_bucket": [5, 9]
}

En los 7 días posteriores, se registran los siguientes activadores:

{ "event_trigger_data": [{"trigger_data": "0", "value": 50}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 45}] }

Los valores se suman de esta manera: 8 + 50 + 45 = 103. Esto genera los siguientes informes a los 14 días + 1 hora:

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

// Report 3
{
  ...
  "trigger_summary_bucket": [100, MAX_INT]
}
Informa los recuentos de activadores

En este ejemplo, se muestra cómo un desarrollador puede configurar una fuente para obtener un recuento de hasta 10 activadores.

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

Los activadores atribuidos con trigger_data establecido en 0 se cuentan y se limitan a 10. El valor del activador se ignora porque summary_window_operator se establece en el recuento. Si se registran 4 activadores y se atribuyen a la fuente, el informe se verá de la siguiente manera:

// 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]
}
Objeto binario con informes más frecuentes

Esta configuración de ejemplo admite a un desarrollador que desea saber si se produjo al menos una conversión en los primeros 10 días (sin importar el valor), pero desea recibir informes en intervalos más frecuentes que los predeterminados. Nuevamente, en este ejemplo, cualquier activador que establezca trigger_data en un valor distinto de 0 no es apto para la atribución. Por eso, este caso de uso se denomina binario.

{
  "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]
  }],
}
Varía las especificaciones de los activadores de una fuente a otra
{
  "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
}

Recomendamos a los desarrolladores que sugieran diferentes casos de uso que puedan tener para esta extensión de API. Asimismo, actualizaremos esta explicación con configuraciones de muestra para esos casos de uso.

Atribución entre varias redes sin redireccionamientos

Las tecnologías publicitarias deben usar redireccionamientos para registrar varios activadores de fuentes de atribución y realizar la atribución entre varias redes. Esta función ayuda a admitir la atribución entre varias redes en la que los redireccionamientos no son posibles entre estas. Más información.

La tecnología publicitaria puede enviar configuración en la respuesta de registro del activador según las fuentes registradas por otras tecnologías publicitarias que se seleccionan para generar fuentes derivadas; luego, estas fuentes derivadas se utilizan para la atribución. Los informes agregados se generan si el activador se atribuye a una fuente derivada. No se admite la generación de informes de eventos para fuentes derivadas.

Las tecnologías publicitarias pueden elegir entre aggregation_keys en sus fuentes registradas que quieren compartir con las tecnologías publicitarias de socios. Estas claves se pueden declarar en el campo opcional shared_aggregation_keys, que se ubica debajo del encabezado de registro de origen Attribution-Reporting-Register-Source:

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

Las fuentes derivadas se generan según la configuración del encabezado de registro del activador Attribution-Reporting-Register-Trigger:

  // Specifies the configuration based on which derived sources should be
  // generated. Those derived sources will be included for source matching at the
  // time of attribution. For example, if adtech2 is registering a trigger with an
  // attribution_config with source_network as adtech1, available sources
  // registered by adtech1 will be considered with additional filtering criteria
  // applied to that set as mentioned in the attribution_config. Derived
  // sources can have different values to priority, post_install_exclusivity_window
  // etc.

  "attribution_config": [
    {
      // Derived sources are created from this adtech's registered sources
      "source_network": "[original source's adtech enrollment ID]",
      //(optional) Filter sources whose priority falls in this range
      "source_priority_range": {
        "start": [priority filter lower bound],
        "end": [priority filter upper bound]
      },
      // (optional) Filter sources whose at least one of filter maps matches these
      // filters
      "source_filters": {
        "key name 1": ["key1 value 1"]
      },
      // (optional) Filter sources whose none of filter map matches these
      // filters
        "source_not_filters": {
          "key name 1": ["key1 value 1"]
        },
      // (optional) Apply this priority to the generated derived sources
      "priority": "[64 bit signed integer]",
      // (optional) The derived source will have expiry set as this or parent
      // source's, whichever is earlier
      "expiry": "[64 bit signed integer]",
      // (optional) set on the derived source
      "filter_data": {
        "key name 1": ["key1 value 1"]
      },
      // (optional) set on the derived source
      "post_install_exclusivity_window": "[64-bit unsigned integer]"
    }
  ]

Esta es una versión con valores de ejemplo agregados:

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

Se agregaron dos campos opcionales nuevos para activar el encabezado de registro. Estos campos habilitan el identificador de la tecnología publicitaria ganadora en las claves de informes agregables:

  • x_network_bit_mapping: ID de inscripción para la asignación de bits del identificador de la tecnología publicitaria
  • x_network_data: Desplazamiento (a la izquierda) para x_network_bit_mapping de la tecnología publicitaria ganadora O la operación con la pieza de clave del activador
Ejemplo:
"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
  }
  …
}

Este es el cálculo resultante de la pieza de clave del activador cuando se genera un informe para la fuente de AdTechB:

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • Valor enrollment_id de AdtechB: 2 (010) (desde x_network_bit_mapping)
  • Pieza de clave del activador resultante: 0x400 | 0x2 << 12 = 0x2400

Limitaciones

Si quieres obtener una lista de las funciones en curso para el entorno de ejecución del SDK, consulta las notas de la versión.

Informa errores y problemas

Tus comentarios son una parte fundamental de Privacy Sandbox en Android. Avísanos si tienes problemas o ideas para mejorar Privacy Sandbox en Android.