Protected Audience API on Android 开发者指南

在阅读 Privacy Sandbox on Android 文档时,请使用开发者预览版Beta 版按钮选择您所使用的程序版本,因为不同版本的说明可能会有所不同。


提供反馈

Protected Audience API on Android(以前称为 FLEDGE)包含 Custom Audience API 和 Ad Selection API。广告技术平台和广告主可以使用这些 API 根据用户之前的应用互动情况来投放定制广告,既限制应用之间共享标识符的行为,也限制与第三方共享用户与应用的互动信息的行为。

Custom Audience API 以“自定义受众群体”(代表一组具有共同意向的用户)抽象为中心。广告主可以为用户注册自定义受众群体,并将相关广告与其相关联。此信息存储在本地,可用于为广告主出价、广告过滤和广告呈现提供依据。

Ad Selection API 提供了一个框架,可以让多个开发者在本地针对自定义受众群体运行竞价。为了做到这一点,系统会考虑与自定义受众群体关联的相关广告,并对广告技术平台向设备返回的广告执行额外的处理。

广告技术平台可以集成这些 API,以实现保护用户隐私的再营销。我们计划在未来的版本中提供对其他用例(包括应用安装广告)的支持。如需详细了解 Protected Audience API on Android,请参阅设计提案

本指南介绍了如何使用 Protected Audience API on Android 来执行以下操作:

  1. 管理自定义受众群体
  2. 在设备上设置和运行广告选择
  3. 报告广告展示次数

准备工作

在开始之前,请完成以下步骤:

  1. 为 Privacy Sandbox on Android 设置开发环境
  2. 将系统映像安装到受支持的设备上设置支持 Privacy Sandbox on Android 的模拟器
  3. 在终端中,使用以下 adb 命令启用对 Protected Audience API 的访问权限(默认处于停用状态)。

      adb shell device_config put adservices ppapi_app_allow_list \"*\"
    
  4. 在应用清单中添加 ACCESS_ADSERVICES_CUSTOM_AUDIENCE 权限:

      <uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" />
    
  5. 在清单的 <application> 元素中引用广告服务配置:

      <property android:name="android.adservices.AD_SERVICES_CONFIG"
                android:resource="@xml/ad_services_config" />
    
  6. 指定清单中引用的广告服务 XML 资源,例如 res/xml/ad_services_config.xml详细了解广告服务权限和 SDK 访问权限控制

      <ad-services-config>
        <custom-audiences allowAllToAccess="true" />
      </ad-services-config>
    
  7. 默认情况下,Ad Selection API 会对竞价或展示报告脚本可以分配的最大内存量施加限制。如需使用内存限制功能,需要使用 WebView 版本 105.0.5195.58 或更高版本。平台会强制执行版本检查,如果不符合此版本要求,对 selectAdsreportImpression API 的调用将失败。您可以通过以下两种方式进行此设置:

    • 方式 1:运行以下 adb 命令以停用此检查:

      adb device_config put fledge_js_isolate_enforce_max_heap_size false
      
    • 方式 2:从 Google Play 商店安装 WebView Beta 版。此版本不得低于上述版本。

加入自定义受众群体

自定义受众群体代表一组由广告主应用确定出的具有共同意向或兴趣的用户。应用或 SDK 可使用自定义受众群体来指示特定受众群体,例如在购物车中添加商品却未结账的用户。如需异步创建或加入自定义受众群体,请执行以下操作:

  1. 初始化 CustomAudienceManager 对象。
  2. 通过指定买方软件包和相关名称等关键参数来创建 CustomAudience 对象。然后,使用 CustomAudience 对象初始化 JoinCustomAudienceRequest 对象。
  3. 使用 JoinCustomAudienceRequest 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 joinCustomAudience()

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

以下参数的组合可以让设备上的每个 CustomAudience 对象具有唯一标识:

  • owner:所有者应用的软件包名称。该参数会隐式设置为调用方应用的软件包名称。
  • buyer:买方广告网络的标识符(此广告网络负责管理面向此自定义受众群体的广告)。
  • name:自定义受众群体的任意名称或标识符。

使用不同的 CustomAudience 实例反复调用 joinCustomAudience() 时,会更新任何具有匹配的 owner, buyername 参数的现有 CustomAudience。为了帮助保护隐私,该 API 的结果不区分“创建”和“更新”。

此外,必须使用以下必需参数创建 CustomAudience

  • 每日更新网址每天在后台查询的 HTTPS 网址,用于更新自定义受众群体的用户出价信号、可信出价数据以及广告的呈现网址和元数据。
  • 出价逻辑网址:在广告选择过程中查询的 HTTPS 网址,用于提取买方的 JavaScript 出价逻辑。查看此 JavaScript 中必需的函数签名
  • 广告呈现 ID:买方广告技术平台设置的任意 ID。这是针对 B&A 生成载荷的优化

CustomAudience 对象的可选参数可以包括:

  • 启用时间:自定义受众群体只能在其启用时间之后参与广告选择和每日更新。设置此参数有助于吸引已流失的应用用户。
  • 到期时间:未来的某个时间,超过此时间后,自定义受众群体会从设备中移除。
  • 用户出价信号:一个 JSON 字符串,包含买方的出价逻辑 JavaScript 会使用的用户信号(例如用户的首选语言区域),用于在广告选择流程中生成出价。此格式有助于广告技术平台跨平台重复使用代码并简化 JavaScript 函数的使用。
  • 可信出价数据:广告选择流程中使用的 HTTPS 网址和字符串列表,用于从可信的键值对服务中提取出价信号。
  • 广告:与要参与广告选择的广告对应的 AdData 对象列表。每个 AdData 对象都包含:
    • 呈现网址:为呈现最终广告而查询的 HTTPS 网址。
    • 元数据:已序列化为字符串的 JSON 对象,包含买方出价逻辑在广告选择流程中要使用的信息。
    • AdFilters:是一个类,其中包含用于在广告选择过程中过滤应用安装广告以及控制广告展示频次不超过频次上限的所有必要信息。

