Move adservices Manager calls to default dispatcher
Details: If the client invokes #get() on the `Future` returned by
*Futures.kt APIs from the main thread/dispatcher, it gets stuck in a deadlock state until the future
times out. Solving it by offloading the call to default dispatcher.
Test: Below tests were done :
1. Ran MeasurementManagerFuturesTest, AdIdManagerFuturesTest,
AppSetIdManagerFuturesTest on Pixel 6a using Android studio
2. Manually tested using measurement sample app
a. Generated the aar using ./gradlew :privacysandbox:ads:ads-adservices-java:build command
b. Added the aar as an implementation(files()) dependency in
measurement sample app.
c. Installed sample app on Pixel 6a and clicked on "Register trigger"
and "Register source"
d. No UI block or crash observed
e. Also used the AAR without the fix to repro the scenario.
Change-Id: I653615f1fd080c9cf124aea0685e03dc96d648f2
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt
index bf0a81b..590eff6 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt
@@ -17,6 +17,7 @@
package androidx.privacysandbox.ads.adservices.java.adid
import android.content.Context
+import android.os.Looper
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import androidx.annotation.RequiresExtension
@@ -27,6 +28,7 @@
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth
import com.google.common.util.concurrent.ListenableFuture
+import kotlin.test.assertNotEquals
import org.junit.Assert
import org.junit.Assume
import org.junit.Before
@@ -91,6 +93,7 @@
// Set up the response that AdIdManager will return when the compat code calls it.
val adId = android.adservices.adid.AdId("1234", false)
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.getMainLooper(), Looper.myLooper())
val receiver = args.getArgument<
OutcomeReceiver>(1)
receiver.onResult(adId)
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFuturesTest.kt
index a558f76..f94bc9d 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFuturesTest.kt
@@ -17,6 +17,7 @@
package androidx.privacysandbox.ads.adservices.java.appsetid
import android.content.Context
+import android.os.Looper
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import androidx.annotation.RequiresExtension
@@ -27,6 +28,7 @@
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.ListenableFuture
+import kotlin.test.assertNotEquals
import org.junit.Assert
import org.junit.Assume
import org.junit.Before
@@ -102,6 +104,7 @@
"1234",
android.adservices.appsetid.AppSetId.SCOPE_APP)
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.getMainLooper(), Looper.myLooper())
val receiver = args.getArgument<
OutcomeReceiver>(1)
receiver.onResult(appSetId)
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
index ba83a13..dbdb25e 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
@@ -19,6 +19,7 @@
import android.adservices.measurement.MeasurementManager
import android.content.Context
import android.net.Uri
+import android.os.Looper
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import android.view.InputEvent
@@ -35,6 +36,7 @@
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import java.time.Instant
+import kotlin.test.assertNotEquals
import org.junit.Assume
import org.junit.Before
import org.junit.Test
@@ -81,6 +83,7 @@
val answer = { args: InvocationOnMock ->
val receiver = args.getArgument>(2)
receiver.onResult(Object())
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
null
}
doAnswer(answer).`when`(measurementManager).deleteRegistrations(any(), any(), any())
@@ -116,6 +119,7 @@
val measurementManager = mockMeasurementManager(mContext)
val managerCompat = from(mContext)
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
val receiver = args.getArgument>(3)
receiver.onResult(Object())
null
@@ -148,6 +152,7 @@
val measurementManager = mockMeasurementManager(mContext)
val managerCompat = from(mContext)
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
val receiver = args.getArgument>(2)
receiver.onResult(Object())
null
@@ -177,6 +182,7 @@
val measurementManager = mockMeasurementManager(mContext)
val managerCompat = from(mContext)
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
val receiver = args.getArgument>(2)
receiver.onResult(Object())
null
@@ -216,6 +222,7 @@
val measurementManager = mockMeasurementManager(mContext)
val managerCompat = from(mContext)
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
val receiver = args.getArgument>(2)
receiver.onResult(Object())
null
@@ -253,6 +260,7 @@
val managerCompat = from(mContext)
val state = MeasurementManager.MEASUREMENT_API_STATE_DISABLED
val answer = { args: InvocationOnMock ->
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
val receiver = args.getArgument>(1)
receiver.onResult(state)
null
diff --git a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFutures.kt b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFutures.kt
index 07b112b..8ffa244 100644
--- a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFutures.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFutures.kt
@@ -50,7 +50,7 @@
@DoNotInline
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
override fun getAdIdAsync(): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mAdIdManager.getAdId()
}.asListenableFuture()
}
diff --git a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFutures.kt b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFutures.kt
index f0812fd..fcae8d8 100644
--- a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFutures.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/appsetid/AppSetIdManagerFutures.kt
@@ -46,7 +46,7 @@
) : AppSetIdManagerFutures() {
@DoNotInline
override fun getAppSetIdAsync(): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mAppSetIdManager.getAppSetId()
}.asListenableFuture()
}
diff --git a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
index 9590fb5..e748f1b 100644
--- a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
@@ -118,7 +118,7 @@
override fun deleteRegistrationsAsync(
deletionRequest: DeletionRequest
): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mMeasurementManager.deleteRegistrations(deletionRequest)
}.asListenableFuture()
}
@@ -129,7 +129,7 @@
attributionSource: Uri,
inputEvent: InputEvent?
): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mMeasurementManager.registerSource(attributionSource, inputEvent)
}.asListenableFuture()
}
@@ -137,7 +137,7 @@
@DoNotInline
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
override fun registerTriggerAsync(trigger: Uri): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mMeasurementManager.registerTrigger(trigger)
}.asListenableFuture()
}
@@ -147,7 +147,7 @@
override fun registerWebSourceAsync(
request: WebSourceRegistrationRequest
): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mMeasurementManager.registerWebSource(request)
}.asListenableFuture()
}
@@ -157,7 +157,7 @@
override fun registerWebTriggerAsync(
request: WebTriggerRegistrationRequest,
): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mMeasurementManager.registerWebTrigger(request)
}.asListenableFuture()
}
@@ -165,7 +165,7 @@
@DoNotInline
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
override fun getMeasurementApiStatusAsync(): ListenableFuture {
- return CoroutineScope(Dispatchers.Main).async {
+ return CoroutineScope(Dispatchers.Default).async {
mMeasurementManager.getMeasurementApiStatus()
}.asListenableFuture()
}