Attribution Reporting API 開發人員指南

Stay organized with collections Save and categorize content based on your preferences.

Attribution Reporting API 的設計宗旨是要避免仰賴跨服務的使用者 ID,進一步保護使用者隱私,同時顧及重要使用情境,支援應用程式的歸因與轉換評估功能。本開發人員指南說明如何設定和測試 Attribution Reporting API,透過呼叫登錄廣告點擊、觀看和轉換事件的相關觸發條件和來源以登錄此類事件。

本指南說明如何設定伺服器端點,以及建構呼叫這些服務的用戶端應用程式。要進一步瞭解 Attribution Reporting API 的整體設計,請參閱設計提案

關鍵字詞

  • 「歸因來源」是指點擊次數或觀看次數。
  • 「觸發條件」是指可歸因於轉換的事件。
  • 「報表」包含觸發條件和對應歸因來源的相關資料。為回應觸發事件,系統會傳送這些報表。Attribution Reporting API 支援事件層級報表匯總報表

事前準備

如要使用 Attribution Reporting API,請完成以下各節列出的伺服器端和用戶端工作。

設定 Attribution Reporting API 端點

Attribution Reporting API 需要一組您可以使用測試裝置或模擬器存取的端點。為下列伺服器端工作各建立一個端點:

您可以透過下列幾種方式設定必要的端點:

  • 設定和執行最快的方法,是將程式碼範例存放區中的 OpenAPI v3 服務定義部署至模擬或微服務平台。您可以使用 PostmanPrism 或任何其他接受此格式的模擬伺服器平台。部署每個端點,並持續追蹤應用程式中使用的 URI。 如要驗證報表提交,請參閱先前對模擬或無伺服器平台發出的呼叫。
  • 使用以 Spring Boot 為基礎的 Kotlin 範例執行您自己的獨立伺服器。在您的雲端供應商或內部基礎架構中部署此伺服器。
  • 使用服務定義做為將端點整合至現有系統的範例。

接受來源登錄

此端點應可從與以下類似的 URI 定址:

https://adtech.example/attribution_source

當用戶端應用程式登錄歸因來源時,就會提供此伺服器端點的 URI。然後 Attribution Reporting API 會發出要求,並包含下列其中一個標頭:

針對點選事件:

Attribution-Reporting-Source-Info: navigation

針對檢視事件:

Attribution-Reporting-Source-Info: event

設定伺服器端點以回應以下項目:

// Metadata associated with attribution source.
Attribution-Reporting-Register-Source: {
  "destination": "[app package name]",
  "source_event_id": "[64 bit unsigned integer]",
  "expiry": "[64 bit signed integer]",
  "source_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
// reports.
Attribution-Reporting-Register-Aggregatable-Source:
[{
  "id": "[key name]",
  "key_piece": "[key piece value]",
 },
 ..
]
// Specify additional ad tech URLs to register this source with.
Attribution-Reporting-Redirects: <Ad tech partner URIs; comma-separated>

以下是新增範例值的範例:

Attribution-Reporting-Register-Source: {
  "destination": "android-app://com.example.advertiser",
  "source_event_id": "234",
  "expiry": "60000",
  "source_priority": "5",
  "filter_data": {
    "product_id": ["1234"]
  }
}
Attribution-Reporting-Register-Aggregatable-Source:
[{
// Generates a "0x159" key piece named (low order bits of the key) for the key
// named "campaignCounts".
  "id": "campaignCounts",
// User saw an ad from campaign 345 (out of 511).
  "key_piece": "0x159",
},
{
// Generates a "0x5" key piece (low order bits of the key) for the key named "geoValue"
  "id": "geoValue",
// Source-side geo region = 5 (US), out of a possible ~100 regions.
  "key_piece": "0x5",
}]
Attribution-Reporting-Redirects: https://adtechpartner1.example?their_ad_click_id=567, https://adtechpartner2.example?their_ad_click_id=890

如果 Attribution-Reporting-Redirects 包含廣告技術合作夥伴的 URI,Attribution Reporting API 就會向每個 URI 發出類似的要求。每個廣告技術合作夥伴都必須使用以下標頭設定回應的伺服器:

Attribution-Reporting-Register-Source: {
  "destination": "[app package name]",
  "source_event_id": "[64 bit unsigned integer]",
  "expiry": "[64 bit signed integer]",
  "source_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-Reporting-Register-Aggregatable-Source:
[{
  "id": "[key name]",
  "key_piece": "[key piece value]",
 },
 ..
]
// The Attribution-Reporting-Redirects header is ignored for ad tech partners.

接受轉換觸發條件登錄

此端點應可從與以下類似的 URI 定址:

https://adtech.example/attribution_trigger

當用戶端應用程式登錄轉換觸發事件時,就會提供該伺服器端點的 URI。然後 Attribution Reporting API 會發出要求,並包含下列其中一個標頭:

設定伺服器端點以回應以下項目:

// Metadata associated with trigger.
Attribution-Reporting-Register-Event-Trigger:
[{
    // 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]",
    "trigger_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.

    // Note: "source_type" can be used as a key to filter based on the source's
    // type "navigation" or "event". The first Event-Trigger that matches (based
    // on the filters/not_filters) will be used for report generation. If none
    // of the event-trigger matches, 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"],
    }
}]

// Specify a list of dictionaries that generates aggregation keys.
Attribution-Reporting-Register-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.
Attribution-Reporting-Register-Aggregatable-Values:
[
   // Each source event can contribute a maximum of L1 = 2^16 to the aggregate
   // histogram.
  {
   "[key_name]": [value]
  }
]
// Specify additional Adtech URLs to register this trigger with.
Attribution-Reporting-Redirects: <Ad tech partner URIs, comma-separated>

以下是新增範例值的範例:

Attribution-Reporting-Register-Event-Trigger: [{
    "trigger_data": "1122", // Returns 010 for CTCs and 0 for VTCs in reports.
    "trigger_priority": "3",
    "deduplication_key": "3344"
    "filters": {
      "product_id": ["1234"],
      "source_type": ["event"]
    }
},
{
    "trigger_data": "4", // Returns 100 for CTCs and 0 for VTCs in reports.
    "trigger_priority": "3",
    "deduplication_key": "3344"
    "filters": {
      "product_id": ["1234"],
      "source_type": ["navigation"]
    }
}
]
Attribution-Reporting-Register-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
  // will take up 9 bits in the resulting key.
    "key_piece": "0x400",// Conversion type purchase = 2
    // Apply this key piece to:
    "source_keys": ["campaignCounts"]
  },
  {
  // 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 will take up 7 bits of space in the resulting key.
  "key_piece": "0xA80",
  // Apply this key piece to:
  "source_keys": ["geoValue", "nonMatchingIdsListedHereAreIgnored"]
  }
]
Attribution-Reporting-Register-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
  }
]
Attribution-Reporting-Redirects:https://adtechpartner.example?app_install=567

