Protected App Signals デベロッパー ガイド

デベロッパーが Protected App Signals API のテストを開始できるように、このドキュメントでは、この API サーフェス内のすべての API について説明し、テスト環境の設定方法の詳細、構成とスクリプトの例を紹介します。

バージョン履歴

2024 年 1 月

PAS MVP リリースをサポートするデベロッパー ガイドの最初のリリース

2024 年 3 月

Android API の M-2024-05 リリースとサーバー側コンポーネントの 2024 年 4 月リリースをサポートするための API の変更。主な変更点:

  • On-Device API に必要な権限の詳細を追加しました
  • オンデバイス シグナルの割り当て管理に関する詳細を追加しました
  • generateBid 署名を更新し、コンテキスト広告の取得と下り(外向き)のサポートに関連する変更を追加しました。
  • 下り(外向き)のサポートを含む reportWin のドキュメントを更新しました
  • Ad Retrieval API のドキュメントを更新し、BYOS 広告取得のサポートと広告取得 UDF を文書化しました

API の概要

Protected Signals API サーフェスには、システムごとに異なる API のサブセットが含まれています。

  • Android API:
    • 次の要素で構成される Signal Curation API
    • Update Signals API
    • Signals Encoding API
    • Protected Auction Support API: Protected App Signals を使用して、入札とオークション(B&A)サーバーで Protected Auction を実行するために SDK で使用されます。
  • サーバーサイド API:
    • Protected Auction API: 入札とオークション サーバーで実行される一連の JS スクリプト。この API を使用すると、販売者と購入者は Protected Auction を実装するロジックを記述できます。
    • Ad Retrieval API: 購入者の入札サーバーで入手可能なコンテキスト情報とユーザー情報に基づき、広告候補のリストを提供します。

Android クライアント

クライアント サイドでは、Protected App Signals サーフェスは 3 つの異なる API で構成されています。

  • Update Signals: デバイス上でシグナルのキュレーションを可能にする Android System API。
  • Signals Encoding: オークション中にサーバーに送信するシグナルを準備する JavaScript API。
  • Protected Auction Support: 入札とオークション サーバーでの Protected Auction の実行をサポートする API。この API は、Protected App Signals に固有のものではなく、Protected Audience API のオークションをサポートするためにも使用されます。

Update Signals API

Update Signals API は、購入者に代わってユーザーとアプリに関連するシグナルを登録する機能を広告テクノロジーに提供します。API は委任モデルで動作します。呼び出し元は、フレームワークによるシグナルの取得元となる URI と、オークションで使用するシグナルをエンコードするロジックを提供します。

API には android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS 権限が必要です。

updateSignals() API は、追加または削除するシグナルと、オークション用にシグナルを準備する方法を記述した JSON オブジェクトを URI から取得します。

Executor executor = Executors.newCachedThreadPool();
ProtectedSignalsManager protectedSignalsManager
     =  ProtectedSignalsManager.get(context);

// Initialize a UpdateSignalsRequest
UpdateSignalsRequest updateSignalsRequest = new
  UpdateSignalsRequest.Builder(Uri.parse("https://example-adtech1.com/signals"))
      .build();

