Guia para desenvolvedores Android da API Protected Audience

Ao ler a documentação do Sandbox de privacidade do Android, use o botão Prévia para desenvolvedores ou Beta para selecionar a versão do programa com que você está trabalhando, porque as instruções podem variar.


Enviar feedback

A API Protected Audience no Android (anteriormente conhecida como FLEDGE) inclui a API Custom Audience e a API Ad Selection. As plataformas de adtech e os anunciantes podem usar essas APIs para veicular anúncios personalizados com base no engajamento do app anterior, limitando o compartilhamento de identificadores entre apps e o de informações sobre interações do usuário com terceiros.

A API Custom Audience tem como foco a abstração do "público-alvo personalizado", que representa um grupo de usuários com intenções em comum. Um anunciante pode registrar um usuário em um público-alvo personalizado e associar anúncios relevantes a ele. Essas informações são armazenadas localmente e podem ser usadas para informar lances de anunciantes, filtragem e renderização de anúncios.

A API Ad Selection oferece um framework para que vários desenvolvedores façam um leilão localmente para um público-alvo personalizado. Para isso, o sistema considera anúncios relevantes associados ao público-alvo personalizado e executa outros processamentos nos anúncios que uma plataforma de adtech retorna ao dispositivo.

As plataformas de tecnologias de publicidade podem integrar essas APIs para implementar o remarketing, que preserva a privacidade dos usuários. O suporte a outros casos de uso, incluindo anúncios de instalação de apps, está planejado para versões futuras. Consulte a proposta de design para saber mais sobre a API Protected Audience.

Este guia descreve como trabalhar com essa API no Android para fazer o seguinte:

  1. Gerenciar públicos-alvo personalizados
  2. Configurar e executar a seleção de anúncios em um dispositivo
  3. Gerar relatórios de impressões de anúncio

Antes de começar

Antes de começar, siga estas etapas:

  1. Configure seu ambiente de desenvolvimento para o Sandbox de privacidade do Android.
  2. Instale uma imagem do sistema em um dispositivo com suporte ou configure um emulador que inclua suporte ao Sandbox de privacidade do Android.
  3. Em um terminal, ative o acesso à API Protected Audience (desativada por padrão) com o seguinte comando adb:

      adb shell device_config put adservices ppapi_app_allow_list \"*\"
    
  4. Inclua uma permissão ACCESS_ADSERVICES_CUSTOM_AUDIENCE no manifesto do app:

      <uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" />
    
  5. Faça referência a uma configuração de serviços de publicidade no elemento <application> do manifesto:

      <property android:name="android.adservices.AD_SERVICES_CONFIG"
                android:resource="@xml/ad_services_config" />
    
  6. Especifique o recurso XML dos serviços de publicidade referenciados no manifesto, como res/xml/ad_services_config.xml. Saiba mais sobre as permissões dos serviços de anúncios e o controle de acesso do SDK.

      <ad-services-config>
        <custom-audiences allowAllToAccess="true" />
      </ad-services-config>
    
  7. Por padrão, a API Ad Selection aplica limites na quantidade máxima de memória que um script de relatório de impressões ou de leilão pode alocar. O recurso de limitação de memória requer a versão 105.0.5195.58 ou mais recente do WebView. A plataforma aplica uma verificação de versão, e as chamadas para as APIs selectAds e reportImpression vão falhar se esse requisito não for atendido. Há duas opções para configurar esse recurso:

    • Opção 1: executar o seguinte comando adb para desativar essa verificação:

      adb device_config put fledge_js_isolate_enforce_max_heap_size false
      
    • Opção 2: instalar o WebView Beta pela Google Play Store, com uma versão igual ou maior que a indicada anteriormente.

Aderir a um público-alvo personalizado

Um público-alvo personalizado representa um grupo de usuários com intenções ou interesses em comum conforme decidido por um app de anunciante. Um app ou SDK pode usar um público-alvo personalizado para indicar um público específico, como alguém que deixou itens no carrinho de compras. Para criar ou fazer parte de um público-alvo personalizado de maneira assíncrona, siga estas etapas:

  1. Inicialize o objeto CustomAudienceManager.
  2. Crie um objeto CustomAudience especificando parâmetros importantes, por exemplo, o pacote do comprador e um nome relevante. Em seguida, inicialize o objeto JoinCustomAudienceRequest com o objeto CustomAudience.
  3. Chame o método joinCustomAudience() assíncrono com o objeto JoinCustomAudienceRequest e os objetos Executor e OutcomeReceiver relevantes.

Kotlin

val customAudienceManager: CustomAudienceManager =
    context.getSystemService(CustomAudienceManager::class.java)

// Initialize a custom audience.
val audience = CustomAudience.Builder()
    .setBuyer(buyer)
    .setName(name)
    ...
    .build()

// Initialize a custom audience request.
val joinCustomAudienceRequest: JoinCustomAudienceRequest =
    JoinCustomAudienceRequest.Builder().setCustomAudience(audience).build()

// Request to join a custom audience.
customAudienceManager.joinCustomAudience(joinCustomAudienceRequest,
    executor,
    outcomeReceiver)

Java

CustomAudienceManager customAudienceManager =
    context.getSystemService(CustomAudienceManager.class);

// Initialize a custom audience.
CustomAudience audience = new CustomAudience.Builder()
    .setBuyer(buyer)
    .setName(name)
    ...
    .build();

// Initialize a custom audience request.
JoinCustomAudienceRequest joinCustomAudienceRequest =
    new JoinCustomAudienceRequest.Builder().setCustomAudience(audience).build();

// Request to join a custom audience.
customAudienceManager.joinCustomAudience(joinCustomAudienceRequest,
    executor,
    outcomeReceiver);

A combinação dos parâmetros abaixo identifica cada objeto CustomAudience de forma exclusiva em um dispositivo:

  • owner: nome do pacote do app do proprietário, definido implicitamente como o nome do pacote do app autor da chamada.
  • buyer: identificador da rede de publicidade do comprador, que gerencia anúncios para o público-alvo personalizado.
  • name: um nome ou identificador arbitrário para o público-alvo personalizado.

Chamar joinCustomAudience() repetidamente com uma instância diferente de CustomAudience atualiza qualquer CustomAudience já existente com parâmetros owner, buyer e name correspondentes. Para preservar a privacidade, o resultado da API não distingue entre "criação" e "atualização".

Além disso, o CustomAudience precisa ser criado com estes parâmetros necessários:

  • URL de atualização diária: um URL HTTPS consultado diariamente em segundo plano para atualizar os indicadores de lances do usuário de um público-alvo personalizado e os dados de lances confiáveis, além de renderizar URLs e metadados de anúncios.
  • URL de lógica de lances: um URL HTTPS consultado durante a seleção de anúncios para buscar a lógica de lances JavaScript de um comprador. Confira as assinaturas de função necessárias no JavaScript.
  • IDs de renderização do anúncio: um ID arbitrário definido pela adtech do comprador. Essa é uma otimização para gerar o payload de lances e leilões (link em inglês).

Os parâmetros opcionais de um objeto CustomAudience podem incluir:

  • Momento de ativação: um público-alvo personalizado só pode participar da seleção de anúncios e das atualizações diárias após o momento da ativação. Isso pode ser útil para engajar usuários que não interagem mais com um app, por exemplo.
  • Prazo de validade: uma data futura para remoção do público personalizado do dispositivo.
  • Indicadores de lances do usuário: uma string JSON com indicadores do usuário, como localidade preferida, que o JavaScript da lógica de lances do comprador vai consumir para gerar lances durante o processo de seleção de anúncios. Esse formato permite que plataformas de adtech reutilizem o código em várias plataformas e facilita o consumo em funções JavaScript.
  • Dados de lances confiáveis: um URL HTTPS e uma lista de strings usadas durante o processo de seleção de anúncios para buscar indicadores de lances de um serviço de chave-valor confiável.
  • Anúncios: uma lista de objetos AdData correspondentes aos anúncios que participam da seleção. Cada objeto AdData consiste em:
    • URL de renderização: um URL HTTPS que é consultado para renderizar o anúncio final.
    • Metadados: um objeto JSON serializado como uma string que contém informações que vão ser consumidas pela lógica de lances do comprador durante o processo de seleção de anúncios.
    • Filtros de anúncios: uma classe que contém todas as informações necessárias para a filtragem de anúncios de instalação de apps e o limite de frequência durante a seleção de anúncios.

Confira um exemplo de instanciação do objeto CustomAudience:

Kotlin

// Minimal initialization of a CustomAudience object
val customAudience: CustomAudience = CustomAudience.Builder()
    .setBuyer(AdTechIdentifier.fromString("my.buyer.domain.name"))
    .setName("example-custom-audience-name")
    .setDailyUpdateUrl(Uri.parse("https://DAILY_UPDATE_URL"))
    .setBiddingLogicUrl(Uri.parse("https://BIDDING_LOGIC_URL"))
    .build()

Java

// Minimal initialization of a CustomAudience object
CustomAudience customAudience = CustomAudience.Builder()
    .setBuyer(AdTechIdentifier.fromString("my.buyer.domain.name"))
    .setName("example-custom-audience-name")
    .setDailyUpdateUrl(Uri.parse("https://DAILY_UPDATE_URL"))
    .setBiddingLogicUrl(Uri.parse("https://BIDDING_LOGIC_URL"))
    .build();

Processar resultados de joinCustomAudience()

O método joinCustomAudience() assíncrono usa o objeto OutcomeReceiver para informar o resultado da chamada de API.

  • O callback onResult() indica que o público-alvo personalizado foi criado ou atualizado.
  • O callback onError() indica duas condições possíveis.

Confira um exemplo de como processar o resultado de joinCustomAudience():

Kotlin

var callback: OutcomeReceiver<Void, AdServicesException> =
    object : OutcomeReceiver<Void, AdServicesException> {
    override fun onResult(result: Void) {
        Log.i("CustomAudience", "Completed joinCustomAudience")
    }

    override fun onError(error: AdServicesException) {
        // Handle error
        Log.e("CustomAudience", "Error executing joinCustomAudience", error)
    }
};

Java

OutcomeReceiver callback = new OutcomeReceiver<Void, AdServicesException>() {
    @Override
    public void onResult(@NonNull Void result) {
        Log.i("CustomAudience", "Completed joinCustomAudience");
    }

    @Override
    public void onError(@NonNull AdServicesException error) {
        // Handle error
        Log.e("CustomAudience", "Error executing joinCustomAudience", error);
    }
};

Sair de um público-alvo personalizado

Se o usuário não atender mais aos critérios comerciais de um determinado público-alvo personalizado, um app ou SDK vai poder chamar leaveCustomAudience() para remover o público-alvo do dispositivo. Para remover um CustomAudience com base nos parâmetros exclusivos, siga estas etapas:

  1. Inicialize o objeto CustomAudienceManager.
  2. Inicialize a LeaveCustomAudienceRequest com os elementos buyer e name do público-alvo personalizado. Para saber mais sobre esses campos de entrada, consulte Aderir a um público-alvo personalizado.
  3. Chame o método assíncrono leaveCustomAudience() com o objeto LeaveCustomAudienceRequest e os objetos Executor e OutcomeReceiver relevantes.

Kotlin

val customAudienceManager: CustomAudienceManager =
    context.getSystemService(CustomAudienceManager::class.java)

// Initialize a LeaveCustomAudienceRequest
val leaveCustomAudienceRequest: LeaveCustomAudienceRequest =
    LeaveCustomAudienceRequest.Builder()
        .setBuyer(buyer)
        .setName(name)
        .build()

// Request to leave a custom audience
customAudienceManager.leaveCustomAudience(
    leaveCustomAudienceRequest,
    executor,
    outcomeReceiver)

Java

CustomAudienceManager customAudienceManager =
    context.getSystemService(CustomAudienceManager.class);

// Initialize a LeaveCustomAudienceRequest
LeaveCustomAudienceRequest leaveCustomAudienceRequest =
    new LeaveCustomAudienceRequest.Builder()
        .setBuyer(buyer)
        .setName(name)
        .build();

// Request to leave a custom audience
customAudienceManager.leaveCustomAudience(
    leaveCustomAudienceRequest,
    executor,
    outcomeReceiver);

O OutcomeReceiver informa o final de uma chamada de API de maneira semelhante à chamada do método joinCustomAudience(). Para proteger a privacidade, um resultado de erro não faz distinção entre erros internos e argumentos inválidos. O callback onResult() é chamado quando a chamada de API é concluída, independente de um público-alvo personalizado correspondente ser removido ou não.

Executar a seleção de anúncios

Para usar a API Protected Audience na seleção de anúncios, chame o método selectAds():

  1. Inicialize um objeto AdSelectionManager.
  2. Crie um objeto AdSelectionConfig.
  3. Chame o método assíncrono selectAds() com o objeto AdSelectionConfig e os objetos Executor e OutcomeReceiver relevantes.

Kotlin

