Руководство разработчика API отчетов об атрибуции

для разработчиков

При изучении документации по Privacy Sandbox на Android используйте кнопку «Предварительная версия для разработчиков» или «Бета-версия» , чтобы выбрать версию программы, с которой вы работаете, поскольку инструкции могут отличаться.


API для отслеживания атрибуции разработан для повышения конфиденциальности пользователей за счет исключения зависимости от идентификаторов пользователей из разных сторонних источников, а также для поддержки ключевых сценариев использования для отслеживания атрибуции и измерения конверсий в разных приложениях. В этом руководстве для разработчиков описывается, как настроить и протестировать API для отслеживания атрибуции, чтобы регистрировать клики по рекламе, просмотры и конверсии путем вызова методов, которые регистрируют соответствующие триггеры и источники для таких событий.

В этом руководстве вы узнаете, как настроить серверные конечные точки и создать клиентское приложение, которое будет вызывать эти сервисы. Подробнее об общей архитектуре API для отчетов об атрибуции можно узнать в предложении по дизайну .

Ключевые термины

  • В качестве источников информации указываются клики или просмотры.
  • Триггеры — это события, которые можно отнести к конверсиям.
  • Отчеты содержат данные о триггере и соответствующем источнике атрибуции. Эти отчеты отправляются в ответ на события-триггеры. API для создания отчетов по атрибуции поддерживает отчеты на уровне событий и агрегированные отчеты .

Прежде чем начать

Для использования API отчетов по атрибуции выполните задачи на стороне сервера и клиента, перечисленные в следующих разделах.

Настройка конечных точек API для отчетов по атрибуции

Для работы API отчетов по атрибуции требуется набор конечных точек, к которым можно получить доступ с тестового устройства или эмулятора. Создайте по одной конечной точке для каждой из следующих задач на стороне сервера:

Существует несколько способов настройки необходимых конечных точек:

  • Самый быстрый способ начать работу — развернуть определения сервисов OpenAPI v3 из нашего репозитория примеров кода на тестовой или микросервисной платформе. Вы можете использовать Postman , Prism или любую другую платформу для создания тестовых серверов, которая поддерживает этот формат. Разверните каждую конечную точку и отслеживайте URI для использования в вашем приложении. Для проверки доставки отчетов обратитесь к вызовам, ранее выполненным к тестовой или бессерверной платформе.
  • Запустите собственный автономный сервер, используя пример на Kotlin на основе Spring Boot . Разверните этот сервер у своего облачного провайдера или во внутренней инфраструктуре.
  • Используйте описания сервисов в качестве примеров для интеграции конечных точек в вашу существующую систему.

Принять регистрацию источника

Данная конечная точка должна быть доступна по URI, аналогичному следующему:

https://adtech.example/attribution_source

Когда клиентское приложение регистрирует источник атрибуции , оно предоставляет URI для этой конечной точки сервера. Затем API отчетов об атрибуции отправляет запрос, включающий один из следующих заголовков:

  • Для событий клика:

    Attribution-Reporting-Source-Info: navigation
    
  • Для просмотра событий:

    Attribution-Reporting-Source-Info: event
    

Настройте конечную точку сервера таким образом, чтобы она отвечала следующим образом:

// Metadata associated with attribution source.
Attribution-Reporting-Register-Source: {
  "destination": "[app package name]",
  "web_destination": "[eTLD+1]",
  "source_event_id": "[64 bit unsigned integer]",
  "expiry": "[64 bit signed integer]",
  "event_report_window": "[64-bit signed integer]",
  "aggregatable_report_window": "[64-bit signed integer]",
  "priority": "[64 bit signed integer]",
  "filter_data": {
    "[key name 1]": ["key1 value 1", "key1 value 2"],
    "[key name 2]": ["key2 value 1", "key2 value 2"],
    // Note: "source_type" key will be automatically generated as
    // one of {"navigation", "event"}.
  },
  // Attribution source metadata specifying histogram contributions in aggregate
  // report.
  "aggregation_keys": {
    "[key1 name]": "[key1 value]",
    "[key2 name]": "[key2 value]",
  },

    "debug_key": "[64-bit unsigned integer]",
    "debug_reporting": [boolean]
}
// Specify additional ad tech URLs to register this source with.
Attribution-Reporting-Redirect: <Ad Tech Partner URI 1>
Attribution-Reporting-Redirect: <Ad Tech Partner URI 2>

Вот пример с добавленными примерными значениями:

Attribution-Reporting-Register-Source: {
  "destination": "android-app://com.example.advertiser",
  "source_event_id": "234",
  "expiry": "259200",
  "event_report_window": "172800",
  "aggregatable_report_window": "172800",
  "priority": "5",
  "filter_data": {
    "product_id": ["1234"]
  },
  "aggregation_keys": {
  // Generates a "0x159" key piece named (low order bits of the key) for the key
  // named "campaignCounts".
  // User saw an ad from campaign 345 (out of 511).
    "campaignCounts": "0x159",

  // Generates a "0x5" key piece (low order bits of the key) for the key named
  // "geoValue".
  // Source-side geo region = 5 (US), out of a possible ~100 regions.
    "geoValue": "0x5",
  },
  // Opts in to receiving verbose debug reports
  "debug_reporting": true
}

Attribution-Reporting-Redirect:
https://adtechpartner1.example?their_ad_click_id=567
Attribution-Reporting-Redirect:
https://adtechpartner2.example?their_ad_click_id=890

Если Attribution-Reporting-Redirects содержит URI партнеров по рекламным технологиям, API Attribution Reporting отправляет аналогичный запрос к каждому URI. Каждый партнер по рекламным технологиям должен настроить сервер, который будет отвечать следующими заголовками:

Attribution-Reporting-Register-Source: {
  "destination": "[app package name]",
  "web_destination": "[eTLD+1]",
  "source_event_id": "[64 bit unsigned integer]",
  "expiry": "[64 bit signed integer]",
  "event_report_window": "[64-bit signed integer]",
  "aggregatable_report_window": "[64-bit signed integer]",
  "priority": "[64 bit signed integer]",
  "filter_data": {
    "[key name 1]": ["key1 value 1", "key1 value 2"],
    "[key name 2]": ["key2 value 1", "key2 value 2"],
    // Note: "source_type" key will be automatically generated as
    // one of {"navigation", "event"}.
  },
  "aggregation_keys": {
    "[key1 name]": "[key1 value]",
    "[key2 name]": "[key2 value]",
  }
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.

Принять регистрацию триггера конверсии

Данная конечная точка должна быть доступна по URI, аналогичному следующему:

https://adtech.example/attribution_trigger

Когда клиентское приложение регистрирует событие-триггер , оно предоставляет URI для этой конечной точки сервера. Затем API отчетов об атрибуции отправляет запрос, включающий один из следующих заголовков:

Настройте конечную точку сервера таким образом, чтобы она отвечала следующим образом:

// Metadata associated with trigger.
Attribution-Reporting-Register-Trigger: {
  "event_trigger_data": [{
    // "trigger_data returned" in event reports is truncated to
    // the last 1 or 3 bits, based on conversion type.
    "trigger_data": "[unsigned 64-bit integer]",
    "priority": "[signed 64-bit integer]",
    "deduplication_key": "[signed 64-bit integer]",
    // "filter" and "not_filters" are optional fields which allow configuring
    // event trigger data based on source's filter_data. They consist of a
    // filter set, which is a list of filter maps. An event_trigger_data object
    // is ignored if none of the filter maps in the set match the source's
    // filter data.
    // Note: "source_type" can be used as a key in a filter map to filter based
    // on the source's "navigation" or "event" type. The first
    // Event-Trigger that matches (based on the filters/not_filters) will be
    // used for report generation. If none of the event-triggers match, no
    // event report will be generated.
    "filters": [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from filters or source's filter_data, it won't be
      // used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }],
    "not_filters":  [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from not_filters or source's filter_data, it won't
      // be used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }]
  }],
  // Specify a list of dictionaries that generates aggregation keys.
  "aggregatable_trigger_data": [
    // Each dictionary entry independently adds pieces to multiple source keys.
    {
      "key_piece": "[key piece value]",
      "source_keys": ["[key name the key piece value applies to]",
      ["list of IDs in source to match. Non-matching IDs are ignored"]]
      // filters/not_filters are optional fields similar to event trigger data
      // filter fields.
      "filters": [{
        "[key name 1]": ["key1 value 1", "key1 value 2"]
      }],
      "not_filters":  [{
          "[key name 1]": ["key1 value 1", "key1 value 2"],
          "[key name 2]": ["key2 value 1", "key2 value 2"],
      }]
    },
    ..
  ],
  // Specify an amount of an abstract value which can be integers in [1, 2^16]
  // to contribute to each key that is attached to aggregation keys in the
  // order they are generated.
  "aggregatable_values": [
     // Each source event can contribute a maximum of L1 = 2^16 to the
     // aggregate histogram.
    {
     "[key_name]": [value]
    },
    ..
  ],
  aggregatable_deduplication_keys: [{
  deduplication_key": [unsigned 64-bit integer],
    "filters": {
        "category": [filter_1, …, filter_H]
      },
    "not_filters": {
        "category": [filter_1, …, filter_J]
      }
  },
  ...
  {
  "deduplication_key": [unsigned 64-bit integer],
    "filters": {
        "category": [filter_1, …, filter_D]
      },
    "not_filters": {
        "category": [filter_1, …, filter_J]
      }
    }
  ]

  "debug_key": "[64-bit unsigned integer]",
  "debug_reporting": [boolean]

}
// Specify additional ad tech URLs to register this trigger with.
// Repeated Header field "Attribution-Reporting-Redirect"
Attribution-Reporting-Redirect: <Ad Tech Partner URI 1>
Attribution-Reporting-Redirect: <Ad Tech Partner URI 2>

Вот пример с добавленными примерными значениями:

Attribution-Reporting-Register-Trigger: {
  "event_trigger_data": [{
    "trigger_data": "1122", // Returns 010 for CTCs and 0 for VTCs in reports.
    "priority": "3",
    "deduplication_key": "3344"
    "filters": [{ // Filter strings can't exceed 25 characters
      "product_id": ["1234"],
      "source_type": ["event"]
    }]
  },
  {
    "trigger_data": "4", // Returns 100 for CTCs and 0 for VTCs in reports.
    "priority": "3",
    "deduplication_key": "3344"
    "filters": [{ // Filter strings can't exceed 25 characters
      "product_id": ["1234"],
      "source_type": ["navigation"]
    }]
  }],
  "aggregatable_trigger_data": [
    // Each dictionary independently adds pieces to multiple source keys.
    {
      // Conversion type purchase = 2 at a 9-bit offset, i.e. 2 << 9.
      // A 9-bit offset is needed because there are 511 possible campaigns,
      // which takes up 9 bits in the resulting key.
      "key_piece": "0x400",// Conversion type purchase = 2
      // Apply this key piece to:
      "source_keys": ["campaignCounts"]
       // Filter strings can't exceed 25 characters
    },
    {
      // Purchase category shirts = 21 at a 7-bit offset, i.e. 21 << 7.
      // A 7-bit offset is needed because there are ~100 regions for the geo
      // key, which takes up 7 bits of space in the resulting key.
      "key_piece": "0xA80",
      // Apply this key piece to:
      "source_keys": ["geoValue", "nonMatchingIdsAreIgnored"]
      // source_key values must not exceed the limit of 25 characters
    }
  ],
  "aggregatable_values":
    {
      // Privacy budget for each key is L1 / 2 = 2^15 (32768).
      // Conversion count was 1.
      // Scale the count to use the full budget allocated: 1 * 32768 = 32768.
      "campaignCounts": 32768,

      // Purchase price was $52.
      // Purchase values for the app range from $1 to $1,024 (integers only).
      // Scaling factor applied is 32768 / 1024 = 32.
      // For $52 purchase, scale the value by 32 ($52 * 32 = $1,664).
      "geoValue": 1664
    }
  ,
  // aggregatable_deduplication_keys is an optional field. Up to 50 "keys"
  // can be included in the aggregatable_deduplication_keys list. Filters, not
  // filters, and deduplication_key are optional fields. If deduplication_key
  // is omitted, it will be treated as a null value. See
  // https://wicg.github.io/attribution-reporting-api/#triggering-aggregatable-attribution
  aggregatable_deduplication_keys:
  [
    {
    deduplication_key": 3,
        "filters": {
          "category": [A]
        }
    },
    {
    "deduplication_key": 4,
        "filters": {
          "category": [C, D]
        },
        "not_filters": {
          "category": [F]
        }
    }
  ]
  // Opts into receiving verbose debug reports
  "debug_reporting": true
}
Attribution-Reporting-Redirect:https://adtechpartner.example?app_install=567

Существует ограничение в 25 байт на идентификатор ключа агрегации и строку фильтра. Это означает, что ваши идентификаторы ключей агрегации и строки фильтров не должны превышать 25 символов. В этом примере campaignCounts имеет длину 14 символов, поэтому это допустимый идентификатор ключа агрегации, а 1234 имеет длину 4 символа, поэтому это допустимая строка фильтра. Если идентификатор ключа агрегации или строка фильтра превышают 25 символов, триггер игнорируется.

Если Attribution-Reporting-Redirect содержит URI партнеров по рекламным технологиям, API Attribution Reporting отправляет аналогичный запрос к каждому URI. Каждый партнер по рекламным технологиям должен настроить сервер, который будет отвечать следующими заголовками:

// Metadata associated with trigger.
Attribution-Reporting-Register-Trigger: {
  "event_trigger_data": [{
    // "trigger_data" returned in event reports is truncated to
    // the last 1 or 3 bits, based on conversion type.
    "trigger_data": "[unsigned 64-bit integer]",
    "priority": "[signed 64-bit integer]",
    "deduplication_key": "[signed 64-bit integer]",
    // filter and not_filters are optional fields which allow configuring
    // different event trigger data based on source's filter_data. They
    // consist of a filter set, which is a list of filter maps. An
    // event_trigger_data object is ignored if none of the filter maps in the
    // set match the source's filter data. Note: "source_type" can be used as
    // a key in a filter map to filter based on the source's "navigation" or
    // "event" type. The first Event-Trigger that matches (based on the
    // filters/not_filters) will be used for report generation. If none of the
    // event-triggers match, no report will be generated.
    "filters": [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from filters or source's filter_data, it won't be
      // used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }],
    "not_filters":  [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from not_filters or source's filter_data, it won't
      // be used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }]
  }],
  "aggregatable_trigger_data": [
    // Each dictionary entry independently adds pieces to multiple source keys.
    {
      "key_piece": "[key piece value]",
      "source_keys": ["[key name the key piece value applies to]",
      ["list of IDs in source to match. Non-matching IDs are ignored"]],
      // filters/not_filters are optional fields similar to event trigger data
      // filter fields.
      "filters": [{
        "[key name 1]": ["key1 value 1", "key1 value 2"]
      }],
      "not_filters":  [{
          "[key name 1]": ["key1 value 1", "key1 value 2"],
          "[key name 2]": ["key2 value 1", "key2 value 2"],
      }]
    },
    ..
  ],
  // Specify an amount of an abstract value which can be integers in [1, 2^16] to
  // contribute to each key that is attached to aggregation keys in the order they
  // are generated.
  "aggregatable_values": [
    // Each source event can contribute a maximum of L1 = 2^16 to the aggregate
    // histogram.
    {
     "[key_name]": [value]
    }
  ]
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.

Принимать отчеты на уровне событий

Этот конечный пункт должен быть доступен по URI. Дополнительную информацию о регистрации URI см. в разделе «Регистрация учетной записи в песочнице конфиденциальности» . (URI определяется на основе серверов, используемых для приема регистрации источника и регистрации триггера.) Используя примеры URI для конечных пунктов, принимающих регистрацию источника и регистрацию триггера , URI этого конечного пункта будет следующим:

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

Настройте этот сервер для приема JSON-запросов, использующих следующий формат:

{
  "attribution_destination": "android-app://com.advertiser.example",
  "source_event_id": "12345678",
  "trigger_data": "2",
  "report_id": "12324323",
  "source_type": "navigation",
  "randomized_trigger_rate": "0.02"
   [Optional] "source_debug_key": "[64-bit unsigned integer]",
   [Optional] "trigger_debug_key": "[64-bit unsigned integer]",
}

Отладочные ключи позволяют получить дополнительную информацию для отчетов по атрибуции; узнайте больше о их настройке.

Принимать сводные отчеты

Этот конечный пункт должен быть доступен по URI. Дополнительную информацию о регистрации URI см. в разделе «Регистрация учетной записи в песочнице конфиденциальности» . (URI определяется на основе серверов, используемых для приема регистрации источника и регистрации триггера.) Используя примеры URI для конечных пунктов, принимающих регистрацию источника и регистрацию триггера , URI этого конечного пункта будет следующим:

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

Для формирования агрегируемых отчетов заполняются как зашифрованные, так и незашифрованные поля. Зашифрованные отчеты позволяют начать тестирование сервиса агрегации, а незашифрованное поле дает представление о том, как заданные пары ключ-значение структурируют данные.

Настройте этот сервер для приема JSON-запросов, использующих следующий формат:

{
  // Info that the aggregation services also need encoded in JSON
  // for use with AEAD. Line breaks added for readability.
  "shared_info": "{
     \"api\":\"attribution-reporting\",
     \"attribution_destination\": \"android-app://com.advertiser.example.advertiser\",
     \"scheduled_report_time\":\"[timestamp in seconds]\",
     \"source_registration_time\": \"[timestamp in seconds]\",
     \"version\":\"[api version]\",
     \"report_id\":\"[UUID]\",
     \"reporting_origin\":\"https://reporter.example\" }",

  // In the current Developer Preview release, The "payload" and "key_id" fields
  // are not used because the platform doesn't yet encrypt aggregate reports.
  // The "debug_cleartext_payload" field holds unencrypted reports.
  "aggregation_service_payloads": [
    {
      "payload": "[base64 HPKE encrypted data readable only by the aggregation service]",
      "key_id": "[string identifying public key used to encrypt payload]",

      "debug_cleartext_payload": "[unencrypted payload]"
    },
  ],

  "source_debug_key": "[64 bit unsigned integer]",
  "trigger_debug_key": "[64 bit unsigned integer]"
}

Отладочные ключи позволяют получить дополнительную информацию для отчетов по атрибуции; узнайте больше о их настройке.

Настройка клиента Android

Клиентское приложение регистрирует источники и триггеры атрибуции, а также позволяет создавать отчеты на уровне событий и агрегированные отчеты. Чтобы подготовить Android-устройство или эмулятор к использованию API отчетов по атрибуции, выполните следующие действия:

  1. Настройте среду разработки для «Песочницы конфиденциальности» на Android.
  2. Установите образ системы на поддерживаемое устройство или настройте эмулятор , который поддерживает «песочницу конфиденциальности» (Privacy Sandbox) на Android.
  3. Включите доступ к API для создания отчетов об атрибуции, выполнив следующую команду ADB. (По умолчанию API отключен.)

    adb shell device_config put adservices ppapi_app_allow_list \"\*\"
  4. Если вы тестируете API для формирования отчетов об атрибуции локально (например, на устройстве, к которому у вас есть физический доступ), выполните эту команду, чтобы отключить регистрацию:

    adb shell device_config put adservices disable_measurement_enrollment_check "true"
  5. Добавьте разрешение ACCESS_ADSERVICES_ATTRIBUTION в файл Android Manifest и создайте конфигурацию рекламных сервисов для вашего приложения, чтобы оно могло использовать API отчетов по атрибуции:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
    
  6. (Необязательно) Если вы планируете получать отладочные отчеты, добавьте разрешение ACCESS_ADSERVICES_AD_ID в файл Android Manifest:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
    
  7. Укажите конфигурацию рекламных сервисов в элементе <application> вашего манифеста:

    <property android:name="android.adservices.AD_SERVICES_CONFIG"
              android:resource="@xml/ad_services_config" />
    
  8. Укажите XML-ресурс рекламных сервисов, на который ссылается манифест, например, res/xml/ad_services_config.xml . Узнайте больше о разрешениях рекламных сервисов и контроле доступа SDK .

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

Зарегистрируйте рекламные мероприятия

Ваше приложение должно регистрировать источники и конверсии по мере их возникновения, чтобы убедиться в корректности их отображения. Класс MeasurementManager содержит методы, которые помогут вам зарегистрировать события, связанные с источниками атрибуции , и триггеры конверсий .

Зарегистрируйте событие источника атрибуции

Когда реклама просматривается или нажимается, приложение издателя вызывает registerSource() для регистрации источника атрибуции, как показано в приведенном фрагменте кода.

API для создания отчетов об атрибуции поддерживает следующие типы событий, связанных с источником атрибуции:

  • Клики, которые обычно регистрируются в методе обратного вызова, аналогичном onClick() . Соответствующее событие-триггер обычно происходит вскоре после клика. Этот тип событий предоставляет больше информации о взаимодействии пользователя и поэтому является хорошим источником атрибуции, которому следует присвоить высокий приоритет.
  • Представления, которые обычно регистрируются в методе обратного вызова, аналогичном onAdShown() . Соответствующее событие-триггер может произойти через несколько часов или дней после события отображения.

Котлин

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager = context.getSystemService(MeasurementManager::class.java)
var exampleClickEvent: InputEvent? = null

// Use the URI of the server-side endpoint that accepts attribution source
// registration.
val attributionSourceUri: Uri =
  Uri.parse("https://adtech.example/attribution_source?AD_TECH_PROVIDED_METADATA")

val future = CompletableFuture<Void>()

adView.setOnTouchListener(_: View?, event: MotionEvent?)) ->
    exampleClickEvent = event
    true
}

// Register Click Event
measurementManager.registerSource(
        attributionSourceUri,
        exampleClickEvent,
        CALLBACK_EXECUTOR,
        future::complete)

// Register View Event
measurementManager.registerSource(
        attributionSourceUri,
        null,
        CALLBACK_EXECUTOR,
        future::complete)

Java

private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();
private InputEvent exampleClickEvent;

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URI of the server-side endpoint that accepts attribution source
// registration.
Uri attributionSourceUri =
Uri.parse("https://adtech.example/attribution_source?AD_TECH_PROVIDED_METADATA");

CompletableFuture<Void> future = new CompletableFuture<>();

adView.setOnTouchListener(v, event)) -> {
    exampleClickEvent = event;
    return true;
}

// Register Click Event
measurementManager.registerSource(attributionSourceUri, exampleClickEvent,
        CALLBACK_EXECUTOR, future::complete);

// Register View Event
measurementManager.registerSource(attributionSourceUri, null,
        CALLBACK_EXECUTOR, future::complete);

После регистрации API отправляет HTTP POST-запрос к конечной точке сервиса по адресу, указанному в параметре attributionSourceUri . Ответ конечной точки содержит значения для destination, source_event_id, expiry и source_priority .

Если компания, предоставляющая рекламные технологии, желает поделиться регистрационными данными источника, исходный URI источника атрибуции может включать перенаправления на другие конечные точки рекламных технологий. Ограничения и правила, применимые к перенаправлениям, подробно описаны в техническом предложении .

Добавлена ​​поддержка цепочек перенаправлений для registerSource и registerTrigger . В дополнение к заголовку регистрации, потребитель API теперь может предоставлять HTTP-перенаправление в качестве ответа сервера, включающее код состояния 302 и заголовок "Location" с указанием следующего URL-адреса для дополнительной регистрации.

В цепочке переадресаций используется только поле "destination", предоставленное при первом посещении. Количество посещений ограничено тем же количеством, что и заголовки "Attribution-Reporting-Redirect". Эта поддержка переадресации дополняет существующую поддержку "Attribution-Reporting-Redirect", и если присутствуют оба заголовка, предпочтение отдается "Attribution-Reporting-Redirect".

Зарегистрируйте событие, запускающее конверсию.

Чтобы зарегистрировать событие, запускающее конверсию, вызовите registerTrigger() в вашем приложении:

Котлин

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager = context.getSystemService(MeasurementManager::class.java)

// Use the URI of the server-side endpoint that accepts trigger registration.
val attributionTriggerUri: Uri =
    Uri.parse("https://adtech.example/trigger?AD_TECH_PROVIDED_METADATA")

val future = CompletableFuture<Void>()

// Register trigger (conversion)
measurementManager.registerTrigger(
        attributionTriggerUri,
        CALLBACK_EXECUTOR,
        future::complete)

Java

private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URI of the server-side endpoint that accepts trigger registration.
Uri attributionTriggerUri =
        Uri.parse("https://adtech.example/trigger?AD_TECH_PROVIDED_METADATA");

CompletableFuture<Void> future = new CompletableFuture<>();

// Register trigger (conversion)
measurementManager.registerTrigger(
        attributionTriggerUri,
        CALLBACK_EXECUTOR,
        future::complete)

После регистрации API отправляет HTTP POST-запрос к конечной точке сервиса по адресу, указанному в параметре attributionTriggerUri . Ответ конечной точки содержит значения для отчетов о событиях и сводных отчетов.

Если исходная рекламная технологическая платформа разрешает совместное использование регистраций триггеров, URI может включать перенаправления на URI, принадлежащие другим рекламным технологическим платформам. Ограничения и правила, применимые к перенаправлениям, подробно описаны в техническом предложении .

Зарегистрируйте межприложениевое и веб-измерение

В случае, когда и приложение, и браузер участвуют в пути пользователя от источника к триггеру, существуют тонкие различия в реализации регистрации рекламных событий. Если пользователь видит рекламу в приложении и перенаправляется в браузер для совершения конверсии, источник регистрируется приложением, а конверсия — веб-браузером. Аналогично, если пользователь начинает работу в веб-браузере и перенаправляется в приложение для совершения конверсии, браузер регистрирует источник, а приложение — конверсию.

Поскольку существуют различия в организации рекламных технологий в веб-версии и на Android, мы добавили новые API для регистрации источников и триггеров, срабатывающих в браузерах. Ключевое отличие этих API от соответствующих API для приложений заключается в том, что мы ожидаем, что браузер будет следовать перенаправлениям, применять любые специфические для браузера фильтры и передавать действительные регистрации на платформу, вызывая функции registerWebSource() или registerWebTrigger() .

Приведённый ниже фрагмент кода демонстрирует пример вызова API, который браузер выполняет для регистрации источника атрибуции перед перенаправлением пользователя в приложение:

Котлин

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager =
        context.getSystemService(MeasurementManager::class.java)
var exampleClickEvent: InputEvent? = null

// Use the URIs of the server-side endpoints that accept attribution source
// registration.
val sourceParam1 = WebSourceParams.Builder(Uri.parse(
        "https://adtech1.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
// True, if debugging is allowed for the ad tech.
    .setDebugKeyAllowed(true)
    .build()

val sourceParam2 = WebSourceParams.Builder(Uri.parse(
        "https://adtech2.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build()

val sourceParam3 = WebSourceParams.Builder(Uri.parse(
        "https://adtech3.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .build()

val sourceParams = Arrays.asList(sourceParam1, sourceParam2, sourceParam3)
val publisherOrigin = Uri.parse("https://publisher.example")
val appDestination = Uri.parse("android-app://com.example.store")
val webDestination = Uri.parse("https://example.com")

val future = CompletableFuture<Void>()

adView.setOnTouchListener {_: View?, event: MotionEvent? ->
    exampleClickEvent = event
    true
}
val clickRegistrationRequest = WebSourceRegistrationRequest.Builder(
          sourceParams,
          publisherOrigin)
      .setAppDestination(appDestination)
      .setWebDestination(webDestination)
      .setInputEvent(event)
      .build()
val viewRegistrationRequest = WebSourceRegistrationRequest.Builder(
          sourceParams,
          publisherOrigin)
      .setAppDestination(appDestination)
      .setWebDestination(webDestination)
      .setInputEvent(null)
      .build()

// Register a web source for a click event.
measurementManager.registerWebSource(
        clickRegistrationRequest,
        CALLBACK_EXECUTOR,
        future::complete)

// Register a web source for a view event.
measurementManager.registerWebSource(
        viewRegistrationRequest,
        CALLBACK_EXECUTOR,
        future::complete)

Java

private static final Executor CALLBACK_EXECUTOR =
        Executors.newCachedThreadPool();
private InputEvent exampleClickEvent;

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URIs of the server-side endpoints that accept attribution source
// registration.
WebSourceParams sourceParam1 = WebSourceParams.Builder(Uri.parse(
        "https://adtech1.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    // True, if debugging is allowed for the ad tech.
    .setDebugKeyAllowed(true)
    .build();

WebSourceParams sourceParam2 = WebSourceParams.Builder(Uri.parse(
        "https://adtech2.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build();

WebSourceParams sourceParam3 = WebSourceParams.Builder(Uri.parse(
        "https://adtech3.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .build();

List<WebSourceParams> sourceParams =
        Arrays.asList(sourceParam1, sourceParam2, sourceParam3);
Uri publisherOrigin = Uri.parse("https://publisher.example");
Uri appDestination = Uri.parse("android-app://com.example.store");
Uri webDestination = Uri.parse("https://example.com");

CompletableFuture<Void> future = new CompletableFuture<>();

adView.setOnTouchListener(v, event) -> {
    exampleClickEvent = event;
    return true;
}

WebSourceRegistrationRequest clickRegistrationRequest =
        new WebSourceRegistrationRequest.Builder(sourceParams, publisherOrigin)
    .setAppDestination(appDestination)
    .setWebDestination(webDestination)
    .setInputEvent(event)
    .build();
WebSourceRegistrationRequest viewRegistrationRequest =
        new WebSourceRegistrationRequest.Builder(sourceParams, publisherOrigin)
    .setAppDestination(appDestination)
    .setWebDestination(webDestination)
    .setInputEvent(null)
    .build();

// Register a web source for a click event.
measurementManager.registerWebSource(clickRegistrationRequest,
        CALLBACK_EXECUTOR, future::complete);

// Register a web source for a view event.
measurementManager.registerWebSource(viewRegistrationRequest,
        CALLBACK_EXECUTOR, future::complete);

Приведённый ниже фрагмент кода демонстрирует пример вызова API, который браузер выполняет для регистрации конверсии после того, как пользователь был перенаправлен из приложения:

Котлин

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

Добавление шума для обеспечения конфиденциальности

Отчеты на уровне событий содержат информацию о получателе, идентификаторе источника атрибуции и данных триггера. Они отправляются в исходном (незашифрованном) формате источнику отчета. Для защиты конфиденциальности пользователя может быть добавлен шум, затрудняющий идентификацию отдельного пользователя. Зашумленные отчеты на уровне событий генерируются и отправляются в соответствии с принципами дифференциальной конфиденциальности . Ниже приведены значения процента шума по умолчанию для различных сценариев:

Тип источника

Исходное значение назначения

Вероятность появления шума в отчете по каждому источнику регистрации

Вид

Либо приложение, либо веб-сайт

0.0000025

Вид

Приложение и веб-сайт

0.0000042

Нажмите

Либо приложение, либо веб-сайт

0.0024263

Нажмите

Приложение и веб-сайт

0.0170218

В методе атрибуции «приложение-веб», где источники могут приводить к конверсиям как в приложение, так и в веб-версию, отчеты на уровне событий могут указывать, произошло ли событие в приложении или на веб-сайте. Чтобы компенсировать эту дополнительную детализацию, генерируемые отчеты содержат ошибки, примерно в 7 раз превышающие ошибки в кликах и в 1,7 раза — в просмотрах.

Некоторые рекламные технологи не требуют от отчетов на уровне событий указания, произошло ли событие в приложении или на веб-сайте. Для уменьшения лишней информации рекламные технологии могут использовать поле coarse_event_report_destinations в заголовке Attribution-Reporting-Register-Source . Если источник с указанным полем coarse_event_report_destinations выигрывает в атрибуции, результирующий отчет включает как приложение, так и веб-сайт без указания места фактического срабатывания триггера.

В следующих примерах пользователь кликает на объявление, и этот источник регистрируется в API. Затем пользователь совершает конверсию как в приложении рекламодателя, так и на веб-сайте рекламодателя. Обе эти конверсии регистрируются как триггеры и приписываются первоначальному клику.

HTTP-заголовок регистрации источника на основе клика:

Attribution-Reporting-Register-Source: {
    "destination": "android-app://com.advertiser.example",
    "web_destination": "https://advertiser.com",
    "source_event_id": "234",
    "expiry": "60000",
    "priority": "5",
    // Ad tech opts out of receiving app-web destination distinction
    // in event report, avoids additional noise
    "coarse_event_report_destinations": "true"
}

В приложении регистрируется триггер с именем пакета com.advertiser.example :

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

Триггер регистрируется в браузере с веб-сайта, имеющего домен eTLD+1 https://advertiser.com :

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

В результате генерируются отчеты на уровне событий. Предполагая, что оба триггера привязаны к источнику, генерируются следующие отчеты на уровне событий:

  {
    "attribution_destination": ["android-app://com.advertiser.example,https://advertiser.com"],
    "scheduled_report_time": "800176400",
    "source_event_id": "53234",
    "trigger_data": "1",
    // Can be "event" if source were registered by user viewing the ad
    "source_type": "navigation",
    // Would be 0.0170218 without coarse_event_report_destinations as true in the source
    "randomized_trigger_rate": 0.0024263
  }

Создание и предоставление отчетов.

API для формирования отчетов об атрибуции отправляет отчеты на конечные точки вашего сервера, которые принимают отчеты на уровне событий и агрегированные отчеты .

Принудительное выполнение заданий по формированию отчетов

После регистрации события-источника или события-триггера система планирует запуск задания по формированию отчетов. По умолчанию это задание выполняется каждые 4 часа. В целях тестирования вы можете принудительно запускать задания по формированию отчетов или сократить интервалы между ними.

Принудительное выполнение задания по атрибуции:

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

Принудительное выполнение задания по формированию отчетов на уровне событий:

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

Принудительное выполнение задания по созданию сводных отчетов:

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

Проверьте вывод в logcat, чтобы увидеть, когда задания были выполнены. Он должен выглядеть примерно так:

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

Принудительная доставка отчетов

Даже если задача формирования отчетов запущена принудительно, система все равно отправляет отчеты в соответствии с запланированным временем доставки , которое варьируется от нескольких часов до нескольких дней. В целях тестирования вы можете перевести системное время устройства на время, превышающее запланированные задержки, чтобы инициировать доставку отчетов.

Проверьте отчеты на вашем сервере.

После отправки отчетов проверьте их доставку, сверив полученные отчеты, соответствующие журналы сервера, такие как история имитированного сервера или данные вашей пользовательской системы.

Расшифруйте ваш сводный отчет

При получении сводного отчета поле debug_cleartext_payload содержит незашифрованную версию вашего сводного отчета. Хотя эта версия отчета не зашифрована, ее все равно необходимо расшифровать.

Ниже приведён пример декодирования содержимого поля debug_cleartext_payload в два этапа: первый — с использованием декодирования Base 64, а второй — с использованием декодирования 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);
});

Тестирование

Чтобы помочь вам начать работу с API для создания отчетов по атрибуции, вы можете использовать проект MeasurementSampleApp на GitHub. Это демонстрационное приложение показывает регистрацию источников атрибуции и регистрацию триггеров.

Для серверных конечных точек рассмотрите следующие справочные материалы или собственное решение:

  • MeasurementAdTechServerSpec включает определения сервисов OpenAPI, которые можно развернуть на поддерживаемых платформах имитации или микросервисов.
  • MeasurementAdTechServer включает в себя эталонную реализацию имитационного сервера на основе приложения Spring Boot для Google App Engine.

Предварительные требования

Разверните фиктивные API на удаленных конечных точках, доступных с вашего тестового устройства или эмулятора. Для упрощения тестирования обратитесь к примерам проектов MeasurementAdTechServerSpec и MeasurementAdTechServer .

Функциональность для тестирования

Предстоящие функции

Гибкая конфигурация на уровне событий

Для начала тестирования рекомендуется использовать конфигурацию по умолчанию для отчетов на уровне событий, но она может быть не идеальной для всех сценариев использования. API отчетов по атрибуции будет поддерживать дополнительные, более гибкие конфигурации, чтобы специалисты по рекламным технологиям имели больший контроль над структурой своих отчетов на уровне событий и могли максимально эффективно использовать данные. Эта дополнительная гибкость будет внедрена в API отчетов по атрибуции в два этапа:

  • Этап 1 : Упрощенная гибкая конфигурация на уровне событий; подмножество Этапа 2.
  • Этап 2 : Полная версия гибкой конфигурации на уровне событий.

Этап 1: Легкий, гибкий уровень мероприятия

В JSON-файл 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>, ...]
  }
}

Пример пользовательской конфигурации

Данная конфигурация предназначена для разработчиков, которые хотят оптимизировать процесс получения отчетов в более ранние периоды.

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

Этап 2: Полностью гибкий уровень мероприятия

В дополнение к параметрам, добавленным на первом этапе, мы добавим в JSON-файл Attribution-Reporting-Register-Source дополнительный необязательный параметр trigger_specs .

{
  // A trigger spec is a set of matching criteria, along with a scheme to
  // generate bucketized output based on accumulated values across multiple
  // triggers within the specified event_report_window. There will be a limit on
  // the number of specs possible to define for a source.
  "trigger_specs": [{
    // This spec will only apply to registrations that set one of the given
    // trigger data values (non-negative integers) in the list.
    // trigger_data will still appear in the event-level report.
    "trigger_data": [<int>, ...]

    // Represents a series of time windows, starting at the source registration
    // time. Reports for this spec will be delivered an hour after the end of
    // each window. Time is encoded as seconds after source registration.
    // end_times must consist of strictly increasing positive integers.
    //
    // Note: specs with identical trigger_data cannot have overlapping windows;
    // this makes sure that triggers match at most one spec. If
    // event_report_windows is omitted, will use the "event_report_window" or
    // "event_report_windows" field specified at the global level for the source
    // (or the default windows if none are specified). End time is exclusive.
    "event_report_windows": {
      "start_time": <int>,
      "end_times": [<int>, ...],
    }

    // Represents an operator that summarizes the triggers within a window
    // count: number of triggers attributed within a window
    // value_sum: sum of the value of triggers within a window
    // The summary is reported as an index into a bucketization scheme. Defaults
    // to "count"
    "summary_window_operator": <one of "count" or "value_sum">,

    // Represents a bucketization of the integers from [0, MAX_INT], encoded as
    // a list of integers where new buckets begin (excluding 0 which is
    // implicitly included).
    // It must consist of strictly increasing positive integers.
    //
    // e.g. [5, 10, 100] encodes the following ranges:
    // [[0, 4], [5, 9], [10, 99], [100, MAX_INT]]
    //
    // At the end of each reporting window, triggers will be summarized into an
    // integer which slots into one of these ranges. Reports will be sent for
    // every new range boundary that is crossed. Reports will never be sent for
    // the range that includes 0, as every source is initialized in this range.
    //
    // If omitted, then represents a trivial mapping
    // [1, 2, ... , MAX_INT]
    // With MAX_INT being the maximum int value defined by the browser.
    "summary_buckets": [<bucket start>, ...]
  }, {
    // Next trigger_spec
  } ...],

  // See description in phase 1.
  "max_event_level_reports": <int>
  // See description in phase 1.
  "event_report_windows": {
    "start_time": <int>,
    "end_times": [<int>, ...]
  }
}

Данная конфигурация полностью определяет пространство вывода отчетов уровня событий для каждой регистрации источника. Для каждой спецификации триггера мы полностью указываем:

  • Набор критериев соответствия:
    • К каким именно данным триггера относится данная спецификация? Этот источник может быть сопоставлен только с триггерами, имеющими одно из указанных значений trigger_data в trigger_specs . Другими словами, если триггер соответствовал бы этому источнику, но его trigger_data не входит в число значений в конфигурации источника, триггер игнорируется.
    • Когда конкретный триггер соответствует этой спецификации (с использованием event_report_windows ). Обратите внимание, что триггер все еще может быть сопоставлен с источником для агрегируемых отчетов, несмотря на несоответствие двум критериям соответствия, упомянутым ранее.
  • Специальный алгоритм для суммирования и группировки всех триггеров в пределах окна атрибуции. Это позволяет триггерам указывать параметр value , который суммируется для конкретной спецификации, но отображается как значение, сгруппированное по категориям.

Триггеры также будут поддерживать добавление необязательного параметра значения в словари внутри event_trigger_data .

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

Каждая регистрация триггера будет соответствовать не более чем одной спецификации триггера и обновлять связанное с ней суммарное значение. В общих чертах, во время срабатывания триггера мы будем:

  • Примените глобальные фильтры атрибуции.
  • Для каждой спецификации триггера оцените поле event_trigger_data в спецификации, чтобы найти совпадение, используя поле event_reporting_window спецификации. Поле верхнего уровня event_reporting_windows выступает в качестве значения по умолчанию в случае, если в какой-либо спецификации триггера отсутствует подполе event_report_windows .
  • Для атрибуции выбирается первая совпавшая спецификация, а суммарное значение увеличивается на value .

Когда завершится выполнение event_report_window для спецификации, мы сопоставим его сводное значение с сегментом и отправим отчет на уровне события для каждого увеличения в сводном сегменте, вызванного значениями атрибутированных триггеров. Отчеты будут содержать одно дополнительное поле — trigger_summary_bucket .

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

Конфигурации, эквивалентные текущей версии.

Ниже представлены эквивалентные конфигурации для источников текущих событий и навигации API соответственно. В частности, для источников навигации это иллюстрирует, почему уровни шума так высоки по сравнению с источниками событий, чтобы поддерживать те же значения эпсилон: источники навигации имеют гораздо большее пространство выходных данных.

Вполне возможно, что существует несколько эквивалентных конфигураций, поскольку некоторые параметры могут быть установлены по умолчанию или обрезаны.

Эквивалентные источники событий
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1],
    "event_report_windows": {
      "end_times": [<30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1],
  }],
  "max_event_level_reports": 1,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}
Эквивалентные источники навигации
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1, 2, 3, 4, 5, 6, 7],
    "event_report_windows": {
      "end_times": [<2 days>, <7 days>, <30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1, 2, 3],
  }],
  "max_event_level_reports": 3,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}

Примеры пользовательских конфигураций

Ниже приведены некоторые дополнительные параметры конфигурации, помимо настроек по умолчанию. Во всех этих примерах разработчики идут на компромиссы, учитывая следующее:

  • Снижение одного параметра конфигурации по умолчанию (количество триггеров, количество данных триггеров, количество окон) для увеличения другого с целью сохранения уровня шума.
  • Сокращение некоторых параметров конфигурации по умолчанию (количество триггеров, количество данных триггеров, количество окон) для снижения уровня шума.

Сегменты значений, запускающие отчеты

Эта примерная конфигурация поддерживает разработчика, который хочет оптимизировать данные о значениях только для одного отчетного периода (например, 7 дней), жертвуя меньшим количеством отчетов ради уменьшения информационного шума. В этом примере любой триггер, устанавливающий trigger_data в значение, отличное от 0, не подлежит атрибуции.

{
  "trigger_specs": [
  {
    "trigger_data": [0],
    "event_report_windows": {
      "end_times": [604800, 1209600] // 7 days, 14 days represented in seconds
    },
    "summary_window_operator": "value_sum",
    "summary_buckets": [5, 10, 100]
  }],
}

Триггеры могут быть зарегистрированы с установленным value поля, которые затем суммируются и группируются. Например, если в течение 7 дней после регистрации источника сработали три триггера со значениями 1, 3 и 4.

{ "event_trigger_data": [{"trigger_data": "0", "value": 1}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 3}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 4}] }

Полученные значения суммируются до 8 и отображаются в следующих отчетах через 7 дней + 1 час:

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

В течение последующих 7 дней регистрируются следующие события:

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

Сумма значений равна 8 + 50 + 45 = 103. В результате получаются следующие отчеты за 14 дней + 1 час:

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

// Report 3
{
  ...
  "trigger_summary_bucket": [100, MAX_INT]
}
Сообщите количество срабатываний триггера

В этом примере показано, как разработчик может настроить источник данных для получения количества триггеров, до 10.

{
  "trigger_specs": [
  {
    "trigger_data": [0],
    "event_report_windows": {
      "end_times": [604800] // 7 days represented in seconds
    },
    // This field could be omitted to save bandwidth since the default is "count"
    "summary_window_operator": "count",
    "summary_buckets": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  }],
}

Триггеры с атрибутами, у которых trigger_data установлено значение 0, учитываются и ограничиваются 10. Значение триггера игнорируется, поскольку summary_window_operator установлено на count. Если зарегистрировано 4 триггера, атрибутированных источнику, отчет будет выглядеть следующим образом:

// Report 1
{
  ...
  "trigger_summary_bucket": [1, 1]
}
// Report 2
{
  ...
  "trigger_summary_bucket": [2, 2]
}
// Report 3
{
  ...
  "trigger_summary_bucket": [3, 3]
}
// Report 4
{
  ...
  "trigger_summary_bucket": [4, 4]
}
Бинарная модель с более частой отчетностью

Эта примерная конфигурация предназначена для разработчика, который хочет узнать, произошла ли хотя бы одна конверсия за первые 10 дней (независимо от значения), но при этом хочет получать отчеты с более частыми интервалами, чем по умолчанию. Опять же, в этом примере любой триггер, устанавливающий trigger_data в значение, отличное от 0, не подлежит атрибуции. Именно поэтому этот вариант использования называется бинарным .

{
  "trigger_specs": [
  {
    "trigger_data": [0],
    "event_report_windows": {
      // 1 day, 2 days, 3 days, 5 days, 7 days, 10 days represented in seconds
      "end_times": [86400, 172800, 259200, 432000, 604800, 864000]
    },
    // This field could be omitted to save bandwidth since the default is "count"
    "summary_window_operator": "count",
    "summary_buckets": [1]
  }],
}
Характеристики триггеров могут различаться в зависимости от источника.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1, 2, 3],
    "event_report_windows": {
      "end_times": [172800, 604800, 2592000] // 2 days, 7 days, 30 days represented in seconds
    }
  }],
  "max_event_level_reports": 3
}
{
  "trigger_specs": [
  {
    "trigger_data": [4, 5, 6, 7],
    "event_report_windows": {
      "end_times": [172800, 604800, 2592000] // 2 days, 7 days, 30 days represented in seconds
    }
  }],
  "max_event_level_reports": 3
}