OutcomeReceiver<Object, Exception> outcomeReceiver = new OutcomeReceiver<Object, Exception>() {
  @Override
  public void onResult(Object o) {
    //Post-success actions
  }

  @Override
  public void onError(Exception error) {
    //Post-failure actions
  };

// Call updateSignals
protectedSignalsManager.updateSignals(updateSignalsRequest,
    executor,
    outcomeReceiver);

プラットフォームは、リクエストで指定された URI に HTTP リクエストを行い、シグナルの更新を取得します。レスポンスには、シグナルの更新とともに、未加工のシグナルをエンコードされたペイロードに変換するためのエンコード ロジックをホストするエンドポイントを含めることができます。シグナルの更新は JSON 形式であることが期待され、次のキーを含めることができます。

JSON オブジェクトの最上位キーは、次の 5 つのコマンドのいずれかに対応する必要があります。

キー

説明

put

新しいシグナルを追加し、同じキーで既存のシグナルを上書きします。この値

は JSON オブジェクトで、キーは追加するキーに対応する Base 64 文字列であり、値は追加する値に対応する Base 64 文字列です。

append

時系列のシグナルに新しいシグナルを付加します。時系列のサイズが所定の最大値を超えた場合には、最も古い

シグナルを削除して、新しいシグナルのスペースを確保します。この値は JSON オブジェクトで、キーは追加先のキーに対応する Base 64 文字列で、値は「values」と「maxSignals」の 2 つのフィールドを持つオブジェクトです。

「values」: 時系列に追加するシグナル値に対応する Base 64 文字列のリスト。

「maxSignals」: この時系列で許可される値の最大数。なお、

キーに関連付けられている現在のシグナルの数が maxSignals を超えると、最も古いシグナルが削除されます。put によって追加されたキーに追記できます。最大数を超える値を追加するとエラーになります。

put_if_not_present

同じキーを持つ既存のシグナルがない場合にのみ、新しいシグナルを追加します。この値は、追加するキーに対応する Base 64 文字列である JSON オブジェクトであり、値は追加する値に対応する Base 64 文字列です。

remove

キーのシグナルを削除します。この値は、削除する必要があるシグナルのキーに対応する Base 64 文字列のリストです。

update_encoder

エンドポイントを更新するアクションと、エンコード ロジックを取得できる URL を

提供します。更新アクションを提供するためのサブキーは「action」で、

現在サポートされている値は「REGISTER」のみです。これを使って、エンコーダ エンドポイントを初めて指定した場合は登録するか、または既存のエンコーダ エンドポイントがある場合は新しく指定したエンドポイントで上書きします。「REGISTER」アクションにはエンドポイントが必要です。エンコーダ エンドポイントを指定するためのサブキーは「endpoint」で、

値はエンドポイントの URI 文字列です。

サンプル JSON リクエストは次のようになります。

{
    "put": {
        "AAAAAQ==": "AAAAZQ==",
        "AAAAAg==": "AAAAZg=="
    },
    "append": {
        "AAAAAw==": {
            "values": [
                "AAAAZw=="
            ],
            "max_signals": 3
        }
    },
    "put_if_not_present": {
        "AAAABA==": "AAAAaQ==",
        "AAAABQ==": "AAAAag=="
    },
    "update_encoder": {
        "action": "REGISTER",
        "endpoint": "https://adtech1.com/Protected App Signals_encode_script.js"
    }
}

シグナルのオンデバイス割り当ては 10 ~ 15 KB 程度です。割り当てを超過すると、PPAPI は FIFO 戦略を使用してシグナルを強制排除します。エビクション プロセスでは、エビクションの頻度を減らすために、短時間の割り当てをわずかに超えることができます。

Signals Encoding API

購入者は、デバイスに保存されたシグナルをエンコードして、Protected Auction 中にサーバーに送信する JavaScript 関数を提供する必要があります。購入者は、UpdateSignal API リクエストに対するレスポンスに「update_encoder」キーを使用してフェッチできる URL を追加することで、このスクリプトを提供できます。スクリプトのシグネチャは次のとおりです。

function encodeSignals(signals, maxSize) {
  let result = new Uint8Array(maxSize);
  // first entry will contain the total size
  let size = 1;
  let keys = 0;
  
  for (const [key, values] of signals.entries()) {
    keys++;
    // In this encoding we only care about the first byte
    console.log("key " + keys + " is " + key)
    result[size++] = key[0];
    result[size++] = values.length;
    for(const value of values) {
      result[size++] = value.signal_value[0];
    }
  }
  result[0] = keys;
  
  return { 'status': 0, 'results': result.subarray(0, size)};
}

signals パラメータは、サイズ 4 の UInt8Arrays 形式のキーから Protected App Signals オブジェクトのリストへのマップです。各 Protected App Signals オブジェクトには、次の 3 つのフィールドがあります。

  • signal_value: シグナルの値を表す UInt8Array。
  • creation_time: シグナルの作成時刻を表す数値(エポック秒単位)。
  • package_name: シグナルを作成したパッケージの名前を表す文字列。

maxSize パラメータは、出力の最大許容配列サイズを示す数値です。

この関数は、次の 2 つのフィールドを持つオブジェクトを出力します。

  • status: スクリプトが正常に実行された場合は 0 にする必要があります。
  • results: maxSize 以下の長さの UInt8Array にする必要があります。この配列はオークション中にサーバーに送信され、prepareDataForAdRetrieval スクリプトによって準備されます。

エンコードにより、広告テクノロジーは特徴量エンジニアリングの最初のステージが得られます。ここでは、独自のカスタム ロジックに基づいて、未加工のシグナルを連結バージョンに圧縮するなどの変換を実行できます。高信頼実行環境(TEE)で Protected Auction が実行される場合、広告テクノロジーのカスタム ロジックに、エンコードによって生成されたシグナル ペイロードへの読み取りアクセス権が付与されます。購入者の B&A TEE で実行されるカスタム ロジックはユーザー定義関数(UDF)と呼ばれ、エンコードされたシグナルやパブリッシャー アプリが提供するその他のコンテキスト シグナルにアクセスし、広告選択(広告の取得と入札)を行います。

シグナル エンコード

登録されたシグナルを使用してエンコード ロジックを提供した購入者は、1 時間ごとにシグナルがオークション ペイロードにエンコードされます。オークション ペイロードのバイト配列はデバイス上で保持され、暗号化され、広告選択データの一部として販売者によって収集され、Protected Auction の一部として組み込まれます。テストのために、次のコマンドを実行して、1 時間ごとの周期外でこのエンコードをトリガーできます。

adb shell cmd jobscheduler run -f com.google.android.adservices.api 29
エンコーダ ロジックのバージョニング

広告テクノロジーのカスタム エンコーダ ロジックをダウンロードするリクエストが行われると、広告テクノロジー エンドポイントはレスポンス ヘッダーのバージョン番号で応答できます。このバージョンは、デバイス上のエンコーダ ロジックとともに保持されます。未加工のシグナルがエンコードされると、エンコードされたペイロードはエンコードに使用されたバージョンとともに保持されます。このバージョンは、Protected Auction の際に B&A サーバーにも送信されるため、広告テクノロジーはバージョンに基づいて入札とエンコードのロジックを調整できます。

Response header for providing encoder version : X_ENCODER_VERSION

Protected Auction Support API

デバイス側では、Protected App Signals のオークションの実行は、Protected Audience のオークションの実行と同じです

入札およびオークション サービス

サーバーサイド API には次のものがあります。

  • Protected Auction API: 購入者と販売者が所有する B&A コンポーネントにデプロイできる一連の JS 関数または UDF で、入札とオークション ロジックを決定できます。
  • Ad Retrieval API: 購入者は、Protected App Signal オークションの広告候補群を提供する REST エンドポイントを実装することで、この API を実装できます。

Protected Auction API

Protected Auction API は、購入者と販売者がオークションと入札のロジックを実装するために使用できる JS API または UDF で構成されています。

購入者の広告テクノロジー UDF
prepareDataForAdRetrieval UDF

Protected App Signals を使用して TEE 広告取得サービスから広告候補を取得する前に、購入者は Protected App シグナルとその他の販売者提供データをデコードして準備する必要があります。購入者の prepareDataForAdRetrieval UDF 出力は広告取得サービスに渡され、入札対象の上位 k 個の広告候補を取得します。

// Inputs
// ------
// encodedOnDeviceSignals: A Uint8Array of bytes from the device.
// encodedOnDeviceSignalsVersion: An integer representing the encoded
//   version of the signals.
// sellerAuctionSignals: Information about auction (ad format, size) derived
//                       contextually.
// contextualSignals: Additional contextual signals that could help in
//                    generating bids.
//
// Outputs
// -------
// Returns a JSON structure to be used for retrieval.
// The structure of this object is left to the adtech.
function prepareDataForAdRetrieval(encodedOnDeviceSignals,encodedOnDeviceSignalsVersion,sellerAuctionSignals,contextualSignals) {
   return {};
}
generateBid UDF

上位 k 個の広告候補が返された後、広告候補は購入者のカスタム入札ロジック generateBid UDF に渡されます。

// Inputs
// ------
// ads: Data string returned by the ads retrieval service. This can include Protected App Signals
//   ads and related ads metadata.
// sellerAuctionSignals: Information about the auction (ad format, size),
//                       derived contextually
// buyerSignals: Any additional contextual information provided by the buyer
// preprocessedDataForRetrieval: This is the output of this UDF.
function generateBid(ads, sellerAuctionSignals, buyerSignals,
                    preprocessedDataForRetrieval,
                    rawSignals, rawSignalsVersion) {
    return { "ad": <ad Value Object>,
             "bid": <float>,
             "render": <render URL string>,
             'adCost': <optional float ad cost>,
             "egressPayload": <limitedEgressPayload>,
             "temporaryUnlimitedEgressPayload": <temporaryUnlimitedEgressPayload>
    };
}

この関数の出力は、広告候補に対する 1 回の入札であり、ProtectedAppSignalsAdWithBidMetadata と同等の JSON として表されます。この関数は 2 つの配列を返すこともできます。この配列は reportWin に渡され、モデルのトレーニングが有効になります(下り(外向き)とモデルのトレーニングの詳細については、PAS の説明にあるレポート セクションをご覧ください)。

reportWin UDF

オークションが終了すると、オークション サービスは購入者向けのレポート URL を生成し、reportWin UDF(これは Protected Audience で使用される reportWin 関数と同じです)を使用してビーコンを登録します。これは、クライアントによって広告がレンダリングされると、デバイスから ping されます。 このメソッドのシグネチャは、モデルのトレーニングを有効にするために使用される egressPayloadtemporaryUnlimitedEgressPayload の 2 つの追加パラメータを除いて、Protected Audience のバージョンとほぼ同じです。これらのパラメータに generateBid の結果が代入されます。

// Inputs / Outputs
// ----------------
// See detailed documentation here.
function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                   buyerReportingSignals,
                   egressPayload, temporaryUnlimitedEgressPayload) {
  // ...
}
販売者の広告テクノロジー UDF
scoreAd UDF

