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
    

以下を返すようにサーバー エンドポイントを設定します。

// 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 がリクエストを実行し、その際に次のいずれかのヘッダーを含めます。

以下を返すようにサーバー エンドポイントを設定します。

// 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

次の形式を使用する 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 クライアントのデバイスまたはエミュレータを準備する手順は次のとおりです。

  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: 柔軟なイベントレベルのライト版

Attribution-Reporting-Register-Source の JSON に、次の 2 つのオプション パラメータを追加します。

  • 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 内の辞書へのオプションの値パラメータ追加もサポートします。

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

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

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

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

{
  ...
  "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、トリガーデータ カーディナリティ、#windows)を減らし、別のディメンションを増してノイズレベルを維持する
  • デフォルト設定の一部のディメンション(#triggers、トリガーデータ カーディナリティ、#windows)を減らし、ノイズレベルを低減する

レポート トリガー値バケット

この設定例では、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 から広告テクノロジー 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
  }
  
}

以下は、AdTechB のソースに関するレポートを生成した場合のトリガー キーピースの計算結果です。

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • AdTechB の enrollment_id 値: 2 (010)x_network_bit_mapping から)
  • 最終的なトリガー キーピース: 0x400 | 0x2 << 12 = 0x2400

制限事項

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

バグと問題を報告する

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