Мы призываем разработчиков предлагать различные варианты использования этого расширения API, и мы обновим это пояснение, добавив примеры конфигураций для этих вариантов.

Межсетевая атрибуция без перенаправлений

Специалистам по рекламным технологиям следует использовать перенаправления для регистрации нескольких триггеров источников атрибуции и для выполнения атрибуции в разных сетях. Эта функция помогает поддерживать атрибуцию в разных сетях, когда перенаправления между сетями невозможны. Подробнее .

Специалисты по рекламным технологиям могут отправлять конфигурацию в ответе на регистрацию триггера, на основе которой выбираются источники, зарегистрированные другими специалистами по рекламным технологиям, для генерации производных источников; эти производные источники затем используются для атрибуции. Сводные отчеты генерируются, если триггер приписывается производному источнику. Генерация отчетов о событиях для производных источников не поддерживается.

Специалисты по рекламным технологиям могут выбирать из aggregation_keys в своих зарегистрированных источниках те ключи, которыми они намерены делиться с партнерскими рекламными технологиями. Эти ключи можно указать в необязательном поле shared_aggregation_keys , расположенном в заголовке регистрации источника Attribution-Reporting-Register-Source :

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

Производные источники генерируются на основе конфигурации, указанной в заголовке регистрации триггера Attribution-Reporting-Register-Trigger :

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

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