下面是一个 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();

处理 joinCustomAudience() 结果

异步 joinCustomAudience() 方法使用 OutcomeReceiver 对象来指示 API 调用的结果。

  • onResult() 回调表示已成功创建或更新自定义受众群体。
  • onError() 回调表示两种可能的情况。

下面是一个关于处理 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);
    }
};

退出自定义受众群体

如果用户不再符合给定自定义受众群体的商家标准,应用或 SDK 可以调用 leaveCustomAudience() 从设备中移除自定义受众群体。如需根据其唯一参数移除 CustomAudience,请执行以下操作:

  1. 初始化 CustomAudienceManager 对象。
  2. 使用自定义受众群体的 buyername 初始化 LeaveCustomAudienceRequest。如需详细了解这些输入字段,请参阅加入自定义受众群体
  3. 使用 LeaveCustomAudienceRequest 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 leaveCustomAudience() 方法。

Kotlin

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

// Initialize a LeaveCustomAudienceRequest
val leaveCustomAudienceRequest: LeaveCustomAudienceRequest =
    JoinCustomAudienceRequest.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 JoinCustomAudienceRequest.Builder()
        .setBuyer(buyer)
        .setName(name)
        .build();

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

与调用 joinCustomAudience() 类似,OutcomeReceiver 会发出信号来表明 API 调用已结束。为了帮助保护隐私,错误结果不区分内部错误和无效参数。当 API 调用完成后,无论是否已成功移除匹配的自定义受众群体,系统都会调用 onResult() 回调。

运行广告选择

如需使用 Protected Audience API 来选择广告,请调用 selectAds() 方法:

  1. 初始化 AdSelectionManager 对象。
  2. 构建 AdSelectionConfig 对象。
  3. 使用 AdSelectionConfig 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 selectAds() 方法。

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

selectAds() 方法需要 AdSelectionConfig 输入,您必须在该输入中指定以下必需参数:

  • 卖方:发起广告选择的卖方广告网络的标识符。
  • 决策逻辑网址:为获取卖方广告网络的 JavaScript 逻辑而查询的 HTTPS 网址。
    • HTTPS 网址:为获取卖方广告网络的 JavaScript 逻辑而查询的网址。 请参阅必需的函数签名
    • 预构建 URI:遵循 FLEDGE 的广告选择格式。 如果传递了不受支持或格式错误的预构建 URI,则会抛出 IllegalArgumentException
  • 自定义受众群体买方:卖方允许参与广告选择流程的买方广告网络标识符的完整列表。这些买方标识符对应于参与的自定义受众群体的 CustomAudience.getBuyer()

您可以选择性地指定以下参数,提高广告选择的定制程度:

  • 广告选择信号:已序列化为字符串的 JSON 对象,包含从 CustomAudience.getBiddingLogicUrl() 提取的买方出价逻辑 JavaScript 要使用的信号。
  • 卖方信号:已序列化为字符串的 JSON 对象,包含从 AdSelectionConfig.getDecisionLogicUrl() 提取的卖方 JavaScript 决策逻辑所使用的信号。
  • 每个买方的信号:已序列化为字符串的 JSON 对象映射,包含从 CustomAudience.getBiddingLogicUrl() 提取的特定买方出价逻辑 JavaScript 要使用的信号,这些信号通过参与的自定义受众群体的买方字段来标识。
  • 内容相关广告:一系列在竞价期间从 Protected Audience 竞价之外直接向买方收集的候选广告。

选择广告后,结果、出价和信号将在内部保留,供报告之用。OutcomeReceiver.onResult() 回调会返回一个包含以下内容的 AdSelectionOutcome

  • 胜出的广告的呈现网址,从 AdData.getRenderUrl() 获取。
  • 设备用户独有的广告选择 ID。此 ID 用于报告广告展示次数。

如果广告选择因参数无效、超时或资源消耗过多等原因而无法成功完成,OutcomeReceiver.onError() 回调会以如下行为抛出 AdServicesException

  • 如果使用无效参数发起广告选择,会收到 AdServicesException,表明 IllegalArgumentException 是错误原因。
  • 所有其他错误会收到 AdServicesException,以 IllegalStateException 为错误原因。

内容相关广告

Protected Audience 可以将内容相关广告纳入 Protected Auction 竞价中。内容相关广告需要在广告技术服务器上选择,并返回 Protected Audience API 之外的设备。然后,内容相关广告可以使用 AdSelectionConfig 包含在竞价中,此时这些广告的运作方式与设备端广告相同,包括符合进行排除性广告过滤的条件。Protected Audience 竞价完成后,您需要调用 reportImpression()。这将在胜出的内容相关广告中调用 reportWin()(采用与展示次数报告相同的格式),以在设备上接收胜出的广告。每个内容相关广告都需要有买方、出价、与报告逻辑的关联、呈现网址和广告元数据。

如需在应用中部署内容相关广告,目标应用需要创建一个 ContextualAds 对象:

Kotlin

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

Java

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

然后,您可以在创建 AdSelectionConfig 时一起传递生成的 ContextualAds 对象:

Kotlin

// Create a new ad
val noFilterAd: AdData = Builder()
  .setMetadata(JSONObject().toString())
  .setRenderUri(Uri.parse(baseUri + NO_FILTER_RENDER_SUFFIX))
  .build()
val noFilterAdWithBid = AdWithBid(noFilterAd, NO_FILTER_BID)
contextualAds.getAdsWithBid().add(noFilterAdWithBid)

Java

// Create a new ad
AdData noFilterAd = new AdData.Builder()
  .setMetadata(new JSONObject().toString())
  .setRenderUri(Uri.parse(baseUri + NO_FILTER_RENDER_SUFFIX))
  .build();
AdWithBid noFilterAdWithBid = new AdWithBid(noFilterAd, NO_FILTER_BID);
contextualAds.getAdsWithBid().add(noFilterAdWithBid);

应用安装广告过滤

应用安装广告过滤可帮助您过滤已安装到设备上的应用的安装广告。

