[go: nahoru, domu]

Merge back compat support to androidx-main

This copies over the changes from both ag/22316069 and ag/24692966 because the cherrypick tool failed. In addition to the testing below, I verified that all the modified files are identical between androidx-platform-dev and androidx-main after this change.

Bug: 299949487
Test: Run all unit tests with the following configurations:
        * U - all tests run and pass except "OlderVersions" tests, which are skipped
        * T release - skips all tests that check for ext4 or ext5, others pass
        * T with ext4 - will run as presubmit
        * T with ext5 - will run as presubmit
        * S release - skips all tests that have version checks, others pass
        * S with M-2023-10 installed via train installer - all tests run and pass except for "OlderVersions" tests and Fledge e2e
        * Same as above, but with updated webview - all tests run and pass except for "OlderVersions" tests

Note that when installing the M-2023-10 train, you need to install all of primary, telemetry, and timezone, or the sdkext version will not bump to 9.

Change-Id: Ia3e9c292d6711b32839da3920d8e60e53ae60398
diff --git a/privacysandbox/ads/ads-adservices-java/build.gradle b/privacysandbox/ads/ads-adservices-java/build.gradle
index d65ff5c0..b3a4b71 100644
--- a/privacysandbox/ads/ads-adservices-java/build.gradle
+++ b/privacysandbox/ads/ads-adservices-java/build.gradle
@@ -36,8 +36,10 @@
 
     androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.0'
     androidTestImplementation project(path: ':privacysandbox:ads:ads-adservices')
+    androidTestImplementation project(path: ':javascriptengine:javascriptengine')
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.kotlinTestJunit)
+    androidTestImplementation(libs.multidex)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
@@ -46,9 +48,14 @@
 
     androidTestImplementation(libs.mockitoCore4, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.dexmakerMockitoInline, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockitoInlineExtended)
 }
 
 android {
+    defaultConfig {
+        multiDexEnabled = true
+    }
+
     namespace "androidx.privacysandbox.ads.adservices.java"
 }
 
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/AndroidManifest.xml b/privacysandbox/ads/ads-adservices-java/src/androidTest/AndroidManifest.xml
index 90665d4..aa19a69 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/AndroidManifest.xml
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/AndroidManifest.xml
@@ -14,11 +14,13 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
     <uses-permission android:name="android.permission.ACCESS_ADSERVICES_TOPICS" />
     <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
     <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
     <uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" />
+    <uses-sdk tools:overrideLibrary="androidx.javascriptengine" />
     <application>
         <property android:name="android.adservices.AD_SERVICES_CONFIG"
             android:resource="@xml/ad_services_config" />
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt
new file mode 100644
index 0000000..d4ad1a2
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.java
+
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.test.filters.SdkSuppress
+
+@SdkSuppress(minSdkVersion = 30)
+object VersionCompatUtil {
+    fun isTPlusWithMinAdServicesVersion(minVersion: Int): Boolean {
+        return Build.VERSION.SDK_INT >= 33 &&
+            SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= minVersion
+    }
+
+    fun isSWithMinExtServicesVersion(minVersion: Int): Boolean {
+        return (Build.VERSION.SDK_INT == 31 || Build.VERSION.SDK_INT == 32) &&
+            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= minVersion
+    }
+
+    fun isTestableVersion(minAdServicesVersion: Int, minExtServicesVersion: Int): Boolean {
+        return isTPlusWithMinAdServicesVersion(minAdServicesVersion) ||
+            isSWithMinExtServicesVersion(minExtServicesVersion)
+    }
+}
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 5f20ba7..35d5109 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
@@ -19,16 +19,18 @@
 import android.content.Context
 import android.os.Looper
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.adid.AdId
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth
 import com.google.common.util.concurrent.ListenableFuture
 import kotlin.test.assertNotEquals
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -36,6 +38,8 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers
 import org.mockito.Mockito
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
 
 @SmallTest
@@ -44,25 +48,45 @@
 @SdkSuppress(minSdkVersion = 30)
 class AdIdManagerFuturesTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdExtServicesSdkExtVersion = VersionCompatUtil.isSWithMinExtServicesVersion(9)
+
     @Before
     fun setUp() {
-        mContext = Mockito.spy(ApplicationProvider.getApplicationContext<Context>())
+        mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.adid.AdIdManager::class.java)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testAdIdOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeFalse("maxSdkVersion = API 33 ext 3 or API 31/32 ext 8",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion=*/ 4,
+                /* minExtServicesVersion=*/ 9))
         Truth.assertThat(AdIdManagerFutures.from(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testAdIdAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val adIdManager = mockAdIdManager(mContext)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
+
+        val adIdManager = mockAdIdManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(adIdManager)
         val managerCompat = AdIdManagerFutures.from(mContext)
 
@@ -76,16 +100,23 @@
         Mockito.verify(adIdManager).getAdId(ArgumentMatchers.any(), ArgumentMatchers.any())
     }
 
-    @SuppressWarnings("NewApi")
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
 
-        private fun mockAdIdManager(spyContext: Context): android.adservices.adid.AdIdManager {
+        private fun mockAdIdManager(
+            spyContext: Context,
+            isExtServices: Boolean
+        ): android.adservices.adid.AdIdManager {
             val adIdManager = Mockito.mock(android.adservices.adid.AdIdManager::class.java)
-            Mockito.`when`(spyContext.getSystemService(
-                android.adservices.adid.AdIdManager::class.java)).thenReturn(adIdManager)
+            // mock the .get() method if using extServices version, otherwise mock getSystemService
+            if (isExtServices) {
+                `when`(android.adservices.adid.AdIdManager.get(ArgumentMatchers.any()))
+                    .thenReturn(adIdManager)
+            } else {
+                `when`(spyContext.getSystemService(
+                    android.adservices.adid.AdIdManager::class.java)).thenReturn(adIdManager)
+            }
             return adIdManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adselection/AdSelectionManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adselection/AdSelectionManagerFuturesTest.kt
index af4ef91..afc5c68 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adselection/AdSelectionManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adselection/AdSelectionManagerFuturesTest.kt
@@ -19,20 +19,22 @@
 import android.content.Context
 import android.net.Uri
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome
 import androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest
 import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
 import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil
 import androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion.from
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth
 import com.google.common.util.concurrent.ListenableFuture
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -46,6 +48,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -53,27 +56,46 @@
 @SdkSuppress(minSdkVersion = 30)
 class AdSelectionManagerFuturesTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdExtServicesSdkExtVersion = VersionCompatUtil.isSWithMinExtServicesVersion(9)
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.adselection.AdSelectionManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testAdSelectionOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeFalse("maxSdkVersion = API 33 ext 3 or API 31/32 ext 8",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion=*/ 4,
+                /* minExtServicesVersion=*/ 9))
         Truth.assertThat(from(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testSelectAds() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val adSelectionManager = mockAdSelectionManager(mContext)
+        val adSelectionManager = mockAdSelectionManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupAdSelectionResponse(adSelectionManager)
         val managerCompat = from(mContext)
 
@@ -95,12 +117,13 @@
 
     @Test
     @SuppressWarnings("NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testReportImpression() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val adSelectionManager = mockAdSelectionManager(mContext)
+        val adSelectionManager = mockAdSelectionManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupAdSelectionResponse(adSelectionManager)
         val managerCompat = from(mContext)
         val reportImpressionRequest = ReportImpressionRequest(adSelectionId, adSelectionConfig)
@@ -119,7 +142,6 @@
 
     @SuppressWarnings("NewApi")
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
         private const val adSelectionId = 1234L
@@ -148,13 +170,20 @@
         private val renderUri = Uri.parse("render-uri.com")
 
         private fun mockAdSelectionManager(
-            spyContext: Context
+            spyContext: Context,
+            isExtServices: Boolean
         ): android.adservices.adselection.AdSelectionManager {
             val adSelectionManager =
                 mock(android.adservices.adselection.AdSelectionManager::class.java)
-            `when`(spyContext.getSystemService(
-                android.adservices.adselection.AdSelectionManager::class.java))
-                .thenReturn(adSelectionManager)
+            // mock the .get() method if using extServices version, otherwise mock getSystemService
+            if (isExtServices) {
+                `when`(android.adservices.adselection.AdSelectionManager.get(any()))
+                    .thenReturn(adSelectionManager)
+            } else {
+                `when`(spyContext.getSystemService(
+                    android.adservices.adselection.AdSelectionManager::class.java))
+                    .thenReturn(adSelectionManager)
+            }
             return adSelectionManager
         }
 
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 a540f73..b92fa9a 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
@@ -19,21 +19,24 @@
 import android.content.Context
 import android.os.Looper
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.appsetid.AppSetId
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.ListenableFuture
 import kotlin.test.assertNotEquals
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.mock
@@ -43,32 +46,50 @@
 import org.mockito.invocation.InvocationOnMock
 
 @SmallTest
+@SuppressWarnings("NewApi")
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = 30)
 class AppSetIdManagerFuturesTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdExtServicesSdkExtVersion = VersionCompatUtil.isSWithMinExtServicesVersion(9)
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.appsetid.AppSetIdManager::class.java)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testAppSetIdOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeFalse("maxSdkVersion = API 33 ext 3 or API 31/32 ext 8",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion=*/ 4,
+                /* minExtServicesVersion=*/ 9))
         assertThat(AppSetIdManagerFutures.from(mContext)).isEqualTo(null)
     }
 
     @Test
-    @SuppressWarnings("NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testAppSetIdAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val appSetIdManager = mockAppSetIdManager(mContext)
+        val appSetIdManager = mockAppSetIdManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(appSetIdManager)
         val managerCompat = AppSetIdManagerFutures.from(mContext)
 
@@ -82,19 +103,24 @@
         verify(appSetIdManager).getAppSetId(any(), any())
     }
 
-    @SuppressWarnings("NewApi")
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
 
         private fun mockAppSetIdManager(
-            spyContext: Context
+            spyContext: Context,
+            isExtServices: Boolean
         ): android.adservices.appsetid.AppSetIdManager {
             val appSetIdManager = mock(android.adservices.appsetid.AppSetIdManager::class.java)
-            `when`(spyContext.getSystemService(
-                android.adservices.appsetid.AppSetIdManager::class.java))
-                .thenReturn(appSetIdManager)
+            // mock the .get() method if using extServices version, otherwise mock getSystemService
+            if (isExtServices) {
+                `when`(android.adservices.appsetid.AppSetIdManager.get(ArgumentMatchers.any()))
+                    .thenReturn(appSetIdManager)
+            } else {
+                `when`(spyContext.getSystemService(
+                    android.adservices.appsetid.AppSetIdManager::class.java))
+                    .thenReturn(appSetIdManager)
+            }
             return appSetIdManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/customaudience/CustomAudienceManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/customaudience/CustomAudienceManagerFuturesTest.kt
index 31b287d..9da1419 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/customaudience/CustomAudienceManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/customaudience/CustomAudienceManagerFuturesTest.kt
@@ -20,8 +20,6 @@
 import android.content.Context
 import android.net.Uri
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.common.AdData
 import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
 import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
@@ -29,13 +27,17 @@
 import androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest
 import androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest
 import androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil
 import androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion.from
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth
 import java.time.Instant
+import org.junit.After
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
@@ -48,6 +50,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -55,27 +58,47 @@
 @SdkSuppress(minSdkVersion = 30)
 class CustomAudienceManagerFuturesTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdExtServicesSdkExtVersion = VersionCompatUtil.isSWithMinExtServicesVersion(9)
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.customaudience.CustomAudienceManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeFalse("maxSdkVersion = API 33 ext 3 or API 31/32 ext 8",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion=*/ 4,
+                /* minExtServicesVersion=*/ 9))
         Truth.assertThat(from(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testJoinCustomAudience() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val customAudienceManager = mockCustomAudienceManager(mContext)
+        val customAudienceManager =
+            mockCustomAudienceManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(customAudienceManager)
         val managerCompat = from(mContext)
 
@@ -100,12 +123,14 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testLeaveCustomAudience() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val customAudienceManager = mockCustomAudienceManager(mContext)
+        val customAudienceManager =
+            mockCustomAudienceManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(customAudienceManager)
         val managerCompat = from(mContext)
 
@@ -124,7 +149,6 @@
     }
 
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
         private val uri: Uri = Uri.parse("abc.com")
@@ -138,10 +162,18 @@
         private const val metadata = "metadata"
         private val ads: List<AdData> = listOf(AdData(uri, metadata))
 
-        private fun mockCustomAudienceManager(spyContext: Context): CustomAudienceManager {
+        private fun mockCustomAudienceManager(
+            spyContext: Context,
+            isExtServices: Boolean
+        ): CustomAudienceManager {
             val customAudienceManager = mock(CustomAudienceManager::class.java)
-            `when`(spyContext.getSystemService(CustomAudienceManager::class.java))
-                .thenReturn(customAudienceManager)
+            // mock the .get() method if using extServices version, otherwise mock getSystemService
+            if (isExtServices) {
+                `when`(CustomAudienceManager.get(any())).thenReturn(customAudienceManager)
+            } else {
+                `when`(spyContext.getSystemService(CustomAudienceManager::class.java))
+                    .thenReturn(customAudienceManager)
+            }
             return customAudienceManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
index 7925188..fdd7e82 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/FledgeCtsDebuggableTest.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 
 import androidx.annotation.RequiresApi;
+import androidx.javascriptengine.JavaScriptSandbox;
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig;
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager;
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome;
@@ -36,7 +37,7 @@
 import androidx.privacysandbox.ads.adservices.customaudience.CustomAudience;
 import androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest;
 import androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData;
-import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo;
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil;
 import androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures;
 import androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures;
 import androidx.test.core.app.ApplicationProvider;
@@ -175,6 +176,10 @@
         testUtil.disableFledgeEnrollmentCheck(true);
         testUtil.enableAdServiceSystemService(true);
         testUtil.enforceFledgeJsIsolateMaxHeapSize(false);
+
+        if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
+            testUtil.enableBackCompat();
+        }
     }
 
     @AfterClass
@@ -196,10 +201,15 @@
         testUtil.disableFledgeEnrollmentCheck(false);
         testUtil.enableAdServiceSystemService(false);
         testUtil.enforceFledgeJsIsolateMaxHeapSize(true);
+
+        if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
+            testUtil.disableBackCompat();
+        }
     }
 
     @Before
     public void setup() throws Exception {
+        Assume.assumeTrue(JavaScriptSandbox.isSupported());
         mAdSelectionClient =
                 new AdSelectionClient(sContext);
         mCustomAudienceClient =
@@ -211,8 +221,11 @@
 
     @Test
     public void testFledgeAuctionSelectionFlow_overall_Success() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -255,8 +268,11 @@
 
     @Test
     public void testAdSelection_etldViolation_failure() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -310,8 +326,11 @@
 
     @Test
     public void testReportImpression_etldViolation_failure() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -379,8 +398,11 @@
 
     @Test
     public void testAdSelection_skipAdsMalformedBiddingLogic_success() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -432,8 +454,11 @@
 
     @Test
     public void testAdSelection_malformedScoringLogic_failure() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -485,8 +510,11 @@
 
     @Test
     public void testAdSelection_skipAdsFailedGettingBiddingLogic_success() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -536,8 +564,11 @@
 
     @Test
     public void testAdSelection_errorGettingScoringLogic_failure() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -594,8 +625,11 @@
 
     @Test
     public void testAdSelectionFlow_skipNonActivatedCA_Success() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -648,8 +682,11 @@
 
     @Test
     public void testAdSelectionFlow_skipExpiredCA_Success() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -708,8 +745,11 @@
 
     @Test
     public void testAdSelectionFlow_skipCAsThatTimeoutDuringBidding_Success() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
@@ -759,8 +799,11 @@
 
     @Test
     public void testAdSelection_overallTimeout_Failure() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2);
         List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