如果 Attribution-Reporting-Redirects 包含廣告技術合作夥伴的 URI,Attribution Reporting API 就會向每個 URI 發出類似的要求。每個廣告技術合作夥伴都必須使用以下標頭設定回應的伺服器:

// Metadata associated with trigger.
Attribution-Reporting-Register-Event-Trigger:
[{
    // 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]",
    "trigger_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.
    // Note: "source_type" can be used as a key to filter based on the source's
    // type "navigation" or "event".
    // The first Event-Trigger that matches (based on the filters/not_filters)
    // will be used for report generation. If none of the event-trigger
    // matches, 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"],
    }
}]

// Specify a list of dictionaries that generates aggregation keys.
Attribution-Reporting-Register-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.
Attribution-Reporting-Register-Aggregatable-Values:
[
   // Each source event can contribute a maximum of L1 = 2^16 to the aggregate
   // histogram.
  {
   "[key_name]": [value]
  }
]
// The Attribution-Reporting-Redirects header is ignored for ad tech partners.

接受事件層級報表

此端點應可從 URI 中定址。在 Android 版 Privacy Sandbox 提供註冊自己的回傳 URI 的支援之前,系統會透過接受來源登錄和觸發條件登錄使用的伺服器 eTLD+1 推斷 URI。針對接受來源登錄接受觸發條件登錄的端點使用範例 URI 時,此端點的 URI 如下:

https://adtech.example/.well-known/attribution-reporting/report-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"
}

接受可匯總的報表

此端點應可從 URI 中定址。在 Android 版 Privacy Sandbox 提供註冊自己的回傳 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 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 用戶端

用戶端應用程式會登錄歸因來源和觸發條件,並產生事件層級和可匯總的報表。如要準備 Android 用戶端裝置或模擬器以使用 Attribution Reporting API,請執行以下操作:

  1. 在 Android 裝置上為 Privacy Sandbox 設定開發環境
  2. 在支援的裝置中安裝系統映像檔,或設定支援 Android 版 Privacy Sandbox 的模擬器

登錄廣告事件

您的應用程式將必須在來源和轉換發生時進行登錄,以確保能正確回報這些資料。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 就可能包含其他廣告技術端點的重新導向。如要進一步瞭解適用於重新導向的限制和規則,請參閱技術提案

登錄轉換觸發事件

如要登錄轉換觸發事件,請在應用程式中呼叫 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 重新導向。如要進一步瞭解重新導向適用的限制和規則,請參閱技術提案

產生並提交報表

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

強制提交報表

即使強制執行報表工作,系統仍會依照排定的提交時間傳送報表,範圍從數小時到數天都有。為了進行測試,您可以提前將裝置系統時間延後到排定的延遲時間,讓系統開始傳送報表。

驗證伺服器報表

傳送報表後,請檢查收到的報表、適用的伺服器記錄 (如模擬伺服器記錄或自訂系統),以驗證提交。

測試

為協助您開始使用 Attribution Reporting API,您可以使用 GitHub 上的 MeasurementSampleApp 專案。此範例應用程式會示範歸因來源登錄和觸發條件登錄。

如為伺服器端點,請考慮下列參考資源或您的自訂解決方案:

必要條件

使用測試裝置或模擬器,在遠端端點上部署模擬 API。為方便測試,請參閱 MeasurementAdTechServerSpecMeasurementAdTechServer 範例專案。

要測試的功能

  • 歸因來源和轉換觸發條件登錄。檢查伺服器端端點是否以正確的格式回應。
  • 執行報表工作
  • 在測試伺服器的後端或主控台驗證報表提交

限制

如需 SDK 執行階段的進行中功能清單,請參閱「版本資訊」。

回報錯誤和問題

您的意見回饋對 Android 版 Privacy Sandbox 至關重要!如果您發現了任何問題,或希望針對 Android 版 Privacy Sandbox 提出改進意見,請告訴我們