此过程的第一步是定义哪些广告主能够过滤已安装的软件包。这项操作需要在您希望广告定位到的应用中执行。

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

当上述代码执行时,传入的广告主能够滤除您在其出价生成期间指定的已安装应用。如果您需要移除广告主对此应用的安装状态的访问权限,请再次运行此代码,并移除该广告主的信息。

下一步是在发布商应用内设置广告过滤。在发布商应用内投放广告的一方(很可能是供应方 SDK)必须初始化其 AdFilters 对象,指明其想要滤除哪些与应用相关的广告:

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

需求方发布商还可以为其自定义受众群体内存在的广告设置 AdFilter

您也可以在实例化新的 AdData 对象时传入 AdFilters

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

频次上限过滤

借助频次上限过滤功能,广告技术平台可以限制广告的展示次数。该功能可减少广告过度展示,并帮助您优化为给定广告系列做出的备用广告选择。

频次上限过滤器有两个主要组成部分:广告事件类型和广告计数器键。您可以使用以下广告事件类型:

  • 胜出(即将推出):胜出事件表示相应广告在竞价中获胜。胜出事件由 Protected Audience API 自动更新,无法被开发者直接调用。胜出事件的相关数据仅针对面向给定自定义受众群体的广告显示。
  • 展示:与 reportImpression 不同,设备端调用方(SSP 或 MMP)会使用 updateAdCounterHistogram() 在所选代码中的特定位置调用展示事件。展示事件会针对给定 DSP 名下的所有广告显示,不限于面向同一自定义受众群体的广告。
  • 观看:设备端调用方(SSP 或 MMP)会通过在所选代码中的某个位置调用 updateAdCounterHistogram() 来调用观看事件。观看事件会针对给定 DSP 名下的所有广告显示,不限于面向同一自定义受众群体的广告。
  • 点击:设备端调用方(SSP 或 MMP)会通过在所选代码中的某个位置调用 updateAdCounterHistogram() 来调用点击事件。点击事件会针对给定 DSP 名下的所有广告显示,不限于面向同一自定义受众群体的广告。

在发布商应用中,设备上存在的 SSP 或 MMP 会调用广告事件。调用 updateAdCounterHistogram() 后,频次上限过滤器中计数器上的计数会递增,这样未来的竞价中便可以显示届时一共有多少用户观看了某个给定广告。广告事件类型不会被强制关联到相应的用户操作,而是会作为准则提供给调用方,帮助其构建事件系统。为了让广告计数器在事件发生时实现计数递增,设备端操作者需要提供在广告竞价中胜出的广告对应的广告选择 ID。

广告计数器键是由买方广告技术平台分配的任意 32 位有符号整数,对应于 DSP 所定义的一组给定广告。由于广告计数器键仅适用于给定 DSP 名下的广告,因此可被自由选择,而不会与来自另一广告技术平台的直方图重叠。广告计数器键用于为某个 DSP 的广告或给定的自定义受众群体递增统计 DSP 专用标识符,以滤除未来竞价中的广告。

通过使用计数器键,您可根据某个特定用户与给定买方广告技术平台的其他广告之间的互动情况,优先将更有望令该用户感兴趣的广告展示给该用户。例如,如果某个广告通过在广告竞价中胜出、获得观看和获得点击实现了较高的互动度,则表示相应事件是一个推断的数据点。举个例子来进一步说明这一点:如果某个广告的宣传对象是左手高尔夫球杆,则可能意味着该广告的目标用户不会对右手高尔夫球杆感兴趣。您可以通过为分配给左手杆广告的计数器键设置一个频次上限过滤器,来滤除宣传右手高尔夫球杆的广告。

如需在竞价中使用频次上限,您必须先创建 KeyedFrequencyCap 对象,如下所示:

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

创建 KeyedFrequencyCap 对象后,您可将其传入 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();

如果您使用频次上限过滤器对 AdFilters 对象进行了填充,则可在创建自定义受众群体时传递该对象:

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

在自定义受众群体中实现频次上限过滤器后,SSP 便可调用必要的点击、观看或展示事件。

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

如果广告达到预设的频次上限过滤限值,则会在竞价中被滤除。过滤发生在针对设备端竞价执行出价逻辑之前,以及在为出价和竞价服务竞价生成载荷时。借助此工具包,广告技术平台可以灵活地使用用户与自定义受众群体中的广告进行的互动,以重点进行广告定位,同时最大限度地减少广告过度展示。

内容相关广告过滤(不进行网络调用)

如果设备没有再营销需求,那么您可以不进行网络调用便针对内容相关广告运行广告选择。借助预构建的 URI 和包含出价的内容相关广告列表,平台可以跳过检索出价逻辑、出价信号和评分信号。平台会使用预构建的 URI 来选择出价最高的内容相关广告。

为了缩短延迟时间,广告技术平台可以运行这样的广告选择流程:仅涵盖内容相关广告、具备广告过滤功能且不进行网络调用。这通过使用预构建的 URI 发出评分信号来实现。您可参阅“支持的预构建 URI 用例和名称”部分,查看一系列 scoreAds 实现。

若要不进行网络调用便运行广告选择:

  1. 设置广告过滤
  2. 制作您的内容相关广告
  3. 创建一个包含以下内容的 AdSelectionConfig 对象:

    1. 一个空白的买方列表
    2. 用于选择最高出价的预构建 URI
    3. 内容相关广告
    4. 一个针对评分信号的空 URI。若要表示您不希望通过提取信任信号用于评分,您可使用空 URI:
    Uri prebuiltURIScoringUri = Uri.parse("ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=your.registered.uri/reporting");
    // Initialize AdSelectionConfig
    AdSelectionConfig adSelectionConfig =
      new AdSelectionConfig.Builder()
        .setSeller(seller)
        .setDecisionLogicUri(prebuiltURIScoringUri)
        .setCustomAudienceBuyers(Collections.emptyList())
        .setAdSelectionSignals(adSelectionSignals)
        .setSellerSignals(sellerSignals)
        .setPerBuyerSignals(perBuyerSignals)
        .setBuyerContextualAds(buyerContextualAds)
        .setTrustedScoringSignalsUri(Uri.EMPTY)
        .build();
    
  4. 运行广告选择:

    adSelectionManager.selectAds(
        adSelectionConfig,
        executor,
        outcomeReceiver);
    