val adSelectionManager: AdSelectionManager =
  context.getSystemService(AdSelectionManager::class.java)

// Initialize AdSelectionConfig
val adSelectionConfig: AdSelectionConfig =
  AdSelectionConfig.Builder().setSeller(seller)
    .setDecisionLogicUrl(decisionLogicUrl)
    .setCustomAudienceBuyers(customAudienceBuyers)
    .setAdSelectionSignals(adSelectionSignals)
    .setSellerSignals(sellerSignals)
    .setPerBuyerSignals(perBuyerSignals)
    .setBuyerContextualAds(
      Collections.singletonMap(
        contextualAds.getBuyer(), contextualAds
      )
    ).build()

// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(
  adSelectionConfig, executor, outcomeReceiver
)

Java

AdSelectionManager adSelectionManager =
    context.getSystemService(AdSelectionManager.class);

// Initialize AdSelectionConfig
AdSelectionConfig adSelectionConfig =
  new AdSelectionConfig.Builder()
    .setSeller(seller)
    .setDecisionLogicUrl(decisionLogicUrl)
    .setCustomAudienceBuyers(customAudienceBuyers)
    .setAdSelectionSignals(adSelectionSignals)
    .setSellerSignals(sellerSignals)
    .setPerBuyerSignals(perBuyerSignals)
    .setBuyerContextualAds(
      Collections.singletonMap(contextualAds.getBuyer(), contextualAds)
    )
    .build();

// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(adSelectionConfig, executor, outcomeReceiver);

O método selectAds() requer uma entrada AdSelectionConfig, em que é preciso especificar os parâmetros abaixo:

  • Vendedor: o identificador da rede de publicidade do vendedor, que inicia a seleção de anúncios.
  • URL de lógica de decisão: um URL HTTPS consultado para receber a lógica JavaScript da rede de publicidade do vendedor.
    • URL HTTPS: consultado para receber a lógica JavaScript da rede de anúncios do vendedor. Consulte as assinaturas de função obrigatórias.
    • URI pré-criado: que segue o formato de seleção de anúncios do FLEDGE. Uma IllegalArgumentException será gerada se um URI pré-criado sem suporte ou malformado for transmitido.
  • Compradores de públicos-alvo personalizados: uma lista completa de identificadores das redes de publicidade de compradores aceitas pelo vendedor para participar do processo de seleção de anúncios. Esses identificadores de comprador correspondem ao método CustomAudience.getBuyer() dos públicos-alvo personalizados participantes.

Os parâmetros abaixo podem ser especificados para ter uma seleção de anúncios mais personalizada:

  • Indicadores de seleção de anúncios: um objeto JSON serializado como uma string que contém indicadores que vão ser consumidos pelo JavaScript da lógica de lances do comprador buscado por CustomAudience.getBiddingLogicUrl().
  • Indicadores de vendedor: um objeto JSON serializado como uma string que contém indicadores que vão ser consumidos pela lógica de decisão JavaScript do vendedor buscada por AdSelectionConfig.getDecisionLogicUrl().
  • Indicadores por comprador: um mapa de objetos JSON serializados como strings que contém indicadores a serem consumidos pelo JavaScript da lógica de lances de compradores específica buscada por CustomAudience.getBiddingLogicUrl(). Essas strings são identificadas pelos campos de comprador dos públicos-alvo personalizados participantes.
  • Anúncios contextuais: um conjunto de candidatos coletados diretamente de compradores durante um leilão que acontece fora de um leilão de da API Protected Audience.

Após a seleção de um anúncio, os resultados, lances e indicadores são armazenados internamente para relatórios. O callback OutcomeReceiver.onResult() retorna um AdSelectionOutcome que contém:

  • Um URL de renderização do anúncio vencedor, extraído de AdData.getRenderUrl().
  • Um ID de seleção de anúncios exclusivo para o usuário do dispositivo. O ID é usado para gerar relatórios sobre a impressão do anúncio.

Se a seleção de anúncios não for concluída por motivos como argumentos inválidos, tempos limite ou consumo excessivo de recursos, o callback OutcomeReceiver.onError() vai gerar uma AdServicesException com estes comportamentos:

  • Se a seleção de anúncios for iniciada com argumentos inválidos, a AdServicesException vai indicar uma IllegalArgumentException como causa.
  • Todos os outros erros vão receber uma AdServicesException com uma IllegalStateException como causa.

Anúncios contextuais

As APIs Protected Audience podem incorporar anúncios contextuais a um leilão protegido. Os anúncios contextuais precisam ser selecionados no servidor da adtech, assinados e retornados ao dispositivo fora das APIs Protected Audience. Eles podem ser incluídos no leilão usando AdSelectionConfig. As assinaturas serão verificadas, e os anúncios contextuais vão funcionar da mesma forma que os anúncios no dispositivo, incluindo a qualificação para filtragem negativa de anúncios. Depois que o leilão da API Protected Audience for concluído, será necessário invocar reportImpression(). reportWin() será chamado no anúncio contextual vencedor, seguindo o mesmo padrão dos relatórios de impressões, para receber o anúncio vencedor em um dispositivo. Cada anúncio contextual precisa de um comprador, um lance, um link para a lógica de relatórios, um URL de renderização e metadados do anúncio.

Para implantar anúncios contextuais, o app de destino precisa criar um objeto SignedContextualAds:

Kotlin

val signedContextualAds: SignedContextualAds =
  Builder().setBuyer(AdTechIdentifier.fromString(mBiddingLogicUri.getHost()))
    //Pass in your valid app install ads
    .setDecisionLogicUri(mContextualLogicUri)
    .setAdsWithBid(appInstallAd)
    .setSignature(signature)
    .build()

Java

SignedContextualAds signedContextualAds = new SignedContextualAds.Builder()
  .setBuyer(AdTechIdentifier.fromString(mBiddingLogicUri.getHost()))
  .setDecisionLogicUri(mContextualLogicUri)
  //Pass in your valid app install ads
  .setAdsWithBid(appInstallAd)
  .setSignature(signature)
  .build();

O objeto SignedContextualAds resultante pode ser transmitido durante a criação do AdSelectionConfig:

Kotlin

val adSelectionConfig = AdSelectionConfig.Builder()
  .setPerBuyerSignedContextualAds(signedContextualAds)
  // Other fields for your config
  .build()

Java

AdSelectionConfig adSelectionConfig = new AdSelectionConfig.Builder()
    .setPerBuyerSignedContextualAds(signedContextualAds)
    //Other fields for your config
    .build();

Filtragem de anúncios de instalação de apps

A filtragem de anúncios de instalação de apps ajuda a filtrar anúncios de instalação de apps que já estão instalados em um dispositivo.

A primeira etapa do processo é definir quais anunciantes podem filtrar o pacote instalado. Isso precisa acontecer no app que você quer segmentar com um anúncio.

Kotlin

//Create a request for setting the app install advertisers
val adtech = AdTechIdentifier.fromString("your.enrolled.uri")
val adtechSet = setOf(adtech)
val request = SetAppInstallAdvertisersRequest(adtechSet)

//Set the app install advertisers in the ad selection manager
mAdSelectionManager.setAppInstallAdvertisers(
  request,
  mExecutor,
  object : OutcomeReceiver<Any?, Exception?>() {
    fun onResult(@NonNull ignoredResult: Any?) {
      Log.v("[your tag]", "Updated app install advertisers")
    }

    fun onError(@NonNull error: Exception?) {
      Log.e("[your tag]", "Failed to update app install advertisers", error)
    }
  })

Java

//Create a request for setting the app install advertisers
AdTechIdentifier adtech = AdTechIdentifier.fromString("your.enrolled.uri");
Set<AdTechIdentifier> adtechSet = Collections.singleton(adtech);
SetAppInstallAdvertisersRequest request = new SetAppInstallAdvertisersRequest(adtechSet);

//Set the app install advertisers in the ad selection manager
mAdSelectionManager.setAppInstallAdvertisers(
  request,
  mExecutor,
  new OutcomeReceiver<Object, Exception>() {
    @Override
    public void onResult(@NonNull Object ignoredResult) {
      Log.v("[your tag]", "Updated app install advertisers");
    }

    @Override
    public void onError(@NonNull Exception error) {
      Log.e("[your tag]", "Failed to update app install advertisers", error);
    }
  });

Quando o código anterior é executado, os anunciantes transmitidos podem filtrar os apps instalados que você especifica durante a geração de lances. Se você precisar impedir que um anunciante tenha acesso ao status de instalação desse app, execute esse código novamente com as informações do anunciante removidas.

A próxima etapa é configurar a filtragem de anúncios no app do editor. A parte que veicula o anúncio (provavelmente um SDK do lado do fornecedor) precisa inicializar o objeto AdFilters com informações sobre quais anúncios relacionados aos apps quer filtrar:

Kotlin

// Instantiate AdFilters object with package names.
val filters: AdFilters = Builder().setAppInstallFilters(
    Builder().setPackageNames(setOf("example.target.app")).build()
  ).build()

Java

// Instantiate AdFilters object with package names.
AdFilters filters = new AdFilters.Builder()
.setAppInstallFilters(
  new AppInstallFilters.Builder()
  .setPackageNames(Collections.singleton("example.target.app"))
  .build())
.build();

Os editores da demanda também podem definir um AdFilter para anúncios que existem dentro dos públicos-alvo personalizados deles.

AdFilters também podem ser transmitidos no ponto de instanciação de um novo objeto AdData:

Kotlin

// Instantiate an AdData object with the AdFilters created in the
// previous example.
val appInstallAd: AdData =
  Builder().setMetadata("{ ... }") // Valid JSON string
    .setRenderUri(Uri.parse("www.example-dsp1.com/.../campaign123.html"))
    .setAdFilters(filters).build()

Java

// Instantiate an AdData object with the AdFilters created in the
// previous example.
AdData appInstallAd = new AdData.Builder()
.setMetadata("{ ... }") // Valid JSON string
.setRenderUri(Uri.parse("www.example-dsp1.com/.../campaign123.html"))
    .setAdFilters(filters)
    .build();

Filtragem do limite de frequência

A filtragem de limite de frequência permite que as adtechs limitem o número de vezes que um anúncio é exibido. A filtragem do limite de frequência reduz a superexposição do anúncio e otimiza a seleção de anúncios alternativos para determinada campanha.

Há dois componentes principais de um filtro de limite de frequência: o tipo de evento de anúncio e a chave do contador de anúncios. Os tipos de evento de anúncio que podem ser usados são estes:

  • Vitória: um evento de vitória indica que o anúncio ganhou um leilão. Os eventos vencedores são atualizados automaticamente pela API Protected Audience e não podem ser chamados diretamente pelo desenvolvedor. Os dados de vitórias só são visíveis para anúncios em um determinado público-alvo personalizado.
  • Impressão: separada de reportImpression, o autor da chamada no dispositivo (SSP ou MMP) usa updateAdCounterHistogram() para invocar eventos de impressão no ponto escolhido no código. Os eventos de impressão são visíveis para todos os anúncios de um determinado DSP e não são limitados a anúncios no mesmo público-alvo personalizado.
  • Visualização: o evento é invocado pelo autor da chamada no dispositivo (SSP ou MMP) em um ponto escolhido no código usando uma chamada para updateAdCounterHistogram(). Os eventos de visualização são visíveis para todos os anúncios de um determinado DSP e não são limitados a anúncios no mesmo público-alvo personalizado.
  • Clique: o evento é invocado pelo autor da chamada no dispositivo (SSP ou MMP) em um ponto escolhido no código usando uma chamada para updateAdCounterHistogram(). Os eventos de clique são visíveis para todos os anúncios de um determinado DSP e não são limitados a anúncios no mesmo público-alvo personalizado.

No app do editor, uma SSP ou MMP que está presente no dispositivo invoca eventos de anúncio. Quando updateAdCounterHistogram() é chamado, o contador de um filtro de limite de frequência é incrementado para que os leilões futuros tenham informações atualizadas sobre a exposição de um usuário a determinado anúncio. Os tipos de evento de anúncio não são vinculados à ação do usuário correspondente e são diretrizes fornecidas para ajudar os autores da chamada a estruturar o sistema de eventos. Para incrementar os contadores de anúncios no momento de um evento, o ator no dispositivo oferece o ID de seleção de anúncios do leilão vencedor.

As chaves de contador de anúncio são números inteiros arbitrários de 32 bits atribuídos por uma adtech do comprador e correspondem a um determinado conjunto de anúncios, conforme definido pela DSP. Como as chaves de contador de anúncios são limitadas somente aos anúncios que pertencem a um determinado DSP, essas chaves podem ser selecionadas sem sobreposição com histogramas de outra adtech. As chaves de contador de anúncio são usadas para incrementar identificadores específicos do DSP em anúncios de um DSP ou em um determinado público-alvo personalizado para filtrar anúncios de leilões futuros.

