受保護的應用程式信號開發人員指南和#39

為協助開發人員開始試用 Protected App Signals API,本文件除了介紹此 API 介面中的所有 API,也會詳細說明如何設定測試環境,並提供設定與指令碼範例。

版本記錄

2024 年 1 月

開發人員指南首次推出,支援 PAS MVP 版本

March 2024

變更 API,以支援 Android API 的 M-2024-05 版本和 2024 年 4 月的伺服器端元件。主要異動:

  • 新增裝置端 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:讓 SDK 使用 Protected App Signals,在出價和競價 (B&A) 伺服器上執行受保護的競價。
  • 伺服器端 API:
    • Protected Auction API:這是在出價和競價伺服器中執行的一系列 JS 指令碼。這個 API 可讓賣方和買方編寫邏輯,實作受保護的競價。
    • Ad Retrieval API:負責根據買方出價伺服器可用的內容比對和使用者資訊,提供候選廣告清單。

Android 用戶端

在用戶端上,Protected App Signals 介面內含三種不同的 API:

  • Update Signals:這個 Android 系統 API 可啟用裝置信號收集功能。
  • Signals Encoding:這個 JavaScript API 可用來準備要在競價期間傳送至伺服器的信號。
  • Protected Auction Support:這個 API 可讓您在出價和競價伺服器上執行 Protected Auction。這並不是 Protected App Signals 特定的 API,也可用於支援 Protected Audience API 的競價。

Update Signals API

Update Signals API 可讓廣告技術代表買方,以便註冊使用者和應用程式相關信號。這個 API 會在委派模型上運作。透過呼叫端提供的 URI,架構可擷取相應信號和用來將信號編碼的邏輯,以便用於競價。

API 需要 android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS 權限。

updateSignals() API 會從 URI 擷取 JSON 物件,描述要新增或移除的信號,以及如何為競價準備這些信號。

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 發出 https 要求,以擷取信號更新。除了信號更新,回應還可包含代管編碼邏輯的端點,用於將原始信號轉換為編碼酬載。信號更新應採用 JSON 格式,並具有下列鍵:

JSON 物件的頂層鍵必須對應以下五個指令中的任一種:

說明

put

新增信號,以相同鍵覆寫任何現有信號。這個鍵

的值是 JSON 物件,其中鍵會採用與放置目標鍵相對應的 base 64 字串,而值則採用與要放置的值相對應的 base 64 字串。

append

將新信號/多個信號附加到信號的時間序列,移除最舊的

信號。這麼一來,如果序列大小超過指定上限,則可為新信號釋出空間。這個鍵的值是 JSON 物件,其中鍵會採用與附加目標鍵相對應的 base 64 字串,而值則是含有「values」和「maxSignals」這兩個欄位的物件。

「values」:代表與要附加至時間序列的信號值相對應的 base 64 字串清單。

「maxSignals」:代表此時間序列中可用值的數量上限。如果

目前與鍵相關聯的信號數量超出 maxSignals,系統將會移除最舊的信號。提醒您,透過 put 新增的鍵也可以是附加目標。但請注意,如果附加數量超過值的上限,就會導致失敗。

put_if_not_present

只在現有信號沒有相同鍵時,才新增信號。這個鍵的值是 JSON 物件,其中鍵會採用與放置目標鍵相對應的 base 64 字串,而值則採用與要放置的值相對應的 base 64 字串。

remove

移除鍵的信號。這個鍵的值是與應刪除的信號鍵相對應的 base 64 字串清單。

update_encoder

提供用來更新端點的動作,以及可用於

擷取編碼邏輯的 URI。提供更新動作的子鍵為「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-15Kb 的順序提供裝置配額,一旦超過配額,PPAPI 便會使用 FIFO 策略撤銷信號。撤銷程序會允許短時間內稍微超過配額,以降低撤銷頻率。

Signals Encoding API

買方必須提供 Java Script 函式,用於對儲存在裝置上的信號進行編碼,才能在 Protected Auction 期間將信號傳送至伺服器。買方可藉由新增網址來提供這個指令碼,只要在對 UpdateSignal API 要求的任何回應中使用「update_encoder」鍵,即可從網址中擷取此指令碼。指令碼具有以下簽名:

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 參數是從鍵到 Protected App Signals 物件清單的對應,格式為大小為 4 的 UInt8Arrays。每個 Protected App Signals 物件都有三個欄位:

  • signal_value:代表信號值的 UInt8Array。
  • creation_time:代表信號建立時間的數字,以 Epoch 紀元時間計算的秒數為單位。
  • package_name:此字串代表建立信號的套件名稱。

maxSize 參數是一組數字,用來說明輸出內容的最大許可陣列大小。

函式應輸出含有以下兩個欄位的物件:

  • status:如果指令碼執行成功,則應為 0。
  • results:應為長度小於或等於 maxSize 的 UInt8Array。這個陣列會在競價期間傳送至伺服器,並由 prepareDataForAdRetrieval 指令碼負責準備。

藉由這種編碼方式,廣告技術可以在特徵工程的初始階段執行轉換,例如根據自己的自訂邏輯將原始信號壓縮為串連版本。請注意,Protected Auction 在受信任的執行環境 (TEE) 中運作時,廣告技術自訂邏輯將具有讀取權限,能夠存取編碼產生的信號酬載。在買方 B&A TEE 中執行的自訂邏輯稱為使用者定義函式 (UDF),同樣也能取得讀取權限,因此能存取發布商應用程式為了執行廣告選擇 (廣告擷取和出價) 作業而提供的編碼信號和其他內容比對信號。