使用预构建的 URI 运行您自己的报告 JavaScript

目前,Privacy Sandbox 平台只有适用于预构建 URI 的基本报告 JavaScript 实现。如果您想运行自己的报告 JavaScript,同时仍使用预构建的 URI 进行低延迟广告选择,则可以替换广告选择和报告生成之间的 DecisionLogicUri

  1. 执行相应步骤,以便使用预构建的 URI 针对内容相关广告运行广告选择
  2. 创建您的 AdSelectionConfig 副本,然后再生成报告

    adSelectionConfigWithYourReportingJS = adSelectionConfig.cloneToBuilder()
      // Replace <urlToFetchYourReportingJS> with your own URL:
      .setDecisionLogicUri(Uri.parse(<urlToFetchYourReportingJS>))
      .build();
    
  3. 生成展示次数报告

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

运行广告瀑布流中介

广告瀑布流中介需要多个由第一方 SDK 中介广告联盟编排的第三方 SDK(第三方广告联盟)。无论竞价是在设备上进行,还是通过出价和竞价服务 (B&A) 运行,广告瀑布流中介都采用相同的方式。

第三方广告联盟

第三方广告联盟需要提供适配器,以便中介广告联盟调用必要的方法来运行竞价:

  • 运行广告选择
  • 报告展示次数

以下是中介广告联盟适配器的示例:

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() {...}
}

每个 SDK 都有自己的广告选择服务管理器和客户端,以及自己的 selectAdsreportImpressions 实现。SDK 提供方可以参阅针对设备端竞价如何运行广告选择的部分,或针对 B&A 竞价的 B&A 说明文档。了解如何报告广告展示次数(按照单个 SSP 展示次数报告生成报告)。

中介广告联盟

与第三方广告联盟类似,中介广告联盟也需要 selectAdsreportImpression 实现。如需了解详情,请参阅关于如何运行广告选择和如何报告广告展示次数的内容。

中介广告联盟负责运行中介链并将自己置入中介链中。下一部分将介绍如何设置并执行此过程。

检索中介链和出价下限

中介广告联盟负责检索第一方 (1P) 内容相关广告、中介链和第三方 (3P) 广告联盟的出价下限。如果请求检索由中介广告联盟执行的内容相关广告,那么就可能发生这种情况。中介链决定了如何遍历第三方广告联盟,而出价下限可以作为 adSelectionSignals 传递给竞价流程。

中介链中的广告网络展示位置

中介 SDK 可以根据第一方广告出价的实时有效每千次展示费用 (eCPM) 将自己置入中介链中。在 Protected Audience API 中,广告出价是不透明的。中介 SDK 只有使用 AdSelectionFromOutcomesConfig 才能将指定第一方广告的出价与链中下一个第三方广告联盟的出价下限进行比较。如果第一方出价高于出价下限,则意味着中介 SDK 位于该第三方广告联盟之前。

运行广告选择

如要检索第一方候选广告,中介广告联盟可以按照运行广告选择部分中的步骤执行设备端竞价。此操作会生成第一方候选广告、出价以及用于中介流程的 AdSelectionId

创建 AdSelectionFromOutcomesConfig

AdSelectionFromOutcomesConfig 允许中介广告联盟传递 AdSelectionIds 列表(之前竞价的结果)、广告选择信号以及用于获取 JavaScript 的 URI,以便从多个候选广告中选择广告。AdSelectionId 的列表及其出价和信号传递给 JavaScript 后,如果高于出价下限,JavaScript 会返回其中一个 AdSelectionIds;如果中介链应继续运行,则返回无。

中介广告联盟使用上一部分中的第一方 AdSelectionId 以及第三方广告联盟所考虑的出价下限来创建 AdSelectionFromOutcomesConfig。您应为中介链中的每个步骤创建一个新的 AdSelectionFromOutcomesConfig

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

广告瀑布流中介的 selectAds() 方法替换项需要 AdSelectionFromOutcomesConfig 输入,您必须在该输入中指定以下必需参数:

  • 卖方:发起广告选择的卖方广告网络的标识符。
  • AdSelectionIds:之前针对第一方广告运行的 selectAds() 的单例列表。
  • 广告选择信号:已序列化为字符串的 JSON 对象,包含买方出价逻辑要使用的信号。在这种情况下,包括针对指定第三方广告联盟检索到的出价下限。
  • 选择逻辑 URI:在广告选择过程中查询的 HTTPS 网址,用于提取中介广告联盟的 JavaScript 来选择胜出的广告。查看此 JavaScript 中必需的函数签名。如果出价高于出价下限,JavaScript 应返回第三方广告,否则返回 null。这样,中介 SDK 找到胜出的广告后,就可以截断中介链。

创建 AdSelectionOutcomesConfig 后,调用链中最靠前的第三方广告联盟的 selectAds() 方法。

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

编排广告瀑布流中介

以下是运行中介流程的操作顺序。

  1. 运行第一方广告选择。
  2. 遍历中介链。对于每个第三方广告联盟,请执行以下操作:
    1. 构建 AdSelectionFromOutcomeConfig,包括第一方 outcomeId 和第三方 SDK 的出价下限。
    2. 使用上一步中的配置调用 selectAds()
    3. 如果结果不为空,则返回广告。
    4. 调用当前 SDK 广告联盟适配器的 selectAds() 方法。如果结果不为空,则返回广告。
  3. 如果未能从链中找到胜出的广告,则返回第一方广告。

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) { ... }
}

报告广告展示次数

报告广告展示次数有两个流程,具体取决于竞价的运行方式。如果您是用单个 SSP 运行竞价,请按照本部分的说明操作。如果您要实现广告瀑布流中介,请按照广告瀑布流中介展示次数报告部分中的步骤操作。

