Guide du développeur sur les signaux d'application protégés

Pour aider les développeurs à tester l'API Protected App Signals, ce document décrit toutes les API de la surface de l'API, explique comment configurer un environnement de test et fournit des exemples de configuration et de scripts. Pour en savoir plus sur l'API Protected App Signals, consultez la proposition de conception.

Présentation de l'API

La surface de l'API Protected Signals inclut différents sous-ensembles d'API sur différents systèmes :

  • API Android :
    • API Signal Curation, composée des éléments suivants :
    • Mettre à jour l'API Signals
    • API Signals Encoding
    • API Protected Auction Support : à utiliser par les SDK pour lancer la mise aux enchères protégée sur les serveurs d'enchères et de mise aux enchères à l'aide de signaux d'application protégés.
  • API côté serveur :
    • API Protected Auction : série de scripts JavaScript exécutés dans les serveurs d'enchères et de mise aux enchères. Cette API permet aux vendeurs et aux acheteurs d'écrire la logique pour implémenter l'enchère protégée.
    • API Ad Retrieval : chargée de fournir une liste d'annonces candidates en fonction des informations contextuelles et utilisateur mises à la disposition du serveur d'enchères de l'acheteur.

Client Android

Côté client, la surface des signaux d'application protégés se compose de trois API différentes :

  • Signaux de mise à jour : API du système Android permettant de gérer les signaux sur l'appareil.
  • Encodage des signaux : API JavaScript pour préparer les signaux à envoyer au serveur lors de l'enchère.
  • Protected Auction Support : API permettant l'exécution d'une enchère protégée sur les serveurs d'enchères et de mise aux enchères. Cette API n'est pas spécifique aux signaux d'application protégés et sert également à prendre en charge les enchères pour l'API Protected Audience.

Mettre à jour l'API Signals

L'API Update Signals permet aux technologies publicitaires d'enregistrer des signaux liés à l'utilisateur inscrit et à l'application pour le compte d'un acheteur. L'API fonctionne sur un modèle de délégation. L'appelant fournit un URI à partir duquel le framework extrait les signaux correspondants, ainsi que la logique permettant d'encoder ces signaux à utiliser lors de l'enchère.

L'API updateSignals() récupère à partir de l'URI un objet JSON qui décrit les signaux à ajouter ou à supprimer, et comment les préparer pour l'enchère.

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);

La plate-forme envoie une requête HTTPS à l'URI fourni dans la requête pour récupérer les mises à jour des signaux. Avec les mises à jour des signaux, la réponse peut inclure un point de terminaison qui héberge la logique d'encodage permettant de convertir les signaux bruts en charge utile encodée. Les mises à jour des signaux doivent être au format JSON et peuvent avoir les clés suivantes :

Les clés de niveau supérieur de l'objet JSON doivent correspondre à l'une des cinq commandes suivantes :

clé

Description

put

Ajoute un nouveau signal en écrasant tous les signaux existants ayant la même clé. La valeur

correspondante est un objet JSON dans lequel les clés sont des chaînes en base64 correspondant à la clé à mettre et les valeurs sont des chaînes en base64 correspondant à la valeur à mettre.

append

Ajoute un ou plusieurs nouveaux signaux à une série temporelle de signaux, en supprimant les plus anciens

afin de faire de la place pour les nouveaux si la taille de la série dépasse la taille maximale donnée. La valeur correspondante est un objet JSON dans lequel les clés sont des chaînes en base64 correspondant à la clé à ajouter. Les valeurs sont des objets comportant deux champs : "values" et "maxSignals".

"values" : liste de chaînes en base64 correspondant aux valeurs de signaux à ajouter à la série temporelle.

"maxSignals" : nombre maximal de valeurs autorisées dans cette série temporelle. Si

le nombre actuel de signaux associés à la clé dépasse la valeur maxSignals, les signaux les plus anciens seront supprimés. Notez que vous pouvez ajouter des signaux à une clé ajoutée par le biais de la méthode "put". Notez que si vous ajoutez plus de valeurs que le nombre maximal autorisé, un échec se produira.

