Android 版プライバシー サンドボックスのドキュメントをご覧になる際は、[デベロッパー プレビュー] または [ベータ版] ボタンで対象のプログラム バージョンを選択してください(手順が異なる場合があります)。
Attribution Reporting API は、クロスパーティ ユーザー ID への依存をなくすことでユーザーのプライバシーを向上させ、アプリを対象としたアトリビューションとコンバージョン測定の主要なユースケースをサポートするように設計されています。このデベロッパー ガイドでは、Attribution Reporting API を設定およびテストし、広告のクリック、ビュー、コンバージョンの登録を、このようなイベントに関連するトリガーとソースを登録するメソッドを呼び出すことによって行う方法を説明します。
このガイドでは、サーバー エンドポイントをセットアップし、これらのサービスを呼び出すクライアント アプリを作成する方法について説明します。Attribution Reporting API の全体的な設計について詳しくは、設計案をご覧ください。
主な用語
- 「アトリビューション ソース」とは、クリックまたはビューのことです。
- 「トリガー」とは、コンバージョンに関連付けることができるイベントです。
- 「レポート」には、トリガーと対応するアトリビューション ソースに関するデータが含まれています。このレポートは、トリガー イベントに応答して送信されます。Attribution Reporting API は、イベントレベル レポートと集計可能レポートをサポートしています。
始める前に
Attribution Reporting API を使用するには、以下のセクションに記載されているサーバー側のタスクとクライアント側のタスクを完了してください。
Attribution Reporting API 用エンドポイントのセットアップ
Attribution Reporting API には、テストデバイスまたはエミュレータからアクセスできる一連のエンドポイントが必要です。次のサーバー側タスクごとに 1 つのエンドポイントを作成します。
- アトリビューション ソース(ビューまたはクリック)を登録する
- トリガー(コンバージョン)を登録する
- イベントレベル レポートを受け入れる
- 集約可能レポートを受け入れる
必要なエンドポイントをセットアップする方法は、いくつかあります。
- 最も早いのは、サンプルコード リポジトリからモックまたはマイクロサービスのプラットフォームに 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
以下を返すようにサーバー エンドポイントを設定します。
// 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]",
"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": [{
"id": "[key name]",
"key_piece": "[key piece value]",
},
..
]
"debug_key": "[64-bit unsigned integer]"
}
// 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": "60000",
"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".
"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-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]",
"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": [{
"id": "[key name]",
"key_piece": "[key piece value]",
},
..
]
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.
コンバージョン トリガーの登録を受け入れる
このエンドポイントは、次の例のような URI からアドレス指定可能である必要があります。
https://adtech.example/attribution_trigger
クライアント アプリがトリガー イベントを登録するとき、このサーバー エンドポイントの URI を指定します。そして、Attribution Reporting 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
// 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 "filters" and
// "not_filters", is used for report generation. If there are no matches,
// no report is generated.
"filters": {
"[key name 1]": ["key1 value 1", "key1 value 2"],
// If a key is missing from "filters" or the attribution source's
// "filter_data", it isn't 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 the attribution source's
// "filter_data", it isn't used during matching.
"[key name 2]": ["key2 value 1", "key2 value 2"],
}
}],
// Specify a list of dictionaries that generates aggregation keys.
"aggregabtable_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" and "not_filters" are optional fields, similar to the 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
// that they're generated.
"aggregabtable_values": [
// Each source event can contribute a maximum of L1 = 2^16 to the aggregate
// histogram.
{
"[key_name]": [value]
},
..
]
"debug_key": "[64-bit unsigned integer]"
}
// 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
}
]
}
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]",
// "filters" and "not_filters" are optional fields which allow configuring
// for configuring different event trigger data based on the attribution]
// source's "filter_data". Note that "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 "filters" and
// "not_filters", is used for report generation. If there are no matches,
// no report is generated.
"filters": {
"[key name 1]": ["key1 value 1", "key1 value 2"],
// If a key is missing from "filters" or the attribution source's
// "filter_data", it isn't 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 the attribution source's
// "filter_data", it isn't 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" and "not_filters" are optional fields, similar to the 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 は、ソースの登録とトリガーの登録の受け入れに使用されるサーバーの eTLD+1 から推定されます)。このエンドポイントの 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
現在、暗号化されたフィールドと暗号化されていないフィールドの両方が集計可能レポートに入力されます。暗号化されたレポートでは、集計サービスを使用してテストを開始できます。一方、暗号化されていないフィールドでは、設定された Key-Value ペアがどのようにデータを構造化しているかを分析できます。
次の形式を使用する 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 クライアントのデバイスまたはエミュレータを準備する手順は次のとおりです。
- Android 版プライバシー サンドボックス用に開発環境をセットアップします。
- サポート対象のデバイスにシステム イメージをインストールするか、Android 版プライバシー サンドボックスのサポートを含むエミュレータをセットアップします。
次の ADB コマンドを実行して、Attribution Reporting API へのアクセスを有効にします(この API はデフォルトで無効になっています)。
adb shell device_config put adservices ppapi_app_allow_list \"*\"
アプリで Attribution Reporting API を使用するには、次のコード スニペットに示すように
ACCESS_ADSERVICES_ATTRIBUTION
権限を追加して、広告サービス構成を作成する必要があります。<uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
マニフェストで参照される広告サービス XML リソースを指定します(
res/xml/ad_services_config.xml
など)。広告サービスの権限と SDK のアクセス制御の詳細をご覧ください。<property android:name="android.adservices.AD_SERVICES_CONFIG" android:resource="@xml/ad_services_config" />
マニフェストの
<application>
要素で広告サービス構成を参照します。<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, expiry
、source_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 へのリダイレクトを含めることができます。リダイレクトに適用される制限やルールは、技術提案で詳しく説明しています。
アプリとウェブにわたる測定を登録する
ソースからトリガーへのユーザー ジャーニーにアプリとブラウザの両方が関与する場合、広告イベントの登録の実装には微妙な違いが生じます。ユーザーにアプリで広告が表示され、そのユーザーがコンバージョンのためにブラウザにリダイレクトされる場合、ソースはアプリにより登録され、コンバージョンはウェブブラウザにより登録されます。同様に、ユーザーがウェブブラウザから開始し、コンバージョンのためにアプリにリダイレクトされた場合、ブラウザはソースを登録し、アプリはコンバージョンを登録します。
ウェブと Android では広告テクノロジーの整理方法が異なるため、ソースとトリガーがブラウザで発生した場合に、それらを登録するための新しい 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);
レポートを生成して配信する
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
フィールドの内容を 2 ステップでデコードする例を示します。まず 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);
});
テスト
Attribution Reporting API の使用を開始する際は、GitHub の MeasurementSampleApp プロジェクトを使用してください。このサンプルアプリでは、アトリビューション ソースの登録とトリガーの登録を行います。
サーバー エンドポイントについては、次のリファレンス資料やカスタム ソリューションを検討してください。
- MeasurementAdTechServerSpec には、サポートされているモックまたはマイクロサービスのプラットフォームにデプロイ可能な OpenAPI サービス定義が含まれています。
- MeasurementAdTechServer には、Google App Engine 用の Spring Boot アプリをベースにしたモックサーバーのリファレンス実装が含まれています。
前提条件
テストデバイスまたはエミュレータからアクセス可能なリモート エンドポイントにモック API をデプロイします。テストを簡単に行うために、MeasurementAdTechServerSpec と MeasurementAdTechServer のサンプル プロジェクトを参照してください。
テストする機能
- アトリビューション ソースとコンバージョン トリガーの登録を行います。サーバーサイド エンドポイントが正しい形式で応答することを確認します。
- レポートジョブを実行します。
- テストサーバーのバックエンドまたはコンソールで、レポートの配信を確認します。
制限事項
SDK ランタイムに関する開発中の機能の一覧については、リリースノートをご覧ください。
バグと問題を報告する
皆様からのフィードバックは、Android 版プライバシー サンドボックスに欠かせない要素です。問題が見つかった場合や Android 版プライバシー サンドボックスを改善するためのアイデアがありましたらお知らせください。