单个 SSP 展示次数报告

通过广告选择工作流程选择胜出的广告后,您可以使用 AdSelectionManager.reportImpression() 方法向参与的买方和卖方平台报告展示次数。如需报告广告展示次数,请执行以下操作:

  1. 初始化 AdSelectionManager 对象。
  2. 使用广告选择 ID 构建 ReportImpressionRequest 对象。
  3. 使用 ReportImpressionRequest 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 reportImpression() 方法。

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)

使用以下必需参数初始化 ReportImpressionRequest

  • 广告选择 ID:设备用户独有的唯一 ID,用于标识成功的广告选择。
  • 广告选择配置selectAds() 调用中使用的相同配置,以提供的广告选择 ID 标识。

异步 reportImpression() 方法使用 OutcomeReceiver 对象来指示 API 调用的结果。

  • onResult() 回调指示展示次数报告网址是否已创建,以及请求是否已安排。
  • onError() 回调表示以下可能的情况:
    • 如果使用无效输入参数初始化调用,会收到 AdServicesException,表明 IllegalArgumentException 是错误原因。
    • 所有其他错误会收到 AdServicesException,以 IllegalStateException 为错误原因。

广告瀑布流中介展示次数报告

中介 SDK 需要跟踪胜出的 SDK 以触发其报告流程。参与中介链的 SDK 应提供一种方法供中介调用,以触发他们自己的报告流程。参与中介竞价的 SDK 可以按照上述步骤实现自己的报告。

SSP 可以使用此第三方 SDK 代码示例作为如何加入中介流程的原型:

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

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

展示次数报告端点

报告展示次数 API 将向卖方平台提供的端点和胜出的买方平台提供的端点发出 HTTPS GET 请求:

买方平台端点:

  • 该 API 会使用自定义受众群体中指定的出价逻辑网址来提取买方提供的 JavaScript(其中包含返回展示次数报告网址的逻辑)。
  • 调用 reportWin() JavaScript 函数,该函数应返回买方的展示次数报告网址。

卖方平台端点:

  • 使用 AdSelectionConfig 对象中指定的决策逻辑网址来提取卖方的决策逻辑 JavaScript。
  • 调用 reportResult() JavaScript 函数,该函数应返回买方的展示次数报告网址。

出价和竞价服务报告

在出价和竞价服务上执行的竞价将在服务器端竞价的加密响应中包含所有必要的报告信息(包括为广告互动报告生成的网址)。解密响应后,系统便会向平台注册相应的网址,因此广告和展示次数报告会遵循上面列出的步骤。

尽最大努力报告展示次数

reportImpression() 方法旨在尽最大努力完成报告。

报告广告互动情况

Protected Audience 支持为呈现的广告生成更精细的互动情况报告。这可以包括查看时间、点击次数、悬停次数或任何其他可收集的实用指标等互动信息。接收这些报告的过程包含两个步骤。首先,买方和卖方必须进行注册才能在其报告 JavaScript 中收到这些报告。然后,客户端需要报告这些事件。

注册以接收互动事件

使用平台提供的 JavaScript 函数 registerAdBeacon 注册在买方的 reportWin() 和卖方的 reportResult() JavaScript 函数中发生的互动事件。如需注册以接收事件报告,只需从报告 JavaScript 中调用平台 JavaScript 函数即可。以下代码段使用的是买方的 reportWin(),但同样的方法也适用于 reportResult()

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

    return reportingUrl;
}

报告互动事件

报告某次展示后,客户端可以使用 AdSelectionManager.reportInteraction() 方法将互动报告给之前注册的胜出买方和卖方平台。若要报告广告事件,请执行以下操作:

  1. 初始化 AdSelectionManager 对象。
  2. 使用广告选择 ID、互动键、互动数据和报告目的地构建一个 ReportInteractionRequest 对象。
  3. 使用 request 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 reportInteraction() 方法。
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);

使用以下必需参数初始化 ReportInteractionRequest

  • 广告选择 ID:从先前返回的 AdSelectionOutcome 中检索到的广告选择 ID。
  • 互动键:由客户端定义的字符串键,用于描述要报告的操作。此键必须与卖方或买方在报告 JavaScript 函数中注册的键一致。
  • 互动数据:一个字符串,其中包含要包含在事件报告中以便 POST 回报告服务器的数据。
  • 报告目的地:一个位掩码,用于指定是否应该向买方和/或卖方报告事件。这些标志由平台提供,最终目的地掩码可以使用按位运算创建。如需向一个目的地报告,您可以直接使用平台提供的标志。如需向多个目的地报告,您可以使用按位或运算 (|) 来组合标志值。

异步 reportInteraction() 方法使用 OutcomeReceiver 对象来指示 API 调用的结果。

  • onResult() 回调表示报告互动调用有效。
  • onError() 回调表示以下可能的情况:
    • 如果应用在后台运行时进行调用,系统将返回一个 IllegalStateException,其中包含失败说明。
    • 如果客户端受到限制而无法调用 reportInteraction(),系统会返回 LimitExceededException
    • 如果软件包未注册而无法调用可保护隐私的 API,系统会返回 SecurityException()
    • 如果应用报告的互动情况不同于调用 selectAds() 的应用,系统会返回 IllegalStateException
  • 如果用户未同意启用 Privacy Sandbox API,调用将静默失败。

互动情况报告端点

报告互动情况 API 会向卖方平台和胜出的买方平台提供的端点发出 HTTPS POST 请求。Protected Audience 会将互动键与报告 JavaScript 中声明的 URI 进行匹配,并针对要报告的每次互动向每个端点发出 POST 请求。请求的内容类型为纯文本,正文为互动数据。

尽最大努力报告互动情况

reportInteraction() 旨在尽最大努力通过 HTTP POST 完成报告。

每日后台更新

创建自定义受众群体时,您的应用或 SDK 可以初始化自定义受众群体元数据。此外,平台还可以通过每日后台更新进程更新以下自定义受众群体元数据。

  • 用户出价信号
  • 可信出价数据
  • AdData 列表