index 57e0e98..33988c1 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
@@ -92,6 +92,7 @@
     public void overrideAllowlists(boolean override) {
         String overrideStr = override ? "*" : "null";
         runShellCommand("device_config put adservices ppapi_app_allow_list " + overrideStr);
+        runShellCommand("device_config put adservices msmt_api_app_allow_list " + overrideStr);
         runShellCommand("device_config put adservices ppapi_app_signature_allow_list "
                 + overrideStr);
         runShellCommand(
@@ -114,6 +115,18 @@
         }
     }
 
+    public void enableBackCompat() {
+        runShellCommand("device_config put adservices enable_back_compat true");
+        runShellCommand("device_config put adservices consent_source_of_truth 3");
+        runShellCommand("device_config put adservices blocked_topics_source_of_truth 3");
+    }
+
+    public void disableBackCompat() {
+        runShellCommand("device_config put adservices enable_back_compat false");
+        runShellCommand("device_config put adservices consent_source_of_truth null");
+        runShellCommand("device_config put adservices blocked_topics_source_of_truth null");
+    }
+
     // Override measurement related kill switch to ignore the effect of actual PH values.
     // If isOverride = true, override measurement related kill switch to OFF to allow adservices
     // If isOverride = false, override measurement related kill switch to meaningless value so that
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java
index c35cb46..c7be1d1 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java
@@ -19,7 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.privacysandbox.ads.adservices.adid.AdId;
-import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo;
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil;
 import androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures;
 import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
 import androidx.test.core.app.ApplicationProvider;
@@ -63,8 +63,11 @@
 
     @Test
     public void testAdId() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         AdIdManagerFutures adIdManager =
                 AdIdManagerFutures.from(ApplicationProvider.getApplicationContext());
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/appsetid/AppSetIdManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/appsetid/AppSetIdManagerTest.java
index ecebc1e..fb2fa2f 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/appsetid/AppSetIdManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/appsetid/AppSetIdManagerTest.java
@@ -19,7 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.privacysandbox.ads.adservices.appsetid.AppSetId;
-import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo;
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil;
 import androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures;
 import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
 import androidx.test.core.app.ApplicationProvider;
@@ -60,8 +60,11 @@
 
     @Test
     public void testAppSetId() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         AppSetIdManagerFutures appSetIdManager =
                 AppSetIdManagerFutures.from(ApplicationProvider.getApplicationContext());
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
index 7bc4ea9..c43168e 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
@@ -22,7 +22,7 @@
 
 import android.net.Uri;
 
-import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo;
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil;
 import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
 import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures;
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest;
@@ -47,6 +47,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
+@SuppressWarnings("NewApi")
 @RunWith(JUnit4.class)
 @SdkSuppress(minSdkVersion = 28) // API 28 required for device_config used by this test
 // TODO: Consider refactoring so that we're not duplicating code.
@@ -78,6 +79,9 @@
         mTestUtil.overrideDisableMeasurementEnrollmentCheck("1");
         mMeasurementManager =
                 MeasurementManagerFutures.from(ApplicationProvider.getApplicationContext());
+        if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
+            mTestUtil.enableBackCompat();
+        }
 
         // Put in a short sleep to make sure the updated config propagates
         // before starting the tests
@@ -92,14 +96,21 @@
         mTestUtil.overrideMeasurementKillSwitches(false);
         mTestUtil.overrideAdIdKillSwitch(false);
         mTestUtil.overrideDisableMeasurementEnrollmentCheck("0");
+        if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
+            mTestUtil.disableBackCompat();
+        }
+
         // Cool-off rate limiter
         TimeUnit.SECONDS.sleep(1);
     }
 
     @Test
     public void testRegisterSource_NoServerSetup_NoErrors() throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         assertThat(mMeasurementManager.registerSourceAsync(
                 SOURCE_REGISTRATION_URI,
@@ -109,8 +120,12 @@
 
     @Test
     public void testRegisterAppSources_NoServerSetup_NoErrors() throws Exception {
+        // Skip the test if the right SDK extension is not present
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
         // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
 
         SourceRegistrationRequest request =
                 new SourceRegistrationRequest.Builder(
@@ -122,18 +137,23 @@
 
     @Test
     public void testRegisterTrigger_NoServerSetup_NoErrors() throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         assertThat(mMeasurementManager.registerTriggerAsync(TRIGGER_REGISTRATION_URI).get())
                 .isNotNull();
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 33)
     public void registerWebSource_NoErrors() throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         WebSourceParams webSourceParams =
                 new WebSourceParams(SOURCE_REGISTRATION_URI, false);
@@ -152,10 +172,12 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 33)
     public void registerWebTrigger_NoErrors() throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         WebTriggerParams webTriggerParams =
                 new WebTriggerParams(TRIGGER_REGISTRATION_URI, false);
@@ -169,11 +191,12 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 33)
     public void testDeleteRegistrations_withRequest_withNoRange_withCallback_NoErrors()
             throws Exception {
         // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // This test should not run for back compat because it depends on adServices running in
+        // system server
+        Assume.assumeTrue(VersionCompatUtil.INSTANCE.isTPlusWithMinAdServicesVersion(5));
 
         DeletionRequest deletionRequest =
                 new DeletionRequest.Builder(
@@ -187,11 +210,13 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 33)
     public void testDeleteRegistrations_withRequest_withEmptyLists_withRange_withCallback_NoErrors()
             throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         DeletionRequest deletionRequest =
                 new DeletionRequest.Builder(
@@ -207,11 +232,13 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 33)
     public void testDeleteRegistrations_withRequest_withInvalidArguments_withCallback_hasError()
             throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         DeletionRequest deletionRequest =
                 new DeletionRequest.Builder(
@@ -230,10 +257,12 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 33)
     public void testMeasurementApiStatus_returnResultStatus() throws Exception {
-        // Skip the test if SDK extension 5 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 5,
+                        /* minExtServicesVersion=*/ 9));
 
         int result = mMeasurementManager.getMeasurementApiStatusAsync().get();
         assertThat(result).isEqualTo(1);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java
index 266394a..9b46fff 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo;
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil;
 import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
 import androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures;
 import androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest;
@@ -73,6 +73,10 @@
         mTestUtil.shouldForceUseBundledFiles(true);
         // Enable verbose logging.
         mTestUtil.enableVerboseLogging();
+
+        if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
+            mTestUtil.enableBackCompat();
+        }
     }
 
     @After
@@ -84,13 +88,19 @@
         mTestUtil.overrideAllowlists(false);
         mTestUtil.enableEnrollmentCheck(false);
         mTestUtil.shouldForceUseBundledFiles(false);