Вот версия с добавленными примерами значений:

  "attribution_config": [
    {
      "source_network": "adtech1-enrollment-id",
      "source_priority_range": {
        "start": 50,
        "end": 100
      },
      "source_filters": {
        "source_type": ["NAVIGATION"]
      },
      "source_not_filters": {
        "product_id": ["789"]
      },
      "priority": "30",
      "expiry": "78901",
      // (optional) set on the derived source
      "filter_data": {
        "product_id": ["1234"]
        },
      // (optional) set on the derived source
      "post_install_exclusivity_window": "7890"
    }
  ]

Добавлены два новых необязательных поля для запуска заголовка регистрации. Эти поля позволяют использовать идентификатор победившей рекламной технологии в ключах агрегируемых отчетов:

  • x_network_bit_mapping : сопоставление битов идентификатора регистрации с идентификатором рекламной технологии.
  • x_network_data : Смещение (левый сдвиг) для операции x_network_bit_mapping ИЛИ с ключевым элементом триггера, используемой в выигрышной рекламной технологии.
Пример:
"Attribution-Reporting-Register-Trigger": {
  "attribution_config": [...],
  "aggregatable_trigger_data": [
    {
     "key_piece": "0x400",
     "source_keys": ["campaignCounts"]
      "x_network_data" : {
        "key_offset" : 12 // [64 bit unsigned integer]
      }
    }
    
  ]
  
  "x_network_bit_mapping": {
   // This mapping is used to generate trigger key pieces with ad tech identifier
   // bits. eg. If Ad Tech-A's sources wins the attribution then 0x1 here will be
   // OR'd with the trigger key pieces to generate the final key piece.
    "Ad-Tech-A-enrollment_id": "0x1", // Identifier bits in hex for A
    "Ad-Tech-B-enrollment_id": "0x2"  // Identifier bits in hex for B
  }
  
}

Вот результат расчета ключевого элемента триггера при создании отчета для источника AdTechB:

  • key_piece : 0x400 (010000000000)
  • key_offset : 12
  • Значение enrollment_id компании AdtechB: 2 (010) (из x_network_bit_mapping )
  • Результирующий ключевой элемент триггера: 0x400 | 0x2 << 12 = 0x2400

Ограничения

Список разрабатываемых функций среды выполнения SDK можно найти в примечаниях к выпуску .

Сообщайте об ошибках и проблемах.

Ваши отзывы играют решающую роль в работе Privacy Sandbox на Android! Сообщите нам о любых обнаруженных проблемах или идеях по улучшению Privacy Sandbox на Android.