此进程会查询自定义受众群体中定义的每日更新网址,该网址可能会返回 JSON 响应。

  • JSON 响应可能包含任何支持且需要更新的元数据字段。
  • 每个 JSON 字段都会进行独立验证。客户端会忽略格式有误的所有字段,这将导致响应中的相应字段不更新。
  • HTTP 响应为空或 JSON 对象“{}”为空将导致元数据无法更新。
  • 响应消息的大小不得超过 10 KB。
  • 所有 URI 都必须使用 HTTPS。
  • trusted_bidding_uri 必须与买方位于同一个 ETLD+1 下。

示例:后台每日更新的 JSON 响应

{
    "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

广告选择工作流程会编排买方提供和卖方提供的 JavaScript 的执行。

买方提供的 JavaScript 从自定义受众群体中指定的出价逻辑网址中提取。返回的 JavaScript 应包含以下函数:

卖方提供的 JavaScript 是从广告选择 API 的 AdSelectionConfig 参数中指定的决策逻辑网址中提取的。返回的 JavaScript 应包含以下函数:

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

输入参数:

  • ad:格式为 var ad = { 'render_url': url, 'metadata': json_metadata }; 的 JSON 对象
  • auction_signals, per_buyer_signals:在竞价配置对象中指定的 JSON 对象
  • custom_audience_bidding_signals:平台生成的 JSON 对象。此 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"
    }
    

    其中:

    • ownerbuyername 是从参与广告选择的自定义受众群体的同名属性中获取的字符串
    • activation_timeexpiration_time 是自定义受众群体的启用时间和到期时间,以自 Unix 公元纪年以来的秒数表示
    • ca_user_bidding_signals 是创建 CustomAudience 时在其 userBiddingSignals 字段中指定的 JSON 字符串
    • trusted_bidding_signals, contextual_signalsuser_signals 是 JSON 对象,作为空对象传递,在未来的版本中会进行填充。其格式并非由平台强制执行,而是由广告技术平台进行管理。

结果:

  • ad:是出价所涉及的广告。允许该脚本返回其所收到的具有不同元数据的广告副本。广告的 render_url 属性应保持不变。
  • bid:表示此广告出价金额的浮点值
  • status:一个整数值,其具体值可为:
    • 0:表示执行成功
    • 1(或任何非零值):表示有任何输入信号无效的情况。如果生成出价返回非零值,那么所有 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 };
}

输入参数:

  • ad:请参阅 generateBid 文档
  • bid:广告的出价金额
  • ad_selection_config:一个 JSON 对象,表示 selectAds API 的 AdSelectionConfig 参数。格式如下:

    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:从 sellerSignals AdSelectionConfig API 参数读取的 JSON 对象

  • trusted_scoring_signal:读取自 AdSelectionConfig API 参数中的 adSelectionSignals 字段

  • contextual_signals, user_signals:JSON 对象,目前作为空对象传递,在未来的版本中会进行填充。其格式并非由平台强制执行,而是由广告技术平台进行管理

  • per_buyer_signals:从 AdSelectionConfig API 参数的 perBuyerSignal 映射中读取的 JSON 对象,用作当前自定义受众群体买方的键。如果映射不包含任何给定买方的条目,则为空。

输出:

  • score:表示此广告得分值的浮点值
  • status:一个整数值,其具体值可为:
    • 0:表示执行成功
    • 1:如果 customAudienceSignals 无效
    • 2:如果 AdSelectionConfig 无效
    • 3:如果任何其他信号无效
    • 任何非零值都会导致流程失败,该值会决定抛出的异常类型

selectOutcome()

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

输入参数:

  • outcomes:JSON 对象 {"id": id_string, "bid": bid_double}
  • selection_signals:在竞价配置对象中指定的 JSON 对象

输出:

  • status0 表示成功,非零表示失败
  • result:传递的结果之一或 null

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

输入参数:

  • ad_selection_config:请参阅 scoreAds 的文档
  • render_url:胜出的广告的呈现网址
  • bid:对胜出的广告的出价
  • contextual_signals:请参阅 generateBid 的文档

输出:

  • status: 0 表示成功,非零表示失败
  • results:包含以下内容的 JSON 对象:
    • signals_for_buyer:将传递给 reportWin 函数的 JSON 对象
    • reporting_url:平台将用于向买方通知展示次数的网址

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

输入参数:

  • ad_selection_signals, per_buyer_signals:请参阅 scoreAd 的文档
  • signals_for_buyerreportResult 返回的 JSON 对象
  • contextual_signals, custom_audience_signals:请参阅 generateBid 的文档

输出:

  • status: 0 表示成功,非零表示失败
  • results:包含以下内容的 JSON 对象:
    • reporting_url:平台将用于向卖方通知展示次数的网址

registerAdBeacon()

function registerAdBeacon(
  interaction_key,
  reporting_uri
)

输入参数

  • interaction_key:表示事件的字符串。平台稍后在报告事件互动时应使用此方法,以查询应收到通知的 reporting_uri。此键需要在买方或卖方注册的内容与卖方报告的内容间进行匹配。
  • reporting_uri:用于接收事件报告的 URI。这应特定于要报告的事件类型。它必须接受 POST 请求,才能处理随事件一起报告的任何数据。

用于实现广告选择的预构建 URI

借助预构建的 URI,广告技术平台能够在 AdSelectionConfigAdSelectionFromOutcomesConfig 类中为广告选择决策逻辑指定 JavaScript 函数。预构建的 URI 不需要进行网络调用即可下载相应的 JavaScript。广告技术平台可以使用预构建的 URI,而无需设置已注册的网域来托管 JavaScript。

使用以下格式构建预构建的 URI:

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

Privacy Sandbox 平台在运行时使用来自此 URI 的信息提供 JavaScript。

如果出现以下情况,则会抛出 IllegalArgumentException

  • URI 中不存在任何必需参数
  • URI 中存在无法识别的参数

支持的预构建 URI 用例和名称

用例 1:ad-selection