販売者は、この UDF を使用して、購入者から受け取った広告のうち、オークションに勝つ広告を選択します。

function scoreAd(adMetadata, bid, auctionConfig,
                 trustedScoringSignals, bid_metadata) {
  // ...
  return {desirability: desirabilityScoreForThisAd,
              allowComponentAuction: true_or_false};
}
reportResult UDF

この UDF を使用すると、販売者は(最終的に)落札広告に関する情報を含むイベントレベルのレポートを実行できます。

function reportResult(auctionConfig, reporting_metadata) {
  // ...
  registerAdBeacon({"click", clickUrl,"view", viewUrl});
  sendReportTo(reportResultUrl);
  return signalsForWinner;
}

Ad Retrieval API

MVP リリースでは、広告取得サービスは購入者が管理するホスト型サービスとなり、入札サービスはこのサービスから広告候補を取得します。2024 年 4 月より、広告取得サーバーは Trusted Execution Environment(TEE)で稼働する必要があり、gRPC/proto インターフェースが公開されます。広告テクノロジー企業は、このサーバーをセットアップし、B&A スタック デプロイの一部としてその URL を指定する必要があります。TEE で実行されるこのサービスの実装は、プライバシー サンドボックス GitHub で入手できます。以降のドキュメントでは、これがデプロイで使用されるコードであることを前提としています。