put_if_not_present

Ajoute un nouveau signal uniquement s'il n'existe aucun signal associé à la même clé. La valeur correspondante est un objet JSON dans lequel les clés sont des chaînes en base64 correspondant à la clé à insérer et les valeurs sont une chaîne en base64 correspondant à la valeur à mettre.

remove

Supprime le signal d'une clé. La valeur correspondante est une liste de chaînes en base64 correspondant aux clés de signaux à supprimer.

update_encoder

Fournit une action pour mettre à jour le point de terminaison et un URI qui peuvent être utilisés

pour récupérer une logique d'encodage. La sous-clé permettant de fournir une action de mise à jour est "action" et

les valeurs actuellement prises en charge sont uniquement "REGISTER". Si le point de terminaison de l'encodeur est fourni pour la première fois, il enregistre le point de terminaison de l'encodeur ou écrase le point de terminaison existant par le nouveau point de terminaison fourni. Vous devez indiquer le point de terminaison pour l'action "REGISTER". La sous-clé permettant de fournir un point de terminaison d'encodeur est "endpoint" et sa valeur est la chaîne URI

du point de terminaison.

Voici comment se présente un exemple de requête 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"
    }
}

API Signals Encoding

Les acheteurs doivent fournir une fonction Java Script à utiliser pour encoder les signaux stockés sur l'appareil à envoyer au serveur lors de l'enchère protégée. Les acheteurs peuvent fournir ce script en ajoutant l'URL à partir de laquelle il peut être extrait à l'aide de la clé "update_encoder" dans l'une des réponses à une requête API UpdateSignal. Le script aura la signature suivante :

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)};
}

Le paramètre signals est un mappage de clés au format UInt8Arrays de taille 4 à des listes d'objets des signaux d'application protégés. Chaque objet des signaux d'application protégés comporte trois champs :

  • signal_value : un UInt8Array représentant la valeur du signal.
  • creation_time : un nombre représentant l'heure de création des signaux en secondes Epoch.
  • package_name : une chaîne représentant le nom du package qui a créé le signal.

Le paramètre maxSize est un nombre décrivant la plus grande taille de tableau autorisée pour la sortie.

La fonction doit générer un objet avec deux champs :

  • status : doit être défini sur 0 si le script a bien été exécuté.
  • results : doit être un UInt8Array de longueur inférieure ou égale à maxSize. Ce tableau sera envoyé au serveur lors des enchères et préparé par le script prepareDataForAdRetrieval.

L'encodage fournit aux technologies publicitaires une étape initiale d'extraction de caractéristiques, au cours de laquelle elles peuvent effectuer des transformations telles que la compression de signaux bruts en versions concaténées en fonction de leur propre logique personnalisée. Notez que lors d'enchères protégées exécutées dans les environnements d'exécution sécurisés (TEE, Trusted Execution Environments), la logique personnalisée des technologies publicitaires dispose d'un accès en lecture aux charges utiles de signaux générées par l'encodage. La logique personnalisée, appelée fonction définie par l'utilisateur (UDF), exécutée dans les environnements d'exécution sécurisés d'enchères et de mises aux enchères de l'acheteur disposera d'un accès en lecture aux signaux encodés et aux autres signaux de contexte fournis par l'application de l'éditeur pour effectuer la sélection des annonces (récupération des annonces et enchères).

Encodage des signaux

Toutes les heures, les acheteurs qui ont fourni une logique d'encodage avec leurs signaux enregistrés sont encodés dans une charge utile d'enchère. Le tableau d'octets de la charge utile de l'enchère est conservé sur l'appareil. Il est chiffré et les données seront collectées par les vendeurs dans le cadre des données sur la sélection des annonces, puis incluses dans le cadre d'une enchère protégée. Pour les tests, vous pouvez déclencher cet encodage en dehors de sa fréquence horaire en exécutant la commande suivante :

adb shell cmd jobscheduler run -f com.google.android.adservices.api 29
Gestion des versions de la logique d'encodeur

