Attribution Reporting API 개발자 가이드

안내가 다를 수 있으므로 Android의 개인 정보 보호 샌드박스 문서를 읽으면서 개발자 프리뷰 또는 베타 버튼을 사용하여 작업 중인 프로그램 버전을 선택하세요.


의견 보내기

Attribution Reporting API는 당사자 간 사용자 식별자에 대한 의존성을 제거하여 사용자 개인 정보 보호를 개선하고, 앱에서 기여 분석 및 전환 측정을 위한 주요 사용 사례를 지원하도록 설계되었습니다. 이 개발자 가이드에서는 광고 클릭, 조회, 전환 등의 이벤트와 관련된 트리거 및 소스를 등록하는 메서드를 호출하여 그러한 이벤트를 등록하는 Attribution Reporting API를 구성하고 테스트하는 방법을 설명합니다.

이 가이드에서는 서버 엔드포인트를 설정하고 그러한 서비스를 호출하는 클라이언트 앱을 빌드하는 방법을 설명합니다. 설계 제안서에서 Attribution Reporting API의 전반적인 설계에 관해 자세히 알아보세요.

주요 용어

  • 기여 분석 소스는 클릭수 또는 조회수를 나타냅니다.
  • 트리거는 전환에 기여할 수 있는 이벤트입니다.
  • 보고서에는 트리거와 관련 기여 분석 소스에 관한 데이터가 포함됩니다. 이러한 보고서는 트리거 이벤트에 관한 응답으로 전송됩니다. Attribution Reporting API는 이벤트 수준 보고서집계 가능한 보고서를 지원합니다.

시작하기 전에

Attribution Reporting API를 사용하려면 다음 섹션에 나와 있는 서버 측 작업과 클라이언트 측 작업을 완료하세요.

Attribution Reporting API 엔드포인트 설정

Attribution Reporting API를 사용하려면 테스트 기기 또는 에뮬레이터에서 액세스할 수 있는 엔드포인트 세트가 필요합니다. 다음 서버 측 작업별로 엔드포인트를 하나씩 만듭니다.

필수 엔드포인트를 설정하는 방법에는 여러 가지가 있습니다.

  • 가장 빨리 시작하고 실행하는 방법은 샘플 코드 저장소에서 OpenAPI v3 서비스 정의를 모의 플랫폼 또는 마이크로서비스 플랫폼에 배포하는 것입니다. Postman 또는 Prism이나 이 형식을 허용하는 다른 모의 서버 플랫폼을 모두 사용할 수 있습니다. 각 엔드포인트를 배포하고 앱에 사용할 URI를 추적합니다. 보고서 전송을 확인하려면 이전에 모의 플랫폼 또는 서버리스 플랫폼에 실행한 호출을 참고하세요.
  • Spring Boot 기반 Kotlin 샘플을 사용하여 고유한 독립형 서버를 실행합니다. 이 서버를 클라우드 제공업체 또는 내부 인프라에 배포합니다.
  • 서비스 정의를 예로 사용하여 엔드포인트를 기존 시스템에 통합합니다.

소스 등록 허용

이 엔드포인트는 다음과 유사한 URI에서 주소 지정이 가능해야 합니다.

https://adtech.example/attribution_source

클라이언트 앱이 기여 분석 소스를 등록할 때 이 서버 엔드포인트의 URI를 제공합니다. 그러면 Attribution Reporting API가 요청을 실행하고 다음 헤더 중 하나를 포함합니다.

  • 클릭 이벤트의 경우:

    Attribution-Reporting-Source-Info: navigation
    
  • 조회 이벤트의 경우:

    Attribution-Reporting-Source-Info: event
    

DP 10 이상 소스 등록 요청에는 다음과 같이 버전 헤더가 포함됩니다.

  Version: 202310

다음으로 응답하도록 서버 엔드포인트를 구성합니다.

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

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

다음은 샘플 값이 추가된 예입니다.

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

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

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

