-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for ARA trigger registration
Summary: Adding capability to invoke Privacy Sandbox's Attribution Reporting API trigger request to Android SDK. Gated behind a new feature check. A trigger request will be sent whenever an app event is enqueued. Reviewed By: KylinChang Differential Revision: D65789881 Privacy Context Container: L1250336 fbshipit-source-id: f5cab6f39e741b5718e501f031d0bed205ba8c29
- Loading branch information
1 parent
e44a8b3
commit 142b47a
Showing
10 changed files
with
421 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
facebook-core/src/main/java/android/adservices/common/AdServicesOutcomeReceiver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
/* | ||
* 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; | ||
|
||
public interface AdServicesOutcomeReceiver<R, E extends Throwable> { | ||
void onResult(R result); | ||
default void onError(@NonNull E error) {} | ||
} |
57 changes: 57 additions & 0 deletions
57
facebook-core/src/main/java/android/adservices/measurement/MeasurementManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
/* | ||
* 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; | ||
import android.content.Context; | ||
import android.net.Uri; | ||
import android.os.OutcomeReceiver; | ||
import java.util.concurrent.Executor; | ||
|
||
/** | ||
* Interface to access the android MeasurementManager class {@link | ||
* android.adservices.measurement.MeasurementManager}. The actual implementation is going to be | ||
* provided at runtime on the device. | ||
*/ | ||
public class MeasurementManager { | ||
|
||
MeasurementManager() { | ||
throw new RuntimeException("Stub!"); | ||
} | ||
|
||
public static MeasurementManager get(Context context) { | ||
throw new RuntimeException("Stub!"); | ||
} | ||
|
||
public void registerTrigger( | ||
Uri trigger, Executor executor, OutcomeReceiver<Object, Exception> callback) { | ||
throw new RuntimeException("Stub!"); | ||
} | ||
|
||
public void registerTrigger( | ||
Uri trigger, Executor executor, AdServicesOutcomeReceiver<Object, Exception> callback) { | ||
throw new RuntimeException("Stub!"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
facebook-core/src/main/java/com/facebook/appevents/gpsara/GpsAraTriggersManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* 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.gpsara | ||
|
||
import android.adservices.common.AdServicesOutcomeReceiver | ||
import android.adservices.measurement.MeasurementManager | ||
import android.annotation.TargetApi | ||
import android.net.Uri | ||
import android.os.OutcomeReceiver | ||
import android.util.Log | ||
import com.facebook.FacebookSdk | ||
import com.facebook.appevents.AppEvent | ||
import com.facebook.internal.AnalyticsEvents | ||
import com.facebook.internal.instrument.crashshield.AutoHandleExceptions | ||
import java.net.URLEncoder | ||
|
||
@AutoHandleExceptions | ||
object GpsAraTriggersManager { | ||
private var enabled = false | ||
private val TAG = GpsAraTriggersManager::class.java.toString() | ||
private const val SERVER_URI = "https://www.facebook.com/privacy_sandbox/mobile/register/trigger" | ||
|
||
@JvmStatic | ||
fun enable() { | ||
enabled = true | ||
} | ||
|
||
fun registerTriggerAsync(applicationId: String, event: AppEvent) { | ||
FacebookSdk.getExecutor().execute { | ||
registerTrigger(applicationId, event) | ||
} | ||
} | ||
|
||
@TargetApi(34) | ||
fun registerTrigger(applicationId: String, event: AppEvent) { | ||
if (applicationId == null) return | ||
if (!canRegisterTrigger()) return | ||
|
||
val context = FacebookSdk.getApplicationContext() | ||
var measurementManager: MeasurementManager? = null | ||
|
||
try { | ||
measurementManager = | ||
context.getSystemService(MeasurementManager::class.java) | ||
if (measurementManager == null) { | ||
// On certain Android versions, Context.getSystemService() returns null since ARA is not yet | ||
// merged into the public SDK. If this happens, we use the factory method to get the | ||
// MeasurementManager instance. | ||
measurementManager = MeasurementManager.get(context.applicationContext) | ||
} | ||
|
||
if (measurementManager == null) { | ||
Log.w(TAG, "FAILURE_GET_MEASUREMENT_MANAGER") | ||
return | ||
} | ||
|
||
val params = getEventParameters(event) | ||
val appIdKey = AnalyticsEvents.PARAMETER_APP_ID | ||
val attributionTriggerUri: Uri = | ||
Uri.parse("$SERVER_URI?$appIdKey=$applicationId&$params") | ||
|
||
// On Android 12 and above, MeasurementManager.registerTrigger() takes an OutcomeReceiver and the | ||
// rest takes an AdServicesOutcomeReceiver. | ||
if (GpsCapabilityChecker.useOutcomeReceiver()) { | ||
val outcomeReceiver: OutcomeReceiver<Any, Exception> = | ||
object : OutcomeReceiver<Any, Exception> { | ||
override fun onResult(result: Any) { | ||
Log.d(TAG, "OUTCOME_RECEIVER_TRIGGER_SUCCESS") | ||
} | ||
|
||
override fun onError(error: Exception) { | ||
Log.d(TAG, "OUTCOME_RECEIVER_TRIGGER_FAILURE") | ||
} | ||
} | ||
|
||
measurementManager.registerTrigger( | ||
attributionTriggerUri, FacebookSdk.getExecutor(), outcomeReceiver | ||
) | ||
} else { | ||
val adServicesOutcomeReceiver: AdServicesOutcomeReceiver<Any, Exception> = | ||
object : AdServicesOutcomeReceiver<Any, Exception> { | ||
override fun onResult(result: Any) { | ||
Log.d(TAG, "AD_SERVICE_OUTCOME_RECEIVER_TRIGGER_SUCCESS") | ||
} | ||
|
||
override fun onError(error: Exception) { | ||
Log.d(TAG, "AD_SERVICE_OUTCOME_RECEIVER_TRIGGER_FAILURE") | ||
} | ||
} | ||
|
||
measurementManager.registerTrigger( | ||
attributionTriggerUri, FacebookSdk.getExecutor(), adServicesOutcomeReceiver | ||
) | ||
} | ||
|
||
} catch (e: Exception) { | ||
Log.w(TAG, "FAILURE_TRIGGER_REGISTRATION_FAILED") | ||
} catch (e: NoClassDefFoundError) { | ||
Log.w(TAG, "FAILURE_TRIGGER_REGISTRATION_NO_CLASS_FOUND") | ||
} catch (e: NoSuchMethodError) { | ||
Log.w(TAG, "FAILURE_TRIGGER_REGISTRATION_NO_METHOD_FOUND") | ||
} | ||
} | ||
|
||
private fun canRegisterTrigger(): Boolean { | ||
if (!enabled) { | ||
return false | ||
} | ||
|
||
try { | ||
Class.forName("android.adservices.measurement.MeasurementManager") | ||
return true | ||
} catch (e: Exception) { | ||
Log.i(TAG, "FAILURE_NO_MEASUREMENT_MANAGER_CLASS") | ||
return false | ||
} catch (e: NoClassDefFoundError) { | ||
Log.i(TAG, "FAILURE_NO_MEASUREMENT_MANAGER_CLASS_DEF") | ||
return false | ||
} | ||
} | ||
|
||
private fun getEventParameters(event: AppEvent): String { | ||
val params = event.getJSONObject() | ||
|
||
if (params == null || params.length() == 0) { | ||
return "" | ||
} | ||
|
||
return params.keys().asSequence().mapNotNull { key -> | ||
val value = params.opt(key) ?: return@mapNotNull null | ||
try { | ||
val encodedKey = URLEncoder.encode(key, "UTF-8") | ||
val encodedValue = URLEncoder.encode(value.toString(), "UTF-8") | ||
"$encodedKey=$encodedValue" | ||
} catch (e: Exception) { | ||
null // Ignore invalid keys | ||
} | ||
} | ||
.joinToString("&") | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
facebook-core/src/main/java/com/facebook/appevents/gpsara/GpsCapabilityChecker.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.facebook.appevents.gpsara | ||
|
||
import android.os.Build | ||
|
||
object GpsCapabilityChecker { | ||
@JvmStatic | ||
fun useOutcomeReceiver(): Boolean { | ||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<!-- | ||
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. | ||
--> | ||
|
||
<!-- | ||
Copyright (C) 2022 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. | ||
--> | ||
|
||
<ad-services-config> | ||
<!-- Attribution Reporting API --> | ||
<attribution allowAllToAccess="true" /> | ||
</ad-services-config> |
Oops, something went wrong.