2024 年 4 月より、B&A バージョンではコンテキスト パス広告の取得がサポートされます。この場合、入札サーバーはオークションのコンテキスト部分で RTB サーバーから送信された広告 ID のリストを受け取ります。識別子は TEE KV サーバーに送信され、入札フェーズで使用されるすべての広告関連情報(たとえば、トップ K の選択で使用される ad-render-url、メタデータ、広告埋め込み)を取得します。この 2 番目のパスでは、特定のロジックをデプロイする必要がないため、ここでは、TEE ベースの広告取得ユースケースを構成する方法のみを説明します。

HandleRequest UDF
function HandleRequest(requestMetadata, preparedDataForAdRetrieval,
                      deviceMetadata, contextualSignals) {
    return adsMetadataString;
}

ここで

  • requestMetadata: JSON。UDF に対するリクエストごとのサーバー メタデータ。現在空です。
  • preparedDataForAdRetrieval: このフィールドの内容は広告取得戦略によって異なります。コンテキスト広告を取得する場合、このパラメータには、デバイスから発生し、入札サービスから渡された未加工のシグナルが含まれます。広告取得サーバーを使用して TEE 広告を取得する場合、このパラメータには prepareDataForAdRetrieval UDF の結果が含まれます。注: この段階で、保護されたアプリ シグナルはデコードされ、暗号化されません。
  • deviceMetadata: 販売者の広告サービスによって転送されたデバイス メタデータを含む JSON オブジェクト。詳細については、B&A のドキュメントをご覧ください。
    • X-Accept-Language: デバイスで使用されている言語。
    • X-User-Agent: デバイスで使用されるユーザー エージェント。
    • X-BnA-Client-IP: デバイスの IP アドレス。
  • contextualSignals: 同じ DSP が運用するコンテキスト入札サーバーからの任意の文字列。UDF は、文字列をデコードして使用できることが想定されています。コンテキスト シグナルには、Protected App Signals を使用して渡された保護されたエンベディングの ML モデル バージョン情報など、あらゆる情報を含めることができます。