selectAds(AdSelectionConfig) 流程支持 ad-selection 用例下的预构建 URI。

预构建 URI 的名称:highest-bid-wins

此预构建的 URI 提供了 JavaScript 代码,用于在出价后选择出价最高的广告。它还提供了一个基本报告函数,用于报告胜出者的 render_uribid

必需参数

reportingUrl:使用胜出广告的 render_uribid 参数化所得的基本报告网址:

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

用法

如果您的基本报告网址为 https://www.ssp.com/reporting,那么预构建的 URI 为:

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

用例 2:ad-selection-from-outcomes

ad-selection-from-outcomes 用例下的预构建 URI 支持 selectAds(AdSelectionFromOutcomesConfig) 工作流。

预构建 URI 的名称:waterfall-mediation-truncation

waterfall-mediation-truncation 预构建 URI 提供了 JavaScript 代码,可用于实现广告瀑布流中介截断逻辑;在这种逻辑中,如果 bid 高于或等于 bid floor,则 JavaScript 会返回第一方广告,否则会返回 null

必需参数

bidFloor:在 getSelectionSignals() 中传递的与中介 SDK 的广告进行比较的出价底价值的键。

用法

如果您的广告选择信号类似于 {"bid_floor": 10},则生成的预构建 URI 将如下所示:

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

测试

为帮助您着手使用 Protected Audience API,我们已使用 Kotlin 和 Java 创建了示例应用,您可在 GitHub 上找到这些应用。

前提条件

在报告广告选择结果和展示次数的过程中,Protected Audience API 需要一些 JavaScript。您可以通过两种方法在测试环境中提供此 JavaScript:

  • 运行具有所需 JavaScript 端点的服务器来返回 JavaScript
  • 通过提供来自本地来源的必要代码来替换远程提取

这两种方法都需要设置 HTTPS 端点来处理展示报告。

HTTPS 端点

如需测试广告选择和展示次数报告,您需要设置 7 个可通过测试设备或模拟器进行访问的 HTTPS 端点:

  1. 提供出价逻辑 JavaScript 的买方端点。
  2. 提供出价信号的端点。
  3. 提供决策逻辑 JavaScript 的卖方端点。
  4. 提供评分信号的端点。
  5. 胜出的买方的展示次数报告端点。
  6. 卖方的展示次数报告端点。
  7. 用于为自定义受众群体提供每日更新的端点。

为方便起见,GitHub 代码库提供用于测试的基本 JavaScript 代码。它还包含 OpenAPI 服务定义,您可将这些定义部署到受支持的模拟或微服务平台。如需了解详情,请参阅项目自述文件

替换 JavaScript 的远程提取

此功能旨在用于端到端测试。如需替换远程提取,您的应用必须在启用开发者选项的调试模式下运行。

如需为您的应用启用调试模式,请将以下行添加到 AndroidManifest.xml 的应用属性中:

<application
  android:debuggable="true">

如需查看如何使用这些替换项的示例,请参阅 GitHub 上的 Protected Audience API 示例应用

您需要自行添加自定义 JavaScript 来处理广告选择例程(例如出价、评分决策和报告)。您可以在 GitHub 代码库中找到处理所有必需请求的基本 JavaScript 代码示例。Protected Audience API 示例应用演示了如何从该文件中读取代码并将其准备好用作替换项。

您可以自行独立替换卖方和买方 JavaScript 提取,但您可能并未为一些 JavaScript 提供替换项,这就需要通过 HTTPS 端点来处理这些 JavaScript。如需了解如何设置服务器以处理这些情况,请参阅自述文件

只能替换您的软件包拥有的自定义受众群体的 JavaScript 提取。

替换卖方 JavaScript

如需设置卖方 JavaScript 替换项,请按照以下代码示例操作:

  1. 初始化 AdSelectionManager 对象。
  2. AdSelectionManager 对象获取对 TestAdSelectionManager 的引用。
  3. 构建 AdSelectionConfig 对象。
  4. 使用 AdSelectionConfig 对象和 String(表示您计划用作替换项的 JavaScript)构建 AddAdSelectionOverrideRequest
  5. 使用 AddAdSelectionOverrideRequest 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 overrideAdSelectionConfigRemoteInfo() 方法。

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

如需详细了解 AdSelectionConfig 中的每个字段的含义,请参阅运行广告选择部分。关键区别在于,可以将 decisionLogicUrl 设置为占位值,因为该值将被忽略。

为了替换广告选择过程中使用的 JavaScript,decisionLogicJs 必须包含适当的卖方函数签名。如需查看示例来了解如何以字符串形式读取 JavaScript 文件,请参阅 GitHub 上的 Protected Audience API 示例应用

异步 overrideAdSelectionConfigRemoteInfo() 方法使用 OutcomeReceiver 对象来指示 API 调用的结果。

onResult() 回调表示已成功应用替换。对 selectAds() 的后续调用将使用您传入作为替换项的任何决策和报告逻辑。

onError() 回调表示两种可能的情况:

  • 如果尝试使用无效参数进行替换,会收到 AdServiceException,表明 IllegalArgumentException 是错误原因。
  • 如果尝试对未在启用开发者选项的调试模式下运行的应用进行替换,会收到 AdServiceException,表明 IllegalStateException 是错误原因。

重置卖方替换项

本部分假定您已经替换了卖方 JavaScript,并且您已经引用上一部分中所使用的 TestAdSelectionManagerAdSelectionConfig

如需重置所有 AdSelectionConfigs 的替换项,请执行以下操作:

  1. 使用相关 OutcomeReceiver 对象调用异步 resetAllAdSelectionConfigRemoteOverrides() 方法。

Kotlin

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

Java

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

重置卖方替换项后,对 selectAds() 的调用将使用存储在 AdSelectionConfig 中的任何 decisionLogicUrl 来尝试获取所需的 JavaScript。

如果对 resetAllAdSelectionConfigRemoteOverrides() 的调用失败,OutComeReceiver.onError() 回调将抛出 AdServiceException。 如果尝试为未在启用开发者选项的调试模式下运行的应用移除替换项,会收到 AdServiceException,表明 IllegalStateException 是错误原因。