Attribution-Reporting-Redirects에 광고 기술 파트너의 URI가 포함되어 있으면 Attribution Reporting API가 각 URI에 비슷한 요청을 실행합니다. 각 광고 기술 파트너는 다음 헤더로 응답하는 서버를 구성해야 합니다.

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

전환 트리거 등록 허용

이 엔드포인트는 다음과 유사한 URI에서 주소 지정이 가능해야 합니다.

https://adtech.example/attribution_trigger

클라이언트 앱이 트리거 이벤트를 등록할 때 이 서버 엔드포인트의 URI를 제공합니다. 그러면 Attribution Reporting API가 제공된 URI에 요청을 실행합니다.

DP 10 이상 트리거 등록 요청에는 다음과 같이 버전 헤더가 포함됩니다.

  Version: 202310

다음으로 응답하도록 서버 엔드포인트를 구성합니다.

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

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

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

다음은 샘플 값이 추가된 예입니다.

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

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

집계 키 ID와 필터 문자열당 25바이트로 제한됩니다. 즉, 집계 키 ID와 필터 문자열은 25자(영문 기준)를 초과하면 안 됩니다. 이 예에서 campaignCounts는 14자이므로 유효한 집계 키 ID이고 1234는 4자이므로 유효한 필터 문자열입니다. 집계 키 ID나 필터 문자열이 25자를 초과하면 트리거가 무시됩니다.

Attribution-Reporting-Redirect에 광고 기술 파트너의 URI가 포함되어 있으면 Attribution Reporting API가 각 URI에 비슷한 요청을 실행합니다. 각 광고 기술 파트너는 다음 헤더로 응답하는 서버를 구성해야 합니다.

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

이벤트 수준 보고서 허용

이 엔드포인트는 URI에서 주소 지정이 가능해야 합니다. URI 등록에 관한 자세한 내용은 개인 정보 보호 샌드박스 계정 등록을 참고하세요. URI는 소스 등록과 트리거 등록을 허용하는 데 사용되는 서버의 출처에서 추론됩니다. 소스 등록을 허용하고 트리거 등록을 허용하는 엔드포인트의 URI 예를 사용할 경우 이 엔드포인트의 URI는 다음과 같습니다.

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

DP 10 이상의 이벤트 수준 보고서 요청에는 다음과 같이 버전 헤더가 포함됩니다.

  Version: 202310

다음 형식을 사용하는 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

DP 10 이상의 집계 가능한 보고서 요청에는 다음과 같이 버전 헤더가 포함됩니다.

  Version: 202310

집계 가능한 보고서에는 암호화된 필드와 암호화되지 않은 필드가 모두 채워져 있습니다. 암호화된 보고서를 사용하면 집계 서비스로 테스트를 시작할 수 있으며 암호화되지 않은 필드는 설정된 키-값 쌍의 데이터 구조화 방식에 관한 유용한 정보를 제공합니다.

다음 형식을 사용하는 JSON 요청을 수락하도록 이 서버를 구성합니다.

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

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

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

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

디버그 키를 사용하면 기여도 보고서를 더 잘 파악할 수 있습니다. 구성하는 방법에 관해 자세히 알아보세요.

Android 클라이언트 설정