UDF は、成功すると文字列を返します。この文字列は入札サーバーに返され、入札サーバーは generateBid UDF に渡されます。単純な文字列でもかまいませんが、通常は、各広告テクノロジーによって独自にスキーマが定義されるシリアル化されたオブジェクトである必要があります。広告テクノロジーの generateBid ロジックが文字列を認識して使用できる限り、スキーマに対する制約はありません。

開発用のシステムをセットアップする

Android

Android 開発環境をセットアップするには、以下を行う必要があります。

  1. デベロッパー プレビュー 10 イメージを実行するエミュレータ(推奨)または物理デバイスを作成する
  2. 以下のコマンドを実行します。
adb shell am start -n com.google.android.adservices.api/com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity

次に、アプリによる広告の提案に同意するオプションを選択します。

  1. 次のコマンドを実行して、関連する API を有効にします。デフォルト設定(無効)が定期的に同期されるため、適当なタイミングでこのコマンドを再度実行することをおすすめします。
adb shell device_config put adservices fledge_custom_audience_service_kill_switch false;  adb shell device_config put adservices fledge_select_ads_kill_switch false; adb shell device_config put adservices fledge_on_device_auction_kill_switch false; adb shell device_config put adservices fledge_auction_server_kill_switch false; adb shell "device_config put adservices disable_fledge_enrollment_check true";  adb shell device_config put adservices ppapi_app_allow_list '\*'; adb shell device_config put adservices fledge_auction_server_overall_timeout_ms 60000;
  1. デバイスを再起動します。
  2. デバイスのオークション キーをオーバーライドして、オークション キー サーバーを指定します。誤ったキーがキャッシュに保存されないようにするには、オークションを実行する前にこの手順を行うことが重要です。

入札およびオークション サービス

B&A サーバーを設定するには、セルフサービスによるセットアップのドキュメントをご覧ください。