As chaves de contador podem ser usadas para priorizar anúncios com maior probabilidade de serem interessantes para determinado usuário com base nas interações dele com outros anúncios de uma adtech. Por exemplo, um anúncio que recebeu um alto nível de engajamento por vitórias em leilões de anúncios, visualizações e cliques representa um ponto de dados inferido. Por exemplo: um anúncio de tacos de golf para canhotos pode indicar que o usuário não tem interesse em tacos para destros. Um filtro de limite de frequência definido para uma chave de contador atribuída a anúncios de "canhoto" pode eliminar anúncios para destros.

Para usar o limite de frequência no leilão, primeiro é necessário criar objetos KeyedFrequencyCap, conforme mostrado abaixo:

Kotlin

// Value used when incrementing frequency counter
val adCounterKey = 123

// Frequency cap exceeded after 2 counts
val keyedFrequencyCapForImpression: KeyedFrequencyCap = Builder(
  adCounterKey, 2, Duration.ofSeconds(10)
).build()

// Frequency cap exceeded after 1 counts
val keyedFrequencyCapForImpression: KeyedFrequencyCap = Builder(
  adCounterKey, 1, Duration.ofSeconds(10)
).build()

Java

// Value used when incrementing frequency counter
int adCounterKey = 123;

// Frequency cap exceeded after 2 counts
KeyedFrequencyCap keyedFrequencyCapForImpression =
  new KeyedFrequencyCap.Builder(
    adCounterKey, 2, Duration.ofSeconds(10)
  ).build();

// Frequency Cap exceeded after 1 counts
KeyedFrequencyCap keyedFrequencyCapForClick =
  new KeyedFrequencyCap.Builder(
    adCounterKey, 1, Duration.ofSeconds(10)
  ).build();

Após a criação, os objetos KeyedFrequencyCap podem ser transmitidos para um AdFilters.

Kotlin

val filters: AdFilters = Builder()
  .setFrequencyCapFilters(
    Builder()
      .setKeyedFrequencyCapsForImpressionEvents(
        ImmutableObject.of(keyedFrequencyCapForImpression)
      )
      .setKeyedFrequencyCapsForClickEvents(
        ImmutableObject.of(keyedFrequencyCapForClick)
      )
  ).build()

Java

AdFilters filters = new AdFilters.Builder()
    .setFrequencyCapFilters(new FrequencyCapFilters.Builder()
        .setKeyedFrequencyCapsForImpressionEvents(
            ImmutableObject.of(keyedFrequencyCapForImpression)
        )
        .setKeyedFrequencyCapsForClickEvents(
            ImmutableObject.of(keyedFrequencyCapForClick)
        )
    ).build();

Quando o objeto AdFilters é preenchido com filtros de limite de frequência, ele pode ser transmitido junto com o público-alvo personalizado:

Kotlin

// Initialize a custom audience.
val audience: CustomAudience = Builder()
  .setBuyer(buyer)
  .setName(name)
  .setAds(
    listOf(
      Builder()
        .setRenderUri(renderUri)
        .setMetadata(JSONObject().toString())
        .setAdFilters(filters)
        .setAdCounterKeys(adCounterKeys)
        .build()
    )
  ).build()

Java

// Initialize a custom audience.
CustomAudience audience = new CustomAudience.Builder()
    .setBuyer(buyer)
    .setName(name)
    .setAds(Collections.singletonList(new AdData.Builder()
        .setRenderUri(renderUri)
        .setMetadata(new JSONObject().toString())
        .setAdFilters(filters)
        .setAdCounterKeys(adCounterKeys)
        .build()))
    .build();

Quando os filtros de limite de frequência são implementados em um público-alvo personalizado, o SSP pode invocar os eventos de clique, visualização ou impressão necessários.

Kotlin

val callerAdTech: AdTechIdentifier = mAdSelectionConfig.getSeller()

val request: UpdateAdCounterHistogramRequest = Builder(
  adSelectionId,
  FrequencyCapFilters.AD_EVENT_TYPE_CLICK,  //CLICK, VIEW, or IMPRESSION
  callerAdTech
).build()

Java

AdTechIdentifier callerAdTech = mAdSelectionConfig.getSeller();

UpdateAdCounterHistogramRequest request =
  new UpdateAdCounterHistogramRequest.Builder(
      adSelectionId,
      FrequencyCapFilters.AD_EVENT_TYPE_CLICK, //CLICK, VIEW, or IMPRESSION
      callerAdTech
).build();

Os anúncios que atingiram os limites de filtro de limite de frequência predefinidos são eliminados do leilão. A filtragem acontece antes que a lógica de lances seja executada em leilões no dispositivo e enquanto o payload é gerado para serviços de lances e leilões. Esse kit de ferramentas oferece às adtechs flexibilidade para usar as interações entre usuários e anúncios nos públicos-alvo personalizados para focar a segmentação de anúncios e minimizar a superexposição.

Como criar assinaturas

Algoritmo de assinatura e geração de chaves

Use o algoritmo ECDSA para assinar seus anúncios. O tipo de chave precisa ser P-256 ECDSA (também conhecido como "secp256r1" ou "prime256v1"). Confira alguns exemplos de como fazer login em várias linguagens:

Java

import java.security.*;

public class ECDSASignatureManager {
    private static KeyPair generateKeyPair() throws Exception {
        // Specify the curve name P-256
        ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");

        // Initialize the KeyPairGenerator
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
        keyPairGenerator.initialize(ecSpec);

        // Generate the KeyPair
        return keyPairGenerator.generateKeyPair();
    }

    public byte[] sign(byte[] data, byte[] privateKey) throws Exception {
        Signature ecdsa = Signature.getInstance("SHA256withECDSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory keyFactory = KeyFactory.getInstance("EC");
        PrivateKey aPrivateKey = keyFactory.generatePrivate(keySpec);

        ecdsa.initSign(aPrivateKey);
        ecdsa.update(data);

        return ecdsa.sign();
    }
}

JavaScript

const crypto = require('crypto');

function generateKeyPair() {
  const keyPair = crypto.generateKeyPairSync('ec', {
    // Specifies ECDSA algorithm
    namedCurve: 'prime256v1',
    publicKeyEncoding: { type: 'spki', format: 'der' },
    privateKeyEncoding: { type: 'pkcs8', format: 'der'}
  });

  return {
    publicKey: keyPair.publicKey,
    privateKey: keyPair.privateKey
  };
}

function signContextualAds(data, privateKey) {
  // Convert the private key from PKCS8 to PEM format
  const base64PrivateKey = Buffer.from(privateKey).toString('base64');
  const pemPrivateKey = `-----BEGIN PRIVATE KEY-----\n${base64PrivateKey}\n-----END PRIVATE KEY-----`;

  // Create a Sign object using SHA256
  const sign = crypto.createSign('SHA256');
  sign.update(Buffer.from(data));

  // Sign the data with ECDSA as specified in the key specs
  return sign.sign(pemPrivateKey);
}

Python

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec

def sign_contextual_ads(data, private_key_der):
    private_key = serialization.load_der_private_key(
        private_key_der,
        password=None,
        backend=default_backend()
    )

    signature = private_key.sign(
        data,
        ec.ECDSA(hashes.SHA256())
    )

    return signature

def generate_key_pair():
    private_key = ec.generate_private_key(ec.SECP256R1())

    # Serialize the keys into DER format
    public_key_der = private_key.public_key().public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )

    private_key_der = private_key.private_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )

    return public_key_der, private_key_der

Serialização

Como as assinaturas serão criadas nos servidores do comprador, é necessário um conjunto de regras canônicas para manter a serialização de um determinado SignedContextualAds consistente em todos os sistemas.

Regras canônicas

Um conjunto de regras pode garantir que a serialização seja consistente entre a adtech e a API Protected Audience. Essas regras vão estar no guia do desenvolvedor, ao lado dos snippets de código de diferentes linguagens, para facilitar a adoção.

  • Os objetos concatenam os valores serializados dos campos com "|" (barra vertical) depois de cada campo. O último campo também terá "|" para simplificar: "field_name_1=value1|field_name_2=value2|".
  • Os campos de objeto são serializados ao concatenar o nome do campo no formato snake case, sinal de igual e o valor do campo serializado: "my_field=|".
  • Todos os campos são classificados por ordem alfabética dos nomes dos campos no objeto.
  • Os campos anuláveis (opcionais) são ignorados (vazios) quando têm valores nulos/não definidos.
  • Os duplos são convertidos em uma precisão de preservação de string de dois dígitos após o ponto decimal.
  • Os inteiros são convertidos em valores de string.
  • Os conjuntos estão em ordem alfabética.
  • As listas mantêm a mesma ordem e são separadas por vírgulas.
  • A string é codificada em byte[] usando UTF-8.

Java

public class ContextualAdsSerializationUtil {
    private static final String FIELD_SEPARATOR = "|";
    private static final String ENUMERATOR_SEPARATOR = ",";

    private final ByteArrayOutputStream mByteArrayStream;

    public ContextualAdsSerializationUtil() {
        this.mByteArrayStream = new ByteArrayOutputStream();
    }

    byte[] getBytes() {
        return this.mByteArrayStream.toByteArray();
    }

    public byte[] serialize(ContextualAds ads) {
        writeContextualAds(ads);
        return getBytes();
    }

    private void writeContextualAds(ContextualAds ads) {
        writeString("buyer=");
        writeField(ads.getBuyer().getIdentifier(), this::writeString);

        writeString("decision_logic_uri=");
        writeField(ads.getDecisionLogicUri().toString(), this::writeString);

        writeString("ads_with_bid=");
        writeList(ads.getAdsWithBid(), this::writeAdWithBid);
    }

    private void writeAdWithBid(AdWithBid adWithBid) {
        writeString("ad_data=");
        writeField(adWithBid.getAdData(), this::writeAdData);

        writeString("bid=");
        writeField(adWithBid.getBid(), this::writeDouble);
    }

    private void writeAdData(AdData adData) {
        writeString("ad_counter_keys=");
        writeSet(adData.getAdCounterKeys(), this::writeInt);

        if (!Objects.isNull(adData.getAdFilters())) {
            writeString("ad_filters=");
            writeField(adData.getAdFilters(), this::writeAdFilters);
        }

        if (!Objects.isNull(adData.getAdRenderId())) {
            writeString("ad_render_id=");
            writeField(adData.getAdRenderId(), this::writeString);
        }

        writeString("metadata=");
        writeField(adData.getMetadata(), this::writeString);

        writeString("render_uri=");
        writeField(adData.getRenderUri().toString(), this::writeString);
    }

    private void writeAdFilters(AdFilters adFilters) {
        if (!Objects.isNull(adFilters.getAppInstallFilters())) {
            writeString("app_install_filters=");
            writeField(adFilters.getAppInstallFilters(), this::writeAppInstallFilter);
        }

        if (!Objects.isNull(adFilters.getFrequencyCapFilters())) {
            writeString("frequency_cap_filters=");
            writeField(adFilters.getFrequencyCapFilters(), this::writeFrequencyCapFilters);
        }
    }

    private void writeAppInstallFilter(AppInstallFilters appInstallFilters) {
        writeString("package_names=");
        writeSet(appInstallFilters.getPackageNames(), this::writeString);
    }

    private void writeFrequencyCapFilters(FrequencyCapFilters frequencyCapFilters) {
        writeString("keyed_frequency_caps_for_click_events=");
        writeList(
            frequencyCapFilters.getKeyedFrequencyCapsForClickEvents(),
            this::writeKeyedFrequencyCap);

        writeString("keyed_frequency_caps_for_impression_events=");
        writeList(
            frequencyCapFilters.getKeyedFrequencyCapsForImpressionEvents(),
            this::writeKeyedFrequencyCap);

        writeString("keyed_frequency_caps_for_view_events=");
        writeList(
            frequencyCapFilters.getKeyedFrequencyCapsForViewEvents(),
            this::writeKeyedFrequencyCap);

        writeString("keyed_frequency_caps_for_win_events=");
        writeList(
            frequencyCapFilters.getKeyedFrequencyCapsForWinEvents(),
            this::writeKeyedFrequencyCap);
    }

    private void writeKeyedFrequencyCap(KeyedFrequencyCap keyedFrequencyCap) {
        writeString("ad_counter_key=");
        writeField(keyedFrequencyCap.getAdCounterKey(), this::writeInt);

        writeString("interval=");
        writeField(keyedFrequencyCap.getInterval().toMillis(), this::writeLong);

        writeString("max_count=");
        writeField(keyedFrequencyCap.getMaxCount(), this::writeInt);
    }

    private <T> void writeField(T field, Consumer<T> writerConsumer) {
        Objects.requireNonNull(field);

        writerConsumer.accept(field);
        writeString(FIELD_SEPARATOR);
    }

    private <T> void writeList(List<T> list, Consumer<T> writerConsumer) {
        FirstElementStateHolder state = new FirstElementStateHolder();
        for (T t : list) {
            if (!state.isFirstThenSetFalse()) {
                writeString(ENUMERATOR_SEPARATOR);
            }
            writerConsumer.accept(t);
        }
        writeString(FIELD_SEPARATOR);
    }