클라이언트 앱은 기여 분석 소스와 트리거를 등록하고 이벤트 수준 보고서 및 집계 가능한 보고서 생성을 가능하게 합니다. Attribution Reporting API를 사용하도록 Android 클라이언트 기기 또는 에뮬레이터를 준비하려면 다음을 진행하세요.

  1. Android의 개인 정보 보호 샌드박스의 개발 환경을 설정합니다.
  2. 지원되는 기기에 시스템 이미지를 설치하거나 Android의 개인 정보 보호 샌드박스 지원이 포함된 에뮬레이터를 설정합니다.
  3. 다음 ADB 명령어를 실행하여 Attribution Reporting API에 대한 액세스를 사용 설정합니다. 이 API는 기본적으로 사용 중지되어 있습니다.

    adb shell device_config put adservices ppapi_app_allow_list \"\*\"
    
  4. Attribution Reporting API를 로컬에서 테스트하는 경우(예: 물리적으로 액세스할 수 있는 기기에서 테스트) 다음 명령어를 실행하여 등록을 사용 중지합니다.

    adb shell device_config put adservices disable_measurement_enrollment_check "true"
    
  5. Android 매니페스트 파일에 ACCESS_ADSERVICES_ATTRIBUTION 권한을 포함하고 앱에서 Attribution Reporting API를 사용할 수 있도록 광고 서비스 구성을 만듭니다.

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
    
  6. (선택사항) 디버그 보고서를 수신하려면 Android 매니페스트 파일에 ACCESS_ADSERVICES_AD_ID 권한을 포함합니다.

    <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()를 호출하여 코드 스니펫에 표시된 것처럼 기여 분석 소스를 등록합니다.

Attribution Reporting API는 다음 유형의 기여 분석 소스 이벤트를 지원합니다.

  • 클릭: 일반적으로 onClick()과 유사한 콜백 메서드 내에서 등록합니다. 관련 트리거 이벤트는 일반적으로 클릭 이벤트 직후에 발생합니다. 이 이벤트 유형은 사용자 상호작용에 관한 자세한 정보를 제공하므로 높은 우선순위를 부여하는 데 적합한 기여 분석 소스 유형입니다.
  • 조회: 일반적으로 onAdShown()과 유사한 콜백 메서드 내에서 등록합니다. 관련 트리거 이벤트는 조회 이벤트가 발생하고 몇 시간 또는 며칠 후에 발생할 수 있습니다.

Kotlin

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

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

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

val future = CompletableFuture<Void>()

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

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

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

Java

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

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

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

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

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

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

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

등록 후 API는 attributionSourceUri로 지정된 주소에서 서비스 엔드포인트에 HTTP POST 요청을 보냅니다. 엔드포인트의 응답에는 destination, source_event_id, expirysource_priority 값이 포함되어 있습니다.

발신 광고 기술에서 소스 등록을 공유하려는 경우 원래의 기여 분석 소스 URI에는 다른 광고 기술 엔드포인트로의 리디렉션이 포함될 수 있습니다. 리디렉션에 적용 가능한 제한과 규칙은 기술 제안서에 자세히 설명되어 있습니다.

registerSourceregisterTrigger의 데이지 체이닝 리디렉션에 관한 지원이 추가되었습니다. 등록 헤더 외에도 API 소비자는 이제 추가 등록을 위해 방문할 다음 URL과 함께 302 상태 코드와 '위치' 헤더가 포함된 HTTP 리디렉션을 서버 응답으로 제공할 수 있습니다.

첫 번째 방문에서 제공되는 '대상' 필드만 데이지 체이닝 전체에서 사용됩니다. 방문 횟수는 'Attribution-Reporting-Redirect' 헤더와 동일하게 제한됩니다. 이 리디렉션 지원은 기존의 'Attribution-Reporting-Redirect' 지원에 추가되는 것으로, 두 가지 모두 제공되는 경우 'Attribution-Reporting-Redirect'가 우선됩니다.

전환 트리거 이벤트 등록

전환 트리거 이벤트를 등록하려면 앱에서 registerTrigger()를 호출합니다.

Kotlin

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

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

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

val future = CompletableFuture<Void>()

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

Java

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

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

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

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

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

등록 후 API는 attributionTriggerUri로 지정된 주소에서 서비스 엔드포인트에 HTTP POST 요청을 보냅니다. 엔드포인트의 응답에는 이벤트 보고서 및 집계 보고서의 값이 포함되어 있습니다.

발신 광고 기술 플랫폼에서 트리거 등록 공유를 허용하는 경우 URI에는 다른 광고 기술 플랫폼에 속한 URI로의 리디렉션이 포함될 수 있습니다. 리디렉션에 적용 가능한 제한과 규칙은 기술 제안서에 자세히 설명되어 있습니다.

