Attribution Reporting API デベロッパー ガイド

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 サービス定義をデプロイする方法です。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
    

DP 10 以降のソース登録リクエストには、次のように Version ヘッダーが含まれます。

  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 ヘッダーが含まれます。

  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 ヘッダーが含まれます。

  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

暗号化されたフィールドと暗号化されていないフィールドの両方が、集計可能レポートに入力されます。暗号化されたレポートでは、集計サービスでテストを開始できますが、暗号化されていないフィールドからは、設定された 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 クライアントのデバイスまたはエミュレータを準備する手順は次のとおりです。

  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 コンシューマーは、登録ヘッダーに加えて、サーバー レスポンスとして HTTP リダイレクトを提供できるようになりました。HTTP リダイレクトには 302 ステータス コードと、追加登録のためにアクセスする次の URL を含む「Location」ヘッダーが含まれています。

最初のアクセスで指定された「destination」フィールドのみがデイジー チェーン全体で使用されます。アクセス回数の上限は、「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 では広告テクノロジーの整理方法が異なるため、ソースとトリガーがブラウザで発生した場合に、それらを登録するための新しい 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 フィールドの内容を 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 をデプロイします。テストを簡単に行うために、MeasurementAdTechServerSpecMeasurementAdTechServer のサンプル プロジェクトを参照してください。

テストする機能

  • アトリビューション ソースとコンバージョン トリガーの登録を行います。サーバーサイド エンドポイントが正しい形式で応答することを確認します。
  • レポートジョブを実行します。
  • テストサーバーのバックエンドまたはコンソールで、レポートの配信を確認します

今後の新機能

柔軟なイベントレベルの構成

ユーティリティ テストを開始するには、イベントレベル レポートのデフォルト構成をおすすめしますが、すべてのユースケースに適しているとは限りません。Attribution Reporting API では、より柔軟なオプションの構成がサポートされるようになります。これにより、広告テクノロジーはイベントレベル レポートの構造をより細かく管理し、データの有用性を最大限に高めることができます。この柔軟性の向上は、次の 2 つのフェーズで Attribution Reporting API に導入されます。

  • フェーズ 1: ライトで柔軟なイベントレベルの構成(フェーズ 2 のサブセット)。
  • フェーズ 2: 柔軟なイベントレベルの構成の完全版。

フェーズ 1: Lite の柔軟なイベントレベル

次の 2 つのオプション パラメータを 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 を使用)。前述の 2 つの一致条件に失敗した場合でも、トリガーが集計可能レポートのソースと照合される場合があります。
  • アトリビューション期間内のすべてのトリガーを要約してバケット化するための特定のアルゴリズム。これにより、トリガーは、特定の仕様に対して合計されるものの、バケット化された値としてレポートされる value パラメータを指定できます。

トリガーでは、event_trigger_data 内の辞書にオプションの value パラメータを追加することもできます。

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

トリガー登録はすべて、最大で 1 つのトリガー仕様と照合され、関連するサマリー値を更新します。おおまかには、トリガー時に以下を行います。

  • グローバル アトリビューション フィルタを適用する。
  • トリガー仕様ごとに、仕様の event_reporting_window を使用して仕様の event_trigger_data を評価し、一致を探します。トリガー仕様のいずれかで event_report_windows サブフィールドが欠落している場合、トップレベルの event_reporting_windows はデフォルト値として機能します。
  • 最初に一致した仕様がアトリビューションに選択され、概要値が value ずつ増加します。

仕様の event_report_window が完了すると、サマリー値がバケットにマッピングされ、アトリビューションされたトリガー値によって発生したサマリー バケットの増分ごとにイベントレベルのレポートが送信されます。レポートには trigger_summary_bucket という追加フィールドがあります。

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

現在のバージョンと同等の構成

以下は、それぞれ、API の現在のイベントとナビゲーションのソースで同等の設定です。特にナビゲーション ソースの場合、同じイプシロン値を維持するためにイベントソースに比べてノイズレベルが非常に高い理由がわかります。ナビゲーション ソースの出力スペースはかなり大きくなります。

一部のパラメータをデフォルトとして設定したり、プルーニングしたりできるため、同等の構成が複数存在する場合があります。

同等のイベントソース
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1],
    "event_report_windows": {
      "end_times": [<30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1],
  }],
  "max_event_level_reports": 1,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}
同等のナビゲーション ソース
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1, 2, 3, 4, 5, 6, 7],
    "event_report_windows": {
      "end_times": [<2 days>, <7 days>, <30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1, 2, 3],
  }],
  "max_event_level_reports": 3,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}

カスタム構成の例

以下は、デフォルト以外のその他の構成です。この例のすべてで、デベロッパーには次のようなトレードオフがあります。

  • デフォルト構成のディメンション(# トリガー、トリガーデータ カーディナリティ、#ウィンドウ)の一部を減らし、別のディメンションを増やしてノイズレベルを維持する
  • デフォルト構成の一部のディメンション(#triggers、トリガーデータ カーディナリティ、#ウィンドウ)を減らしてノイズレベルを下げる

トリガー値バケットを報告する

この構成例は、1 つのレポート期間(7 日間など)のみの価値データを重視して最適化し、レポート期間を短くすることでノイズを軽減したいデベロッパーをサポートします。この例では、trigger_data を 0 以外の値に設定するトリガーはアトリビューションの対象外です。

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

value フィールド セットでトリガーを登録し、合計してバケット化できます。たとえば、ソースの登録から 7 日以内に、値が 1、3、4 の 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 はカウントするように設定されているため、トリガー値は無視されます。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 拡張機能のさまざまなユースケースを提案することをおすすめします。この説明資料では、それらのユースケースのサンプル構成を更新します。

リダイレクトなしのクロス ネットワーク アトリビューション

広告テクノロジーは、リダイレクトを使用して複数のアトリビューション ソース トリガーを登録し、クロスネットワーク アトリビューションを行う必要があります。この機能は、ネットワークをまたいでリダイレクトを実行できないクロスネットワーク アトリビューションをサポートするのに役立ちます。詳細

広告テクノロジーは、派生ソースを生成するために他の広告テクノロジーによって登録されたソースに基づいて、トリガー登録レスポンスで構成を送信できます。その後、これらの派生ソースがアトリビューションに使用されます。トリガーが派生ソースに関連付けられると、集計レポートが生成されます。派生ソースのイベント レポートの生成はサポートされていません。

広告テクノロジーは、パートナー広告テクノロジーと共有する登録済みソースの 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"
    }
  ]

トリガー登録ヘッダーに 2 つの新しいオプション フィールドが追加されました。これらのフィールドにより、集計可能レポートのキーで落札広告テクノロジーの ID が有効になります。

  • x_network_bit_mapping: 登録 ID と広告テクノロジー識別子のビットマッピング
  • x_network_data: 落札広告テクノロジーの x_network_bit_mapping OR オペレーションのトリガーのキーピースとのオフセット(左シフト)
例:
"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
  }
  …
}

アドテック B のソースに関するレポートを生成した場合のトリガーのキーピースの計算は次のようになります。

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • アドテック B の enrollment_id 値: 2 (010)x_network_bit_mapping から)
  • 結果のトリガーのキーピース: 0x400 | 0x2 << 12 = 0x2400

制限事項

SDK ランタイムに関する開発中の機能の一覧については、リリースノートをご覧ください。

バグと問題を報告する

皆様からのフィードバックは、Android 版プライバシー サンドボックスに欠かせない要素です。問題が見つかった場合や Android 版プライバシー サンドボックスを改善するためのアイデアがありましたらお知らせください。