    private <T> void writeSet(Set<T> set, Consumer<T> writerConsumer) {
        FirstElementStateHolder state = new FirstElementStateHolder();
        set.stream()
            .sorted()
            .forEach(
                (value) -> {
                    if (!state.isFirstThenSetFalse()) {
                        writeString(ENUMERATOR_SEPARATOR);
                    }
                    writerConsumer.accept(value);
                });
        writeString(FIELD_SEPARATOR);
    }

    void writeString(String value) {
        mByteArrayStream.writeBytes(value.getBytes(StandardCharsets.UTF_8));
    }

    void writeLong(long value) {
        writeString(Long.toString(value));
    }

    void writeDouble(double value) {
        writeString(String.format("%.2f", value));
    }

    void writeInt(int value) {
        writeString(Integer.toString(value));
    }

    private static class FirstElementStateHolder {

        private boolean mIsFirst = true;

        public boolean isFirstThenSetFalse() {
            boolean initialValue = mIsFirst;
            mIsFirst = false;
            return initialValue;
        }
    }
}

JavaScript

class SignedContextualAdsHashUtil {
  constructor() {
    this.serializedString = '';
  }

  serialize(ads) {
    this.writeContextualAds(ads);
    return new TextEncoder().encode(this.serializedString);
  }

  writeContextualAds(ads) {
    this.writeString('buyer=');
    this.writeField(ads.buyer.identifier, value => this.writeString(value));

    this.writeString('decision_logic_uri=');
    this.writeField(ads.decisionLogicUri, value => this.writeString(value));

    this.writeString('ads_with_bid=');
    this.writeList(ads.adsWithBid, adWithBid => this.writeAdWithBid(adWithBid));
  }

  writeAdWithBid(adWithBid) {
    this.writeString('ad_data=');
    this.writeField(adWithBid.adData, adData => this.writeAdData(adData));

    this.writeString('bid=');
    this.writeField(adWithBid.bid, value => this.writeDouble(value));
  }

  writeAdData(adData) {
    this.writeString('ad_counter_keys=');
    this.writeSet([...adData.adCounterKeys], value => this.writeInt(value));

    if (adData.adFilters) {
      this.writeString('ad_filters=');
      this.writeField(adData.adFilters, adFilters => this.writeAdFilters(adFilters));
    }

    if (adData.adRenderId) {
      this.writeString('ad_render_id=');
      this.writeField(adData.adRenderId, value => this.writeString(value));
    }

    this.writeString('metadata=');
    this.writeField(adData.metadata, value => this.writeString(value));

    this.writeString('render_uri=');
    this.writeField(adData.renderUri, value => this.writeString(value));
  }

  writeAdFilters(adFilters) {
    if (adFilters.appInstallFilters) {
      this.writeString('app_install_filters=');
      this.writeField(adFilters.appInstallFilters, appInstallFilters => this.writeAppInstallFilter(appInstallFilters));
    }

    if (adFilters.frequencyCapFilters) {
      this.writeString('frequency_cap_filters=');
      this.writeField(adFilters.frequencyCapFilters, frequencyCapFilters => this.writeFrequencyCapFilters(frequencyCapFilters));
    }
  }

  writeAppInstallFilter(appInstallFilters) {
    this.writeString('package_names=');
    this.writeSet([...appInstallFilters.packageNames], value => this.writeString(value));
  }

  writeFrequencyCapFilters(frequencyCapFilters) {
    this.writeString('keyed_frequency_caps_for_click_events=');
    this.writeList(frequencyCapFilters.keyedFrequencyCapsForClickEvents, cap => this.writeKeyedFrequencyCap(cap));

    this.writeString('keyed_frequency_caps_for_impression_events=');
    this.writeList(frequencyCapFilters.keyedFrequencyCapsForImpressionEvents, cap => this.writeKeyedFrequencyCap(cap));

    this.writeString('keyed_frequency_caps_for_view_events=');
    this.writeList(frequencyCapFilters.keyedFrequencyCapsForViewEvents, cap => this.writeKeyedFrequencyCap(cap));

    this.writeString('keyed_frequency_caps_for_win_events=');
    this.writeList(frequencyCapFilters.keyedFrequencyCapsForWinEvents, cap => this.writeKeyedFrequencyCap(cap));
  }

  writeKeyedFrequencyCap(cap) {
    this.writeString('ad_counter_key=');
    this.writeField(cap.adCounterKey, value => this.writeInt(value));

    this.writeString('interval=');
    this.writeField(cap.interval, value => this.writeLong(value));

    this.writeString('max_count=');
    this.writeField(cap.maxCount, value => this.writeInt(value));
  }

  writeField(field, writer) {
    writer(field);
    this.writeString('|');
  }

  writeList(list, writer) {
    list.forEach((item, index) => {
      if (index > 0) this.writeString(',');
      writer(item);
    });
    this.writeString('|');
  }

  writeSet(set, writer) {
    [...set].sort().forEach((item, index) => {
      if (index > 0) this.writeString(',');
      writer(item);
    });
    this.writeString('|');
  }

  writeString(value) {
    this.serializedString += value;
  }

  writeLong(value) {
    this.writeString(value.toString());
  }

  writeDouble(value) {
    this.writeString(value.toFixed(2));
  }

  writeInt(value) {
    this.writeString(value.toString());
  }
}

module.exports = {
  SignedContextualAdsHashUtil,
};

Python

class SignedContextualAdsHashUtil:
    def __init__(self):
        self.serialized_string = ''

    def serialize(self, ads):
        self.write_contextual_ads(ads)
        return self.serialized_string.encode('utf-8')

    def write_contextual_ads(self, ads):
        self.write_string('buyer=')
        self.write_field(ads.buyer.identifier, lambda v: self.write_string(v))

        self.write_string('decision_logic_uri=')
        self.write_field(ads.decision_logic_uri, lambda v: self.write_string(v))

        self.write_string('ads_with_bid=')
        self.write_list(ads.ads_with_bid, self.write_ad_with_bid)

    def write_ad_with_bid(self, ad_with_bid):
        self.write_string('ad_data=')
        self.write_field(ad_with_bid.ad_data, self.write_ad_data)

        self.write_string('bid=')
        self.write_field(ad_with_bid.bid, lambda v: self.write_double(v))

    def write_ad_data(self, ad_data):
        self.write_string('ad_counter_keys=')
        self.write_set(ad_data.ad_counter_keys, lambda v: self.write_int(v))

        if ad_data.ad_filters:
            self.write_string('ad_filters=')
            self.write_field(ad_data.ad_filters, self.write_ad_filters)

        if ad_data.ad_render_id:
            self.write_string('ad_render_id=')
            self.write_field(ad_data.ad_render_id, lambda v: self.write_string(v))

        self.write_string('metadata=')
        self.write_field(ad_data.metadata, lambda v: self.write_string(v))

        self.write_string('render_uri=')
        self.write_field(ad_data.render_uri, lambda v: self.write_string(v))

    def write_ad_filters(self, ad_filters):
        if ad_filters.app_install_filters:
            self.write_string('app_install_filters=')
            self.write_field(ad_filters.app_install_filters,
                            self.write_app_install_filter)

        if ad_filters.frequency_cap_filters:
            self.write_string('frequency_cap_filters=')
            self.write_field(ad_filters.frequency_cap_filters,
                            self.write_frequency_cap_filters)

    def write_app_install_filter(self, app_install_filters):
        self.write_string('package_names=')
        self.write_set(app_install_filters.package_names,
                      lambda v: self.write_string(v))

    def write_frequency_cap_filters(self, frequency_cap_filters):
        for attr in ['keyed_frequency_caps_for_click_events',
                    'keyed_frequency_caps_for_impression_events',
                    'keyed_frequency_caps_for_view_events',
                    'keyed_frequency_caps_for_win_events']:
            self.write_string(f'{attr}=')
            self.write_list(getattr(frequency_cap_filters, attr),
                            self.write_keyed_frequency_cap)

    def write_keyed_frequency_cap(self, cap):
        self.write_string('ad_counter_key=')
        self.write_field(cap.ad_counter_key, lambda v: self.write_int(v))

        self.write_string('interval=')
        self.write_field(cap.interval, lambda v: self.write_long(v))

        self.write_string('max_count=')
        self.write_field(cap.max_count, lambda v: self.write_int(v))

    def write_field(self, field, writer):
        writer(field)
        self.write_string('|')

    def write_list(self, list_items, writer):
        for index, item in enumerate(list_items):
            if index > 0:
                self.write_string(',')
            writer(item)
        self.write_string('|')

    def write_set(self, set_items, writer):
        for index, item in enumerate(sorted(set_items)):
            if index > 0:
                self.write_string(',')
            writer(item)
        self.write_string('|')

    def write_string(self, value):
        self.serialized_string += value

    def write_long(self, value):
        self.write_string(str(value))

    def write_double(self, value):
        self.write_string(f'{value:.2f}')

    def write_int(self, value):
        self.write_string(str(value))

Exemplos

Exemplo 1: objeto de anúncios contextuais com todos os campos, considerando que você tenha o objeto de anúncios contextuais abaixo.

  ContextualAds contextualAds = new ContextualAds.Builder()
      .setBuyer(new AdTechIdentifier.Builder().setIdentifier("https://example.com").build()) // Replaced 'buyer' variable
      .setDecisionLogicUri(URI.create("example.com/decisionLogic")) // Replaced 'decisionLogicUri'
      .setAdsWithBid(
          List.of(
              new AdWithBid.Builder()
                  .setAdData(new AdData.Builder()
                      .setMetadata("metadata") // Replaced 'metadata'
                      .setAdFilters(buildAdFilters()) // Created a helper for filters
                      .setAdRenderId("987")  // Replaced 'adRenderId'
                      .setAdCounterKeys(buildAdCounterKeys()) // Created a helper
                      .setRenderUri(URI.create("example.com/render")) // Replaced 'adRenderUri'
                      .build())
                  .setBid(5.10) // Replaced 'bid' (as double)
                  .build()))
      .build();

  // Helper function to build FrequencyCapFilters
  private static FrequencyCapFilters buildAdFilters() {
      KeyedFrequencyCap keyedFrequencyCap = new KeyedFrequencyCap.Builder()
              .setAdCounterKey(42) // Replaced 'adCounterKey'
              .setMaxCount(43) // Replaced 'maxCount'
              .setInterval(Duration.ofDays(1)) // Replaced 'oneDay'
              .build();

    return new FrequencyCapFilters.Builder()
            // ... set all KeyedFrequencyCaps using keyedFrequencyCap ...
            .build();
  }

  // Helper function to convert adCounterKeys string
  private static Set<Integer> buildAdCounterKeys() {
      return Arrays.stream("1,2,3".split(",")) // Replaced 'adCounterKeys'
              .map(Integer::valueOf)
              .collect(Collectors.toSet());
  }

A representação da string do byte[] serializado precisa ser assim:

    "buyer=https://example.com|decision_logic_uri=example.com/decisionLogic|ads_with_bid=ad_data=ad_counter_keys=1,2,3|ad_filters=app_install_filters=package_names=com.awesome.app1,com.awesome.app2||frequency_cap_filters=keyed_frequency_caps_for_click_events=ad_counter_key=42|interval=86400000|max_count=43||keyed_frequency_caps_for_impression_events=ad_counter_key=42|interval=86400000|max_count=43||keyed_frequency_caps_for_view_events=ad_counter_key=42|interval=86400000|max_count=43||keyed_frequency_caps_for_win_events=ad_counter_key=42|interval=86400000|max_count=43||||ad_render_id=987|metadata=metadata|render_uri=example.com/render||bid=5.10||"

Exemplo 2: objeto de anúncios contextuais com campos vazios. No objeto abaixo, "adFilters" e "adCounterKeys" estão vazios, o que os torna "nullable" e "nonnull", respectivamente.

  ContextualAds contextualAds = new ContextualAds.Builder()
            .setBuyer(new AdTechIdentifier.Builder().setIdentifier("https://example.com").build())
            .setDecisionLogicUri(URI.create("example.com/decisionLogic"))
            .setAdsWithBid(
                List.of(
                    new AdWithBid.Builder()
                        .setAdData(new AdData.Builder()
                            .setMetadata("metadata")
                            .setAdRenderId("987")
                            .setRenderUri(URI.create("example.com/render"))
                            .build())
                        .setBid(5.10)
                        .build()))
            .build();

Conforme visto na string serializada abaixo, se um campo anulável for definido como nulo (ou não for definido), ele vai ser omitido da serialização. No entanto, se um campo NonNull não for definido, ele será incluído na serialização com uma string vazia como valor.

  "buyer=https://example.com|decision_logic_uri=example.com/decisionLogic|ads_with_bid=ad_data=ad_counter_keys=|ad_render_id=987|metadata=metadata|render_uri=example.com/render||bid=5.10||"