このドキュメントでは、販売者は変更する必要がないため、購入者固有のサーバーの構成方法について説明します。

前提条件

B&A サービス スタックをデプロイする前に、購入者の広告テクノロジーは次のことを行う必要があります。

  • 独自の TEE 広告取得サービスがデプロイされていることを確認します(関連セクションを参照)。
  • 広告テクノロジーに必要なすべての UDF(prepareDataForAdRetrievalgenerateBidreportWinHandleRequest)が定義され、ホストされていることを確認します。

Protected Audience を使用する Protected Auction と B&A がどのように連携するかを理解しておくと役立ちますが、必須ではありません。

Terraform の構成

Protected App Signals を使用するには、広告テクノロジーが次のことを行う必要があります。

  • B&A で Protected App Signals のサポートを有効にする。
  • prepareDataForAdRetrieval, generateBidreportWin の新しい UDF を取得できる URL エンドポイントを指定する。

また、このガイドでは、リマーケティングに B&A を使用する広告テクノロジーが、引き続き通常どおりリマーケティング オークションの既存のすべての構成フラグを設定することを前提としています。

購入者の広告テクノロジーの設定

このデモファイルを例として使用し、購入者は次のフラグを設定する必要があります。

  • Protected App Signals を有効にする: Protected App Signals のデータの収集を有効にします。
  • Protected App Signals の URL: Protected App Signals サーバーの URL に設定します。

広告テクノロジーは、次のフィールドのプレースホルダを正しい URL に置き換える必要があります。

module "buyer" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"
    PROTECTED_APP_SIGNALS_GENERATE_BID_TIMEOUT_MS = "60000"
    TEE_AD_RETRIEVAL_KV_SERVER_ADDR               = "<service mesh address of the instance>"
    AD_RETRIEVAL_TIMEOUT_MS                       = "60000"
    BUYER_CODE_FETCH_CONFIG                       = <<EOF
    {
        "protectedAppSignalsBiddingJsUrl": "<URL to Protected App Signals generateBid UDF>",
        "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
        "urlFetchPeriodMs": 13000000,
        "prepareDataForAdsRetrievalJsUrl": "<URL to the UDF>"
    }
    EOF

  }  # runtime_flags

}  # Module "buyer"

販売者の広告テクノロジーの設定

このデモファイルを例として使用し、販売者は次のフラグを設定する必要があります。(注: ここでは、Protected App Signals に関連する構成のみを特に表示しています)。広告テクノロジーは、プレースホルダを正しい URL に置き換える必要があります。

module "seller" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"

    SELLER_CODE_FETCH_CONFIG                           = <<EOF
  {
    "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
    "urlFetchPeriodMs": 13000000,
    "protectedAppSignalsBuyerReportWinJsUrls": {"<Buyer Domain>": "URL to reportWin UDF"}
  }
  EOF

  }  # runtime_flags

}  # Module "seller"

KV および広告取得サービス

広告の取得をサポートするように選択した戦略に応じて、KV サービスの 1 つまたは 2 つのインスタンスをデプロイする必要があります。TEE ベースの広告取得に使用される KV インスタンス インスタンスを Ad Retrieval Server とし、コンテキスト パスベースの取得をサポートするインスタンスを KV Lookup Server と呼びます。

どちらの場合も、サーバーのデプロイは KV サーバーの GitHub にあるドキュメントに従います。2 つの場合の違いは、ルックアップ ケースは追加構成なしですぐに機能しますが、取得では取得ロジックを実装するために HandleRequest UDF をデプロイする必要があります。詳細については、KV サーバー オンボーディング ガイドをご覧ください。B&A では、両方のサービスが入札サービスと同じサービス メッシュにデプロイされることが想定されています。

セットアップ例