+        if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
+            mTestUtil.disableBackCompat();
+        }
     }
 
     @Ignore // b/278931615
     @Test
     public void testTopicsManager_runClassifier() throws Exception {
-        // Skip the test if SDK extension 4 is not present.
-        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+        // Skip the test if the right SDK extension is not present.
+        Assume.assumeTrue(
+                VersionCompatUtil.INSTANCE.isTestableVersion(
+                        /* minAdServicesVersion=*/ 4,
+                        /* minExtServicesVersion=*/ 9));
 
         TopicsManagerFutures topicsManager =
                 TopicsManagerFutures.from(ApplicationProvider.getApplicationContext());
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 0106e76..cb58c43 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
@@ -21,10 +21,9 @@
 import android.net.Uri
 import android.os.Looper
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
 import android.view.InputEvent
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil
 import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion.from
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
 import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
@@ -36,6 +35,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
 import java.util.concurrent.ExecutionException
@@ -46,6 +47,7 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
+import org.junit.After
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
@@ -61,6 +63,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -68,27 +71,46 @@
 @SdkSuppress(minSdkVersion = 30)
 class MeasurementManagerFuturesTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdExtServicesSdkExtVersion = VersionCompatUtil.isSWithMinExtServicesVersion(9)
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.measurement.MeasurementManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testMeasurementOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 4", sdkExtVersion < 5)
+        Assume.assumeFalse("maxSdkVersion = API 33 ext 4 or API 31/32 ext 8",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion=*/ 5,
+                /* minExtServicesVersion=*/ 9))
         assertThat(from(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testDeleteRegistrationsAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = from(mContext)
 
         // Set up the request.
@@ -98,7 +120,7 @@
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
             null
         }
-        doAnswer(answer).`when`(measurementManager).deleteRegistrations(any(), any(), any())
+        doAnswer(answer).`when`(mMeasurementManager).deleteRegistrations(any(), any(), any())
 
         // Actually invoke the compat code.
         val request = DeletionRequest(
@@ -115,28 +137,30 @@
         val captor = ArgumentCaptor.forClass(
             android.adservices.measurement.DeletionRequest::class.java
         )
-        verify(measurementManager).deleteRegistrations(captor.capture(), any(), any())
+        verify(mMeasurementManager).deleteRegistrations(captor.capture(), any(), any())
 
         // Verify that the request that the compat code makes to the platform is correct.
         verifyDeletionRequest(captor.value)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterSourceAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val inputEvent = mock(InputEvent::class.java)
-        val measurementManager = mockMeasurementManager(mContext)
         val managerCompat = from(mContext)
+
         val answer = { args: InvocationOnMock ->
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
             receiver.onResult(Object())
             null
         }
-        doAnswer(answer).`when`(measurementManager).registerSource(any(), any(), any(), any())
+        doAnswer(answer).`when`(mMeasurementManager).registerSource(any(), any(), any(), any())
 
         // Actually invoke the compat code.
         managerCompat!!.registerSourceAsync(uri1, inputEvent).get()
@@ -144,7 +168,7 @@
         // Verify that the compat code was invoked correctly.
         val captor1 = ArgumentCaptor.forClass(Uri::class.java)
         val captor2 = ArgumentCaptor.forClass(InputEvent::class.java)
-        verify(measurementManager).registerSource(
+        verify(mMeasurementManager).registerSource(
             captor1.capture(),
             captor2.capture(),
             any(),
@@ -156,27 +180,29 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterTriggerAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = from(mContext)
+
         val answer = { args: InvocationOnMock ->
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(2)
             receiver.onResult(Object())
             null
         }
-        doAnswer(answer).`when`(measurementManager).registerTrigger(any(), any(), any())
+        doAnswer(answer).`when`(mMeasurementManager).registerTrigger(any(), any(), any())
 
         // Actually invoke the compat code.
         managerCompat!!.registerTriggerAsync(uri1).get()
 
         // Verify that the compat code was invoked correctly.
         val captor1 = ArgumentCaptor.forClass(Uri::class.java)
-        verify(measurementManager).registerTrigger(
+        verify(mMeasurementManager).registerTrigger(
             captor1.capture(),
             any(),
             any())
@@ -186,20 +212,22 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterWebSourceAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = from(mContext)
+
         val answer = { args: InvocationOnMock ->
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(2)
             receiver.onResult(Object())
             null
         }
-        doAnswer(answer).`when`(measurementManager).registerWebSource(any(), any(), any())
+        doAnswer(answer).`when`(mMeasurementManager).registerWebSource(any(), any(), any())
 
         val request = WebSourceRegistrationRequest.Builder(
             listOf(WebSourceParams(uri2, false)), uri1)
@@ -212,7 +240,7 @@
         // Verify that the compat code was invoked correctly.
         val captor1 = ArgumentCaptor.forClass(
             android.adservices.measurement.WebSourceRegistrationRequest::class.java)
-        verify(measurementManager).registerWebSource(
+        verify(mMeasurementManager).registerWebSource(
             captor1.capture(),
             any(),
             any())
@@ -226,20 +254,22 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterWebTriggerAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = from(mContext)
+
         val answer = { args: InvocationOnMock ->
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(2)
             receiver.onResult(Object())
             null
         }
-        doAnswer(answer).`when`(measurementManager).registerWebTrigger(any(), any(), any())
+        doAnswer(answer).`when`(mMeasurementManager).registerWebTrigger(any(), any(), any())
 
         val request = WebTriggerRegistrationRequest(listOf(WebTriggerParams(uri1, false)), uri2)
 
@@ -249,7 +279,7 @@
         // Verify that the compat code was invoked correctly.
         val captor1 = ArgumentCaptor.forClass(
             android.adservices.measurement.WebTriggerRegistrationRequest::class.java)
-        verify(measurementManager).registerWebTrigger(
+        verify(mMeasurementManager).registerWebTrigger(
             captor1.capture(),
             any(),
             any())
@@ -263,13 +293,15 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testMeasurementApiStatusAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = from(mContext)
+
         val state = MeasurementManager.MEASUREMENT_API_STATE_DISABLED
         val answer = { args: InvocationOnMock ->
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
@@ -277,14 +309,14 @@
             receiver.onResult(state)
             null
         }
-        doAnswer(answer).`when`(measurementManager).getMeasurementApiStatus(any(), any())
+        doAnswer(answer).`when`(mMeasurementManager).getMeasurementApiStatus(any(), any())
 
         // Actually invoke the compat code.
         val result = managerCompat!!.getMeasurementApiStatusAsync()
         result.get()
 
         // Verify that the compat code was invoked correctly.
-        verify(measurementManager).getMeasurementApiStatus(any(), any())
+        verify(mMeasurementManager).getMeasurementApiStatus(any(), any())
 
         // Verify that the result.
         assertThat(result.get() == state)
@@ -292,21 +324,23 @@
 
     @ExperimentalFeatures.RegisterSourceOptIn
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterSourceAsync_allSuccess() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val inputEvent = mock(InputEvent::class.java)
-        val measurementManager = mockMeasurementManager(mContext)
         val managerCompat = from(mContext)
+
         val successCallback = { args: InvocationOnMock ->
             assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
             receiver.onResult(Object())
             null
         }
-        doAnswer(successCallback).`when`(measurementManager)
+        doAnswer(successCallback).`when`(mMeasurementManager)
             .registerSource(any(), any(), any(), any())
 
         // Actually invoke the compat code.
@@ -316,12 +350,12 @@
         managerCompat!!.registerSourceAsync(request).get()
 
         // Verify that the compat code was invoked correctly.
-        verify(measurementManager).registerSource(
+        verify(mMeasurementManager).registerSource(
             eq(uri1),
             eq(inputEvent),
             any(Executor::class.java),
             any())
-        verify(measurementManager).registerSource(
+        verify(mMeasurementManager).registerSource(
             eq(uri2),
             eq(inputEvent),
             any(Executor::class.java),
@@ -329,15 +363,17 @@
     }
 
     @ExperimentalFeatures.RegisterSourceOptIn
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     @Test
     fun testRegisterSource_15thOf20Fails_atLeast15thExecutes() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val mMeasurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val mockInputEvent = mock(InputEvent::class.java)
         val managerCompat = from(mContext)
+
         val successCallback = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
             receiver.onResult(Object())
@@ -354,10 +390,10 @@
         val uris = (1..20).map { i ->
             val uri = Uri.parse("www.uri$i.com")
             if (i == 15) {
-                doAnswer(errorCallback).`when`(measurementManager)
+                doAnswer(errorCallback).`when`(mMeasurementManager)
                     .registerSource(eq(uri), any(), any(), any())
             } else {
-                doAnswer(successCallback).`when`(measurementManager)
+                doAnswer(successCallback).`when`(mMeasurementManager)
                     .registerSource(eq(uri), any(), any(), any())
             }
             uri
@@ -382,13 +418,13 @@
         // registerSource gets called 1-20 times. We cannot predict the exact number because
         // uri15 would crash asynchronously. Other uris may succeed and those threads on default
         // dispatcher won't crash.
-        verify(measurementManager, atLeastOnce()).registerSource(
+        verify(mMeasurementManager, atLeastOnce()).registerSource(
             any(),
             eq(mockInputEvent),
             any(),
             any()
         )
-        verify(measurementManager, atMost(20)).registerSource(
+        verify(mMeasurementManager, atMost(20)).registerSource(
             any(),
             eq(mockInputEvent),
             any(),
@@ -397,17 +433,25 @@
     }
 
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     companion object {
 
         private val uri1: Uri = Uri.parse("www.abc.com")
         private val uri2: Uri = Uri.parse("http://www.xyz.com")
         private lateinit var mContext: Context
 
-        private fun mockMeasurementManager(spyContext: Context): MeasurementManager {
+        private fun mockMeasurementManager(
+            spyContext: Context,
+            isExtServices: Boolean
+        ): MeasurementManager {
             val measurementManager = mock(MeasurementManager::class.java)
-            `when`(spyContext.getSystemService(MeasurementManager::class.java))
-                .thenReturn(measurementManager)
+            // mock the .get() method if using extServices version, otherwise mock getSystemService
+            if (isExtServices) {
+                `when`(android.adservices.measurement.MeasurementManager.get(any()))
+                    .thenReturn(measurementManager)
+            } else {
+                `when`(spyContext.getSystemService(MeasurementManager::class.java))
+                    .thenReturn(measurementManager)
+            }
             return measurementManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/topics/TopicsManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/topics/TopicsManagerFuturesTest.kt
index c44c32f..6d84365 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/topics/TopicsManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/topics/TopicsManagerFuturesTest.kt
@@ -20,8 +20,7 @@
 import android.adservices.topics.TopicsManager
 import android.content.Context
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.java.VersionCompatUtil
 import androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion.from
 import androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest
 import androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse
@@ -29,8 +28,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.ListenableFuture
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -44,6 +46,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -51,43 +54,65 @@
 @SdkSuppress(minSdkVersion = 30)
 class TopicsManagerFuturesTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdExtServicesSdkExtVersion = VersionCompatUtil.isSWithMinExtServicesVersion(9)
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.topics.TopicsManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testTopicsOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeFalse("maxSdkVersion = API 33 ext 3 or API 31/32 ext 8",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion=*/ 4,
+                /* minExtServicesVersion=*/ 9))
         assertThat(from(mContext)).isEqualTo(null)
     }
 
     @Test
     @SuppressWarnings("NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testTopicsAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 4,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val topicsManager = mockTopicsManager(mContext)
+        val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupTopicsResponse(topicsManager)
         val managerCompat = from(mContext)
 
         // Actually invoke the compat code.
-        val request =
-            GetTopicsRequest.Builder().setAdsSdkName(mSdkName).setShouldRecordObservation(true)
-                .build()
+        val request = GetTopicsRequest.Builder()
+            .setAdsSdkName(mSdkName)
+            .setShouldRecordObservation(true)
+            .build()
 
-        val result: ListenableFuture<GetTopicsResponse> = managerCompat!!.getTopicsAsync(request)
+        val result: ListenableFuture<GetTopicsResponse> =
+            managerCompat!!.getTopicsAsync(request)
 
         // Verify that the result of the compat call is correct.
         verifyResponse(result.get())
 
         // Verify that the compat code was invoked correctly.
-        val captor = ArgumentCaptor.forClass(android.adservices.topics.GetTopicsRequest::class.java)
+        val captor = ArgumentCaptor
+            .forClass(android.adservices.topics.GetTopicsRequest::class.java)
         verify(topicsManager).getTopics(captor.capture(), any(), any())
 
         // Verify that the request that the compat code makes to the platform is correct.
@@ -96,12 +121,13 @@
 
     @Test
     @SuppressWarnings("NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testTopicsAsyncPreviewSupported() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            VersionCompatUtil.isTestableVersion(
+                /* minAdServicesVersion= */ 5,
+                /* minExtServicesVersion=*/ 9))
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val topicsManager = mockTopicsManager(mContext)
+        val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupTopicsResponse(topicsManager)
         val managerCompat = from(mContext)
 
@@ -110,13 +136,15 @@
             GetTopicsRequest.Builder().setAdsSdkName(mSdkName).setShouldRecordObservation(false)
                 .build()
 
-        val result: ListenableFuture<GetTopicsResponse> = managerCompat!!.getTopicsAsync(request)
+        val result: ListenableFuture<GetTopicsResponse> =
+            managerCompat!!.getTopicsAsync(request)
 
         // Verify that the result of the compat call is correct.
         verifyResponse(result.get())
 
         // Verify that the compat code was invoked correctly.
-        val captor = ArgumentCaptor.forClass(android.adservices.topics.GetTopicsRequest::class.java)
+        val captor =
+            ArgumentCaptor.forClass(android.adservices.topics.GetTopicsRequest::class.java)
         verify(topicsManager).getTopics(captor.capture(), any(), any())
 
         // Verify that the request that the compat code makes to the platform is correct.
@@ -127,14 +155,18 @@
         private lateinit var mContext: Context
         private val mSdkName: String = "sdk1"
 
-        @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
-        private fun mockTopicsManager(spyContext: Context): TopicsManager {
+        private fun mockTopicsManager(spyContext: Context, isExtServices: Boolean): TopicsManager {
             val topicsManager = mock(TopicsManager::class.java)
-            `when`(spyContext.getSystemService(TopicsManager::class.java)).thenReturn(topicsManager)
+            // mock the .get() method if using extServices version, otherwise mock getSystemService
+            if (isExtServices) {
+                `when`(TopicsManager.get(any())).thenReturn(topicsManager)
+            } else {
+                `when`(spyContext.getSystemService(TopicsManager::class.java))
+                    .thenReturn(topicsManager)
+            }
             return topicsManager
         }
 
-        @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
         private fun setupTopicsResponse(topicsManager: TopicsManager) {
             // Set up the response that TopicsManager will return when the compat code calls it.
             val topic1 = Topic(1, 1, 1)
@@ -152,7 +184,6 @@
             )
         }
 
-        @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
         private fun verifyRequest(topicsRequest: android.adservices.topics.GetTopicsRequest) {
             // Set up the request that we expect the compat code to invoke.
             val expectedRequest =
@@ -161,7 +192,6 @@
             Assert.assertEquals(expectedRequest.adsSdkName, topicsRequest.adsSdkName)
         }
 
-        @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
         private fun verifyRequestPreviewApi(
             topicsRequest: android.adservices.topics.GetTopicsRequest
         ) {
@@ -176,7 +206,6 @@
             )
         }
 
-        @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
         private fun verifyResponse(getTopicsResponse: GetTopicsResponse) {
             Assert.assertEquals(2, getTopicsResponse.topics.size)
             val topic1 = getTopicsResponse.topics[0]
diff --git a/privacysandbox/ads/ads-adservices/build.gradle b/privacysandbox/ads/ads-adservices/build.gradle
index 866cc02..1c3fa23 100644
--- a/privacysandbox/ads/ads-adservices/build.gradle
+++ b/privacysandbox/ads/ads-adservices/build.gradle
@@ -41,6 +41,7 @@
 
     androidTestImplementation(libs.mockitoCore4, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.dexmakerMockitoInline, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockitoInlineExtended)
 }
 
 android {
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt
index 197f46b..ce7e661 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt
@@ -20,14 +20,18 @@
 import android.os.OutcomeReceiver
 import android.os.ext.SdkExtensions
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
+import org.junit.After
 import org.junit.Assert
-import org.junit.Assume.assumeTrue
+import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,27 +48,42 @@
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = 30)
 class AdIdManagerTest {
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 4
+    private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersion() >= 9
 
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.adid.AdIdManager::class.java)
+                .startMocking();
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testAdIdOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-        assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExtVersion)
+        Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
         assertThat(AdIdManager.obtain(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testAdIdAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val adIdManager = mockAdIdManager(mContext)
+        val adIdManager = mockAdIdManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(adIdManager)
         val managerCompat = AdIdManager.obtain(mContext)
 
@@ -85,10 +104,19 @@
     companion object {
         private lateinit var mContext: Context
 
-        private fun mockAdIdManager(spyContext: Context): android.adservices.adid.AdIdManager {
+        private fun mockAdIdManager(
+            spyContext: Context,
+            isExtServices: Boolean
+        ): android.adservices.adid.AdIdManager {
             val adIdManager = mock(android.adservices.adid.AdIdManager::class.java)
-            `when`(spyContext.getSystemService(android.adservices.adid.AdIdManager::class.java))
-                .thenReturn(adIdManager)
+            // only mock the .get() method if using extServices version
+            if (isExtServices) {
+                `when`(android.adservices.adid.AdIdManager.get(any()))
+                    .thenReturn(adIdManager)
+            } else {
+                `when`(spyContext.getSystemService(android.adservices.adid.AdIdManager::class.java))
+                    .thenReturn(adIdManager)
+            }
             return adIdManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerTest.kt
index 8d71ea2..8f4b4a24 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerTest.kt
@@ -20,17 +20,19 @@
 import android.content.Context
 import android.net.Uri
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion.obtain
 import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
 import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
+import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -44,33 +46,50 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = 30)
 class AdSelectionManagerTest {
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 4
+    private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersion() >= 9
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = mockitoSession()
+                .mockStatic(android.adservices.adselection.AdSelectionManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testAdSelectionOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExtVersion)
+        Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
         assertThat(obtain(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testSelectAds() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val adSelectionManager = mockAdSelectionManager(mContext)
+        val adSelectionManager = mockAdSelectionManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupAdSelectionResponse(adSelectionManager)
         val managerCompat = obtain(mContext)
 
@@ -92,13 +111,13 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testReportImpression() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val adSelectionManager = mockAdSelectionManager(mContext)
+        val adSelectionManager = mockAdSelectionManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupAdSelectionResponse(adSelectionManager)
+
         val managerCompat = obtain(mContext)
         val reportImpressionRequest = ReportImpressionRequest(adSelectionId, adSelectionConfig)
 
@@ -117,7 +136,6 @@
     }
 
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
         private const val adSelectionId = 1234L
@@ -146,13 +164,20 @@
         private val renderUri = Uri.parse("render-uri.com")
 
         private fun mockAdSelectionManager(
-            spyContext: Context
+            spyContext: Context,
+            isExtServices: Boolean
         ): android.adservices.adselection.AdSelectionManager {
             val adSelectionManager =
                 mock(android.adservices.adselection.AdSelectionManager::class.java)
             `when`(spyContext.getSystemService(
                 android.adservices.adselection.AdSelectionManager::class.java))
                 .thenReturn(adSelectionManager)
+            // only mock the .get() method if using extServices version
+            if (isExtServices) {
+                `when`(android.adservices.adselection.AdSelectionManager.get(any()))
+                    .thenReturn(adSelectionManager)
+            }
+
             return adSelectionManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerTest.kt
index f3f3e99..8167b8f 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerTest.kt
@@ -20,12 +20,16 @@
 import android.os.OutcomeReceiver
 import android.os.ext.SdkExtensions
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -44,28 +48,42 @@
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = 30)
 class AppSetIdManagerTest {
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 4
+    private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersion() >= 9
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.appsetid.AppSetIdManager::class.java)
+                .startMocking();
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testAppSetIdOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExtVersion)
+        Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
         assertThat(AppSetIdManager.obtain(mContext)).isEqualTo(null)
     }
 
     @Test
-    @SuppressWarnings("NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testAppSetIdAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val appSetIdManager = mockAppSetIdManager(mContext)
+        val appSetIdManager = mockAppSetIdManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(appSetIdManager)
         val managerCompat = AppSetIdManager.obtain(mContext)
 
@@ -87,12 +105,19 @@
         private lateinit var mContext: Context
 
         private fun mockAppSetIdManager(
-            spyContext: Context
+            spyContext: Context,
+            isExtServices: Boolean
         ): android.adservices.appsetid.AppSetIdManager {
             val appSetIdManager = mock(android.adservices.appsetid.AppSetIdManager::class.java)
-            `when`(spyContext.getSystemService(
-                android.adservices.appsetid.AppSetIdManager::class.java))
-                .thenReturn(appSetIdManager)
+            // only mock the .get() method if using extServices version
+            if (isExtServices) {
+                `when`(android.adservices.appsetid.AppSetIdManager.get(any()))
+                    .thenReturn(appSetIdManager)
+            } else {
+                `when`(spyContext.getSystemService(
+                    android.adservices.appsetid.AppSetIdManager::class.java))
+                    .thenReturn(appSetIdManager)
+            }
             return appSetIdManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerTest.kt
index a107e7d..53d4f16 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerTest.kt
@@ -20,19 +20,21 @@
 import android.content.Context
 import android.net.Uri
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.common.AdData
 import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
 import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
 import androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion.obtain
+import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth
 import java.time.Instant
 import kotlinx.coroutines.runBlocking
+import org.junit.After
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
@@ -45,6 +47,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -52,27 +55,44 @@
 @SdkSuppress(minSdkVersion = 30)
 class CustomAudienceManagerTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 4
+    private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersion() >= 9
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.customaudience.CustomAudienceManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExtVersion)
+        Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
         Truth.assertThat(obtain(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testJoinCustomAudience() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val customAudienceManager = mockCustomAudienceManager(mContext)
+        val customAudienceManager =
+            mockCustomAudienceManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(customAudienceManager)
         val managerCompat = obtain(mContext)
 
@@ -99,12 +119,12 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testLeaveCustomAudience() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val customAudienceManager = mockCustomAudienceManager(mContext)
+        val customAudienceManager =
+            mockCustomAudienceManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupResponse(customAudienceManager)
         val managerCompat = obtain(mContext)
 
@@ -125,7 +145,6 @@
     }
 
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
         private val uri: Uri = Uri.parse("abc.com")
@@ -139,10 +158,17 @@
         private const val metadata = "metadata"
         private val ads: List<AdData> = listOf(AdData(uri, metadata))
 
-        private fun mockCustomAudienceManager(spyContext: Context): CustomAudienceManager {
+        private fun mockCustomAudienceManager(
+            spyContext: Context,
+            isExtServices: Boolean
+        ): CustomAudienceManager {
             val customAudienceManager = mock(CustomAudienceManager::class.java)
             `when`(spyContext.getSystemService(CustomAudienceManager::class.java))
                 .thenReturn(customAudienceManager)
+            // only mock the .get() method if using extServices version
+            if (isExtServices) {
+                `when`(CustomAudienceManager.get(any())).thenReturn(customAudienceManager)
+            }
             return customAudienceManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
index 1e0a826..1b9f302 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
@@ -20,20 +20,21 @@
 import android.content.Context
 import android.net.Uri
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
 import android.view.InputEvent
-import androidx.annotation.RequiresExtension
 import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import androidx.testutils.fail
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
-import kotlin.IllegalArgumentException
 import kotlinx.coroutines.runBlocking
+import org.junit.After
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
@@ -49,6 +50,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -56,27 +58,43 @@
 @SdkSuppress(minSdkVersion = 30)
 class MeasurementManagerTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 5
+    private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersion() >= 9
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.measurement.MeasurementManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testMeasurementOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 4", sdkExtVersion < 5)
+        Assume.assumeTrue("maxSdkVersion = API 33 ext 4", !mValidAdServicesSdkExtVersion)
+        Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
         assertThat(obtain(mContext)).isEqualTo(null)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testDeleteRegistrations() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = obtain(mContext)
 
         // Set up the request.
@@ -111,14 +129,14 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterSource() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val inputEvent = mock(InputEvent::class.java)
-        val measurementManager = mockMeasurementManager(mContext)
         val managerCompat = obtain(mContext)
+
         val answer = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
             receiver.onResult(Object())
@@ -146,12 +164,11 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterTrigger() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = obtain(mContext)
         val answer = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(2)
@@ -177,12 +194,11 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterWebSource() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = obtain(mContext)
         val answer = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(2)
@@ -218,15 +234,15 @@
     }
 
     @ExperimentalFeatures.RegisterSourceOptIn
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     @Test
     fun testRegisterSource_allSuccess() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val mockInputEvent = mock(InputEvent::class.java)
         val managerCompat = obtain(mContext)
+
         val successCallback = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
             receiver.onResult(Object())
@@ -252,15 +268,15 @@
     }
 
     @ExperimentalFeatures.RegisterSourceOptIn
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     @Test
     fun testRegisterSource_15thOf20Fails_remaining5DoNotExecute() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val mockInputEvent = mock(InputEvent::class.java)
         val managerCompat = obtain(mContext)
+
         val successCallback = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
             receiver.onResult(Object())
@@ -317,12 +333,11 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterWebTrigger() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
         val managerCompat = obtain(mContext)
         val answer = { args: InvocationOnMock ->
             val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(2)
@@ -356,63 +371,33 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testMeasurementApiStatus() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
-        val managerCompat = obtain(mContext)
-        val state = MeasurementManager.MEASUREMENT_API_STATE_ENABLED
-        val answer = { args: InvocationOnMock ->
-            val receiver = args.getArgument<OutcomeReceiver<Int, Exception>>(1)
-            receiver.onResult(state)
-            null
-        }
-        doAnswer(answer).`when`(measurementManager).getMeasurementApiStatus(any(), any())
-
-        // Actually invoke the compat code.
-        val actualResult = runBlocking {
-            managerCompat!!.getMeasurementApiStatus()
-        }
-
-        // Verify that the compat code was invoked correctly.
-        verify(measurementManager).getMeasurementApiStatus(any(), any())
-
-        // Verify that the request that the compat code makes to the platform is correct.
-        assertThat(actualResult == state)
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
+        callAndVerifyGetMeasurementApiStatus(
+            measurementManager,
+            /* state= */ MeasurementManager.MEASUREMENT_API_STATE_ENABLED,
+            /* expectedResult= */ MeasurementManager.MEASUREMENT_API_STATE_ENABLED)
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testMeasurementApiStatusUnknown() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val measurementManager = mockMeasurementManager(mContext)
-        val managerCompat = obtain(mContext)
-        val answer = { args: InvocationOnMock ->
-            val receiver = args.getArgument<OutcomeReceiver<Int, Exception>>(1)
-            receiver.onResult(6 /* Greater than values returned in SdkExtensions.AD_SERVICES = 5 */)
-            null
-        }
-        doAnswer(answer).`when`(measurementManager).getMeasurementApiStatus(any(), any())
+        val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersion)
 
-        // Actually invoke the compat code.
-        val actualResult = runBlocking {
-            managerCompat!!.getMeasurementApiStatus()
-        }
-
-        // Verify that the compat code was invoked correctly.
-        verify(measurementManager).getMeasurementApiStatus(any(), any())
-
-        // Verify that the request that the compat code makes to the platform is correct.
+        // Call with a value greater than values returned in SdkExtensions.AD_SERVICES = 5
         // Since the compat code does not know the returned state, it sets it to UNKNOWN.
-        assertThat(actualResult == 5)
+        callAndVerifyGetMeasurementApiStatus(
+            measurementManager,
+            /* state= */ 6,
+            /* expectedResult= */ 5)
     }
 
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     companion object {
 
         private val uri1: Uri = Uri.parse("www.abc.com")
@@ -420,13 +405,46 @@
 
         private lateinit var mContext: Context
 
-        private fun mockMeasurementManager(spyContext: Context): MeasurementManager {
+        private fun mockMeasurementManager(
+            spyContext: Context,
+            isExtServices: Boolean
+        ): MeasurementManager {
             val measurementManager = mock(MeasurementManager::class.java)
             `when`(spyContext.getSystemService(MeasurementManager::class.java))
                 .thenReturn(measurementManager)
+            // only mock the .get() method if using the extServices version
+            if (isExtServices) {
+                `when`(MeasurementManager.get(any()))
+                    .thenReturn(measurementManager)
+            }
             return measurementManager
         }
 
+        private fun callAndVerifyGetMeasurementApiStatus(
+            measurementManager: android.adservices.measurement.MeasurementManager,
+            state: Int,
+            expectedResult: Int
+        ) {
+            val managerCompat = obtain(mContext)
+            val answer = { args: InvocationOnMock ->
+                val receiver = args.getArgument<OutcomeReceiver<Int, Exception>>(1)
+                receiver.onResult(state)
+                null
+            }
+            doAnswer(answer).`when`(measurementManager).getMeasurementApiStatus(any(), any())
+
+            // Actually invoke the compat code.
+            val actualResult = runBlocking {
+                managerCompat!!.getMeasurementApiStatus()
+            }
+
+            // Verify that the compat code was invoked correctly.
+            verify(measurementManager).getMeasurementApiStatus(any(), any())
+
+            // Verify that the request that the compat code makes to the platform is correct.
+            assertThat(actualResult == expectedResult)
+        }
+
         private fun verifyDeletionRequest(request: android.adservices.measurement.DeletionRequest) {
             // Set up the request that we expect the compat code to invoke.
             val expectedRequest = android.adservices.measurement.DeletionRequest.Builder()
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt
index d79e94e..82a492b 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt
@@ -20,15 +20,17 @@
 import android.adservices.topics.TopicsManager
 import android.content.Context
 import android.os.OutcomeReceiver
-import android.os.ext.SdkExtensions
-import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion.obtain
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
+import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
@@ -42,6 +44,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
 
 @SmallTest
 @SuppressWarnings("NewApi")
@@ -49,42 +52,60 @@
 @SdkSuppress(minSdkVersion = 30)
 class TopicsManagerTest {
 
+    private var mSession: StaticMockitoSession? = null
+    private val mValidAdServicesSdkExt4Version = AdServicesInfo.adServicesVersion() >= 4
+    private val mValidAdServicesSdkExt5Version = AdServicesInfo.adServicesVersion() >= 5
+    private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersion() >= 9
+
     @Before
     fun setUp() {
         mContext = spy(ApplicationProvider.getApplicationContext<Context>())
+
+        if (mValidAdExtServicesSdkExtVersion) {
+            // setup a mockitoSession to return the mocked manager
+            // when the static method .get() is called
+            mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(android.adservices.topics.TopicsManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        }
+    }
+
+    @After
+    fun tearDown() {
+        mSession?.finishMocking()
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
     fun testTopicsOlderVersions() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
-
-        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", sdkExtVersion < 4)
+        Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExt4Version)
+        Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
         assertThat(obtain(mContext)).isEqualTo(null)
     }
 
     @Test
-    @SuppressWarnings("NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     fun testTopicsAsync() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+            mValidAdServicesSdkExt4Version || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 4", sdkExtVersion >= 4)
-        val topicsManager = mockTopicsManager(mContext)
+        val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupTopicsResponse(topicsManager)
         val managerCompat = obtain(mContext)
 
         // Actually invoke the compat code.
         val result = runBlocking {
-            val request =
-                GetTopicsRequest.Builder().setAdsSdkName(mSdkName).setShouldRecordObservation(true)
-                    .build()
+            val request = GetTopicsRequest.Builder()
+                .setAdsSdkName(mSdkName)
+                .setShouldRecordObservation(true)
+                .build()
 
             managerCompat!!.getTopics(request)
         }
 
         // Verify that the compat code was invoked correctly.
-        val captor = ArgumentCaptor.forClass(android.adservices.topics.GetTopicsRequest::class.java)
+        val captor = ArgumentCaptor
+            .forClass(android.adservices.topics.GetTopicsRequest::class.java)
         verify(topicsManager).getTopics(captor.capture(), any(), any())
 
         // Verify that the request that the compat code makes to the platform is correct.
@@ -95,26 +116,28 @@
     }
 
     @Test
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testTopicsAsyncPreviewSupported() {
-        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+            mValidAdServicesSdkExt5Version || mValidAdExtServicesSdkExtVersion)
 
-        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
-        val topicsManager = mockTopicsManager(mContext)
+        val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExtVersion)
         setupTopicsResponse(topicsManager)
         val managerCompat = obtain(mContext)
 
         // Actually invoke the compat Preview API code.
         val result = runBlocking {
             val request =
-                GetTopicsRequest.Builder().setAdsSdkName(mSdkName).setShouldRecordObservation(false)
+                GetTopicsRequest.Builder()
+                    .setAdsSdkName(mSdkName)
+                    .setShouldRecordObservation(false)
                     .build()
 
             managerCompat!!.getTopics(request)
         }
 
         // Verify that the compat code was invoked correctly.
-        val captor = ArgumentCaptor.forClass(android.adservices.topics.GetTopicsRequest::class.java)
+        val captor = ArgumentCaptor
+            .forClass(android.adservices.topics.GetTopicsRequest::class.java)
         verify(topicsManager).getTopics(captor.capture(), any(), any())
 
         // Verify that the request that the compat code makes to the platform is correct.
@@ -125,14 +148,17 @@
     }
 
     @SdkSuppress(minSdkVersion = 30)
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
     companion object {
         private lateinit var mContext: Context
         private val mSdkName: String = "sdk1"
 
-        private fun mockTopicsManager(spyContext: Context): TopicsManager {
+        private fun mockTopicsManager(spyContext: Context, isExtServices: Boolean): TopicsManager {
             val topicsManager = mock(TopicsManager::class.java)
             `when`(spyContext.getSystemService(TopicsManager::class.java)).thenReturn(topicsManager)
+            // only mock the .get() method if using extServices version
+            if (isExtServices) {
+                `when`(TopicsManager.get(any())).thenReturn(topicsManager)
+            }
             return topicsManager
         }
 
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt
index 17431a8..632da1b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt
@@ -20,13 +20,8 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.os.LimitExceededException
-import android.os.ext.SdkExtensions
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
 import androidx.annotation.RequiresPermission
-import androidx.core.os.asOutcomeReceiver
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
-import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
  * AdId Manager provides APIs for app and ad-SDKs to access advertising ID. The advertising ID is a
@@ -45,38 +40,6 @@
     @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
     abstract suspend fun getAdId(): AdId
 
-    @SuppressLint("ClassVerificationFailure", "NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
-    private class Api33Ext4Impl(
-        private val mAdIdManager: android.adservices.adid.AdIdManager
-    ) : AdIdManager() {
-        constructor(context: Context) : this(
-            context.getSystemService<android.adservices.adid.AdIdManager>(
-                android.adservices.adid.AdIdManager::class.java
-            )
-        )
-
-        @DoNotInline
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
-        override suspend fun getAdId(): AdId {
-            return convertResponse(getAdIdAsyncInternal())
-        }
-
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
-        private suspend fun
-            getAdIdAsyncInternal(): android.adservices.adid.AdId = suspendCancellableCoroutine {
-                continuation ->
-            mAdIdManager.getAdId(
-                Runnable::run,
-                continuation.asOutcomeReceiver()
-            )
-        }
-
-        private fun convertResponse(response: android.adservices.adid.AdId): AdId {
-            return AdId(response.adId, response.isLimitAdTrackingEnabled)
-        }
-    }
-
     companion object {
         /**
          *  Creates [AdIdManager].
@@ -87,10 +50,11 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): AdIdManager? {
-            return if (AdServicesInfo.version() >= 4) {
-                Api33Ext4Impl(context)
+            return if (AdServicesInfo.adServicesVersion() >= 4) {
+                AdIdManagerApi33Ext4Impl(context)
+            } else if (AdServicesInfo.extServicesVersion() >= 9) {
+                AdIdManagerApi31Ext9Impl(context)
             } else {
-                // TODO(b/261770989): Extend this to older versions.
                 null
             }
         }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi31Ext9Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi31Ext9Impl.kt
new file mode 100644
index 0000000..28d3d76
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi31Ext9Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.adid
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+class AdIdManagerApi31Ext9Impl(context: Context) : AdIdManagerImplCommon(
+    android.adservices.adid.AdIdManager.get(context))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi33Ext4Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi33Ext4Impl.kt
new file mode 100644
index 0000000..a043fba
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi33Ext4Impl.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.adid
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+class AdIdManagerApi33Ext4Impl(context: Context) : AdIdManagerImplCommon(
+    context.getSystemService(
+        android.adservices.adid.AdIdManager::class.java))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerImplCommon.kt
new file mode 100644
index 0000000..4ba59b2
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerImplCommon.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.adid
+
+import android.adservices.common.AdServicesPermissions
+import android.annotation.SuppressLint
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.core.os.asOutcomeReceiver
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("ClassVerificationFailure", "NewApi")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+open class AdIdManagerImplCommon(
+    private val mAdIdManager: android.adservices.adid.AdIdManager
+) : AdIdManager() {
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
+    override suspend fun getAdId(): AdId {
+        return convertResponse(getAdIdAsyncInternal())
+    }
+
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
+    private suspend fun
+        getAdIdAsyncInternal(): android.adservices.adid.AdId = suspendCancellableCoroutine {
+            continuation ->
+        mAdIdManager.getAdId(
+            Runnable::run,
+            continuation.asOutcomeReceiver()
+        )
+    }
+
+    private fun convertResponse(response: android.adservices.adid.AdId): AdId {
+        return AdId(response.adId, response.isLimitAdTrackingEnabled)
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManager.kt
index 7e280c4..311bc63 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManager.kt
@@ -21,16 +21,9 @@
 import android.content.Context
 import android.os.LimitExceededException
 import android.os.TransactionTooLargeException
-import android.os.ext.SdkExtensions
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
 import androidx.annotation.RequiresPermission
-import androidx.core.os.asOutcomeReceiver
-import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
-import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
 import java.util.concurrent.TimeoutException
-import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
  * AdSelection Manager provides APIs for app and ad-SDKs to run ad selection processes as well
@@ -75,109 +68,6 @@
     @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
     abstract suspend fun reportImpression(reportImpressionRequest: ReportImpressionRequest)
 
-    @SuppressLint("NewApi", "ClassVerificationFailure")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
-    private class Api33Ext4Impl(
-        private val mAdSelectionManager: android.adservices.adselection.AdSelectionManager
-    ) : AdSelectionManager() {
-        constructor(context: Context) : this(
-            context.getSystemService<android.adservices.adselection.AdSelectionManager>(
-                android.adservices.adselection.AdSelectionManager::class.java
-            )
-        )
-
-        @DoNotInline
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
-        override suspend fun selectAds(adSelectionConfig: AdSelectionConfig): AdSelectionOutcome {
-            return convertResponse(selectAdsInternal(convertAdSelectionConfig(adSelectionConfig)))
-        }
-
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
-        private suspend fun selectAdsInternal(
-            adSelectionConfig: android.adservices.adselection.AdSelectionConfig
-        ): android.adservices.adselection.AdSelectionOutcome = suspendCancellableCoroutine { cont
-            ->
-            mAdSelectionManager.selectAds(
-                adSelectionConfig,
-                Runnable::run,
-                cont.asOutcomeReceiver()
-            )
-        }
-
-        private fun convertAdSelectionConfig(
-            request: AdSelectionConfig
-        ): android.adservices.adselection.AdSelectionConfig {
-            return android.adservices.adselection.AdSelectionConfig.Builder()
-                .setAdSelectionSignals(convertAdSelectionSignals(request.adSelectionSignals))
-                .setCustomAudienceBuyers(convertBuyers(request.customAudienceBuyers))
-                .setDecisionLogicUri(request.decisionLogicUri)
-                .setSeller(android.adservices.common.AdTechIdentifier.fromString(
-                    request.seller.identifier))
-                .setPerBuyerSignals(convertPerBuyerSignals(request.perBuyerSignals))
-                .setSellerSignals(convertAdSelectionSignals(request.sellerSignals))
-                .setTrustedScoringSignalsUri(request.trustedScoringSignalsUri)
-                .build()
-        }
-
-        private fun convertAdSelectionSignals(
-            request: AdSelectionSignals
-        ): android.adservices.common.AdSelectionSignals {
-            return android.adservices.common.AdSelectionSignals.fromString(request.signals)
-        }
-
-        private fun convertBuyers(
-            buyers: List<AdTechIdentifier>
-        ): MutableList<android.adservices.common.AdTechIdentifier> {
-            var ids = mutableListOf<android.adservices.common.AdTechIdentifier>()
-            for (buyer in buyers) {
-                ids.add(android.adservices.common.AdTechIdentifier.fromString(buyer.identifier))
-            }
-            return ids
-        }
-
-        private fun convertPerBuyerSignals(
-            request: Map<AdTechIdentifier, AdSelectionSignals>
-        ): Map<android.adservices.common.AdTechIdentifier,
-            android.adservices.common.AdSelectionSignals?> {
-            var map = HashMap<android.adservices.common.AdTechIdentifier,
-                android.adservices.common.AdSelectionSignals?>()
-            for (key in request.keys) {
-                val id = android.adservices.common.AdTechIdentifier.fromString(key.identifier)
-                val value = if (request[key] != null) convertAdSelectionSignals(request[key]!!)
-                    else null
-                map[id] = value
-            }
-            return map
-        }
-
-        private fun convertResponse(
-            response: android.adservices.adselection.AdSelectionOutcome
-        ): AdSelectionOutcome {
-            return AdSelectionOutcome(response.adSelectionId, response.renderUri)
-        }
-
-        @DoNotInline
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
-        override suspend fun reportImpression(reportImpressionRequest: ReportImpressionRequest) {
-            suspendCancellableCoroutine<Any> { continuation ->
-                mAdSelectionManager.reportImpression(
-                    convertReportImpressionRequest(reportImpressionRequest),
-                    Runnable::run,
-                    continuation.asOutcomeReceiver()
-                )
-            }
-        }
-
-        private fun convertReportImpressionRequest(
-            request: ReportImpressionRequest
-        ): android.adservices.adselection.ReportImpressionRequest {
-            return android.adservices.adselection.ReportImpressionRequest(
-                request.adSelectionId,
-                convertAdSelectionConfig(request.adSelectionConfig)
-            )
-        }
-    }
-
     companion object {
         /**
          *  Creates [AdSelectionManager].
@@ -188,8 +78,10 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): AdSelectionManager? {
-            return if (AdServicesInfo.version() >= 4) {
-                Api33Ext4Impl(context)
+            return if (AdServicesInfo.adServicesVersion() >= 4) {
+                AdSelectionManagerApi33Ext4Impl(context)
+            } else if (AdServicesInfo.extServicesVersion() >= 9) {
+                AdSelectionManagerApi31Ext9Impl(context)
             } else {
                 null
             }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerApi31Ext9Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerApi31Ext9Impl.kt
new file mode 100644
index 0000000..d968dd2
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerApi31Ext9Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.adselection
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+class AdSelectionManagerApi31Ext9Impl(context: Context) : AdSelectionManagerImplCommon(
+    android.adservices.adselection.AdSelectionManager.get(context))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerApi33Ext4Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerApi33Ext4Impl.kt
new file mode 100644
index 0000000..b3cdd9c
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerApi33Ext4Impl.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.adselection
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+class AdSelectionManagerApi33Ext4Impl(context: Context) : AdSelectionManagerImplCommon(
+    context.getSystemService(
+        android.adservices.adselection.AdSelectionManager::class.java))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerImplCommon.kt
new file mode 100644
index 0000000..e780975
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/AdSelectionManagerImplCommon.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.adselection
+
+import android.adservices.common.AdServicesPermissions
+import android.annotation.SuppressLint
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
+import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+open class AdSelectionManagerImplCommon(
+    protected val mAdSelectionManager: android.adservices.adselection.AdSelectionManager
+    ) : AdSelectionManager() {
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    override suspend fun selectAds(adSelectionConfig: AdSelectionConfig): AdSelectionOutcome {
+        return convertResponse(selectAdsInternal(convertAdSelectionConfig(adSelectionConfig)))
+    }
+
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    private suspend fun selectAdsInternal(
+        adSelectionConfig: android.adservices.adselection.AdSelectionConfig
+    ): android.adservices.adselection.AdSelectionOutcome = suspendCancellableCoroutine { cont
+        ->
+        mAdSelectionManager.selectAds(
+            adSelectionConfig,
+            Runnable::run,
+            cont.asOutcomeReceiver()
+        )
+    }
+
+    private fun convertAdSelectionConfig(
+        request: AdSelectionConfig
+    ): android.adservices.adselection.AdSelectionConfig {
+        return android.adservices.adselection.AdSelectionConfig.Builder()
+            .setAdSelectionSignals(convertAdSelectionSignals(request.adSelectionSignals))
+            .setCustomAudienceBuyers(convertBuyers(request.customAudienceBuyers))
+            .setDecisionLogicUri(request.decisionLogicUri)
+            .setSeller(android.adservices.common.AdTechIdentifier.fromString(
+                request.seller.identifier))
+            .setPerBuyerSignals(convertPerBuyerSignals(request.perBuyerSignals))
+            .setSellerSignals(convertAdSelectionSignals(request.sellerSignals))
+            .setTrustedScoringSignalsUri(request.trustedScoringSignalsUri)
+            .build()
+    }
+
+    private fun convertAdSelectionSignals(
+        request: AdSelectionSignals
+    ): android.adservices.common.AdSelectionSignals {
+        return android.adservices.common.AdSelectionSignals.fromString(request.signals)
+    }
+
+    private fun convertBuyers(
+        buyers: List<AdTechIdentifier>
+    ): MutableList<android.adservices.common.AdTechIdentifier> {
+        val ids = mutableListOf<android.adservices.common.AdTechIdentifier>()
+        for (buyer in buyers) {
+            ids.add(android.adservices.common.AdTechIdentifier.fromString(buyer.identifier))
+        }
+        return ids
+    }
+
+    private fun convertPerBuyerSignals(
+        request: Map<AdTechIdentifier, AdSelectionSignals>
+    ): Map<android.adservices.common.AdTechIdentifier,
+        android.adservices.common.AdSelectionSignals?> {
+        val map = HashMap<android.adservices.common.AdTechIdentifier,
+            android.adservices.common.AdSelectionSignals?>()
+        for (key in request.keys) {
+            val id = android.adservices.common.AdTechIdentifier.fromString(key.identifier)
+            val value = if (request[key] != null) convertAdSelectionSignals(request[key]!!)
+            else null
+            map[id] = value
+        }
+        return map
+    }
+
+    private fun convertResponse(
+        response: android.adservices.adselection.AdSelectionOutcome
+    ): AdSelectionOutcome {
+        return AdSelectionOutcome(response.adSelectionId, response.renderUri)
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    override suspend fun reportImpression(reportImpressionRequest: ReportImpressionRequest) {
+        suspendCancellableCoroutine<Any> { continuation ->
+            mAdSelectionManager.reportImpression(
+                convertReportImpressionRequest(reportImpressionRequest),
+                Runnable::run,
+                continuation.asOutcomeReceiver()
+            )
+        }
+    }
+
+    private fun convertReportImpressionRequest(
+        request: ReportImpressionRequest
+    ): android.adservices.adselection.ReportImpressionRequest {
+        return android.adservices.adselection.ReportImpressionRequest(
+            request.adSelectionId,
+            convertAdSelectionConfig(request.adSelectionConfig)
+        )
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManager.kt
index d780956..88d57f1 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManager.kt
@@ -19,12 +19,7 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.os.LimitExceededException
-import android.os.ext.SdkExtensions
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
-import androidx.core.os.asOutcomeReceiver
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
-import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
  * AppSetIdManager provides APIs for app and ad-SDKs to access appSetId for non-monetizing purpose.
@@ -39,39 +34,6 @@
      */
     abstract suspend fun getAppSetId(): AppSetId
 
-    @SuppressLint("ClassVerificationFailure", "NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
-    private class Api33Ext4Impl(
-        private val mAppSetIdManager: android.adservices.appsetid.AppSetIdManager
-    ) : AppSetIdManager() {
-        constructor(context: Context) : this(
-            context.getSystemService<android.adservices.appsetid.AppSetIdManager>(
-                android.adservices.appsetid.AppSetIdManager::class.java
-            )
-        )
-
-        @DoNotInline
-        override suspend fun getAppSetId(): AppSetId {
-            return convertResponse(getAppSetIdAsyncInternal())
-        }
-
-        private suspend fun getAppSetIdAsyncInternal(): android.adservices.appsetid.AppSetId =
-            suspendCancellableCoroutine {
-                    continuation ->
-                mAppSetIdManager.getAppSetId(
-                    Runnable::run,
-                    continuation.asOutcomeReceiver()
-                )
-            }
-
-        private fun convertResponse(response: android.adservices.appsetid.AppSetId): AppSetId {
-            if (response.scope == android.adservices.appsetid.AppSetId.SCOPE_APP) {
-                return AppSetId(response.id, AppSetId.SCOPE_APP)
-            }
-            return AppSetId(response.id, AppSetId.SCOPE_DEVELOPER)
-        }
-    }
-
     companion object {
 
         /**
@@ -83,10 +45,11 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): AppSetIdManager? {
-            return if (AdServicesInfo.version() >= 4) {
-                Api33Ext4Impl(context)
+            return if (AdServicesInfo.adServicesVersion() >= 4) {
+                AppSetIdManagerApi33Ext4Impl(context)
+            } else if (AdServicesInfo.extServicesVersion() >= 9) {
+                AppSetIdManagerApi31Ext9Impl(context)
             } else {
-                // TODO(b/261770989): Extend this to older versions.
                 null
             }
         }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerApi31Ext9Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerApi31Ext9Impl.kt
new file mode 100644
index 0000000..390e785
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerApi31Ext9Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.appsetid
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+class AppSetIdManagerApi31Ext9Impl(context: Context) : AppSetIdManagerImplCommon(
+    android.adservices.appsetid.AppSetIdManager.get(context))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerApi33Ext4Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerApi33Ext4Impl.kt
new file mode 100644
index 0000000..84af87a
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerApi33Ext4Impl.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.appsetid
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+class AppSetIdManagerApi33Ext4Impl(context: Context) : AppSetIdManagerImplCommon(
+    context.getSystemService(
+        android.adservices.appsetid.AppSetIdManager::class.java))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerImplCommon.kt
new file mode 100644
index 0000000..720370d
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/appsetid/AppSetIdManagerImplCommon.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.appsetid
+
+import android.annotation.SuppressLint
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+import androidx.core.os.asOutcomeReceiver
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("ClassVerificationFailure", "NewApi")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+open class AppSetIdManagerImplCommon(
+    private val mAppSetIdManager: android.adservices.appsetid.AppSetIdManager
+) : AppSetIdManager() {
+
+    @DoNotInline
+    override suspend fun getAppSetId(): AppSetId {
+        return convertResponse(getAppSetIdAsyncInternal())
+    }
+
+    private suspend fun getAppSetIdAsyncInternal(): android.adservices.appsetid.AppSetId =
+        suspendCancellableCoroutine {
+                continuation ->
+            mAppSetIdManager.getAppSetId(
+                Runnable::run,
+                continuation.asOutcomeReceiver()
+            )
+        }
+
+    private fun convertResponse(response: android.adservices.appsetid.AppSetId): AppSetId {
+        if (response.scope == android.adservices.appsetid.AppSetId.SCOPE_APP) {
+            return AppSetId(response.id, AppSetId.SCOPE_APP)
+        }
+        return AppSetId(response.id, AppSetId.SCOPE_DEVELOPER)
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManager.kt
index 981aeaa..a9b91d7 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManager.kt
@@ -20,16 +20,8 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.os.LimitExceededException
-import android.os.ext.SdkExtensions
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
 import androidx.annotation.RequiresPermission
-import androidx.core.os.asOutcomeReceiver
-import androidx.privacysandbox.ads.adservices.common.AdData
-import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
-import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
-import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
  * This class provides APIs for app and ad-SDKs to join / leave custom audiences.
@@ -94,111 +86,6 @@
     @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
     abstract suspend fun leaveCustomAudience(request: LeaveCustomAudienceRequest)
 
-    @SuppressLint("ClassVerificationFailure", "NewApi")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
-    private class Api33Ext4Impl(
-        private val customAudienceManager: android.adservices.customaudience.CustomAudienceManager
-        ) : CustomAudienceManager() {
-        constructor(context: Context) : this(
-            context.getSystemService<android.adservices.customaudience.CustomAudienceManager>(
-                android.adservices.customaudience.CustomAudienceManager::class.java
-            )
-        )
-
-        @DoNotInline
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
-        override suspend fun joinCustomAudience(request: JoinCustomAudienceRequest) {
-            suspendCancellableCoroutine { continuation ->
-                customAudienceManager.joinCustomAudience(
-                    convertJoinRequest(request),
-                    Runnable::run,
-                    continuation.asOutcomeReceiver()
-                )
-            }
-        }
-
-        @DoNotInline
-        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
-        override suspend fun leaveCustomAudience(request: LeaveCustomAudienceRequest) {
-            suspendCancellableCoroutine { continuation ->
-                customAudienceManager.leaveCustomAudience(
-                    convertLeaveRequest(request),
-                    Runnable::run,
-                    continuation.asOutcomeReceiver()
-                )
-            }
-        }
-
-        private fun convertJoinRequest(
-            request: JoinCustomAudienceRequest
-        ): android.adservices.customaudience.JoinCustomAudienceRequest {
-            return android.adservices.customaudience.JoinCustomAudienceRequest.Builder()
-                .setCustomAudience(convertCustomAudience(request.customAudience))
-                .build()
-        }
-
-        private fun convertLeaveRequest(
-            request: LeaveCustomAudienceRequest
-        ): android.adservices.customaudience.LeaveCustomAudienceRequest {
-            return android.adservices.customaudience.LeaveCustomAudienceRequest.Builder()
-                .setBuyer(convertAdTechIdentifier(request.buyer))
-                .setName(request.name)
-                .build()
-        }
-
-        private fun convertCustomAudience(
-            request: CustomAudience
-        ): android.adservices.customaudience.CustomAudience {
-            return android.adservices.customaudience.CustomAudience.Builder()
-                .setActivationTime(request.activationTime)
-                .setAds(convertAdData(request.ads))
-                .setBiddingLogicUri(request.biddingLogicUri)
-                .setBuyer(convertAdTechIdentifier(request.buyer))
-                .setDailyUpdateUri(request.dailyUpdateUri)
-                .setExpirationTime(request.expirationTime)
-                .setName(request.name)
-                .setTrustedBiddingData(convertTrustedSignals(request.trustedBiddingSignals))
-                .setUserBiddingSignals(convertBiddingSignals(request.userBiddingSignals))
-                .build()
-        }
-
-        private fun convertAdData(
-            input: List<AdData>
-        ): List<android.adservices.common.AdData> {
-            val result = mutableListOf<android.adservices.common.AdData>()
-            for (ad in input) {
-                result.add(android.adservices.common.AdData.Builder()
-                    .setMetadata(ad.metadata)
-                    .setRenderUri(ad.renderUri)
-                    .build())
-            }
-            return result
-        }
-
-        private fun convertAdTechIdentifier(
-            input: AdTechIdentifier
-        ): android.adservices.common.AdTechIdentifier {
-            return android.adservices.common.AdTechIdentifier.fromString(input.identifier)
-        }
-
-        private fun convertTrustedSignals(
-            input: TrustedBiddingData?
-        ): android.adservices.customaudience.TrustedBiddingData? {
-            if (input == null) return null
-            return android.adservices.customaudience.TrustedBiddingData.Builder()
-                .setTrustedBiddingKeys(input.trustedBiddingKeys)
-                .setTrustedBiddingUri(input.trustedBiddingUri)
-                .build()
-        }
-
-        private fun convertBiddingSignals(
-            input: AdSelectionSignals?
-        ): android.adservices.common.AdSelectionSignals? {
-            if (input == null) return null
-            return android.adservices.common.AdSelectionSignals.fromString(input.signals)
-        }
-    }
-
     companion object {
         /**
          *  Creates [CustomAudienceManager].
@@ -209,8 +96,10 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): CustomAudienceManager? {
-            return if (AdServicesInfo.version() >= 4) {
-                Api33Ext4Impl(context)
+            return if (AdServicesInfo.adServicesVersion() >= 4) {
+                CustomAudienceManagerApi33Ext4Impl(context)
+            } else if (AdServicesInfo.extServicesVersion() >= 9) {
+                CustomAudienceManagerApi31Ext9Impl(context)
             } else {
                 null
             }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerApi31Ext9Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerApi31Ext9Impl.kt
new file mode 100644
index 0000000..1e294ce
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerApi31Ext9Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.customaudience
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+class CustomAudienceManagerApi31Ext9Impl(context: Context) : CustomAudienceManagerImplCommon(
+    android.adservices.customaudience.CustomAudienceManager.get(context))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerApi33Ext4Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerApi33Ext4Impl.kt
new file mode 100644
index 0000000..fd658d7
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerApi33Ext4Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.customaudience
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+class CustomAudienceManagerApi33Ext4Impl(context: Context) : CustomAudienceManagerImplCommon(
+    context.getSystemService(android.adservices.customaudience.CustomAudienceManager::class.java))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerImplCommon.kt
new file mode 100644
index 0000000..44f09a8
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/CustomAudienceManagerImplCommon.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.customaudience
+
+import android.adservices.common.AdServicesPermissions
+import android.annotation.SuppressLint
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.AdData
+import androidx.privacysandbox.ads.adservices.common.AdSelectionSignals
+import androidx.privacysandbox.ads.adservices.common.AdTechIdentifier
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+open class CustomAudienceManagerImplCommon(
+    protected val customAudienceManager: android.adservices.customaudience.CustomAudienceManager
+    ) : CustomAudienceManager() {
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    override suspend fun joinCustomAudience(request: JoinCustomAudienceRequest) {
+        suspendCancellableCoroutine { continuation ->
+            customAudienceManager.joinCustomAudience(
+                convertJoinRequest(request),
+                Runnable::run,
+                continuation.asOutcomeReceiver()
+            )
+        }
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    override suspend fun leaveCustomAudience(request: LeaveCustomAudienceRequest) {
+        suspendCancellableCoroutine { continuation ->
+            customAudienceManager.leaveCustomAudience(
+                convertLeaveRequest(request),
+                Runnable::run,
+                continuation.asOutcomeReceiver()
+            )
+        }
+    }
+
+    private fun convertJoinRequest(
+        request: JoinCustomAudienceRequest
+    ): android.adservices.customaudience.JoinCustomAudienceRequest {
+        return android.adservices.customaudience.JoinCustomAudienceRequest.Builder()
+            .setCustomAudience(convertCustomAudience(request.customAudience))
+            .build()
+    }
+
+    private fun convertLeaveRequest(
+        request: LeaveCustomAudienceRequest
+    ): android.adservices.customaudience.LeaveCustomAudienceRequest {
+        return android.adservices.customaudience.LeaveCustomAudienceRequest.Builder()
+            .setBuyer(convertAdTechIdentifier(request.buyer))
+            .setName(request.name)
+            .build()
+    }
+
+    private fun convertCustomAudience(
+        request: CustomAudience
+    ): android.adservices.customaudience.CustomAudience {
+        return android.adservices.customaudience.CustomAudience.Builder()
+            .setActivationTime(request.activationTime)
+            .setAds(convertAdData(request.ads))
+            .setBiddingLogicUri(request.biddingLogicUri)
+            .setBuyer(convertAdTechIdentifier(request.buyer))
+            .setDailyUpdateUri(request.dailyUpdateUri)
+            .setExpirationTime(request.expirationTime)
+            .setName(request.name)
+            .setTrustedBiddingData(convertTrustedSignals(request.trustedBiddingSignals))
+            .setUserBiddingSignals(convertBiddingSignals(request.userBiddingSignals))
+            .build()
+    }
+
+    private fun convertAdData(
+        input: List<AdData>
+    ): List<android.adservices.common.AdData> {
+        val result = mutableListOf<android.adservices.common.AdData>()
+        for (ad in input) {
+            result.add(android.adservices.common.AdData.Builder()
+                .setMetadata(ad.metadata)
+                .setRenderUri(ad.renderUri)
+                .build())
+        }
+        return result
+    }
+
+    private fun convertAdTechIdentifier(
+        input: AdTechIdentifier
+    ): android.adservices.common.AdTechIdentifier {
+        return android.adservices.common.AdTechIdentifier.fromString(input.identifier)
+    }
+
+    private fun convertTrustedSignals(
+        input: TrustedBiddingData?
+    ): android.adservices.customaudience.TrustedBiddingData? {
+        if (input == null) return null
+        return android.adservices.customaudience.TrustedBiddingData.Builder()
+            .setTrustedBiddingKeys(input.trustedBiddingKeys)
+            .setTrustedBiddingUri(input.trustedBiddingUri)
+            .build()
+    }
+
+    private fun convertBiddingSignals(
+        input: AdSelectionSignals?
+    ): android.adservices.common.AdSelectionSignals? {
+        if (input == null) return null
+        return android.adservices.common.AdSelectionSignals.fromString(input.signals)
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt
index 9b36692..6f8b056 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt
@@ -21,24 +21,38 @@
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
-/**
- * Temporary replacement for BuildCompat.AD_SERVICES_EXTENSION_INT.
- * TODO(b/261755947) Replace with AD_SERVICES_EXTENSION_INT after new core library release
- */
 internal object AdServicesInfo {
 
-    fun version(): Int {
-        return if (Build.VERSION.SDK_INT >= 30) {
+    fun adServicesVersion(): Int {
+        return if (Build.VERSION.SDK_INT >= 33) {
             Extensions30Impl.getAdServicesVersion()
         } else {
             0
         }
     }
 
+    fun extServicesVersion(): Int {
+        return if (Build.VERSION.SDK_INT == 31 || Build.VERSION.SDK_INT == 32) {
+            Extensions30ExtImpl.getAdExtServicesVersion()
+        } else {
+            0
+        }
+    }
+
     @RequiresApi(30)
     private object Extensions30Impl {
         @DoNotInline
         fun getAdServicesVersion() =
             SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
     }
+
+    @RequiresApi(30)
+    private object Extensions30ExtImpl {
+        // For ExtServices, there is no AD_SERVICES extension version, so we need to check
+        // for the build version. Use S for now, but this can be changed to R when we add
+        // support for R later.
+        @DoNotInline
+        fun getAdExtServicesVersion() =
+            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S)
+    }
 }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
index de62bd2..4641d2b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
@@ -20,18 +20,11 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.net.Uri
-import android.os.ext.SdkExtensions
 import android.util.Log
 import android.view.InputEvent
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
 import androidx.annotation.RequiresPermission
-import androidx.core.os.asOutcomeReceiver
 import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
  * This class provides APIs to manage ads attribution using Privacy Sandbox.
@@ -103,167 +96,6 @@
     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
     abstract suspend fun getMeasurementApiStatus(): Int
 
-    @SuppressLint("NewApi", "ClassVerificationFailure")
-    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
-    private class Api33Ext5Impl(
-        private val mMeasurementManager: android.adservices.measurement.MeasurementManager
-    ) : MeasurementManager() {
-        constructor(context: Context) : this(
-            context.getSystemService<android.adservices.measurement.MeasurementManager>(
-                android.adservices.measurement.MeasurementManager::class.java
-            )
-        )
-
-        @DoNotInline
-        override suspend fun deleteRegistrations(deletionRequest: DeletionRequest) {
-            suspendCancellableCoroutine<Any> { continuation ->
-                mMeasurementManager.deleteRegistrations(
-                    convertDeletionRequest(deletionRequest),
-                    Runnable::run,
-                    continuation.asOutcomeReceiver()
-                )
-            }
-        }
-
-        private fun convertDeletionRequest(
-            request: DeletionRequest
-        ): android.adservices.measurement.DeletionRequest {
-            return android.adservices.measurement.DeletionRequest.Builder()
-                .setDeletionMode(request.deletionMode)
-                .setMatchBehavior(request.matchBehavior)
-                .setStart(request.start)
-                .setEnd(request.end)
-                .setDomainUris(request.domainUris)
-                .setOriginUris(request.originUris)
-                .build()
-        }
-
-        @DoNotInline
-        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
-        override suspend fun registerSource(attributionSource: Uri, inputEvent: InputEvent?) {
-            suspendCancellableCoroutine<Any> { continuation ->
-                mMeasurementManager.registerSource(
-                    attributionSource,
-                    inputEvent,
-                    Runnable::run,
-                    continuation.asOutcomeReceiver()
-                )
-            }
-        }
-
-        @DoNotInline
-        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
-        override suspend fun registerTrigger(trigger: Uri) {
-            suspendCancellableCoroutine<Any> { continuation ->
-                mMeasurementManager.registerTrigger(
-                    trigger,
-                    Runnable::run,
-                    continuation.asOutcomeReceiver())
-            }
-        }
-
-        @DoNotInline
-        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
-        override suspend fun registerWebSource(request: WebSourceRegistrationRequest) {
-            suspendCancellableCoroutine<Any> { continuation ->
-                mMeasurementManager.registerWebSource(
-                    convertWebSourceRequest(request),
-                    Runnable::run,
-                    continuation.asOutcomeReceiver())
-            }
-        }
-
-        @DoNotInline
-        @ExperimentalFeatures.RegisterSourceOptIn
-        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
-        override suspend fun registerSource(
-            request: SourceRegistrationRequest
-        ): Unit = coroutineScope {
-            request.registrationUris.forEach { uri ->
-                launch {
-                    suspendCancellableCoroutine<Any> { continuation ->
-                        mMeasurementManager.registerSource(
-                            uri,
-                            request.inputEvent,
-                            Runnable::run,
-                            continuation.asOutcomeReceiver()
-                        )
-                    }
-                }
-            }
-        }
-
-        private fun convertWebSourceRequest(
-            request: WebSourceRegistrationRequest
-        ): android.adservices.measurement.WebSourceRegistrationRequest {
-            return android.adservices.measurement.WebSourceRegistrationRequest
-                .Builder(
-                    convertWebSourceParams(request.webSourceParams),
-                    request.topOriginUri)
-                .setWebDestination(request.webDestination)
-                .setAppDestination(request.appDestination)
-                .setInputEvent(request.inputEvent)
-                .setVerifiedDestination(request.verifiedDestination)
-                .build()
-        }
-
-        private fun convertWebSourceParams(
-            request: List<WebSourceParams>
-        ): List<android.adservices.measurement.WebSourceParams> {
-            var result = mutableListOf<android.adservices.measurement.WebSourceParams>()
-            for (param in request) {
-                result.add(android.adservices.measurement.WebSourceParams
-                    .Builder(param.registrationUri)
-                    .setDebugKeyAllowed(param.debugKeyAllowed)
-                    .build())
-            }
-            return result
-        }
-
-        @DoNotInline
-        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
-        override suspend fun registerWebTrigger(request: WebTriggerRegistrationRequest) {
-            suspendCancellableCoroutine<Any> { continuation ->
-                mMeasurementManager.registerWebTrigger(
-                    convertWebTriggerRequest(request),
-                    Runnable::run,
-                    continuation.asOutcomeReceiver())
-            }
-        }
-
-        private fun convertWebTriggerRequest(
-            request: WebTriggerRegistrationRequest
-        ): android.adservices.measurement.WebTriggerRegistrationRequest {
-            return android.adservices.measurement.WebTriggerRegistrationRequest
-                .Builder(
-                    convertWebTriggerParams(request.webTriggerParams),
-                    request.destination)
-                .build()
-        }
-
-        private fun convertWebTriggerParams(
-            request: List<WebTriggerParams>
-        ): List<android.adservices.measurement.WebTriggerParams> {
-            var result = mutableListOf<android.adservices.measurement.WebTriggerParams>()
-            for (param in request) {
-                result.add(android.adservices.measurement.WebTriggerParams
-                    .Builder(param.registrationUri)
-                    .setDebugKeyAllowed(param.debugKeyAllowed)
-                    .build())
-            }
-            return result
-        }
-
-        @DoNotInline
-        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
-        override suspend fun getMeasurementApiStatus(): Int = suspendCancellableCoroutine {
-                continuation ->
-            mMeasurementManager.getMeasurementApiStatus(
-                Runnable::run,
-                continuation.asOutcomeReceiver())
-        }
-    }
-
     companion object {
         /**
          * This state indicates that Measurement APIs are unavailable. Invoking them will result
@@ -284,9 +116,12 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): MeasurementManager? {
-            Log.d("MeasurementManager", "AdServicesInfo.version=${AdServicesInfo.version()}")
-            return if (AdServicesInfo.version() >= 5) {
-                Api33Ext5Impl(context)
+            Log.d("MeasurementManager",
+                "AdServicesInfo.version=${AdServicesInfo.adServicesVersion()}")
+            return if (AdServicesInfo.adServicesVersion() >= 5) {
+                MeasurementManagerApi33Ext5Impl(context)
+            } else if (AdServicesInfo.extServicesVersion() >= 9) {
+                MeasurementManagerApi31Ext9Impl(context)
             } else {
                 null
             }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi31Ext9Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi31Ext9Impl.kt
new file mode 100644
index 0000000..4d55606
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi31Ext9Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.measurement
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+class MeasurementManagerApi31Ext9Impl(context: Context) : MeasurementManagerImplCommon(
+    android.adservices.measurement.MeasurementManager.get(context))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi33Ext5Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi33Ext5Impl.kt
new file mode 100644
index 0000000..cf0de76
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi33Ext5Impl.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.measurement
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+class MeasurementManagerApi33Ext5Impl(context: Context) : MeasurementManagerImplCommon(
+    context.getSystemService(android.adservices.measurement.MeasurementManager::class.java))
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerImplCommon.kt
new file mode 100644
index 0000000..3618240
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerImplCommon.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.measurement
+
+import android.adservices.common.AdServicesPermissions
+import android.annotation.SuppressLint
+import android.net.Uri
+import android.os.Build
+import android.os.ext.SdkExtensions
+import android.view.InputEvent
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+open class MeasurementManagerImplCommon(
+    protected val mMeasurementManager: android.adservices.measurement.MeasurementManager
+    ) : MeasurementManager() {
+    @DoNotInline
+    override suspend fun deleteRegistrations(deletionRequest: DeletionRequest) {
+        suspendCancellableCoroutine<Any> { continuation ->
+            mMeasurementManager.deleteRegistrations(
+                convertDeletionRequest(deletionRequest),
+                Runnable::run,
+                continuation.asOutcomeReceiver()
+            )
+        }
+    }
+
+    private fun convertDeletionRequest(
+        request: DeletionRequest
+    ): android.adservices.measurement.DeletionRequest {
+        return android.adservices.measurement.DeletionRequest.Builder()
+            .setDeletionMode(request.deletionMode)
+            .setMatchBehavior(request.matchBehavior)
+            .setStart(request.start)
+            .setEnd(request.end)
+            .setDomainUris(request.domainUris)
+            .setOriginUris(request.originUris)
+            .build()
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    override suspend fun registerSource(attributionSource: Uri, inputEvent: InputEvent?) {
+        suspendCancellableCoroutine<Any> { continuation ->
+            mMeasurementManager.registerSource(
+                attributionSource,
+                inputEvent,
+                Runnable::run,
+                continuation.asOutcomeReceiver()
+            )
+        }
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    override suspend fun registerTrigger(trigger: Uri) {
+        suspendCancellableCoroutine<Any> { continuation ->
+            mMeasurementManager.registerTrigger(
+                trigger,
+                Runnable::run,
+                continuation.asOutcomeReceiver())
+        }
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    override suspend fun registerWebSource(request: WebSourceRegistrationRequest) {
+        suspendCancellableCoroutine<Any> { continuation ->
+            mMeasurementManager.registerWebSource(
+                convertWebSourceRequest(request),
+                Runnable::run,
+                continuation.asOutcomeReceiver())
+        }
+    }
+
+    @DoNotInline
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    override suspend fun registerSource(
+        request: SourceRegistrationRequest
+    ): Unit = coroutineScope {
+        request.registrationUris.forEach { uri ->
+            launch {
+                suspendCancellableCoroutine<Any> { continuation ->
+                    mMeasurementManager.registerSource(
+                        uri,
+                        request.inputEvent,
+                        Runnable::run,
+                        continuation.asOutcomeReceiver()
+                    )
+                }
+            }
+        }
+    }
+
+    private fun convertWebSourceRequest(
+        request: WebSourceRegistrationRequest
+    ): android.adservices.measurement.WebSourceRegistrationRequest {
+        return android.adservices.measurement.WebSourceRegistrationRequest
+            .Builder(
+                convertWebSourceParams(request.webSourceParams),
+                request.topOriginUri)
+            .setWebDestination(request.webDestination)
+            .setAppDestination(request.appDestination)
+            .setInputEvent(request.inputEvent)
+            .setVerifiedDestination(request.verifiedDestination)
+            .build()
+    }
+
+    private fun convertWebSourceParams(
+        request: List<WebSourceParams>
+    ): List<android.adservices.measurement.WebSourceParams> {
+        var result = mutableListOf<android.adservices.measurement.WebSourceParams>()
+        for (param in request) {
+            result.add(android.adservices.measurement.WebSourceParams
+                .Builder(param.registrationUri)
+                .setDebugKeyAllowed(param.debugKeyAllowed)
+                .build())
+        }
+        return result
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    override suspend fun registerWebTrigger(request: WebTriggerRegistrationRequest) {
+        suspendCancellableCoroutine<Any> { continuation ->
+            mMeasurementManager.registerWebTrigger(
+                convertWebTriggerRequest(request),
+                Runnable::run,
+                continuation.asOutcomeReceiver())
+        }
+    }
+
+    private fun convertWebTriggerRequest(
+        request: WebTriggerRegistrationRequest
+    ): android.adservices.measurement.WebTriggerRegistrationRequest {
+        return android.adservices.measurement.WebTriggerRegistrationRequest
+            .Builder(
+                convertWebTriggerParams(request.webTriggerParams),
+                request.destination)
+            .build()
+    }
+
+    private fun convertWebTriggerParams(
+        request: List<WebTriggerParams>
+    ): List<android.adservices.measurement.WebTriggerParams> {
+        var result = mutableListOf<android.adservices.measurement.WebTriggerParams>()
+        for (param in request) {
+            result.add(android.adservices.measurement.WebTriggerParams
+                .Builder(param.registrationUri)
+                .setDebugKeyAllowed(param.debugKeyAllowed)
+                .build())
+        }
+        return result
+    }
+
+    @DoNotInline
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    override suspend fun getMeasurementApiStatus(): Int = suspendCancellableCoroutine {
+            continuation ->
+        mMeasurementManager.getMeasurementApiStatus(
+            Runnable::run,
+            continuation.asOutcomeReceiver())
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt
index 828fdf9..ab533f0 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt
@@ -50,10 +50,12 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): TopicsManager? {
-            return if (AdServicesInfo.version() >= 5) {
+            return if (AdServicesInfo.adServicesVersion() >= 5) {
                 TopicsManagerApi33Ext5Impl(context)
-            } else if (AdServicesInfo.version() == 4) {
+            } else if (AdServicesInfo.adServicesVersion() == 4) {
                 TopicsManagerApi33Ext4Impl(context)
+            } else if (AdServicesInfo.extServicesVersion() >= 9) {
+                TopicsManagerApi31Ext9Impl(context)
             } else {
                 null
             }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi31Ext9Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi31Ext9Impl.kt
new file mode 100644
index 0000000..c9aedf9
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi31Ext9Impl.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.topics
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
+class TopicsManagerApi31Ext9Impl(context: Context) : TopicsManagerImplCommon(
+    android.adservices.topics.TopicsManager.get(context)) {
+
+    override fun convertRequest(
+        request: GetTopicsRequest
+    ): android.adservices.topics.GetTopicsRequest {
+        return android.adservices.topics.GetTopicsRequest.Builder()
+            .setAdsSdkName(request.adsSdkName)
+            .setShouldRecordObservation(request.shouldRecordObservation)
+            .build()
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt
index 7dc5752..2b09952 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt
@@ -1,7 +1,24 @@
+/*
+ * Copyright 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 androidx.privacysandbox.ads.adservices.topics
 
 import android.adservices.common.AdServicesPermissions
 import android.annotation.SuppressLint
+import android.os.Build
 import android.os.ext.SdkExtensions
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresExtension
@@ -13,6 +30,7 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @SuppressLint("NewApi")
 @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
 open class TopicsManagerImplCommon(
     private val mTopicsManager: android.adservices.topics.TopicsManager
 ) : TopicsManager() {