Filtragem de anúncios contextuais sem chamadas de rede

Se não houver demanda de remarketing no dispositivo, será possível executar a seleção de anúncios contextuais sem chamadas de rede. Com URIs pré-criados e uma lista de anúncios contextuais com lances, a plataforma pode pular a extração da lógica de lances, indicadores de lances e indicadores de pontuação. A plataforma usa um URI pré-criado para selecionar o anúncio contextual com o lance mais alto.

Para melhorar a latência, as adtechs podem executar um fluxo de seleção de anúncios que inclui apenas anúncios contextuais com funcionalidade de filtragem de anúncios sem chamadas de rede. Isso é feito usando URIs pré-criados para indicadores de pontuação. Consulte a seção Casos de uso e nomes de URI pré-criados com suporte para uma lista de implementações de scoreAds.

Para executar a seleção de anúncios sem chamadas de rede:

  1. Configure a filtragem de anúncios.
  2. Crie anúncios contextuais.
  3. Crie um objeto AdSelectionConfig com o seguinte:

    1. Uma lista vazia de compradores.
    2. Um URI pré-criado para selecionar o lance mais alto.
    3. Anúncios contextuais
    4. Um URI vazio para os indicadores de pontuação. O URI vazio pode indicar que você não quer usar a busca de indicadores de confiança para pontuação:
    Uri prebuiltURIScoringUri = Uri.parse("ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=your.registered.uri/reporting");
    // Initialize AdSelectionConfig
    AdSelectionConfig adSelectionConfig =
      new AdSelectionConfig.Builder()
        .setSeller(seller)
        .setDecisionLogicUri(prebuiltURIScoringUri)
        .setCustomAudienceBuyers(Collections.emptyList())
        .setAdSelectionSignals(adSelectionSignals)
        .setSellerSignals(sellerSignals)
        .setPerBuyerSignals(perBuyerSignals)
        .setBuyerContextualAds(buyerContextualAds)
        .setTrustedScoringSignalsUri(Uri.EMPTY)
        .build();
    
  4. Executar a seleção de anúncios:

    adSelectionManager.selectAds(
        adSelectionConfig,
        executor,
        outcomeReceiver);
    

Executar seu próprio JavaScript de relatório usando URIs pré-criados

Atualmente, a plataforma do Sandbox de privacidade tem apenas uma implementação básica de JavaScript de relatórios disponível para URIs pré-criados. Se você quiser executar seu próprio JavaScript de relatório enquanto ainda usa URIs pré-criados para uma seleção de anúncios de baixa latência, substitua o DecisionLogicUri entre a seleção de anúncios e as execuções de relatórios.

  1. Siga as etapas para seleção de anúncios contextuais usando URIs pré-criados.
  2. Crie uma cópia do AdSelectionConfig antes de gerar os relatórios.

    adSelectionConfigWithYourReportingJS = adSelectionConfig.cloneToBuilder()
      // Replace <urlToFetchYourReportingJS> with your own URL:
      .setDecisionLogicUri(Uri.parse(<urlToFetchYourReportingJS>))
      .build();
    
  3. Gere relatórios de impressão.

    // adSelectionId is from the result of the previous selectAds run
    ReportImpressionRequest request = new ReportImpressionRequest(
      adSelectionId,
      adSelectionConfigWithYourReportingJS);
    adSelectionManager.reportImpression(
      request,
      executor,
      outcomeReceiver);
    

Executar a mediação em hierarquia

A mediação em hierarquia exige que vários SDKs de terceiros (redes de terceiros) sejam orquestrados por uma rede primária de mediação de SDK. A mediação em hierarquia é feita da mesma maneira, independente de o leilão ter ocorrido no dispositivo ou sido realizado nos serviços de lances e leilões (B&A, na sigla em inglês).

Redes de terceiros

As redes de terceiros precisam fornecer um adaptador que permita à rede de mediação invocar os métodos necessários para realizar um leilão:

  • Executar a seleção de anúncios
  • Gerar relatórios de impressões

Confira um exemplo de adaptador de rede de mediação:

Kotlin

class NetworkAdaptor {
    private val adSelectionManager : AdSelectionManager

    init {
        adSelectionManager = context.getSystemService(AdSelectionManager::class.java)
    }

    fun selectAds() {...}

    fun reportImpressions() {...}
}

Java

class NetworkAdaptor {
    AdSelectionManager adSelectionManager;

    public NetworkAdaptor() {
        AdSelectionManager adSelectionManager =
            context.getSystemService(AdSelectionManager.class);
    }

    public void selectAds() {...}

    public void reportImpressions() {...}
}

Cada SDK tem os próprios gerentes e clientes de serviço de seleção de anúncios, além da implementação de selectAds e reportImpressions. Os provedores de SDK podem consultar as seções sobre como executar a seleção de anúncios para leilões no dispositivo ou a explicação de B&A para leilões desse tipo. Confirma como gerar relatórios de impressões de anúncios seguindo os relatórios de impressão de SSP única para geração de relatórios.

Rede de mediação

Assim como as redes de terceiros, as redes de mediação precisam das implementações de selectAds e reportImpression. Consulte as seções sobre como executar a seleção de anúncios e gerar relatórios de impressões de anúncios para saber mais.

As redes de mediação são responsáveis por executar a cadeia de mediação e se posicionarem nela. A próxima seção mostra como configurar e executar esse processo.

Recuperar cadeia de mediação e lances mínimos

A rede de mediação é responsável por recuperar os anúncios contextuais próprios, a cadeia de mediação e os lances mínimos de redes de terceiros. Isso pode acontecer em uma solicitação para recuperar anúncios contextuais executados pela rede de mediação. A cadeia de mediação determina como iterar nas redes de terceiros, e os lances mínimos podem ser transmitidos para o processo de leilão como adSelectionSignals.

Posição da rede na cadeia de mediação

Um SDK de mediação pode se posicionar na cadeia de mediação com base no eCPM ativo dos lances de anúncios próprios. Na API Protected Audience, os lances de anúncios são opacos. Um SDK de mediação precisa usar AdSelectionFromOutcomesConfig para comparar o lance de determinado anúncio próprio ao lance mínimo da próxima rede de terceiros na cadeia. Caso o lance próprio seja maior que o lance mínimo, isso significa que o SDK de mediação está posicionado na frente dessa rede de terceiros.

Executar a seleção de anúncios

Para recuperar um candidato a anúncio próprio, a rede de mediação pode executar um leilão no dispositivo de acordo com as etapas da seção Executar a seleção de anúncios. Isso gera um candidato a anúncio próprio, um lance e um AdSelectionId, que é usado no processo de mediação.

Criar uma AdSelectionFromOutcomesConfig

Uma AdSelectionFromOutcomesConfig permite que a rede de mediação transmita uma lista de AdSelectionIds (resultados de leilões anteriores), indicadores de seleção de anúncios e um URI para buscar o JavaScript que seleciona um anúncio entre vários candidatos. A lista de AdSelectionIds com os lances e os indicadores deles vai ser transmitida ao JavaScript, que poderá retornar um dos AdSelectionIds, se superar o lance mínimo, ou nenhum deles, se a cadeia de mediação continuar.

As redes de mediação criam uma AdSelectionFromOutcomesConfig com o AdSelectionId próprio da seção anterior e o lance mínimo da rede de terceiros considerada. É preciso criar uma nova AdSelectionFromOutcomesConfig para cada etapa na cadeia de mediação.

Kotlin

fun  runSelectOutcome(
    adSelectionClient : AdSelectionClient,
    outcome1p : AdSelectionOutcome,
    network3p : NetworkAdapter) : ListenableFuture<AdSelectionOutcome?> {
    val config = AdSelectionFromOutcomesConfig.Builder()
        .setSeller(seller)
        .setAdSelectionIds(listOf(outcome1p))
        .setSelectionSignals({"bid_floor": bid_floor})
        .setSelectionLogicUri(selectionLogicUri)
        .build()
    return adSelectionClient.selectAds(config)
}

Java

public ListenableFuture<AdSelectionOutcome> runSelectOutcome(AdSelectionOutcome outcome1p,
                                              NetworkAdapter network3p) {
    AdSelectionFromOutcomesConfig config = new AdSelectionFromOutcomesConfig.Builder()
            .setSeller(seller)
            .setAdSelectionIds(Collection.singletonList(outcome1p))
            .setSelectionSignals({"bid_floor": bid_floor})
            .setSelectionLogicUri(selectionLogicUri)
            .build();

    return adSelectionClient.selectAds(config){}
}

A substituição do método selectAds() para a mediação em hierarquia requer uma entrada AdSelectionFromOutcomesConfig, em que é necessário especificar os seguintes parâmetros:

  • Vendedor: o identificador da rede de publicidade do vendedor, que inicia a seleção de anúncios.
  • AdSelectionIds: uma lista singleton de uma execução anterior de selectAds() para um anúncio próprio.
  • Indicadores de seleção de anúncios: um objeto JSON, serializado como uma string, que contém indicadores que vão ser usados pela lógica de lances do comprador. Nesse caso, inclua o lance mínimo recuperado para a rede de terceiros especificada.
  • URI da lógica de seleção: um URL HTTPS consultado durante a seleção de anúncios para buscar o JavaScript da rede de mediação e selecionar um anúncio vencedor. Confira as assinaturas de função necessárias no JavaScript. O JavaScript retornará o anúncio de terceiros se o lance for maior que o mínimo. Caso contrário, retornará null. Assim, o SDK de mediação pode truncar a cadeia de mediação quando um vencedor é encontrado.

Com a AdSelectionOutcomesConfig criada, chame o método selectAds() da rede de terceiros que está no primeiro lugar da cadeia.

Kotlin

val adSelectionManager = context.getSystemService(AdSelectionManager::class.java)

// Initialize AdSelectionFromOutcomesConfig
AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig =
  AdSelectionFromOutcomesConfig.Builder()
    .setSeller(seller)
    .setAdSelectionIds(listof(outcome1p))
    .setSelectionSignals({"bid_floor": bid_floor})
    .setSelectionLogicUri(selectionLogicUri)
    .setAdSelectionIds(outcomeIds)
    .build()

// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(
    adSelectionFromOutcomesConfig,
    executor,
    outcomeReceiver)

Java

AdSelectionManager adSelectionManager =
    context.getSystemService(AdSelectionManager.class);

// Initialize AdSelectionFromOutcomesConfig
AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig =
        new AdSelectionFromOutcomesConfig.Builder()
            .setSeller(seller)
            .setAdSelectionIds(Collection.singletonList(outcome1p))
            .setSelectionSignals({"bid_floor": bid_floor})
            .setSelectionLogicUri(selectionLogicUri)
            .setAdSelectionIds(outcomeIds)
            .build();

// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(
    adSelectionFromOutcomesConfig,
    executor,
    outcomeReceiver);

Orquestrar a mediação em hierarquia

Confira abaixo a ordem das operações executadas no processo de mediação.

  1. Exibir a seleção de anúncios próprios.
  2. Iterar na cadeia de mediação. Para cada rede de terceiros, siga estas etapas:
    1. Crie AdSelectionFromOutcomeConfig incluindo o outcomeId próprio e o lance mínimo do SDK de terceiros.
    2. Chame selectAds() com a configuração da etapa anterior.
    3. Se o resultado não estiver vazio, o anúncio será retornado.
    4. Chame o método selectAds() do adaptador de rede do SDK atual. Se o resultado não estiver vazio, o anúncio será retornado.
  3. Se nenhum vencedor for encontrado na cadeia, o anúncio próprio será retornado.

Kotlin

fun runWaterfallMediation(mediationChain : List<NetworkAdapter>)
  : Pair<AdSelectionOutcome, NetworkAdapter> {
    val outcome1p = runAdSelection()

    var outcome : AdSelectionOutcome
    for(network3p in mediationChain) {
      outcome = runSelectOutcome(outcome1p, network3p)
      if (outcome1p.hasOutcome() && outcome.hasOutcome()) {
          return Pair(outcome, this)
      }

      outcome = network3p.runAdSelection()
      if(outcome.hasOutcome()) {
          return Pair(outcome, network3p)
      }
    }
  return Pair(outcome1p, this)
}

Java

class MediationNetwork {
    AdSelectionManager adSelectionManager;

    public MediationNetwork() {
        AdSelectionManager adSelectionManager =
            context.getSystemService(AdSelectionManager.class);
    }

    public void runAdSelection() {...}

    public void reportImpressions() {...}

    public Pair<AdSelectionOutcome, NetworkAdapter> runWaterfallMediation(
            List<NetworkAdapter> mediationChain) {
        AdSelectionOutcome outcome1p = runAdSelection();

        AdSelectionOutcome outcome;
        for(NetworkAdapter network3p: mediationChain) {
            if (outcome1p.hasOutcome() &&
              (outcome = runSelectOutcome(outcome1p, network3p)).hasOutcome()) {
                return new Pair<>(outcome, this);
            }

            if((outcome = network3p.runAdSelection()).hasOutcome()) {
                return new Pair<>(outcome, network3p);
            }
        }
        return new Pair<>(outcome1p, this);
    }