교차 앱 및 웹 측정 등록

소스에서 트리거로 이어지는 사용자 여정에서 앱과 브라우저가 모두 기여한 경우 광고 이벤트 등록 구현에는 미묘한 차이가 있습니다. 사용자가 앱에서 광고를 보고 전환을 위해 브라우저로 리디렉션되는 경우 소스는 앱에서 등록되고 전환은 웹브라우저에서 등록됩니다. 마찬가지로 사용자가 웹브라우저에서 시작하고 전환을 위해 앱으로 이동되면 브라우저에서 소스를 등록하고 앱에서 전환을 등록합니다.

광고 기술이 웹과 Android에서 구성되는 방식에 차이가 있으므로 Google에서는 소스 및 트리거가 브라우저에서 발생할 때 이를 등록하는 새 API를 추가했습니다. 이러한 API와 이에 상응하는 앱 기반 API의 주요 차이점은 브라우저가 리디렉션을 따르고 브라우저 관련 필터를 적용하며 registerWebSource() 또는 registerWebTrigger()를 호출하여 플랫폼에 유효한 등록을 전달할 것으로 예상된다는 점입니다.

다음 코드 스니펫은 사용자를 앱으로 안내하기 전에 브라우저가 기여 분석 소스를 등록하기 위해 실행하는 API 호출의 예를 보여줍니다.

Kotlin

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

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

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

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

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

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

val future = CompletableFuture<Void>()

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

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

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

Java

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

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

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

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

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

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

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

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

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

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

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

다음 코드 스니펫은 사용자가 앱에서 이동한 후 브라우저가 전환을 등록하기 위해 실행하는 API 호출의 예를 보여줍니다.

Kotlin

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

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

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

