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.
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:
- Gerenciar públicos-alvo personalizados
- Configurar e executar a seleção de anúncios em um dispositivo
- Gerar relatórios de impressões de anúncio
Antes de começar
Antes de começar, siga estas etapas:
- Configure seu ambiente de desenvolvimento para o Sandbox de privacidade do Android.
- Instale uma imagem do sistema em um dispositivo com suporte ou configure um emulador que inclua suporte ao Sandbox de privacidade do Android.
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 \"*\"
Inclua uma permissão
ACCESS_ADSERVICES_CUSTOM_AUDIENCE
no manifesto do app:<uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" />
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" />
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>
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
ereportImpression
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:
- Inicialize o objeto
CustomAudienceManager
. - Crie um objeto
CustomAudience
especificando parâmetros importantes, por exemplo, o pacote do comprador e um nome relevante. Em seguida, inicialize o objetoJoinCustomAudienceRequest
com o objetoCustomAudience
. - Chame o método
joinCustomAudience()
assíncrono com o objetoJoinCustomAudienceRequest
e os objetosExecutor
eOutcomeReceiver
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 objetoAdData
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.- Se a
JoinCustomAudienceRequest
for inicializada com argumentos inválidos, aAdServicesException
vai indicar umaIllegalArgumentException
como causa. - Todos os outros erros vão receber uma
AdServicesException
com umaIllegalStateException
como causa.
- Se a
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:
- Inicialize o objeto
CustomAudienceManager
. - Inicialize a
LeaveCustomAudienceRequest
com os elementosbuyer
ename
do público-alvo personalizado. Para saber mais sobre esses campos de entrada, consulte Aderir a um público-alvo personalizado. - Chame o método assíncrono
leaveCustomAudience()
com o objetoLeaveCustomAudienceRequest
e os objetosExecutor
eOutcomeReceiver
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()
:
- Inicialize um objeto
AdSelectionManager
. - Crie um objeto
AdSelectionConfig
. - Chame o método assíncrono
selectAds()
com o objetoAdSelectionConfig
e os objetosExecutor
eOutcomeReceiver
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 umaIllegalArgumentException
como causa. - Todos os outros erros vão receber uma
AdServicesException
com umaIllegalStateException
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) usaupdateAdCounterHistogram()
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:
- Configure a filtragem de anúncios.
- Crie anúncios contextuais.
Crie um objeto
AdSelectionConfig
com o seguinte:- Uma lista vazia de compradores.
- Um URI pré-criado para selecionar o lance mais alto.
- Anúncios contextuais
- 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();
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.
- Siga as etapas para seleção de anúncios contextuais usando URIs pré-criados.
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();
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.
- Exibir a seleção de anúncios próprios.
- Iterar na cadeia de mediação. Para cada rede de terceiros, siga estas etapas:
- Crie
AdSelectionFromOutcomeConfig
incluindo ooutcomeId
próprio e o lance mínimo do SDK de terceiros. - Chame
selectAds()
com a configuração da etapa anterior. - Se o resultado não estiver vazio, o anúncio será retornado.
- Chame o método
selectAds()
do adaptador de rede do SDK atual. Se o resultado não estiver vazio, o anúncio será retornado.
- Crie
- 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:
- Inicialize um objeto
AdSelectionManager
. - Crie um objeto
ReportImpressionRequest
com o ID de seleção de anúncios. - Chame o método assíncrono
reportImpression()
com o objetoReportImpressionRequest
e os objetosExecutor
eOutcomeReceiver
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 umaIllegalArgumentException
como causa. - Todos os outros erros vão receber uma
AdServicesException
com umaIllegalStateException
como causa.
- Se a chamada for inicializada com um argumento de entrada inválido, a
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:
- Inicialize um objeto
AdSelectionManager
. - 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. - Chame o método assíncrono
reportInteraction()
com o objetorequest
e os objetosExecutor
eOutcomeReceiver
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()
, umaLimitExceededException
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()
, umaIllegalStateException
será retornada.
- Se a chamada for feita quando o app estiver sendo executado em segundo plano, uma
- 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 formatovar 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
ename
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
eexpiration_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 campouserBiddingSignals
daCustomAudience
no momento da criação.trusted_bidding_signals, contextual_signals
euser_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 propriedaderender_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 degenerateBid
.bid
: o valor do lance do anúncio.ad_selection_config
: um objeto JSON que representa o parâmetroAdSelectionConfig
da APIselectAds
. 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âmetrosellerSignals
da APIAdSelectionConfig
.trusted_scoring_signal
: lido no campoadSelectionSignals
do parâmetro da APIAdSelectionConfig
.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 mapaperBuyerSignal
do parâmetro da APIAdSelectionConfig
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 descoreAds
.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 degenerateBid
.
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çãoreportWin
.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 descoreAd
.signals_for_buyer
: um objeto JSON retornado porreportResult
.contextual_signals, custom_audience_signals
: consulte a documentação degenerateBid
.
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 oreporting_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:
- Endpoint do comprador, que mostra o JavaScript da lógica de lances.
- Um endpoint que disponibiliza indicadores de lances.
- Endpoint do vendedor, que mostra o JavaScript da lógica de decisão.
- Um endpoint que disponibiliza sinais de pontuação.
- Endpoint de relatórios de impressão do comprador vencedor.
- Endpoint de relatórios de impressão do vendedor.
- 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:
- Inicialize um objeto
AdSelectionManager
. - Receba uma referência a
TestAdSelectionManager
no objetoAdSelectionManager
. - Crie um objeto
AdSelectionConfig
. - Crie uma
AddAdSelectionOverrideRequest
com o objetoAdSelectionConfig
e umaString
que representa o JavaScript que você quer usar como substituição. - Chame o método assíncrono
overrideAdSelectionConfigRemoteInfo()
com o objetoAddAdSelectionOverrideRequest
e os objetosExecutor
eOutcomeReceiver
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 umaIllegalArgumentException
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 indicarIllegalStateException
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:
- Chame o método
resetAllAdSelectionConfigRemoteOverrides()
assíncrono com o objetoOutcomeReceiver
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
- Siga as etapas para aderir a um público-alvo personalizado.
- 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. - Chame o método assíncrono
overrideCustomAudienceRemoteInfo()
com o objetoAddCustomAudienceOverrideRequest
e os objetosExecutor
eOutcomeReceiver
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 umaIllegalArgumentException
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 indicarIllegalStateException
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:
- Chame o método
resetAllCustomAudienceOverrides()
assíncrono com os objetosExecutor
eOutcomeReceiver
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.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Suporte para a segmentação por público-alvo personalizado usando a API Protected Audience
- Notas da versão
- Protected Audience: guia de integração