    /* Runs comparison by creating an AdSelectionFromOutcomesConfig */
    public AdSelectionOutcome runSelectOutcome(AdSelectionOutcome outcome1p,
                                              NetworkAdapter network3p) { ... }
}

Gerar relatórios de impressões de anúncios

Existem dois fluxos para gerar relatórios de uma impressão de anúncio, dependendo de como o leilão é realizado. Se você é uma SSP única que está fazendo um leilão, siga esta seção. Se você quer implementar a mediação em hierarquia, siga as etapas da seção sobre os relatórios de impressão de mediação em hierarquia.

Relatórios de impressão de SSP única

Depois que um anúncio vencedor for escolhido no fluxo de trabalho da seleção de anúncios, vai ser possível relatar a impressão de volta às plataformas de compra e venda participantes com o método AdSelectionManager.reportImpression(). Para gerar um relatório sobre uma impressão de anúncio, siga estas etapas:

  1. Inicialize um objeto AdSelectionManager.
  2. Crie um objeto ReportImpressionRequest com o ID de seleção de anúncios.
  3. Chame o método assíncrono reportImpression() com o objeto ReportImpressionRequest e os objetos Executor e OutcomeReceiver relevantes.

Java

AdSelectionManager adSelectionManager =
    context.getSystemService(AdSelectionManager.class);

// Initialize a ReportImpressionRequest
ReportImpressionRequest reportImpressionRequest =
        new ReportImpressionRequest.Builder()
                .setAdSelectionId(adSelectionId)
                .setAdSelectionConfig(adSelectionConfig)
                .build();

// Request to report the impression with the ReportImpressionRequest
adSelectionManager.reportImpression(
    reportImpressionRequest,
    executor,
    outcomeReceiver);

Kotlin

val adSelectionManager = context.getSystemService(AdSelectionManager::class.java)

// Initialize a ReportImpressionRequest
val adSelectionConfig: ReportImpressionRequest =
    ReportImpressionRequest.Builder()
        .setAdSelectionId(adSelectionId)
        .setAdSelectionConfig(adSelectionConfig)
        .build()

// Request to report the impression with the ReportImpressionRequest
adSelectionManager.reportImpression(
    reportImpressionRequest,
    executor,
    outcomeReceiver)

Inicialize o ReportImpressionRequest com os parâmetros necessários abaixo:

  • ID de seleção de anúncios: o ID exclusivo de um usuário de dispositivo e que identifica uma seleção de anúncios bem-sucedida.
  • Configuração de seleção de anúncios: a mesma configuração usada na chamada selectAds() identificada pelo ID de seleção de anúncios informado.

O método reportImpression() assíncrono usa o objeto OutcomeReceiver para informar o resultado da chamada de API.

  • O callback onResult() indica se os URLs de relatórios de impressão foram criados e se a solicitação foi agendada.
  • O callback onError() indica estas condições possíveis:
    • Se a chamada for inicializada com um argumento de entrada inválido, a AdServicesException vai indicar uma IllegalArgumentException como causa.
    • Todos os outros erros vão receber uma AdServicesException com uma IllegalStateException como causa.

Relatórios de impressão de mediação em hierarquia

Um SDK de mediação precisa monitorar o SDK vencedor para acionar os fluxos de relatórios dele. Os SDKs que participam de uma cadeia de mediação precisam fornecer um método que o mediador invoca para acionar o próprio fluxo de relatórios. Um SDK que participa de um leilão mediado pode seguir as etapas acima para implementar o próprio relatório.

As SSPs podem usar este exemplo de código do SDK de terceiros como um protótipo da participação nos fluxos de mediação:

Pair<AdSelectionOutcome, NetworkAdapter> winnerOutcomeAndNetwork =
         mediationSdk.orchestrateMediation(mediationChain);