次のシナリオを考えてみましょう。Protected App Signals API を使用して、広告テクノロジーがユーザーのアプリの使用状況に基づいて関連するシグナルを保存します。この例では、複数のアプリからのアプリ内購入を表すシグナルが保存されています。オークション中に暗号化されたシグナルが収集され、B&A で実行されている Protected Auction に渡されます。B&A で実行されている購入者の UDF は、シグナルを使用して広告の候補をフェッチし、入札単価を計算します。

[購入者] Signals の例

キーが 0、値が 1 のシグナルを追加します。

{
  "put": {
    "AA==": "AQ=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

キーが 1、値が 2 のシグナルを追加します。

{
  "put": {
    "AQ==": "Ag=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

[購入者] encodeSignals の例

各シグナルを 2 バイトにエンコードします。1 番目のバイトはシグナルキーの最初のバイト、2 番目のバイトはシグナル値の最初のバイトです。

function encodeSignals(signals, maxSize) {
  // if there are no signals don't write a payload
  if (signals.size === 0) {
      return {};
  }

  let result = new Uint8Array(signals.size * 2);
  let index = 0;
  
  for (const [key, values] of signals.entries()) {
    result[index++] = key[0];
    result[index++] = values[0].signal_value[0];
  }
  
  return { 'status': 0, 'results': result};
}

[購入者] prepareDataForAdRetrieval の例

/**
 * `encodedOnDeviceSignals` is a Uint8Array and would contain
 * the app signals emanating from device. For purpose of the
 * demo, in our sample example, we assume that device is sending
 * the signals with pair of bytes formatted as following:
 * "<id><In app spending>". Where id corresponds to an ad category
 * that user uses on device, and the in app spending is a measure
 * of how much money the user has spent in this app category
 * previously. In our example, id of 0 will correspond to a
 * fitness ad category and a non-zero id will correspond to
 * food app category -- though this info will be useful
 * later in the B&A pipeline.
 *
 * Returns a JSON object indicating what type of ad(s) may be
 * most relevant to the user. In a real setup ad techs might
 * want to decode the signals as part of this script.
 *
 * Note: This example script makes use of only encoded device signals
 * but adtech can take other signals into account as well to prepare
 * the data that will be useful down stream for ad retrieval and
 * bid generation. The max length of the app signals used in this
 * sample example is arbitrarily limited to 4 bytes.
 */
function prepareDataForAdRetrieval(encodedOnDeviceSignals,
                                   encodedOnDeviceSignalsVersion,
                                   sellerAuctionSignals,
                                   contextualSignals) {
  if (encodedOnDeviceSignals.length === 0 || encodedOnDeviceSignals.length > 4 ||
      encodedOnDeviceSignals.length % 2 !== 0) {
     throw "Expected encoded signals length to be an even number in (0, 4]";
  }

  var preparedDataForAdRetrieval = {};
  for (var i = 0; i < encodedOnDeviceSignals.length; i += 2) {
    preparedDataForAdRetrieval[encodedOnDeviceSignals[i]] = encodedOnDeviceSignals[i + 1];
  }
  return preparedDataForAdRetrieval;
}

[購入者] 広告取得 UDF の例

この例では、広告取得サーバーが、上位 k 個の広告候補のそれぞれについてメタデータ(この例では各広告の ID ですが、後で入札単価を生成するのに役立つ他のデータを含めることもできます)を送信します。

function HandleRequest(requestMetadata, protectedSignals, deviceMetadata,
                      contextualSignals) {
 return "[{\"adId\":\"0\"},{\"adId\":\"1\"}]"

[購入者] generateBid の例

/**
 * This script receives the data returned by the ad retrieval service
 * in the `ads` argument. This argument is supposed to contain all
 * the Protected App Signals related ads and the metadata obtained from the retrieval
 * service.
 *
 * `preparedDataForAdRetrieval` argument contains the data returned
 * from the `prepareDataForAdRetrieval` UDF.
 *
 * This script is responsible for generating bids for the ads
 * collected from the retrieval service and ad techs can decide to
 * run a small inference model as part of this script in order to
 * decide the best bid given all the signals available to them.
 *
 * For the purpose of the demo, this sample script assumes
 * that ad retrieval service has sent us most relevant ads for the
 * user and this scripts decides on the ad render URL as well as
 * what value to bid for each ad based on the previously decoded
 * device signals. For simplicity sake, this script only considers
 * 2 types of app categories i.e. fitness and food.
 *
 * Note: Only one bid is returned among all the
 * input ad candidates.
 */
function generateBid(ads, sellerAuctionSignals, buyerSignals, preparedDataForAdRetrieval) {
  if (ads === null) {
    console.log("No ads obtained from the ad retrieval service")
    return {};
  }     
        
  const kFitnessAd = "0";
  const kFoodAd = "1";
  const kBuyerDomain = "https://buyer-domain.com";
        
  let resultingBid = 0;
  let resultingRender = kBuyerDomain + "/no-ad";
  for (let i = 0 ; i < ads.length; ++i) {
    let render = "";
    let bid = 0;
    switch (ads[i].adId) {
      case kFitnessAd:
        render = kBuyerDomain + "/get-fitness-app";
        bid = preparedDataForAdRetrieval[kFitnessAd];
        break;
      case kFoodAd:
        render = kBuyerDomain + "/get-fastfood-app";
        bid = preparedDataForAdRetrieval[kFoodAd];
        break;
      default:
        console.log("Unknown ad category");
        render = kBuyerDomain + "/no-ad";
        break;
    }
    console.log("Existing bid: " + resultingBid + ", incoming candidate bid: " + bid);
    if (bid > resultingBid) {
      resultingBid = bid;
      resultingRender = render;
    }
  }
  return {"render": resultingRender, "bid": resultingBid};
}

[購入者] reportWin の例

reportWin UDF が、オークションで落札したことを購入者に報告します。

function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                                       buyerReportingSignals, directFromSellerSignals,
                                       egressPayload,
                                       temporaryUnlimitedEgressPayload) {
  sendReportTo("https://buyer-controlled-domain.com/");
  registerAdBeacon({"clickEvent":"https://buyer-controlled-domain.com/clickEvent"});
  return;
}

[販売者] KV サーバーのセットアップ

販売者は、広告レンダリング URL から対応するスコアリング シグナルへのマッピングを使用できるように、スコアリング シグナル KV サーバーを設定する必要があります。たとえば、https:/buyer-domain.com/get-fitness-apphttps:/buyer-domain.com/get-fastfood-app が購入者によって返される場合、販売者は、https://key-value-server-endpoint.com?client_type=1&renderUrls=<render-url-returned-by-the-buyer>GET を使用して SFE からクエリされたときに、次の例のようなスコアリング シグナルのレスポンスを受け取ることができます。

{
   "renderUrls" : {
      "https:/buyer-domain.com/get-fitness-app" : [
         "1",
         "2"
      ],
      "https:/buyer-domain.com/get-fastfood-app" : [
         "3",
         "4"
      ]
   }
}

[販売者] scoreAd の例

/**
 * This module generates a random desirability score for the Protected App
 * Signals ad in this example. In a production deployment,
 * however, the sellers would want to use all the available signals to generate
 * a score for the ad.
 */
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function scoreAd(adMetadata, bid, auctionConfig,
                                   trustedScoringSignals, deviceSignals,
                                   directFromSellerSignals) {
  return {
    "desirability": getRandomInt(10000),
    "allowComponentAuction": false
  };
}

[販売者] reportResult の例

function reportResult(auctionConfig, sellerReportingSignals, directFromSellerSignals){
  let signalsForWinner = {};
    sendReportTo("https://seller-controlled-domain.com");
    registerAdBeacon({"clickEvent":
                    "https://seller-controlled-domain.com/clickEvent"});
    return signalsForWinner;
}

サンプルアプリ

API を使用して、上記のようなシンプルなフローを使用するアプリを作成する方法の例として、Protected App Signals サンプルアプリを作成しました。このサンプル リポジトリをご覧ください。