Skip to content

Commit

Permalink
Creating a client class for joining a custom audience
Browse files Browse the repository at this point in the history
Summary:
## Context
GPS Protected Audience API is an on-device technology for ads retargeting purpose. It allows ad tech to join users into custom audience groups based on their app events, and shows more personalized ads to users based on the groups. We will utilize the API on our client-side reranking product.

## This diff
* Added stub classes for PA library
* Added PA configuration to app settings.
* Added the skeleton of PACustomAudienceClient, which performs joining a user to CA group.
* Triggered the PA when an app event is logged (gated).

Reviewed By: youerkang

Differential Revision: D66840709

fbshipit-source-id: 432522c4c0b6ea29b80285bcc927b6e7e7eaff37
  • Loading branch information
Shen Guo authored and facebook-github-bot committed Dec 16, 2024
1 parent 9af77af commit 0fb74d3
Show file tree
Hide file tree
Showing 15 changed files with 353 additions and 33 deletions.
1 change: 1 addition & 0 deletions facebook-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@
<!-- Support for Google Privacy Sandbox adservices API -->
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" />

</manifest>
23 changes: 23 additions & 0 deletions facebook-core/src/main/java/android/adservices/common/AdData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

package android.adservices.common;

import androidx.annotation.NonNull;

public class AdData {
public static final class Builder {
public AdData.Builder setMetadata(@NonNull String metadata) {
throw new RuntimeException("Stub!");
}

public AdData build() {
throw new RuntimeException("Stub!");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/

/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.adservices.common;

import androidx.annotation.NonNull;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

package android.adservices.common;

import androidx.annotation.NonNull;

public class AdTechIdentifier {
@NonNull
public static AdTechIdentifier fromString(@NonNull String source) {
throw new RuntimeException("Stub!");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

package android.adservices.customaudience;

import android.adservices.common.AdData;
import android.adservices.common.AdTechIdentifier;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.List;

public class CustomAudience {
public static class Builder {
@NonNull
public CustomAudience.Builder setBuyer(@NonNull AdTechIdentifier buyer) {
throw new RuntimeException("Stub!");
}

@NonNull
public CustomAudience.Builder setName(@NonNull String name) {
throw new RuntimeException("Stub!");
}

@NonNull
public CustomAudience.Builder setDailyUpdateUri(@NonNull Uri dailyUpdateUri) {
throw new RuntimeException("Stub!");
}

@NonNull
public CustomAudience.Builder setBiddingLogicUri(@NonNull Uri biddingLogicUri) {
throw new RuntimeException("Stub!");
}
public CustomAudience.Builder setAds(@Nullable List<AdData> ads) {
throw new RuntimeException("Stub!");
}

public CustomAudience build() {
throw new RuntimeException("Stub!");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

package android.adservices.customaudience;

import android.content.Context;
import android.os.OutcomeReceiver;

import androidx.annotation.NonNull;

import java.util.concurrent.Executor;

public class CustomAudienceManager {

public static CustomAudienceManager get(@NonNull Context context) {
throw new RuntimeException("Stub!");
}

public void joinCustomAudience(
@NonNull JoinCustomAudienceRequest joinCustomAudienceRequest,
@NonNull Executor executor,
@NonNull OutcomeReceiver<Object, Exception> receiver) {
throw new RuntimeException("Stub!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

package android.adservices.customaudience;

import androidx.annotation.NonNull;

public class JoinCustomAudienceRequest {
public static class Builder {
public JoinCustomAudienceRequest.Builder setCustomAudience(
@NonNull CustomAudience customAudience) {
throw new RuntimeException("Stub!");
}

@NonNull
public JoinCustomAudienceRequest build() {
throw new RuntimeException("Stub!");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/

/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package android.adservices.measurement;

import android.adservices.common.AdServicesOutcomeReceiver;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.facebook.appevents.AppEventQueue.flush
import com.facebook.appevents.AppEventQueue.getKeySet
import com.facebook.appevents.AppEventQueue.persistToDisk
import com.facebook.appevents.gps.ara.GpsAraTriggersManager
import com.facebook.appevents.gps.pa.PACustomAudienceClient
import com.facebook.appevents.integrity.BannedParamManager.processFilterBannedParams
import com.facebook.appevents.iap.InAppPurchase
import com.facebook.appevents.iap.InAppPurchaseDedupeConfig
Expand Down Expand Up @@ -686,6 +687,9 @@ internal constructor(activityName: String, applicationId: String?, accessToken:
if (isEnabled(FeatureManager.Feature.GPSARATriggers)) {
GpsAraTriggersManager.registerTriggerAsync(accessTokenAppId.applicationId, event)
}
if (isEnabled(FeatureManager.Feature.GPSPACAProcessing)) {
PACustomAudienceClient.joinCustomAudience()
}

// Make sure Activated_App is always before other app events
if (!event.getIsImplicit() && !isActivateAppEventRequested) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.facebook.appevents.aam.MetadataIndexer
import com.facebook.appevents.cloudbridge.AppEventsCAPIManager
import com.facebook.appevents.eventdeactivation.EventDeactivationManager
import com.facebook.appevents.gps.ara.GpsAraTriggersManager
import com.facebook.appevents.gps.pa.PACustomAudienceClient
import com.facebook.appevents.iap.InAppPurchaseManager
import com.facebook.appevents.integrity.BannedParamManager
import com.facebook.appevents.integrity.BlocklistEventsManager
Expand Down Expand Up @@ -115,6 +116,11 @@ object AppEventsManager {
GpsAraTriggersManager.enable()
}
}
checkFeature(FeatureManager.Feature.GPSPACAProcessing) { enabled ->
if (enabled) {
PACustomAudienceClient.enable()
}
}
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.appevents.gps

import android.os.Build
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/


package com.facebook.appevents.gps.pa

import android.adservices.common.AdTechIdentifier
import android.adservices.customaudience.CustomAudience
import android.adservices.customaudience.CustomAudienceManager
import android.adservices.customaudience.JoinCustomAudienceRequest
import android.annotation.TargetApi
import android.net.Uri
import android.os.OutcomeReceiver
import android.util.Log
import com.facebook.FacebookSdk
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions
import java.util.concurrent.Executors

@AutoHandleExceptions
object PACustomAudienceClient {
private val TAG = "Fledge: " + PACustomAudienceClient::class.java.simpleName
private const val BUYER = "facebook.com"
private var enabled = false
private var customAudienceManager: CustomAudienceManager? = null

@JvmStatic
@TargetApi(34)
fun enable() {
val context = FacebookSdk.getApplicationContext()
try {
customAudienceManager = CustomAudienceManager.get(context)
if (customAudienceManager != null) {
enabled = true
}
} catch (e: Exception) {
Log.w(TAG, "Failed to get CustomAudienceManager: " + e.message)
} catch (e: NoClassDefFoundError) {
Log.w(TAG, "Failed to get CustomAudienceManager: " + e.message)
} catch (e: NoSuchMethodError) {
Log.w(TAG, "Failed to get CustomAudienceManager: " + e.message)
}
}

@TargetApi(34)
fun joinCustomAudience() {
if (!enabled) return

val callback: OutcomeReceiver<Any, Exception> =
object : OutcomeReceiver<Any, Exception> {
override fun onResult(result: Any) {
Log.i(TAG, "Successfully joined custom audience")
}

override fun onError(error: Exception) {
Log.e(TAG, error.toString())
}
}

try {
val ca: CustomAudience =
CustomAudience.Builder().setName("").setBuyer(AdTechIdentifier.fromString(BUYER))
.setDailyUpdateUri(Uri.parse("")).setBiddingLogicUri(Uri.parse(""))
.build()
val request: JoinCustomAudienceRequest =
JoinCustomAudienceRequest.Builder().setCustomAudience(ca).build()
customAudienceManager?.joinCustomAudience(
request,
Executors.newSingleThreadExecutor(),
callback
)
} catch (e: Exception) {
Log.w(TAG, "Failed to join Custom Audience: " + e.message)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ object FeatureManager {
arrayOf("com.facebook.appevents.ondeviceprocessing.")
featureMapping[Feature.IapLogging] = arrayOf("com.facebook.appevents.iap.")
featureMapping[Feature.Monitoring] = arrayOf("com.facebook.internal.logging.monitor")
featureMapping[Feature.GPSARATriggers] = arrayOf("com.facebook.appevents.gpsara.GpsARAManager")
featureMapping[Feature.GPSARATriggers] = arrayOf("com.facebook.appevents.gps.ara.GpsARAManager")
featureMapping[Feature.GPSPACAProcessing] = arrayOf("com.facebook.appevents.gps.pa.PACustomAudienceClient")
}

private fun getGKStatus(feature: Feature): Boolean {
Expand Down Expand Up @@ -155,7 +156,8 @@ object FeatureManager {
Feature.Monitoring,
Feature.IgnoreAppSwitchToLoggedOut,
Feature.BypassAppSwitch,
Feature.GPSARATriggers -> false
Feature.GPSARATriggers,
Feature.GPSPACAProcessing -> false

else -> true
}
Expand Down Expand Up @@ -209,6 +211,7 @@ object FeatureManager {
Megatron(0x00040000),
Elora(0x00050000),
GPSARATriggers(0x00060000), /* privacy sandbox - attribution reporting API*/
GPSPACAProcessing(0x00070000), /* privacy sandbox - protected audience API*/
// Features in LoginKit
/** Essential of LoginKit */
Login(0x01000000),
Expand Down Expand Up @@ -257,6 +260,7 @@ object FeatureManager {
Megatron -> "Megatron"
Elora -> "Elora"
GPSARATriggers -> "GPSARATriggers"
GPSPACAProcessing-> "GPSPACAProcessing"
ServiceUpdateCompliance -> "ServiceUpdateCompliance"
Login -> "LoginKit"
ChromeCustomTabsPrefetching -> "ChromeCustomTabsPrefetching"
Expand Down
2 changes: 2 additions & 0 deletions facebook-core/src/main/res/xml/ad_services_config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@
<ad-services-config>
<!-- Attribution Reporting API -->
<attribution allowAllToAccess="true" />
<!-- Protected Audience on Android API -->
<custom-audiences allowAllToAccess="true" />
</ad-services-config>
Loading

0 comments on commit 0fb74d3

Please sign in to comment.