if (winner.first.hasOutcome()) {
      winner.second.reportImpressions(winner.first.getAdSelectionId());

Endpoints de relatórios de impressão

A API Report Impression emite solicitações GET HTTPS para os endpoints fornecidos pela plataforma de venda e pela plataforma de compra vencedora:

Endpoint da plataforma de compra:

  • A API usa o URL da lógica de lances especificado no público-alvo personalizado para buscar o JavaScript fornecido pelo comprador, que inclui uma lógica para retornar um URL de relatório de impressões.
  • Invoque a função reportWin() do JavaScript, que precisa retornar o URL de relatório de impressão do comprador.

Endpoint da plataforma de venda:

  • Use o URL da lógica de decisão especificado no objeto AdSelectionConfig para buscar o JavaScript da lógica de decisão do vendedor.
  • Invoque a função reportResult() do JavaScript, que precisa retornar o URL de relatório de impressão do comprador.

Relatórios de serviços de lances e leilões

Um leilão realizado pelos serviços de lances e leilões terá todas as informações de relatório necessárias, incluindo URLs gerados para relatórios de interação com anúncios, incluídos na resposta criptografada do leilão do lado do servidor. Quando a resposta é descriptografada, os URLs adequados são registrados na plataforma para que os relatórios de anúncios e impressões sigam as mesmas etapas listadas acima.

Melhor resultado de relatórios de impressão

O método reportImpression() foi projetado para oferecer a melhor conclusão de relatórios possível.

Gerar relatórios de interações com anúncios

A API Protected Audience oferece suporte para gerar relatórios sobre interações mais granulares para um anúncio renderizado. Isso pode incluir interações como tempo de visualização, cliques, passagem do cursor ou qualquer outra métrica útil que pode ser coletada. O processo para receber esses relatórios requer duas etapas. Primeiro, compradores e vendedores precisam se registrar para receber esses relatórios no JavaScript de relatórios. Em seguida, o cliente vai precisar informar esses eventos.

Como fazer o registro para receber eventos de interação

O registro de eventos de interação acontece no método reportWin() do comprador e nas funções JavaScript reportResult() do vendedor, usando uma função JavaScript fornecida pela plataforma: registerAdBeacon. Para fazer o registro para receber um relatório de eventos, basta chamar a função JavaScript da plataforma no JavaScript do relatório. O snippet abaixo usa o reportWin() de um comprador, mas a mesma abordagem se aplica a reportResult().

reportWin(
  adSelectionSignals,
  perBuyerSignals,
  signalsForBuyer,
  contextualSignals,
  customAudienceSignals) {
    ...
    // Calculate reportingUri, clickUri, viewUri, and hoverUri
    registerAdBeacon("click", clickUri)
    registerAdBeacon("view", viewUri)
    registerAdBeacon("hover", hoverUri)

    return reportingUrl;
}

Gerar relatórios de eventos de interação

Depois de informar uma impressão, os clientes podem relatar as interações para as plataformas de compra e venda vencedoras registradas anteriormente com o método AdSelectionManager.reportInteraction(). Para informar um evento de anúncio:

  1. Inicialize um objeto AdSelectionManager.
  2. Crie um objeto ReportInteractionRequest com o ID da seleção de anúncios, a chave de interação, os dados de interação e o destino do relatório.
  3. Chame o método assíncrono reportInteraction() com o objeto request e os objetos Executor e OutcomeReceiver relevantes.
AdSelectionManager adSelectionManager =
    context.getSystemService(AdSelectionManager.class);

// Initialize a ReportInteractionRequest
ReportInteractionRequest request =
  new ReportInteractionRequest.Builder()
    .setAdSelectionId(adSelectionId)
    .setInteractionKey("view")
    .setInteractionData("{ viewTimeInSeconds : 1 }") // Can be any string
    .setReportingDestinations(
      FLAG_REPORTING_DESTINATION_BUYER | FLAG_REPORTING_DESTINATION_SELLER
    )
    .build();

// Request to report the impression with the ReportImpressionRequest
adSelectionManager.reportInteraction(
  reportImpressionRequest,
  executor,
  outcomeReceiver);

Inicialize o ReportInteractionRequest com os parâmetros necessários abaixo:

  • ID de seleção de anúncios: um ID extraído de um AdSelectionOutcome retornado anteriormente.
  • Chave de interação: uma chave de string definida pelo cliente que descreve a ação informada. Ela precisa corresponder à chave que foi registrada pelo vendedor ou pelo comprador nas funções JavaScript de relatórios.
  • Dados de interação: uma string que contém dados a serem incluídos no relatório de eventos de teste automático de inicialização (POST) enviado para os servidores de relatórios.
  • Relatórios de destino: uma bitmask que especifica se os eventos precisam ser informados ao comprador, ao vendedor ou a ambos. Essas sinalizações são fornecidas pela plataforma e a máscara de destino final pode ser criada usando operações bit a bit. Para informar um destino, você pode usar a flag fornecida pela plataforma diretamente. Para informar vários destinos, você pode usar o operador bit a bit OR (|) para combinar os valores de sinalização.

O método reportInteraction() assíncrono usa o objeto OutcomeReceiver para informar o resultado da chamada de API.

  • O callback onResult() indica que a chamada de interação de relatório é válida.
  • O callback onError() indica estas condições possíveis:
    • Se a chamada for feita quando o app estiver sendo executado em segundo plano, uma IllegalStateException com uma descrição da falha será retornada.
    • Se o cliente for impedido de chamar reportInteraction(), uma LimitExceededException será retornada.
    • Se o pacote não estiver inscrito para chamar as APIs de preservação de privacidade, uma SecurityException() será retornada.
    • Se as interações de relatórios do app forem diferentes do app que chamou selectAds(), uma IllegalStateException será retornada.
  • Se o usuário não consentir com a ativação das APIs do Sandbox de privacidade, a chamada vai falhar silenciosamente.

Endpoints de relatórios de interação

A API de interação de relatório emite solicitações HTTPS POST para endpoints fornecidos pela plataforma de venda e pela plataforma de compra vencedora. A API Protected Audience vai fazer a correspondência das chaves de interação com os URIs declarados no JavaScript de relatórios e vai emitir uma solicitação POST para cada endpoint em cada interação relatada. O tipo de conteúdo da solicitação é texto simples, com o corpo sendo os dados de interação.

Melhor resultado de relatórios de interação

O método reportInteraction() foi criado para oferecer uma conclusão de relatório de melhor resultado por HTTP POST.

Atualização diária em segundo plano

Ao criar um público-alvo personalizado, seu app ou SDK pode inicializar metadados de público-alvo personalizado. Além disso, a plataforma pode atualizar as seguintes partes dos metadados de público personalizado com um processo diário de atualização em segundo plano.

  • Indicadores de lances do usuário
  • Dados confiáveis de lances
  • Lista AdData

Esse processo consulta o URL de atualização diária definido no público-alvo personalizado, e o URL pode retornar uma resposta JSON.

  • A resposta JSON pode conter qualquer um dos campos de metadados com suporte que precisam ser atualizados.
  • Cada campo JSON é validado de forma independente. O cliente ignora os campos incorretos que não resultam em atualizações para esse campo específico na resposta.
  • Uma resposta HTTP vazia ou um objeto JSON vazio "{}" não resulta em atualizações de metadados.
  • A resposta precisa ter no máximo 10 KB.
  • Todos os URIs precisam usar HTTPS.
  • É necessário que trusted_bidding_uri compartilhe o mesmo ETLD+1 que o comprador.

Exemplo: resposta JSON para atualização diária em segundo plano

{
    "user_bidding_signals" : { ... },  // Valid JSON object
    "trusted_bidding_data" : {
        "trusted_bidding_uri" : 'example-dsp1-key-value-service.com',
        "trusted_bidding_keys" : [ 'campaign123', 'campaign456', ... ]
    },
    'ads' : [
        {
            "render_uri" : 'www.example-dsp1.com/.../campaign123.html',
            'metadata' : { ... }  // Valid JSON object
        },
        {
            "render_uri" : 'www.example-dsp1.com/.../campaign456.html',
            'metadata' : { ... }  // Valid JSON object
        },
        ...
    ]
}

JavaScript para seleção de anúncios

O fluxo de trabalho da seleção de anúncios orquestra a execução do JavaScript fornecido pelo comprador e pelo vendedor.

O JavaScript fornecido pelo comprador é buscado no URL da lógica de lances especificado no público-alvo personalizado. O JavaScript retornado precisa incluir estas funções:

O JavaScript fornecido pelo vendedor é buscado do URL da lógica de decisão especificado no parâmetro AdSelectionConfig da API Ad Selection. O JavaScript retornado precisa incluir as funções abaixo:

generateBid()

function generateBid(
  ad,
  auction_signals,
  per_buyer_signals,
  trusted_bidding_signals,
  contextual_signals,
  user_signals,
  custom_audience_bidding_signals) {
  return {'status': 0, 'ad': ad, 'bid': ad.metadata.result };
}

Parâmetros de entrada:

  • ad: um objeto JSON com o formato var ad = { 'render_url': url, 'metadata': json_metadata };.
  • auction_signals, per_buyer_signals: objetos JSON especificados no objeto de configuração do leilão.
  • custom_audience_bidding_signals: objeto JSON gerado pela plataforma. O formato desse objeto JSON é:

    var custom_audience_signals = {
      "owner":"ca_owner",
      "buyer":"ca_buyer",
      "name":"ca_name",
      "activation_time":"ca_activation_time_epoch_ms",
      "expiration_time":"ca_expiration_time_epoch_ms",
      "user_bidding_signals":"ca_user_bidding_signals"
    }
    

    em que:

    • owner, buyer e name são strings extraídas das propriedades com o mesmo nome do público-alvo personalizado que participa da seleção de anúncios.
    • activation_time e expiration_time são os momentos da ativação e de expiração do público-alvo personalizado, expressos em segundos desde a era Unix (link em inglês).
    • ca_user_bidding_signals é uma string JSON especificada no campo userBiddingSignals da CustomAudience no momento da criação.
    • trusted_bidding_signals, contextual_signals e user_signals são objetos JSON. No momento, eles são transmitidos como objetos vazios e serão preenchidos em versões futuras. O formato deles não é aplicado pela plataforma e é gerenciado pelas tecnologias de publicidade.

Resultado:

  • ad: é o anúncio a que o lance se refere. O script pode retornar uma cópia do anúncio recebido com metadados diferentes. A propriedade render_url do anúncio não vai mudar.
  • bid: um valor flutuante que representa o valor do lance do anúncio.
  • status: um valor inteiro que pode ser:
    • 0: para uma execução bem-sucedida.
    • 1: (ou qualquer valor diferente de zero) caso algum dos indicadores de entrada seja inválido. Caso um valor diferente de zero seja retornado por generate-bid, o processo de lances vai ser invalidado para todos os anúncios de CA.

scoreAd()

function scoreAd(
  ad,
  bid,
  ad_selection_config,
  seller_signals,
  trusted_scoring_signals,
  contextual_signal,
  user_signal,
  custom_audience_signal) {
    return {'status': 0, 'score': score };
}

Parâmetros de entrada:

  • ad: consulte a documentação de generateBid.
  • bid: o valor do lance do anúncio.
  • ad_selection_config: um objeto JSON que representa o parâmetro AdSelectionConfig da API selectAds. O formato é:

    var ad_selection_config = {
      'seller': 'seller',
      'decision_logic_url': 'url_of_decision_logic',
      'custom_audience_buyers': ['buyer1', 'buyer2'],
      'auction_signals': auction_signals,
      'per_buyer_signals': per_buyer_signals,
      'contextual_ads': [ad1, ad2]
    }
    
  • seller_signals: objetos JSON lidos no parâmetro sellerSignals da API AdSelectionConfig.

  • trusted_scoring_signal: lido no campo adSelectionSignals do parâmetro da API AdSelectionConfig.

  • contextual_signals, user_signals: objetos JSON. No momento, eles são transmitidos como objetos vazios e vão ser preenchidos em versões futuras. O formato deles não é aplicado pela plataforma e é gerenciado pela adtech.

  • per_buyer_signals: objeto JSON lido do mapa perBuyerSignal do parâmetro da API AdSelectionConfig usando como chave o comprador atual do público-alvo personalizado. Vai ser vazio se o mapa não tiver nenhuma entrada para o comprador em questão.

Resultado:

  • score: um valor flutuante que representa o valor da pontuação do anúncio.
  • status: um valor inteiro que pode ser:
    • 0: para uma execução bem-sucedida.
    • 1: caso o indicador customAudienceSignals seja inválido.
    • 2: caso o indicador AdSelectionConfig seja inválido.
    • 3: caso algum dos outros indicadores seja inválido.
    • Qualquer valor diferente de zero causa a falha do processo. O valor determina o tipo de exceção gerada.

selectOutcome()

function selectOutcome(
  outcomes,
  selection_signals) {
    return {'status': 0, 'result': null};
}

Parâmetros de entrada:

  • outcomes: um objeto JSON {"id": id_string, "bid": bid_double}.
  • selection_signals: objetos JSON especificados no objeto de configuração do leilão.

Resultado:

  • status: 0 para sucesso e valores diferentes de zero para falhas.
  • result: um dos resultados transmitidos ou nulos.

reportResult()

function reportResult(ad_selection_config, render_url, bid, contextual_signals) {
   return {
      'status': status,
      'results': {'signals_for_buyer': signals_for_buyer, 'reporting_url': reporting_url }
   };
}

Parâmetros de entrada:

  • ad_selection_config: consulte a documentação de scoreAds.
  • render_url: o URL de renderização do anúncio vencedor.
  • bid: o lance oferecido para o anúncio vencedor.
  • contextual_signals: consulte a documentação de generateBid.

Resultado:

  • status: 0 para sucesso e valores diferentes de zero para falhas.
  • results: um objeto JSON que contém:
    • signals_for_buyer: um objeto JSON transmitido para a função reportWin.
    • reporting_url: um URL usado pela plataforma para notificar a impressão ao comprador.

reportWin()

function reportWin(
   ad_selection_signals,
   per_buyer_signals,
   signals_for_buyer,
   contextual_signals,
   custom_audience_signals) {
   return {'status': 0, 'results': {'reporting_url': reporting_url } };
}

Parâmetros de entrada:

  • ad_selection_signals, per_buyer_signals: consulte a documentação de scoreAd.
  • signals_for_buyer: um objeto JSON retornado por reportResult.
  • contextual_signals, custom_audience_signals: consulte a documentação de generateBid.

Resultado:

  • status: 0 para sucesso e valores diferentes de zero para falhas.
  • results: um objeto JSON contendo:
    • reporting_url: um URL usado pela plataforma para notificar a impressão ao vendedor.

registerAdBeacon()

function registerAdBeacon(
  interaction_key,
  reporting_uri
)

Parâmetros de entrada:

  • interaction_key: uma string que representa o evento. É usada pela plataforma mais tarde ao relatar interações de eventos para procurar o reporting_uri que precisa ser notificado. Essa chave precisa corresponder ao que o comprador ou o vendedor registra e ao que o vendedor informa.
  • reporting_uri: um URI para receber relatórios de eventos. Precisa ser específico para o tipo de evento que está sendo informado. Ele precisa aceitar uma solicitação POST para processar todos os dados informados com o evento.

URIs pré-criados da seleção de anúncios

Com os URIs pré-criados, as adtechs podem indicar funções JavaScript para a lógica de decisão da seleção de anúncios nas classes AdSelectionConfig e AdSelectionFromOutcomesConfig. URIs pré-criados não exigem chamadas de rede para fazer o download do JavaScript correspondente. As adtechs podem usar URIs pré-criados sem precisar configurar um domínio registrado para hospedar o JavaScript.

Um URI pré-criado é construído usando este formato:

ad-selection-prebuilt:<use-case>/<name>?<required-script-generation-parameters>

A plataforma do Sandbox de privacidade fornece JavaScript usando as informações desse URI no ambiente de execução.

Uma IllegalArgumentException será gerada se:

  • Algum dos parâmetros obrigatórios não estiver presente no URI.
  • Houver parâmetros não reconhecidos no URI.

Casos de uso e nomes de URI pré-criados com suporte

Caso de uso 1: seleção de anúncios

Os URIs pré-criados no caso de uso ad-selection possuem suporte do fluxo selectAds(AdSelectionConfig).

Nome do URI pré-criado: highest-bid-wins

Esse URI pré-criado fornece um JavaScript que escolhe o anúncio com o lance mais alto após o leilão. Ele também fornece uma função básica de relatórios para informar o render_uri e o bid do vencedor.

Parâmetros obrigatórios

reportingUrl: o URL de relatório básico parametrizado com o render_uri e o bid do anúncio vencedor:

<reportingUrl>?render_uri=<renderUriOfWinnigAd>&bid=<bidOfWinningAd>

Uso

Se o URL de relatório base for https://www.ssp.com/reporting, o URI pré-criado será:

`ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=https://www.ssp.com/reporting`

Caso de uso 2: ad-selection-from-outcomes

Os URIs pré-criados no caso de uso ad-selection-from-outcomes possuem suporte do fluxo de trabalho selectAds(AdSelectionFromOutcomesConfig).

Nome do URI pré-criado: waterfall-mediation-truncation

O URI pré-criado waterfall-mediation-truncation fornece um JavaScript que implementa a lógica de truncamento da mediação em hierarquia, em que o JavaScript retornará um anúncio primário se o bid for maior ou igual a bid floor. Caso contrário, retornará null.

Parâmetros obrigatórios

bidFloor: a chave do valor mínimo do lance transmitido no método getSelectionSignals(), que é comparada com o anúncio do SDK de mediação.

Uso

Se os indicadores de seleção de anúncios forem semelhantes a {"bid_floor": 10}, o URI pré-criado resultante será:

`ad-selection-prebuilt://ad-selection-from-outcomes/waterfall-mediation-truncation/?bidFloor=bid_floor`

Testes

Para ajudar você a começar a usar a API Protected Audience, criamos apps de exemplo em Kotlin e Java que podem ser encontrados no GitHub (link em inglês).

Pré-requisitos

A API Protected Audience exige algum conhecimento de JavaScript durante a seleção de anúncios e a geração de relatórios de impressões. Há duas formas de fornecer esse JavaScript em um ambiente de teste:

  • Executar um servidor com os endpoints de HTTPS necessários que retornam o JavaScript.
  • Substituir a busca remota fornecendo o código necessário de uma fonte local.

Ambas as abordagens exigem a configuração de um endpoint de HTTPS para processar os relatórios de impressões.

Endpoints de HTTPS

Para testar a seleção de anúncios e os relatórios de impressão, configure sete endpoints de HTTPS que o dispositivo de teste ou emulador possa acessar:

  1. Endpoint do comprador, que mostra o JavaScript da lógica de lances.
  2. Um endpoint que disponibiliza indicadores de lances.
  3. Endpoint do vendedor, que mostra o JavaScript da lógica de decisão.
  4. Um endpoint que disponibiliza sinais de pontuação.
  5. Endpoint de relatórios de impressão do comprador vencedor.
  6. Endpoint de relatórios de impressão do vendedor.
  7. Um endpoint para disponibilizar as atualizações diárias de um público-alvo personalizado.

Por conveniência, o repositório do GitHub fornece um código JavaScript básico para fins de teste. Ele também inclui definições de serviço da OpenAPI, que podem ser implantadas em uma plataforma de simulação ou microsserviços com suporte. Para conferir mais detalhes, consulte o arquivo README (em inglês) do projeto.

Substituir a busca remota de JavaScript

Esse recurso é destinado a testes completos. Para substituir a busca remota, o app precisa ser executado no modo de depuração com as opções do desenvolvedor ativadas.

Para ativar o modo de depuração no aplicativo, adicione a linha abaixo ao atributo do aplicativo no AndroidManifest.xml:

<application
  android:debuggable="true">

Consulte o app de amostra da API Protected Audience (em inglês) no GitHub para saber como usar essas substituições.

Você precisa adicionar seu próprio JavaScript personalizado para processar rotinas de seleção de anúncios, como lances, decisões de pontuação e relatórios. Acesse o repositório do GitHub (em inglês) para conferir exemplos básicos de código JavaScript que processam todas as solicitações necessárias. O aplicativo de exemplo da API Protected Audience mostra como ler o código desse arquivo e prepará-lo para uso como substituição.

É possível substituir a busca do JavaScript de venda e compra de forma independente, embora seja necessário ter um endpoint de HTTPS para mostrar qualquer JavaScript para que você não está fornecendo substituições. Consulte o README (em inglês) para saber como configurar um servidor que processa esses casos.

Só é possível substituir a busca em JavaScript por públicos-alvo personalizados que pertencem ao seu pacote.

Substituir o JavaScript de venda

Para configurar uma substituição do JavaScript de venda, faça o seguinte, conforme demonstrado neste exemplo de código:

  1. Inicialize um objeto AdSelectionManager.
  2. Receba uma referência a TestAdSelectionManager no objeto AdSelectionManager.
  3. Crie um objeto AdSelectionConfig.
  4. Crie uma AddAdSelectionOverrideRequest com o objeto AdSelectionConfig e uma String que representa o JavaScript que você quer usar como substituição.
  5. Chame o método assíncrono overrideAdSelectionConfigRemoteInfo() com o objeto AddAdSelectionOverrideRequest e os objetos Executor e OutcomeReceiver correspondentes.

Kotlin

val testAdSelectionManager: TestAdSelectionManager =
  context.getSystemService(AdSelectionManager::class.java).getTestAdSelectionManager()

// Initialize AdSelectionConfig =
val adSelectionConfig = new AdSelectionConfig.Builder()
    .setSeller(seller)
    .setDecisionLogicUrl(decisionLogicUrl)
    .setCustomAudienceBuyers(customAudienceBuyers)
    .setAdSelectionSignals(adSelectionSignals)
    .setSellerSignals(sellerSignals)
    .setPerBuyerSignals(perBuyerSignals)
    .build()

// Initialize AddAddSelectionOverrideRequest
val request = AddAdSelectionOverrideRequest.Builder()
    .setAdSelectionConfig(adSelectionConfig)
    .setDecisionLogicJs(decisionLogicJS)
    .build()

// Run the call to override the JavaScript for the given AdSelectionConfig
// Note that this only takes effect in apps marked as debuggable
testAdSelectionManager.overrideAdSelectionConfigRemoteInfo(
    request,
    executor,
    outComeReceiver)

Java

TestAdSelectionManager testAdSelectionManager =
  context.getSystemService(AdSelectionManager.class).getTestAdSelectionManager();

// Initialize AdSelectionConfig =
AdSelectionConfig adSelectionConfig = new AdSelectionConfig.Builder()
    .setSeller(seller)
    .setDecisionLogicUrl(decisionLogicUrl)
    .setCustomAudienceBuyers(customAudienceBuyers)
    .setAdSelectionSignals(adSelectionSignals)
    .setSellerSignals(sellerSignals)
    .setPerBuyerSignals(perBuyerSignals)
    .build();

// Initialize AddAddSelectionOverrideRequest
AddAdSelectionOverrideRequest request = AddAdSelectionOverrideRequest.Builder()
    .setAdSelectionConfig(adSelectionConfig)
    .setDecisionLogicJs(decisionLogicJS)
    .build();

// Run the call to override the JavaScript for the given AdSelectionConfig
// Note that this only takes effect in apps marked as debuggable
testAdSelectionManager.overrideAdSelectionConfigRemoteInfo(
    request,
    executor,
    outComeReceiver);

Consulte a seção Executar a seleção de anúncios para mais informações sobre o que cada um dos campos na AdSelectionConfig representa. A principal diferença é que o decisionLogicUrl pode ser definido como um valor do marcador que será ignorado.

Para substituir o JavaScript usado durante a seleção de anúncios, o decisionLogicJs precisa conter as assinaturas de função adequadas do lado do vendedor. Confira como ler um arquivo JavaScript como uma string no app de exemplo da API Protected Audience no GitHub (link em inglês).

O método overrideAdSelectionConfigRemoteInfo() assíncrono usa o objeto OutcomeReceiver para informar o resultado da chamada de API.

O callback onResult() indica que a substituição foi aplicada. Futuras chamadas para selectAds() vão usar a lógica de decisões e relatórios que você transmitiu como a substituição.

O callback onError() indica duas condições possíveis:

  • Se ocorrer uma tentativa de substituição com argumentos inválidos, a AdServiceException vai indicar uma IllegalArgumentException como causa.
  • Se ocorrer uma tentativa de substituição com um app que não está sendo executado no modo de depuração com as opções do desenvolvedor ativadas, a AdServiceException vai indicar IllegalStateException como causa.

Redefinir substituições de venda

Esta seção pressupõe que você substituiu o JavaScript de venda e tem uma referência ao TestAdSelectionManager e à AdSelectionConfig usados na seção anterior.

Para redefinir as substituições de todas as AdSelectionConfigs, faça o seguinte:

  1. Chame o método resetAllAdSelectionConfigRemoteOverrides() assíncrono com o objeto OutcomeReceiver adequado.

Kotlin

// Resets overrides for all AdSelectionConfigs
testAadSelectionManager.resetAllAdSelectionConfigRemoteOverrides(
  outComeReceiver)

Java

// Resets overrides for all AdSelectionConfigs
testAdSelectionManager.resetAllAdSelectionConfigRemoteOverrides(
    outComeReceiver);

Após redefinir as substituições do lado do vendedor, as chamadas para selectAds() usam qualquer decisionLogicUrl armazenado na AdSelectionConfig para tentar buscar o JavaScript necessário.

Se a chamada para resetAllAdSelectionConfigRemoteOverrides() falhar, o callback OutComeReceiver.onError() vai fornecer uma AdServiceException. Se ocorrer uma tentativa de remoção de substituições com um app que não está sendo executado no modo de depuração com as opções do desenvolvedor ativadas, AdServiceException vai indicar IllegalStateException como causa.

Substituir o JavaScript de compra

  1. Siga as etapas para aderir a um público-alvo personalizado.
  2. Crie uma AddCustomAudienceOverrideRequest com o comprador e o nome do público-alvo personalizado que você quer substituir, além da lógica de lances e os dados que você quer usar como substituição.
  3. Chame o método assíncrono overrideCustomAudienceRemoteInfo() com o objeto AddCustomAudienceOverrideRequest e os objetos Executor e OutcomeReceiver correspondentes.

Kotlin

val testCustomAudienceManager: TestCustomAudienceManager =
  context.getSystemService(CustomAudienceManager::class.java).getTestCustomAudienceManager()

// Join custom audience

// Build the AddCustomAudienceOverrideRequest
val request = AddCustomAudienceOverrideRequest.Builder()
    .setBuyer(buyer)
    .setName(name)
    .setBiddingLogicJs(biddingLogicJS)
    .setTrustedBiddingSignals(trustedBiddingSignals)
    .build()

// Run the call to override JavaScript for the given custom audience
testCustomAudienceManager.overrideCustomAudienceRemoteInfo(
    request,
    executor,
    outComeReceiver)

Java

TestCustomAudienceManager testCustomAudienceManager =
  context.getSystemService(CustomAudienceManager.class).getTestCustomAudienceManager();

// Join custom audience

// Build the AddCustomAudienceOverrideRequest
AddCustomAudienceOverrideRequest request =
    AddCustomAudienceOverrideRequest.Builder()
        .setBuyer(buyer)
        .setName(name)
        .setBiddingLogicJs(biddingLogicJS)
        .setTrustedBiddingSignals(trustedBiddingSignals)
        .build();

// Run the call to override JavaScript for the given custom audience
testCustomAudienceManager.overrideCustomAudienceRemoteInfo(
    request,
    executor,
    outComeReceiver);

Os valores de comprador e nome são os mesmos usados para criar o público-alvo personalizado. Saiba mais sobre esses campos.

Além disso, é possível especificar dois outros parâmetros:

  • biddingLogicJs: JavaScript que contém a lógica do comprador usada durante a seleção do anúncio. Confira as assinaturas de função necessárias no JavaScript.
  • trustedBiddingSignals: indicadores de lance a serem usados durante a seleção do anúncio. Para fins de testes, essa pode ser uma string vazia.

O método overrideCustomAudienceRemoteInfo() assíncrono usa o objeto OutcomeReceiver para informar o resultado da chamada de API.

O callback onResult() indica que a substituição foi aplicada. As próximas chamadas para selectAds() usam a lógica de lances e relatórios que você transmitiu como a substituição.

O callback onError() indica duas condições possíveis.

  • Se ocorrer uma tentativa de substituição com argumentos inválidos, a AdServiceException vai indicar uma IllegalArgumentException como causa.
  • Se ocorrer uma tentativa de substituição com um app que não está sendo executado no modo de depuração com as opções do desenvolvedor ativadas, a AdServiceException vai indicar IllegalStateException como causa.

Redefinir substituições de compra

Esta seção pressupõe que você substituiu o JavaScript de compra e tem uma referência ao TestCustomAudienceManager usado na seção anterior.

Para redefinir substituições de todos os públicos-alvo personalizados, faça o seguinte:

  1. Chame o método resetAllCustomAudienceOverrides() assíncrono com os objetos Executor e OutcomeReceiver correspondentes.

Kotlin

// Resets overrides for all custom audiences
testCustomAudienceManager.resetCustomAudienceRemoteInfoOverride(
    executor,
    outComeReceiver)

Java

// Resets overrides for all custom audiences
testCustomAudienceManager.resetCustomAudienceRemoteInfoOverride(
    executor,
    outComeReceiver)

Após redefinir as substituições de compra, todas as próximas chamadas para selectAds() vão usar qualquer biddingLogicUrl e trustedBiddingData armazenados no CustomAudience para tentar buscar o JavaScript necessário.

Se a chamada para resetCustomAudienceRemoteInfoOverride() falhar, o callback OutComeReceiver.onError() vai fornecer uma AdServiceException. Se ocorrer uma tentativa de remoção de substituições com um app que não está sendo executado no modo de depuração com as opções do desenvolvedor ativadas, a AdServiceException vai indicar IllegalStateException como causa.

Configurar um servidor de relatórios

Ao usar substituições de busca remota, você ainda vai precisar configurar um servidor que o dispositivo ou emulador pode acessar para responder a eventos de relatórios. Um endpoint simples que retorna 200 é suficiente para realizar testes. O repositório do GitHub inclui definições de serviço da OpenAPI, que podem ser implantadas em uma plataforma de simulação ou microsserviços com suporte. Para conferir mais detalhes, consulte o arquivo README (em inglês) do projeto.

Ao procurar pelas definições da OpenAPI, também procure pelo reporting-server.json. Esse arquivo contém um endpoint simples que retorna 200, representando um código de resposta HTTP. Esse endpoint é usado durante selectAds() e indica à API Protected Audience que o relatório de impressões foi concluído.

Funcionalidade para testes

  • Faça exercícios para aderir ou sair e configurar um público-alvo personalizado com base em ações anteriores do usuário.
  • Inicie a seleção de anúncios no dispositivo usando JavaScripts hospedados remotamente.
  • Observe como a associação de um app a configurações de públicos-alvo personalizados pode afetar os resultados da seleção de anúncios.
  • Use os relatórios de impressão após a seleção de anúncios.

Limitações

A tabela abaixo lista as limitações de processamento da API Protected Audience. Os limites apresentados podem estar sujeitos a mudanças com base no feedback. Para recursos em desenvolvimento, leia as notas da versão.

Componente Descrição do limite Valor do limite
Público-alvo (CA) personalizado Número máximo de anúncios por CA 100
Número máximo de CAs por aplicativo 1000
Número máximo de apps que podem criar um CA 1000
Tempo de atraso máximo para ativação de um CA a partir do horário de criação 60 dias
Tempo de expiração máximo de um CA a partir do tempo de ativação 60 dias
Número máximo de CAs no dispositivo 4000
Tamanho máximo do nome do CA 200 bytes
Tamanho máximo do URI de busca diária 400 bytes
Tamanho máximo do URI da lógica de lances 400 bytes
Tamanho máximo dos dados de lances confiáveis 10 KB
Tamanho máximo dos indicadores de lances do usuário 10 KB
Taxa máxima de chamada de leaveCustomAudience por comprador 1 por segundo
Taxa máxima de chamada de joinCustomAudience por comprador 1 por segundo
Busca em segundo plano de CA Tempo limite da conexão 5 segundos
Tempo limite de leitura HTTP 30 segundos
Tamanho total máximo do download 10 KB
Duração máxima de uma iteração de busca 5 minutos
Número máximo de CAs atualizadas por job 1000
Seleção de anúncios Número máximo de compradores A confirmar
Número máximo de CAs por comprador A confirmar
Número máximo de anúncios em um leilão A confirmar
Tempo limite da conexão inicial 5 segundos
Tempo limite de leitura da conexão 5 segundos
Tempo máximo de execução da AdSelection geral 10 segundos
Tempo máximo de execução de lances por CA na AdSelection 5 segundos
Tempo máximo de execução da pontuação na AdSelection 5 segundos
Tempo máximo de execução da AdSelection por comprador A confirmar
Tamanho máximo dos indicadores de seleção de anúncio/vendedor/por comprador A confirmar
Tamanho máximo de scripts de vendedor/comprador A confirmar
Tarifa máxima de chamadas para selectAds 1 QPS
Relatórios de impressão Tempo mínimo antes de remover a seleção de anúncios da persistência 24 horas
Número máximo de seleções de anúncios de armazenamento A confirmar
Tamanho máximo do URL de saída do relatório A confirmar
Tempo máximo para relatórios de impressão A confirmar
Número máximo de novas tentativas para chamadas de notificação A confirmar
Tempo limite de conexão 5 segundos
Tempo total de execução máximo para reportImpression 2 segundos
Tarifa máxima de chamadas para reportImpressions 1 QPS
Relatórios de eventos Número máximo de beacons por comprador por leilão 10

Número máximo de beacons por vendedor por leilão

10

Tamanho máximo da chave de evento

40 bytes

Tamanho máximo dos dados do evento

64 KB

Anúncios Tamanho máximo da lista de anúncios 10 KB compartilhados por todos os AdData em um único CA para contexto
URLs Tamanho máximo de qualquer string de URL aceita como entrada A confirmar
JavaScript Tempo máximo de execução 1 segundo para lances e pontuação para o relatório de impressão
Memória máxima usada 10 MB

Informar bugs e problemas

Seu feedback é uma parte crucial do Sandbox de privacidade no Android. Avise nossa equipe sobre problemas encontrados ou ideias para melhorar o Sandbox de privacidade do Android.