Lorsqu'une requête est effectuée pour télécharger la logique d'encodeur personnalisé de la technologie publicitaire, le point de terminaison de la technologie publicitaire peut répondre avec un numéro de version dans les en-têtes de réponse. Cette version est conservée avec la logique d'encodeur sur l'appareil. Lorsque les signaux bruts sont encodés, la charge utile encodée est conservée avec la version utilisée pour l'encodage. Cette version est également envoyée au serveur d'enchères et de mise aux enchères lors d'une mise aux enchères protégée, afin que les technologies publicitaires puissent aligner leur logique d'enchères et d'encodage en fonction de la version.

Response header for providing encoder version : X_ENCODER_VERSION

API Protected Auction Support

Côté appareil, le lancement d'une mise aux enchères pour les signaux d'application protégés revient à la même mise aux enchères que pour les audiences protégées.

Services d'enchères et de mise aux enchères

Les API côté serveur incluent :

  • API Protected Auction : série de fonctions JS, ou UDF, que les acheteurs et les vendeurs peuvent déployer sur les composants des enchères et de mise aux enchères dont ils sont propriétaires, afin de déterminer les enchères et la logique d'enchères.
  • API Ad Retrieval : les acheteurs peuvent implémenter cette API en implémentant un point de terminaison REST qui sera chargé de fournir un ensemble d'annonces candidates pour la mise aux enchères des signaux d'application protégés.

API Protected Auction

L'API Protected Auction se compose d'une API JS ou de plusieurs UDF que les acheteurs et les vendeurs peuvent utiliser pour implémenter leur logique d'enchères et de mise aux enchères.

Fonction définie par l'utilisateur (UDF) de technologies publicitaires pour les acheteurs
UDF prepareDataForAdRetrieval

Avant que les signaux d'application protégés puissent être utilisés pour récupérer des annonces candidates à partir du service de récupération d'annonces BYOS, les acheteurs doivent décoder et préparer ces signaux ainsi que d'autres données fournies par le vendeur. La sortie de l'UDF prepareDataForAdRetrieval de l'acheteur est transmise au service de récupération d'annonces afin de récupérer les k premières annonces candidates pour les enchères.

// 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 {};
}
UDF generateBid

Une fois les k premières annonces candidates renvoyées, elles sont transmises à la logique d'enchères personnalisées de l'acheteur, UDF generateBid :

// 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) {
    return { "ad": <ad Value Object>,
             "bid": <float>,
             "render": <render URL string>
    };
}

Le résultat de cette fonction est une enchère unique pour une annonce candidate, représentée sous la forme d'un fichier JSON équivalent à ProtectedAppSignalAdWithBidBidMetadata.

UDF reportWin

À la fin d'une enchère, le service d'enchères génère des URL de rapport pour les acheteurs et enregistre les balises à l'aide d'UDF reportWin (il s'agit de la même fonction reportWin utilisée pour Protected Audiences). L'appareil enverra un ping une fois l'annonce diffusée par le client. La signature de cette méthode est presque identique à la version de l'API Protected Audience, à l'exception d'un argument supplémentaire comme dans protectedAppSignalsEgress. Cet argument n'est pas compatible pour le moment.

// Inputs / Outputs
// ----------------
// See detailed documentation here.
function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                   buyerReportingSignals, protectedAppSignalsEgress) {
  // ...
}
Fonctions définies par l'utilisateur (UDF) de technologies publicitaires pour les vendeurs
UDF scoreAd

Les vendeurs utilisent cette UDF pour sélectionner les annonces envoyées par les acheteurs qui remporteront l'enchère.

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

Cette UDF permet au vendeur (à terme) de créer des rapports au niveau des événements avec les informations concernant l'annonce gagnante.

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

API Ad Retrieval

Pour ce produit minimum viable, le service de récupération d'annonces sera géré et hébergé par l'acheteur. Le service d'enchères récupérera les annonces candidates à partir de ce service. Pour DP10, ce serveur diffuse les requêtes de récupération d'annonces à l'aide d'une interface HTTP / JSON avec un verbe PUT. Après DP10, ce serveur doit s'exécuter dans un environnement d'exécution sécurisé (TEE) et exposer une interface GRPC/proto. Les entreprises de technologie publicitaire doivent configurer ce serveur et fournir leur URL lors du déploiement de la pile d'enchères et dans le cadre de la mise aux enchères.