替换买方 JavaScript

  1. 按照相应步骤加入自定义受众群体
  2. 构建要用作替换项的出价逻辑和数据,并使用要替换的自定义受众群体的买方名称构建 AddCustomAudienceOverrideRequest
  3. 使用 AddCustomAudienceOverrideRequest 对象及相关的 ExecutorOutcomeReceiver 对象调用异步 overrideCustomAudienceRemoteInfo() 方法

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

买方名称的值就是创建自定义受众群体时所使用的值。详细了解这些字段

此外,您还可以另外指定两个参数:

  • biddingLogicJs:此 JavaScript 包含要在广告选择过程中使用的买方逻辑。查看此 JavaScript 中必需的函数签名
  • trustedBiddingSignals:在广告选择过程中使用的出价信号。为便于测试,它可以是空字符串。

异步 overrideCustomAudienceRemoteInfo() 方法使用 OutcomeReceiver 对象来指示 API 调用的结果。

onResult() 回调表示已成功应用替换。对 selectAds() 的后续调用将使用您传入作为替换项的任何出价和报告逻辑。

onError() 回调表示两种可能的情况。

  • 如果尝试使用无效参数进行替换,会收到 AdServiceException,表明 IllegalArgumentException 是错误原因。
  • 如果尝试对未在启用开发者选项的调试模式下运行的应用进行替换,会收到 AdServiceException,表明 IllegalStateException 是错误原因。

重置买方替换项

本部分假定您已经替换了买方 JavaScript,并且您已经引用上一部分中所使用的 TestCustomAudienceManager

如需重置所有自定义受众群体的替换项,请执行以下操作:

  1. 使用相关的 ExecutorOutcomeReceiver 对象调用异步 resetAllCustomAudienceOverrides() 方法。

Kotlin

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

Java

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

重置买方替换项后,对 selectAds() 的后续调用将使用存储在 CustomAudience 中的 biddingLogicUrltrustedBiddingData 来尝试获取所需的 JavaScript。

如果对 resetCustomAudienceRemoteInfoOverride() 的调用失败,OutComeReceiver.onError() 回调将抛出 AdServiceException。如果尝试为未在启用开发者选项的调试模式下运行的应用移除替换项,会收到 AdServiceException,表明 IllegalStateException 是错误原因。

设置报告服务器

使用远程提取替换项时,您仍然需要设置一个服务器,以便设备或模拟器访问该服务器来响应报告事件。返回 200 的简单端点足以满足测试需求。GitHub 代码库包含 OpenAPI 服务定义,您可将这些定义部署到受支持的模拟或微服务平台。如需了解详情,请参阅项目自述文件

在查找 OpenAPI 定义时,请查找 reporting-server.json。 此文件包含一个可返回 200(代表 HTTP 响应代码)的简单端点。在 selectAds() 期间,此端点用于向 Protected Audience API 发送已成功完成展示次数报告的信号。

需要测试的功能

  • 根据之前的用户操作,加入或退出和设置自定义受众群体。
  • 通过远程托管的 JavaScript 在设备上启动广告选择。
  • 观察应用与自定义受众群体设置之间的关联如何影响广告选择结果。
  • 在广告选择完成后,进行展示次数报告。

限制

下表列出了有关 Protected Audience API 处理的限制。显示的限制可能会根据反馈而发生变化。如需了解正在开发中的功能,请参阅版本说明

组件 限制说明 限制值
自定义受众群体 (CA) 每个 CA 的广告数量上限 100
每个应用的 CA 数量上限 1000
可以创建 CA 的应用数量上限 1000
从创建时间算起的 CA 启用时间延迟上限 60 天
从启用时间算起的 CA 有效期上限 60 天
设备上的 CA 数量上限 4000
CA 名称的大小上限 200 个字节
每日提取 URI 的大小上限 400 个字节
出价逻辑 URI 的大小上限 400 个字节
可信出价数据的大小上限 10 KB
用户出价信号的大小上限 10 KB
每个买方的 leaveCustomAudience 调用率上限 每秒 1 次
每个买方的 joinCustomAudience 调用率上限 每秒 1 次
CA 后台提取 连接超时 5 秒
HTTP 读取超时 30 秒
总下载大小上限 10 KB
提取迭代的时长上限 5 分钟
每个作业更新的 CA 数量上限 1000
广告选择 买方数量上限 待定
每个买方的 CA 数量上限 待定
参与竞价的广告数量上限 待定
初始连接超时 5 秒
连接读取超时 5 秒
整个 AdSelection 的执行时间上限 10 秒
AdSelection 期间每个 CA 的出价执行时间上限 5 秒
AdSelection 期间的评分执行时间上限 5 秒
AdSelection 期间每个买方的执行时间上限 待定
广告选择/卖方/每个买方信号的大小上限 待定
卖方/买方脚本的大小上限 待定
selectAds 的调用率上限 1 QPS
展示次数报告 从保留类型中移除广告选择前的最短时间 24 小时
存储空间广告选择的数量上限 待定
报告输出网址的大小上限 待定
展示次数报告的时间上限 待定
通知调用的重试次数上限 待定
连接超时 5 秒
reportImpression 的总执行时长上限 2 秒
reportImpressions 的调用率上限 1 QPS
事件报告 每次竞价中每个买方的信标数量上限 10

每次竞价中每个卖方的信标数量上限

10

事件键的大小上限

40 个字节

事件数据的大小上限

64KB

广告 广告列表的大小上限 根据上下文,所有 AdData 在单个 CA 中的总大小上限为 10 KB
网址 作为输入接受的任何网址字符串的长度上限 待定
JavaScript 执行时间上限 展示次数报告的出价和评分执行时间上限为 1 秒
使用的内存上限 10 MB

报告 bug 和问题

您的反馈对 Privacy Sandbox on Android 至关重要!如果发现任何问题或有任何关于改进 Privacy Sandbox on Android 的想法,欢迎告诉我们。