val triggerParam2 = WebTriggerParams.Builder(Uri.parse(
        "https://adtech2.example/trigger?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build()

val triggerParams = Arrays.asList(triggerParam1, triggerParam2)
val advertiserOrigin = Uri.parse("https://advertiser.example")

val future = CompletableFuture<Void>()

val triggerRegistrationRequest = WebTriggerRegistrationRequest.Builder(
        triggerParams,
        advertiserOrigin)
    .build()

// Register the web trigger (conversion).
measurementManager.registerWebTrigger(
    triggerRegistrationRequest,
    CALLBACK_EXECUTOR,
    future::complete)

Java

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

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

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

WebTriggerParams triggerParam2 = WebTriggerParams.Builder(Uri.parse(
        "https://adtech2.example/trigger?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build();

List<WebTriggerParams> triggerParams =
        Arrays.asList(triggerParam1, triggerParam2);
Uri advertiserOrigin = Uri.parse("https://advertiser.example");

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

WebTriggerRegistrationRequest triggerRegistrationRequest =
        new WebTriggerRegistrationRequest.Builder(
            triggerParams, advertiserOrigin)
    .build();

// Register the web trigger (conversion).
measurementManager.registerWebTrigger( triggerRegistrationRequest,
        CALLBACK_EXECUTOR, future::complete);

개인 정보 보호를 위한 노이즈 추가

이벤트 수준 보고서에는 대상, 기여 분석 소스 ID, 트리거 데이터가 포함됩니다. 원본(암호화되지 않은) 형식으로 보고 출처로 전송됩니다. 사용자의 개인 정보를 보호하기 위해 노이즈를 추가하여 개별 사용자를 식별하기 더 어렵게 만들 수 있습니다. 노이즈 이벤트 수준 보고서는 개인 정보 차등 보호 프레임워크에 따라 생성되고 전송됩니다. 다음은 다양한 시나리오의 기본 노이즈 비율 값입니다.

소스 유형

소스 대상 값

소스 등록당 노이즈 보고 확률

보기

앱 또는 웹

0.0000025

보기

앱 및 웹

0.0000042

클릭

앱 또는 웹

0.0024263

클릭

앱 및 웹

0.0170218

소스가 앱 및 웹 대상으로의 전환을 유도할 수 있는 앱-웹 기여 분석 측정에서 이벤트 수준 보고서는 트리거가 앱에서 발생했는지 웹에서 발생했는지 지정할 수 있습니다. 이러한 추가 세부정보를 보완하기 위해, 생성된 노이즈 보고서는 클릭의 경우 최대 7배, 조회의 경우 최대 1.7배입니다.

일부 광고 기술에는 트리거가 앱 대상에서 발생했는지 웹 대상에서 발생했는지 지정하기 위해 이벤트 수준 보고서가 필요하지 않습니다. 광고 기술은 Attribution-Reporting-Register-Source 헤더 아래의 coarse_event_report_destinations 필드를 사용하여 노이즈를 줄일 수 있습니다. coarse_event_report_destinations 필드가 지정된 소스가 기여도를 부여받으면 결과 보고서에는 실제 트리거가 발생한 위치를 구분하지 않고 앱과 웹 대상이 모두 포함됩니다.

다음 예에서는 사용자가 광고를 클릭하면 소스가 API에 등록됩니다. 그러면 사용자는 광고주 앱과 광고주 웹사이트에서 모두 전환합니다. 이 두 전환은 모두 트리거로 등록되며 초기 클릭이 기여한 것으로 간주됩니다.

클릭 기반 소스 등록 HTTP 헤더:

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

트리거가 패키지 이름 com.advertiser.example이 있는 앱에서 등록됩니다.

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

트리거가 eTLD+1 도메인 https://advertiser.com이 있는 웹사이트의 브라우저에서 등록됩니다.

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

결과 이벤트 수준 보고서가 생성됩니다. 두 트리거가 모두 소스로 인해 발생한다고 가정하면 다음 이벤트 수준 보고서가 생성됩니다.

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

보고서 생성 및 전송

Attribution Reporting API는 이벤트 수준 보고서집계 가능한 보고서를 허용하는 서버 엔드포인트에 보고서를 전송합니다.

보고 작업 강제 실행

기여 분석 소스 이벤트를 등록하거나 트리거 이벤트를 등록하면 시스템에서 실행할 보고 작업을 예약합니다. 기본적으로 이 작업은 4시간마다 실행됩니다. 테스트 목적에서는 보고 작업을 강제로 실행하거나 작업 간 간격을 단축할 수 있습니다.

기여 분석 작업을 강제 실행하려면 다음을 진행합니다.

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

다음과 같이 이벤트 수준 보고 작업을 강제 실행합니다.

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

다음과 같이 집계 가능 보고 작업을 강제 실행합니다.

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

작업이 실행된 시점을 보려면 logcat의 출력을 확인합니다. 다음과 같아야 합니다.

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

보고서 강제 전송

보고 작업이 강제로 실행되더라도 시스템은 몇 시간에서 며칠까지 걸리는 예약된 전송 시간에 따라 보고서를 계속 전송합니다. 테스트 목적을 위해, 기기 시스템 시간을 예약된 지연 이후로 변경하여 보고서 전송을 시작할 수 있습니다.

서버에서 보고서 확인

보고서가 전송되면 수신된 보고서나 모의 서버 기록 같은 관련 서버 로그 또는 맞춤 시스템을 확인하여 전송을 확인합니다.

집계 보고서 디코딩

집계 보고서를 수신하면 debug_cleartext_payload 필드는 암호화되지 않은 버전의 집계 보고서를 보유합니다. 이 버전의 보고서는 암호화되지 않았지만 여전히 디코딩해야 합니다.

다음은 debug_cleartext_payload 필드의 콘텐츠를 두 단계로 디코딩하는 예입니다. 첫 번째 단계에서는 Base64 디코딩을 사용하고 두 번째 단계에서는 CBOR 디코딩을 사용합니다.

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

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

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

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

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

테스트

Attribution Reporting API를 시작하려면 GitHub의 MeasurementSampleApp 프로젝트를 사용하면 됩니다. 이 샘플 앱은 기여 분석 소스 등록 및 트리거 등록 과정을 보여줍니다.

서버 엔드포인트와 관련해서는 다음 참조 리소스 또는 맞춤 솔루션을 사용하는 것이 좋습니다.

  • MeasurementAdTechServerSpec은 지원되는 모의 플랫폼 또는 마이크로서비스 플랫폼에 배포할 수 있는 OpenAPI 서비스 정의를 포함하고 있습니다.
  • MeasurementAdTechServer는 Google App Engine용 Spring Boot 앱에 기반한 모의 서버의 참조 구현을 포함하고 있습니다.

기본 요건

테스트 기기 또는 에뮬레이터에서 액세스할 수 있는 원격 엔드포인트에 모의 API를 배포합니다. 테스트 편의를 위해 MeasurementAdTechServerSpecMeasurementAdTechServer 샘플 프로젝트를 참고하세요.

테스트할 기능

  • 기여 분석 소스 및 전환 트리거 등록을 실행합니다. 서버 측 엔드포인트가 올바른 형식으로 응답하는지 확인합니다.
  • 보고 작업을 실행합니다.
  • 테스트 서버의 백엔드 또는 콘솔에서 보고서 전송을 확인합니다.

출시 예정 기능

유연한 이벤트 수준 구성

유틸리티 테스트를 시작할 때는 이벤트 수준 보고의 기본 구성이 권장되지만 일부 사용 사례에는 적합하지 않을 수 있습니다. Attribution Reporting API는 보다 유연한 선택적 구성을 지원하므로 광고 기술이 이벤트 수준 보고서의 구조를 더 잘 제어하고 데이터의 유용성을 극대화할 수 있습니다. 이러한 추가 유연성은 다음 두 단계를 통해 Attribution Reporting API에 도입할 수 있습니다.

  • 1단계: 유연한 이벤트 수준 구성의 라이트 버전. 2단계의 하위 집합
  • 2단계: 유연한 이벤트 수준 구성의 전체 버전

1단계: 라이트 버전의 유연한 이벤트 수준

Attribution-Reporting-Register-Source의 JSON에 다음 두 가지 선택적 매개변수를 추가합니다.

  • max_event_level_reports
  • event_report_windows
{
  ...
  // Optional. This is a parameter that acts across all trigger types for the
  // lifetime of this source. It restricts the total number of event-level
  // reports that this source can generate. After this maximum is hit, the
  // source is no longer capable of producing any new data. The use of
  // priority in the trigger attribution algorithm in the case of multiple
  // attributable triggers remains unchanged. Defaults to 3 for navigation
  // sources and 1 for event sources
  "max_event_level_reports": <int>,

  // Optional. Represents a series of time windows, starting at 0. Reports
  // for this source will be delivered an hour after the end of each window.
  // Time is encoded as seconds after source registration. If
  // event_report_windows is omitted, will use the default windows. This
  // field is mutually exclusive with the existing `event_report_window` field.
  // // End time is exclusive.
  "event_report_windows": {
    "start_time": <int>,
    "end_times": [<int>, ...]
  }
}

맞춤 구성 예시

이 구성 예시는 이전 보고 기간의 보고서 수신을 최적화하려는 개발자를 지원합니다.

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

2단계: 전체 버전의 유연한 이벤트 수준

1단계에서 추가한 매개변수 외에도 선택적 매개변수 trigger_specsAttribution-Reporting-Register-Source의 JSON에 추가합니다.

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

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

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

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

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

이 구성은 소스 등록별로 이벤트 수준 보고서의 출력 공간을 완전히 지정합니다. 모든 트리거 사양의 경우 다음을 완전히 지정합니다.

  • 일치 기준 집합:
    • 이 사양이 적용되는 특정 트리거 데이터. 이 소스는 trigger_specs에 지정된 trigger_data 값 중 하나가 있는 트리거에만 일치시킬 수 있습니다. 즉, 트리거가 이 소스와 일치하지만 trigger_data가 소스 구성의 값 중 하나가 아니라면 트리거가 무시됩니다.
    • 특정 트리거가 이 사양과 일치하는 경우(event_report_windows 사용). 앞서 언급한 두 가지 일치 기준에 실패하더라도 트리거는 여전히 집계 가능한 보고서의 소스와 일치할 수 있습니다.
  • 기여 산정 기간 내에 모든 트리거를 요약하고 버킷화하는 특정 알고리즘. 이렇게 하면 트리거가 특정 사양에 대해 합산되지만 버케팅된 값으로 보고되는 value 매개변수를 지정할 수 있습니다.

또한 트리거는 event_trigger_data 내 사전에 선택적 값 매개변수를 추가할 수 있도록 지원합니다.

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

모든 트리거 등록은 최대 1개의 트리거 사양과 일치하고 연결된 요약 값을 업데이트합니다. 대략적으로 트리거 시에는 다음과 같은 작업이 이루어집니다.

  • 전역 기여 분석 필터를 적용합니다.
  • 모든 트리거 사양의 경우 사양의 event_reporting_window를 통해 사양의 event_trigger_data를 평가하여 일치하는 항목을 찾습니다. 최상위 event_reporting_windows는 트리거 사양이 누락된 event_report_windows 하위 필드인 경우 기본값으로 작동합니다.
  • 첫 번째로 일치하는 사양이 기여 분석을 위해 선택되고 요약 값이 value씩 증가합니다.

사양의 event_report_window가 완료되면 요약 값을 버킷에 매핑하고 기여 분석 트리거 값으로 인한 요약 버킷의 모든 증분에 대한 이벤트 수준 보고서를 전송합니다. 보고서에는 trigger_summary_bucket이라는 추가 필드 하나가 포함됩니다.

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

현재 버전과 동일한 구성

다음은 각각 API 현재 이벤트 및 탐색 소스에 상응하는 구성입니다. 특히 탐색 소스의 경우 이는 동일한 epsilon 값을 유지하기 위해 이벤트 소스에 비해 노이즈 수준이 너무 높은 이유를 보여줍니다. 탐색 소스는 출력 공간이 훨씬 더 큽니다.

일부 매개변수를 기본값으로 설정하거나 프루닝할 수 있다는 점을 고려할 때 동등한 여러 구성이 있을 수 있습니다.

상응하는 이벤트 소스
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1],
    "event_report_windows": {
      "end_times": [<30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1],
  }],
  "max_event_level_reports": 1,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}
상응하는 탐색 소스
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1, 2, 3, 4, 5, 6, 7],
    "event_report_windows": {
      "end_times": [<2 days>, <7 days>, <30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1, 2, 3],
  }],
  "max_event_level_reports": 3,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}