信號編碼

如果買方已透過註冊信號提供編碼邏輯,其信號每隔一小時就會編碼至競價酬載中。競價酬載的位元組陣列會保留在裝置上、經過加密,並由賣方收集做為廣告選擇資料的一部分,以便納入 Protected Auction 中。如要進行測試,您可以執行下列指令,以非每小時的週期觸發這組編碼:

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:買方可以藉由實作 REST 端點來實作此 API,該端點負責為 Protected App Signal 競價提供一組候選廣告。

Protected Auction API

Protected Auction API 由 JS API 或 UDF 組成,買方和賣方可用這些 API 和 UDF 自行實作競價和出價邏輯。

買方廣告技術 UDF
prepareDataForAdRetrieval UDF

買方必須先解碼並準備受保護的應用程式信號,以及其他賣方提供的資料,才能使用受保護的應用程式信號從 TEE 廣告擷取服務中擷取候選廣告。買方的 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>
    };
}

這個函式的輸出結果是廣告候選的單一出價,以與 ProtectedAppSignalsAdWithBidMetadata 相同的 JSON 表示。函式也可以傳回兩個陣列,系統會將其傳遞至 reportWin 以啟用模型訓練 (如要進一步瞭解輸出和模型訓練,請參閱 PAS 說明中的報表章節)

reportWin UDF

競價結束後,競價服務會為買方產生報表網址,並使用 reportWin UDF (這與 Protected Audiences 所用的相同 reportWin 函式) 註冊信標。當用戶端顯示廣告後,裝置隨即會連線偵測 (ping) 此 UDF。這個方法的簽章與 Protected Audience 版本幾乎相同,但用於啟用模型訓練的兩個額外參數 egressPayloadtemporaryUnlimitedEgressPayload,並會填入 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 月起,廣告擷取伺服器必須在受信任的執行環境 (TEE) 中執行,並公開 GRPC/proto 介面。廣告技術公司必須設定這個伺服器,並在 B&A 堆疊部署中提供其網址。在 TEE 中執行的這項服務實作列於 Privacy Sandbox GitHub。其餘說明文件中,我們假設這是部署作業中使用的程式碼。

B&A 版本自 2024 年 4 月起支援內容比對路徑廣告擷取。在這種情況下,出價伺服器會收到即時出價伺服器在競價內容相關部分傳送的廣告 ID 清單。系統會將 ID 傳送至 TEE KV 伺服器,以擷取要在出價階段中使用的所有廣告相關資訊 (例如要在頂端選取項目中使用的 ad-render-url、中繼資料和廣告嵌入)。這個第二個路徑不需要部署任何特定邏輯,因此只會記錄如何設定 TEE 的廣告擷取用途。

處理要求 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 信號傳入的受保護嵌入的機器學習模型版本資訊。

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 Auction 與 Protected Audience 如何搭配 B&A 運作也有所幫助,但這並非硬性規定。

Terraform 設定

如要使用 Protected App Signals,廣告技術必須:

  • 在 B&A 中啟用 Protected App Signals 支援功能。
  • 提供網址端點,以便系統從中擷取 prepareDataForAdRetrieval, generateBidreportWin 的新 UDF。

此外,本指南假設廣告技術希望使用 B&A 進行再行銷,所以會繼續照常設定再行銷競價的所有現有設定標記。

買方廣告技術設定

以這個示範檔案為例,買方需要設定下列標記:

  • 啟用 Protected App Signals:啟用後即可收集 Protected App Signals 資料。
  • Protected App Signals 網址:設定為 Protected App Signals 伺服器的網址。

廣告技術必須將以下欄位的預留位置替換成正確的網址:

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 相關設定)。廣告技術需確實將預留位置替換成正確的網址:

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 服務的執行個體。我們會將用於 TEE 廣告擷取的 KV 執行個體稱為 Ad Retrieval Server,並將執行個體稱為 KV Lookup Server,以支援內容比對路徑的擷取作業。

在這兩種情況下,伺服器部署皆遵循 KV 伺服器 GitHub 中的說明文件,兩種情況的差異在於查詢案例無需任何其他設定即可立即運作,而擷取案例則需要部署 HandleRequest UDF 來實作擷取邏輯。詳情請參閱 KV 伺服器入門指南。請注意,B&A 需要將這兩項服務部署在與出價服務相同的服務網格中。

設定範例

設想以下情境:使用 Protected App Signals API 時,廣告技術會根據使用者的應用程式使用情形儲存相關信號。在本範例中,儲存的信號代表多個應用程式的應用程式內購項目。系統會在競價期間收集加密信號,並傳遞至在 B&A 中執行的 Protected Auction。在 B&A 中執行的買方 UDF 會使用這些信號擷取候選廣告並計算出價。

[買方] 信號範例

新增鍵為 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 範例

將每個信號編碼為兩個位元組,第一個位元組是信號鍵的第一個位元組,第二個位元組則是信號值的第一個位元組。

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 伺服器設定

賣方必須設定評分信號 KV 伺服器,使廣告顯示網址能夠與相應的評分信號產生對應關係;舉例來說,如果買方要傳回 https:/buyer-domain.com/get-fitness-apphttps:/buyer-domain.com/get-fastfood-app,當 SFE 在 https://key-value-server-endpoint.com?client_type=1&renderUrls=<render-url-returned-by-the-buyer> 上使用 GET 提出查詢時,賣方可獲得下列評分信號回應:

{
   "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 範例應用程式,歡迎前往這個範例存放區查看。