Format d'entrée

Les données d'entrée requises pour extraire les annonces candidates sont les signaux d'application protégés dans le bloc partitions > arguments, où la séquence d'arguments se présente comme suit :

  • Version JSON concaténée des données renvoyées par prepareDataForAdRetrieval.
  • Métadonnées de l'appareil envoyées par le client.
  • Signaux contextuels.
{
   "partitions" : [
      {
         "arguments" : [
            {
               "data" : "{\"key1\": \"1\", \"key2\": \"2\"}"
            },
            {
               "data" : {
                  "X-Accept-Language" : "en-US,en;q=0.9",
                  "X-BnA-Client-IP" : "1.2.3.4",
                  "X-User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
               }
            },
            {
               "data" : "{\"key3\": \"3\"}"
            }
         ]
      }
   ]
}

Format de sortie

La sortie du service contiendra un objet JSON au format suivant :

{"singlePartition":{"stringOutput": "arbitrary text string"}}

Les technologies publicitaires peuvent intégrer des métadonnées dans les annonces candidates en tant que valeur de la clé singlePartition > stringOutput. Les métadonnées peuvent être une entrée dans la fonction définie par l'utilisateur generateBid personnalisée de ces technologies publicitaires.

Configurer le système pour le développement

Android

Pour configurer votre environnement de développement Android, vous devez procéder comme suit :

  1. Créez un émulateur (de préférence) ou un appareil physique qui exécute l'image Preview développeur 10.
  2. Pour ce faire, exécutez la commande suivante :
adb shell am start -n com.google.android.adservices.api/com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity

Sélectionnez ensuite l'option affichée permettant d'autoriser les annonces suggérées par les applis.

  1. Exécutez la commande suivante pour activer les API appropriées. Vous devrez peut-être réexécuter cette commande de temps en temps, car la configuration par défaut de la colonne "Désactivé" sera régulièrement synchronisée.
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. Redémarrez l'appareil.
  2. Ignorez les clés d'enchères de l'appareil pour qu'elles pointent vers votre serveur de clés d'enchères. Il est important d'effectuer cette étape avant de tenter de lancer une mise aux enchères afin d'éviter la mise en cache de clés incorrectes.

Services d'enchères et de mise aux enchères

Pour configurer les serveurs d'enchères et de mise aux enchères, consultez la documentation de configuration en libre-service.

Ce document explique comment configurer les serveurs propres à l'acheteur, car aucune modification n'est requise pour les vendeurs.

Conditions préalables

Avant de déployer une pile de services d'enchères et de mise aux enchères, la technologie publicitaire de l'acheteur doit :

  • S'assurer qu'elle a déployé son propre service de récupération d'annonces BYOS (voir la section correspondante).
  • S'assurer toutes les fonctions définies par l'utilisateur (UDF) nécessaires (prepareDataForAdRetrieval, generateBid, reportWin) sont définies et hébergées pour la technologie publicitaire.

Il est également utile de comprendre le fonctionnement des enchères protégées avec l'API Protected Audience avec les enchères et mises aux enchères, mais ce n'est pas obligatoire.

Configuration Terraform

Pour utiliser les signaux d'application protégés, les technologies publicitaires doivent :

  • Prendre en charge des signaux d'application protégés dans les enchères et mises aux enchères.
  • Fournir les points de terminaison d'URL à partir desquels les nouvelles UDF pour prepareDataForAdRetrieval, generateBid et reportWin peuvent être extraites.

En outre, ce guide part du principe que les technologies publicitaires qui souhaitent utiliser les enchères et mises aux enchères pour le remarketing doivent définir comme d'habitude tous les indicateurs de configuration existants pour les enchères de remarketing.

Configuration des technologies publicitaires des acheteurs