맞춤 구성 예시

다음은 기본값 이외의 추가 구성입니다. 이러한 모든 예시에서 개발자가 얻는 장단점은 다음과 같습니다.

  • 노이즈 수준을 유지하기 위해 기본 구성의 일부 측정기준(#triggers, 트리거 데이터 카디널리티, #windows)을 줄여 다른 측정기준을 늘립니다.
  • 노이즈 수준을 낮추기 위해 기본 구성의 일부 측정기준(#triggers, 트리거 데이터 카디널리티, #windows)을 줄입니다.

트리거 값 버킷 보고

이 구성 예시는 하나의 보고 기간(예: 7일) 동안만 값 데이터에 맞게 최적화하여 더 적은 노이즈를 위해 보고 기간을 줄이는 개발자를 지원합니다. 이 예시에서 trigger_data를 0이 아닌 값으로 설정하는 모든 트리거는 기여 분석에 사용할 수 없습니다.

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

트리거는 합산되고 버케팅되는 value 필드 집합으로 등록될 수 있습니다. 예를 들어 값이 1, 3, 4인 소스 등록 후 7일 이내에 트리거가 3개 있는 경우입니다.

{ "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일 동안 전환이 1회 이상 발생했는지 확인하되 기본값보다 더 자주 보고서를 수신하려는 개발자를 지원합니다. 다시 말하지만, 이 예시에서 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 확장 프로그램의 다양한 사용 사례를 제안하는 것이 좋습니다. Google에서는 이러한 사용 사례를 위한 샘플 구성으로 이 설명 내용을 업데이트할 예정입니다.

리디렉션 없는 교차 네트워크 기여 분석

광고 기술은 여러 기여 분석 소스 트리거를 등록하고 교차 네트워크 기여 분석을 실행하기 위해 리디렉션을 사용해야 합니다. 이 기능은 네트워크 간에 리디렉션이 불가능한 교차 네트워크 기여 분석을 지원합니다. 자세히 알아보기

광고 기술은 파생 소스를 생성하기 위해 다른 광고 기술에서 등록한 소스를 기반으로 트리거 등록 응답에서 구성을 전송할 수 있습니다. 이렇게 파생된 소스는 기여 분석에 사용됩니다. 트리거에 파생된 소스 기여가 적용될 경우 집계 보고서가 생성됩니다. 파생된 소스에 대한 이벤트 보고서 생성은 지원되지 않습니다.

광고 기술은 등록된 소스의 aggregation_keys 중에서 파트너 광고 기술과 공유할 항목을 선택할 수 있습니다. 이러한 키는 소스 등록 헤더 Attribution-Reporting-Register-Source 아래에 있는 선택적 shared_aggregation_keys 필드에서 선언할 수 있습니다.

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

파생된 소스는 트리거 등록 헤더 Attribution-Reporting-Register-Trigger의 구성을 기반으로 생성됩니다.

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

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

다음은 예시 값이 추가된 버전입니다.

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

등록 헤더를 트리거하는 두 개의 새로운 선택적 필드가 추가됩니다. 이러한 필드는 낙찰된 광고 기술의 식별자를 집계 가능한 보고서 키에서 사용 설정합니다.

  • x_network_bit_mapping: 광고 기술 식별자 비트 매핑 등록 ID
  • x_network_data: 낙찰된 광고 기술의 x_network_bit_mapping 또는 트리거 키 조각을 사용한 오프셋(왼쪽 시프트)
예:
"Attribution-Reporting-Register-Trigger": {
  "attribution_config": [...],
  "aggregatable_trigger_data": [
    {
     "key_piece": "0x400",
     "source_keys": ["campaignCounts"]
      "x_network_data" : {
        "key_offset" : 12 // [64 bit unsigned integer]
      }
    }
    …
  ]
  …
  "x_network_bit_mapping": {
   // This mapping is used to generate trigger key pieces with AdTech identifier
   // bits. eg. If AdTechA's sources wins the attribution then 0x1 here will be
   // OR'd with the trigger key pieces to generate the final key piece.
    "AdTechA-enrollment_id": "0x1", // Identifier bits in hex for A
    "AdTechB-enrollment_id": "0x2"  // Identifier bits in hex for B
  }
  …
}

다음은 AdTechB 소스에 대한 보고서를 생성할 때 결과로 발생하는 트리거 키 조각 계산입니다.

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • AdtechB의 enrollment_id 값: 2 (010)(x_network_bit_mapping에서)
  • 결과 트리거 키 조각: 0x400 | 0x2 << 12 = 0x2400

제한사항

SDK 런타임의 진행 중인 기능 목록은 출시 노트를 참고하세요.

버그 및 문제 신고

여러분의 의견은 Android의 개인 정보 보호 샌드박스에서 매우 중요한 부분입니다. 발견한 문제나 Android의 개인 정보 보호 샌드박스 개선을 위한 아이디어가 있다면 알려 주세요.