En prenant ce fichier de démonstration comme exemple, les acheteurs doivent définir les indicateurs suivants :

  • Activer les signaux d'application protégés : permet de collecter les données des signaux d'application protégés.
  • URL des signaux d'application protégés : définit les URL des serveurs de signaux d'application protégés.

Les technologies publicitaires doivent remplacer les URL appropriées dans les espaces réservés pour les champs suivants :

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

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"
    PROTECTED_APP_SIGNALS_GENERATE_BID_TIMEOUT_MS = "60000"
    AD_RETRIEVAL_KV_SERVER_ADDR                   = "<Ad Retrieval Service URL>"
    BYOS_AD_RETRIEVAL_SERVER                      = "true"
    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"

Configuration des technologies publicitaires des vendeurs

En prenant ce fichier démo comme exemple, les vendeurs doivent définir les indicateurs suivants. (Remarque : Seule la configuration liée aux signaux d'application protégés est mise en surbrillance ici.) Les technologies publicitaires doivent s'assurer de remplacer les URL appropriées dans les espaces réservés :

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"

Exemple de configuration

Prenons le scénario suivant : à l'aide de l'API Protected App Signals, une technologie publicitaire stocke des signaux pertinents en fonction de l'utilisation de l'application par l'utilisateur. Dans notre exemple, les signaux qui représentent les achats via plusieurs applications sont stockés. Lors d'une enchère, les signaux chiffrés sont collectés et transmis à des enchères protégées qui se déroulent dans les enchères et mises aux enchères. Les UDF de l'acheteur exécutées dans les enchères et les mises aux enchères utilisent ces signaux pour extraire les annonces candidates et calculer une enchère.

[Acheteur] Exemples de signaux

Ajoute un signal avec une clé de 0 et une valeur de 1.

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

Ajoute un signal avec une clé de 1 et une valeur de 2.

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

[Acheteur] Exemple d'utilisation d'encodeSignals

Encode chaque signal en deux octets, le premier octet étant le premier octet de la clé du signal et le deuxième octet étant le premier octet de la valeur du signal.

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};
}

[Acheteur] Exemple d'utilisation de 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;
}

[Acheteurs] Exemple de réponse du serveur de récupération d'annonces

Dans notre exemple, le serveur de récupération d'annonces envoie des métadonnées (c'est-à-dire l'identifiant de chaque annonce de cet exemple, mais il peut contenir d'autres données pour chacune des annonces susceptibles de vous aider à générer des enchères ultérieurement) pour chacune des k premières annonces candidates.

{"singlePartition":{"stringOutput":"[{\"adId\":\"0\"},{\"adId\":\"1\"}]"}}

[Acheteurs] Exemple d'utilisation de 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};
}

[Acheteurs] Exemple d'utilisation de reportWin

L'UDF reportWin indique à l'acheteur qu'il a remporté l'enchère. Dans le MVP, vous trouverez un paramètre supplémentaire appelé egressFeatures, qui n'est pas utilisé dans ce MVP, mais qui reste obligatoire dans la signature par souci d'exactitude.

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

[Vendeur] Configuration du serveur de KV

Les vendeurs doivent configurer un serveur de KV pour les signaux d'évaluation afin qu'un mappage entre les URL de rendu des annonces et les signaux d'évaluation correspondants soit disponible. Par exemple : si https:/buyer-domain.com/get-fitness-app et https:/buyer-domain.com/get-fastfood-app doivent être renvoyé par l'acheteur, le vendeur peut avoir l'exemple suivant de réponse des signaux d'évaluation lorsqu'il est interrogé par le SFE à l'aide d'un GET sur https://key-value-server-endpoint.com?client_type=1&renderUrls=<render-url-returned-by-the-buyer> :

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

[Vendeur] Exemple d'utilisation de 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
  };
}

[Vendeur] Exemple d'utilisation de reportResult

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

Application exemple

Pour illustrer comment l'API peut être utilisée pour créer une application qui utilise un flux simple (comme décrit ci-dessus), nous avons créé un exemple d'application Protected App Signals, disponible dans cet exemple de dépôt.