Merge changes Ibcbdc9c4,Id3ce3d36 into androidx-main

* changes:
  Code cleanup for Camera2ExtensionsTestUtil with CameraIdExtensionModePair for type safety
  Extend ExtensionsDisabledQuirk to all Motoroal Devices with Extensions v1.1.0 and older
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
index 808e43d..88efac3 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
@@ -182,7 +182,7 @@
     @Test
     fun correctAvailability_whenExtensionIsNotAvailable() {
         // Skips the test if extensions availability is disabled by quirk.
-        assumeFalse(ExtensionsTestUtil.extensionsDisabledByQuirk(lensFacing, extensionMode))
+        assumeFalse(ExtensionsTestUtil.extensionsDisabledByQuirk())
 
         extensionsManager = ExtensionsManager.getInstanceAsync(
             context,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index 57d7a47..9deb408 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -207,10 +207,7 @@
     /**
      * Returns whether extensions is disabled by quirk.
      */
-    public static boolean extensionsDisabledByQuirk(@CameraSelector.LensFacing int lensFacing,
-            @ExtensionMode.Mode int extensionMode) {
-
-        return new ExtensionDisabledValidator().shouldDisableExtension(
-                CameraUtil.getCameraIdWithLensFacing(lensFacing), extensionMode);
+    public static boolean extensionsDisabledByQuirk() {
+        return new ExtensionDisabledValidator().shouldDisableExtension();
     }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
index 7715f57..17a7a78 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
@@ -56,10 +56,8 @@
             new ExtensionDisabledValidator();
     private final AdvancedExtenderImpl mAdvancedExtenderImpl;
     private String mCameraId;
-    private final @ExtensionMode.Mode int mMode;
 
     public AdvancedVendorExtender(@ExtensionMode.Mode int mode) {
-        mMode = mode;
         try {
             switch (mode) {
                 case ExtensionMode.BOKEH:
@@ -101,7 +99,7 @@
     public boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
 
-        if (mExtensionDisabledValidator.shouldDisableExtension(cameraId, mMode)) {
+        if (mExtensionDisabledValidator.shouldDisableExtension()) {
             return false;
         }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
index 2a9f2d0..37b5610 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
@@ -123,7 +123,7 @@
     public boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
 
-        if (mExtensionDisabledValidator.shouldDisableExtension(cameraId, mMode)) {
+        if (mExtensionDisabledValidator.shouldDisableExtension()) {
             return false;
         }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
index 6b58434..ec3f327 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.extensions.internal;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.Logger;
@@ -75,6 +76,38 @@
         return getInstance().isAdvancedExtenderSupportedInternal();
     }
 
+    /**
+     * Check if the Runtime Version meets the minimum compatible version requirement. This implies
+     * that the runtime version is equal to or newer than the version.
+     *
+     * <p> The compatible version is comprised of the major and minor version numbers. The patch
+     * number is ignored.
+     *
+     * @param version The minimum compatible version required
+     * @return True if the Runtime version meets the minimum version requirement and False
+     * otherwise.
+     */
+    public static boolean isMinimumCompatibleVersion(@NonNull Version version) {
+        return ExtensionVersion.getRuntimeVersion()
+                .compareTo(version.getMajor(), version.getMinor()) >= 0;
+    }
+
+    /**
+     * Check if the Runtime Version meets the maximum compatible version requirement. This implies
+     * that the runtime version is equal to or older than the version.
+     *
+     * <p> The compatible version is comprised of the major and minor version numbers. The patch
+     * number is ignored.
+     *
+     * @param version The maximum compatible version required
+     * @return True if the Runtime version meets the maximum version requirement and False
+     * otherwise.
+     */
+    public static boolean isMaximumCompatibleVersion(@NonNull Version version) {
+        return ExtensionVersion.getRuntimeVersion()
+                .compareTo(version.getMajor(), version.getMinor()) <= 0;
+    }
+
     abstract boolean isAdvancedExtenderSupportedInternal();
 
     /**
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
index c9dd6cd..992962c 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
@@ -18,46 +18,43 @@
 
 import android.os.Build;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.Quirk;
-import androidx.camera.extensions.ExtensionMode;
 import androidx.camera.extensions.internal.ExtensionVersion;
 import androidx.camera.extensions.internal.Version;
 
 
 /**
  * <p>QuirkSummary
- *     Bug Id: b/199408131, b/214130117
- *     Description: Quirk required to disable extension for some devices. An example is that
- *                  Pixel 5's availability check result of the basic extension interface should
- *                  be false, but it actually returns true. Therefore, force disable Basic
- *                  Extender capability on the device. Another example is that Motorola razr 5G's
- *                  availability check results of both back and front camera are true, but it
- *                  will cause the black preview screen issue. Therefore, force disable the bokeh
- *                  mode on the device.
- *     Device(s): Pixel 5, Motorola razr 5G
- *     @see androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator
+ * Bug Id: b/199408131, b/214130117, b/255956506
+ * Description: Quirk required to disable extension for some devices. An example is that
+ * Pixel 5's availability check result of the basic extension interface should
+ * be false, but it actually returns true. Therefore, force disable Basic
+ * Extender capability on the device. Another example is to ensure Motorola devices meet the
+ * minimum quality requirements for camera extensions support. Common issues encountered with
+ * Motorola extensions include: Bokeh not supported on some devices, SurfaceView not supported,
+ * Image doesn't appear after taking a picture, Preview is pauses after resuming.
+ * Device(s): Pixel 5, Motorola
+ *
+ * @see androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class ExtensionDisabledQuirk implements Quirk {
-    private boolean mIsAdvancedInterface = isAdvancedExtenderSupported();
+    private final boolean mIsAdvancedInterface = isAdvancedExtenderSupported();
 
     static boolean load() {
-        return isPixel5() || isMotoRazr5G() || isAdvancedExtenderSupported();
+        return isPixel5() || isMoto() || isAdvancedExtenderSupported();
     }
 
     /**
      * Checks whether extension should be disabled.
      */
-    public boolean shouldDisableExtension(@NonNull String cameraId,
-            @ExtensionMode.Mode int extensionMode) {
+    public boolean shouldDisableExtension() {
         if (isPixel5() && !mIsAdvancedInterface) {
             // 1. Disables Pixel 5's Basic Extender capability.
             return true;
-        } else if (isMotoRazr5G() && ("0".equals(cameraId) || "1".equals(cameraId)) && (
-                ExtensionMode.BOKEH == extensionMode)) {
-            // 2. Disables Motorola Razr 5G's bokeh capability.
+        } else if (isMoto() && ExtensionVersion.isMaximumCompatibleVersion(Version.VERSION_1_1)) {
+            // 2. Disables Motorola extensions capability for version 1.1 and older.
             return true;
         }
 
@@ -68,14 +65,12 @@
         return "google".equalsIgnoreCase(Build.BRAND) && "redfin".equalsIgnoreCase(Build.DEVICE);
     }
 
-    private static boolean isMotoRazr5G() {
-        return "motorola".equalsIgnoreCase(Build.BRAND) && "smith".equalsIgnoreCase(Build.DEVICE);
+    private static boolean isMoto() {
+        return "motorola".equalsIgnoreCase(Build.BRAND);
     }
 
     private static boolean isAdvancedExtenderSupported() {
-        if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_2) < 0) {
-            return false;
-        }
-        return ExtensionVersion.isAdvancedExtenderSupported();
+        return ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_2)
+                && ExtensionVersion.isAdvancedExtenderSupported();
     }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
index 3b9bcd1..166eabf 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
@@ -16,9 +16,7 @@
 
 package androidx.camera.extensions.internal.compat.workaround;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
-import androidx.camera.extensions.ExtensionMode;
 import androidx.camera.extensions.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.extensions.internal.compat.quirk.ExtensionDisabledQuirk;
 
@@ -40,8 +38,7 @@
     /**
      * Checks whether extension should be disabled.
      */
-    public boolean shouldDisableExtension(@NonNull String cameraId,
-            @ExtensionMode.Mode int extensionMode) {
-        return mQuirk != null && mQuirk.shouldDisableExtension(cameraId, extensionMode);
+    public boolean shouldDisableExtension() {
+        return mQuirk != null && mQuirk.shouldDisableExtension();
     }
 }
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMaximumCompatibleTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMaximumCompatibleTest.kt
new file mode 100644
index 0000000..b21dea5
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMaximumCompatibleTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.internal
+
+import android.os.Build
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.resetSingleton
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.setTestApiVersion
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+    minSdk = Build.VERSION_CODES.LOLLIPOP,
+    instrumentedPackages = arrayOf("androidx.camera.extensions.internal")
+)
+class ExtensionVersionMaximumCompatibleTest(private val config: TestConfig) {
+
+    @Before
+    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
+    fun setUp() {
+        val field = VersionName::class.java.getDeclaredField("CURRENT")
+        field.isAccessible = true
+        field[null] = VersionName(config.targetVersion)
+    }
+
+    @After
+    fun tearDown() {
+        resetSingleton(ExtensionVersion::class.java, "sExtensionVersion")
+    }
+
+    @Test
+    fun isMaximumCompatibleVersion() {
+        setTestApiVersion(config.targetVersion)
+
+        val version = Version.parse(config.maximumCompatibleVersion)!!
+        assertThat(ExtensionVersion.isMaximumCompatibleVersion(version))
+            .isEqualTo(config.expectedResult)
+    }
+
+    data class TestConfig(
+        val targetVersion: String,
+        val maximumCompatibleVersion: String,
+        val expectedResult: Boolean
+    )
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+        fun createTestSet(): List<TestConfig> {
+            return listOf(
+                TestConfig("1.1.0", "1.1.0", true),
+                TestConfig("1.1.0", "1.2.0", true),
+                TestConfig("1.1.0", "1.0.0", false),
+                TestConfig("1.1.0", "0.9.0", false),
+
+                // Test to ensure the patch version is ignored
+                TestConfig("1.2.1", "1.2.0", true),
+                TestConfig("1.2.0", "1.2.1", true),
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMinimumCompatibleTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMinimumCompatibleTest.kt
new file mode 100644
index 0000000..d92c03db1
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/ExtensionVersionMinimumCompatibleTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.internal
+
+import android.os.Build
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.resetSingleton
+import androidx.camera.extensions.internal.util.ExtensionsTestUtil.setTestApiVersion
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+    minSdk = Build.VERSION_CODES.LOLLIPOP,
+    instrumentedPackages = arrayOf("androidx.camera.extensions.internal")
+)
+class ExtensionVersionMinimumCompatibleTest(private val config: TestConfig) {
+
+    @Before
+    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
+    fun setUp() {
+        val field = VersionName::class.java.getDeclaredField("CURRENT")
+        field.isAccessible = true
+        field[null] = VersionName(config.targetVersion)
+    }
+
+    @After
+    fun tearDown() {
+        resetSingleton(ExtensionVersion::class.java, "sExtensionVersion")
+    }
+
+    @Test
+    fun isMinimumCompatibleVersion() {
+        setTestApiVersion(config.targetVersion)
+        val version = Version.parse(config.minimumCompatibleVersion)!!
+        assertThat(ExtensionVersion.isMinimumCompatibleVersion(version))
+            .isEqualTo(config.expectedResult)
+    }
+
+    data class TestConfig(
+        val targetVersion: String,
+        val minimumCompatibleVersion: String,
+        val expectedResult: Boolean
+    )
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+        fun createTestSet(): List<TestConfig> {
+            return listOf(
+                TestConfig("1.1.0", "1.1.0", true),
+                TestConfig("1.1.0", "1.0.0", true),
+                TestConfig("1.1.0", "1.2.0", false),
+
+                // Test to ensure the patch version is ignored
+                TestConfig("1.1.1", "1.1.0", true),
+                TestConfig("1.1.0", "1.1.1", true),
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
index a70923e..d34b6c6 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
@@ -17,7 +17,6 @@
 package androidx.camera.extensions.internal.compat.workaround
 
 import android.os.Build
-import androidx.camera.extensions.ExtensionMode
 import androidx.camera.extensions.internal.ExtensionVersion
 import androidx.camera.extensions.internal.util.ExtensionsTestUtil.resetSingleton
 import androidx.camera.extensions.internal.util.ExtensionsTestUtil.setTestApiVersionAndAdvancedExtender
@@ -41,7 +40,7 @@
 
     @Before
     fun setUp() {
-        setTestApiVersionAndAdvancedExtender("1.2.0", config.isAdvancedInterface)
+        setTestApiVersionAndAdvancedExtender(config.version, config.isAdvancedInterface)
     }
 
     @After
@@ -56,19 +55,13 @@
         ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", config.device)
 
         val validator = ExtensionDisabledValidator()
-        assertThat(
-            validator.shouldDisableExtension(
-                config.cameraId,
-                config.extensionMode
-            )
-        ).isEqualTo(config.shouldDisableExtension)
+        assertThat(validator.shouldDisableExtension()).isEqualTo(config.shouldDisableExtension)
     }
 
     class TestConfig(
         val brand: String,
         val device: String,
-        val cameraId: String,
-        val extensionMode: Int,
+        val version: String,
         val isAdvancedInterface: Boolean,
         val shouldDisableExtension: Boolean
     )
@@ -79,36 +72,24 @@
         fun createTestSet(): List<TestConfig> {
             return listOf(
                 // Pixel 5 extension capability is disabled on basic extender
-                TestConfig("Google", "Redfin", "0", ExtensionMode.BOKEH, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.HDR, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.NIGHT, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.FACE_RETOUCH, false, true),
-                TestConfig("Google", "Redfin", "0", ExtensionMode.AUTO, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.BOKEH, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.HDR, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.NIGHT, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.FACE_RETOUCH, false, true),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.AUTO, false, true),
+                TestConfig("Google", "Redfin", "1.2.0", false, true),
 
                 // Pixel 5 extension capability is enabled on advanced extender
-                TestConfig("Google", "Redfin", "0", ExtensionMode.NIGHT, true, false),
-                TestConfig("Google", "Redfin", "1", ExtensionMode.NIGHT, true, false),
+                TestConfig("Google", "Redfin", "1.2.0", true, false),
 
-                // Motorola Razr 5G bokeh mode is disabled. Other extension modes should still work.
-                TestConfig("Motorola", "Smith", "0", ExtensionMode.BOKEH, false, true),
-                TestConfig("Motorola", "Smith", "0", ExtensionMode.HDR, false, false),
-                TestConfig("Motorola", "Smith", "1", ExtensionMode.BOKEH, false, true),
-                TestConfig("Motorola", "Smith", "1", ExtensionMode.HDR, false, false),
-                TestConfig("Motorola", "Smith", "2", ExtensionMode.BOKEH, false, false),
-                TestConfig("Motorola", "Smith", "2", ExtensionMode.HDR, false, false),
+                // All Motorola devices should be disabled for version 1.1.0 and older.
+                TestConfig("Motorola", "Smith", "1.1.0", false, true),
+                TestConfig("Motorola", "Hawaii P", "1.1.0", false, true),
+
+                // Make sure Motorola device would still be enabled for newer versions
+                // Motorola doesn't support this today but making sure there is a path to enable
+                TestConfig("Motorola", "Hawaii P", "1.2.0", false, false),
 
                 // Other cases should be kept normal.
-                TestConfig("", "", "0", ExtensionMode.BOKEH, false, false),
-                TestConfig("", "", "1", ExtensionMode.BOKEH, false, false),
+                TestConfig("", "", "1.2.0", false, false),
 
                 // Advanced extender is enabled for all devices
-                TestConfig("", "", "0", ExtensionMode.BOKEH, true, false),
-                TestConfig("", "", "1", ExtensionMode.BOKEH, true, false),
+                TestConfig("", "", "1.2.0", true, false),
             )
         }
     }
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
index 7cdee86..5fbc9a3 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -39,16 +40,13 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 28)
-class AdvancedExtenderValidationTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
-    private val validation = AdvancedExtenderValidation(cameraId, extensionMode)
+class AdvancedExtenderValidationTest(config: CameraIdExtensionModePair) {
+    private val validation = AdvancedExtenderValidation(config.cameraId, config.extensionMode)
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt
index 31fb57d..168e040 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/BindUnbindUseCasesStressTest.kt
@@ -34,6 +34,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.VERIFICATION_TARGET_IMAGE_ANALYSIS
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.VERIFICATION_TARGET_IMAGE_CAPTURE
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.VERIFICATION_TARGET_PREVIEW
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -67,10 +68,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class BindUnbindUseCasesStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class BindUnbindUseCasesStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -96,6 +94,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -133,8 +132,8 @@
         @JvmField val stressTest = StressTestRule()
 
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt
index 1475998..d294dbc 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureExtenderValidationTest.kt
@@ -31,6 +31,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.launchCameraExtensionsActivity
 import androidx.camera.integration.extensions.util.HOME_TIMEOUT_MS
 import androidx.camera.integration.extensions.util.waitForPreviewViewStreaming
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -60,10 +61,7 @@
 @SmallTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class ImageCaptureExtenderValidationTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class ImageCaptureExtenderValidationTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -86,6 +84,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -119,8 +118,8 @@
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
@@ -133,8 +132,8 @@
         // Creates the ImageCaptureExtenderImpl to retrieve the target format/resolutions pair list
         // from vendor library for the target effect mode.
         val impl = CameraXExtensionsTestUtil.createImageCaptureExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
 
@@ -149,8 +148,8 @@
         // Creates the ImageCaptureExtenderImpl to check that onPresetSession() returns null when
         // API level is older than 28.
         val impl = CameraXExtensionsTestUtil.createImageCaptureExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
         assertThat(impl.onPresetSession()).isNull()
@@ -166,7 +165,7 @@
         // the getEstimatedCaptureLatencyRange function.
         val latencyInfo = extensionsManager.getEstimatedCaptureLatencyRange(
             baseCameraSelector,
-            extensionMode
+            config.extensionMode
         )
 
         // Calls bind to lifecycle to get the selected camera
@@ -179,7 +178,7 @@
 
         // Creates ImageCaptureExtenderImpl directly to retrieve the capture latency range info
         val impl = CameraXExtensionsTestUtil.createImageCaptureExtenderImpl(
-            extensionMode,
+            config.extensionMode,
             cameraId,
             characteristics
         )
@@ -201,7 +200,10 @@
             setOrientationNatural()
         }
 
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(
+            config.cameraId,
+            config.extensionMode
+        )
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt
index 9cf35f4..941ad66 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ImageCaptureTest.kt
@@ -25,6 +25,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.launchCameraExtensionsActivity
 import androidx.camera.integration.extensions.util.HOME_TIMEOUT_MS
 import androidx.camera.integration.extensions.util.takePictureAndWaitForImageSavedIdle
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraUtil.PreTestCameraIdList
@@ -48,7 +49,7 @@
  */
 @LargeTest
 @RunWith(Parameterized::class)
-class ImageCaptureTest(private val cameraId: String, private val extensionMode: Int) {
+class ImageCaptureTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -62,7 +63,7 @@
     private val context = ApplicationProvider.getApplicationContext<Context>()
 
     companion object {
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -87,7 +88,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
-        assumeExtensionModeSupported(extensionsManager, cameraId, extensionMode)
+        assumeExtensionModeSupported(extensionsManager, config.cameraId, config.extensionMode)
     }
 
     @After
@@ -114,7 +115,7 @@
      */
     @Test
     fun takePictureWithExtensionMode() {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt
index 51c1624..04404e4 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/LifecycleStatusChangeStressTest.kt
@@ -28,6 +28,7 @@
 import androidx.camera.integration.extensions.util.takePictureAndWaitForImageSavedIdle
 import androidx.camera.integration.extensions.util.waitForPreviewViewIdle
 import androidx.camera.integration.extensions.util.waitForPreviewViewStreaming
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraUtil.PreTestCameraIdList
@@ -57,10 +58,7 @@
  */
 @LargeTest
 @RunWith(Parameterized::class)
-class LifecycleStatusChangeStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class LifecycleStatusChangeStressTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -74,7 +72,7 @@
     private val context = ApplicationProvider.getApplicationContext<Context>()
 
     companion object {
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -95,8 +93,8 @@
         // Checks whether the extension mode can be supported first before launching the activity.
         CameraXExtensionsTestUtil.assumeExtensionModeSupported(
             extensionsManager,
-            cameraId,
-            extensionMode
+            config.cameraId,
+            config.extensionMode
         )
 
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
@@ -147,7 +145,7 @@
         verificationTarget: Int,
         repeatCount: Int = CameraXExtensionsTestUtil.getStressTestRepeatingCount()
     ) {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
@@ -188,7 +186,7 @@
         verificationTarget: Int,
         repeatCount: Int = CameraXExtensionsTestUtil.getStressTestRepeatingCount()
     ) {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt
index d0a1279..44aa1be 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCameraStressTest.kt
@@ -27,6 +27,7 @@
 import androidx.camera.core.UseCase
 import androidx.camera.extensions.ExtensionsManager
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -56,10 +57,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class OpenCloseCameraStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class OpenCloseCameraStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -79,6 +77,7 @@
     @Before
     fun setUp(): Unit = runBlocking {
         assumeTrue(CameraXExtensionsTestUtil.isTargetDeviceAvailableForExtensions())
+        val (cameraId, extensionMode) = config
         cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
         extensionsManager = ExtensionsManager.getInstanceAsync(
             context,
@@ -125,8 +124,8 @@
         @JvmField val stressTest = StressTestRule()
 
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt
index 5dedbdf..902b8ad 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/OpenCloseCaptureSessionStressTest.kt
@@ -41,6 +41,7 @@
 import androidx.camera.extensions.ExtensionMode
 import androidx.camera.extensions.ExtensionsManager
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -69,10 +70,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class OpenCloseCaptureSessionStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class OpenCloseCaptureSessionStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -101,6 +99,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -175,7 +174,7 @@
                 val extensionEnabledCameraEventMonitorCameraSelector =
                     getExtensionsCameraEventMonitorCameraSelector(
                         extensionsManager,
-                        extensionMode,
+                        config.extensionMode,
                         baseCameraSelector
                     )
 
@@ -205,8 +204,8 @@
         @JvmField val stressTest = StressTestRule()
 
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
 
         /**
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt
index c0a25f0..9cf7c98 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewExtenderValidationTest.kt
@@ -29,6 +29,7 @@
 import androidx.camera.extensions.internal.ExtensionVersion
 import androidx.camera.extensions.internal.Version
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -53,10 +54,7 @@
 @SmallTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class PreviewExtenderValidationTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class PreviewExtenderValidationTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -79,6 +77,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -110,8 +109,8 @@
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
 
@@ -124,8 +123,8 @@
         // Creates the ImageCaptureExtenderImpl to retrieve the target format/resolutions pair list
         // from vendor library for the target effect mode.
         val impl = CameraXExtensionsTestUtil.createPreviewExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
 
@@ -140,8 +139,8 @@
         // Creates the ImageCaptureExtenderImpl to check that onPresetSession() returns null when
         // API level is older than 28.
         val impl = CameraXExtensionsTestUtil.createPreviewExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
         assertThat(impl.onPresetSession()).isNull()
@@ -150,8 +149,8 @@
     @Test
     fun returnCorrectProcessor() {
         val impl = CameraXExtensionsTestUtil.createPreviewExtenderImpl(
-            extensionMode,
-            cameraId,
+            config.extensionMode,
+            config.cameraId,
             cameraCharacteristics
         )
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt
index f96fbed..571c3bf 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.kt
@@ -40,6 +40,7 @@
 import androidx.camera.extensions.ExtensionMode
 import androidx.camera.extensions.ExtensionsManager
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -73,10 +74,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 21)
-class PreviewProcessorTimestampTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class PreviewProcessorTimestampTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -155,6 +153,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
+        val (cameraId, extensionMode) = config
         baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
 
@@ -191,7 +190,7 @@
             val timestampExtensionEnabledCameraSelector =
                 getTimestampExtensionEnabledCameraSelector(
                     extensionsManager,
-                    extensionMode,
+                    config.extensionMode,
                     baseCameraSelector
                 )
 
@@ -236,8 +235,8 @@
 
     companion object {
         @JvmStatic
-        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
-        val parameters: Collection<Array<Any>>
+        @get:Parameterized.Parameters(name = "config = {0}")
+        val parameters: Collection<CameraIdExtensionModePair>
             get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
 
         /**
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt
index 03371e2..8154c16 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewTest.kt
@@ -24,6 +24,7 @@
 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.launchCameraExtensionsActivity
 import androidx.camera.integration.extensions.util.HOME_TIMEOUT_MS
 import androidx.camera.integration.extensions.util.waitForPreviewViewStreaming
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -48,7 +49,7 @@
  */
 @LargeTest
 @RunWith(Parameterized::class)
-class PreviewTest(private val cameraId: String, private val extensionMode: Int) {
+class PreviewTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -60,7 +61,7 @@
     private lateinit var extensionsManager: ExtensionsManager
 
     companion object {
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -86,7 +87,7 @@
             cameraProvider
         )[10000, TimeUnit.MILLISECONDS]
 
-        assumeExtensionModeSupported(extensionsManager, cameraId, extensionMode)
+        assumeExtensionModeSupported(extensionsManager, config.cameraId, config.extensionMode)
     }
 
     @After
@@ -114,7 +115,7 @@
      */
     @Test
     fun previewWithExtensionModeCanEnterStreamingState() {
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
@@ -128,7 +129,7 @@
             ApplicationProvider.getApplicationContext(),
             extensionsManager,
             cameraId,
-            extensionMode
+            config.extensionMode
         )
         assumeTrue(
             "Cannot find next camera id that supports extensions mode($extensionsMode)",
@@ -140,8 +141,8 @@
      */
     @Test
     fun previewCanEnterStreamingStateAfterSwitchingCamera() {
-        assumeNextCameraIdExtensionModeSupported(cameraId, extensionMode)
-        val activityScenario = launchCameraExtensionsActivity(cameraId, extensionMode)
+        assumeNextCameraIdExtensionModeSupported(config.cameraId, config.extensionMode)
+        val activityScenario = launchCameraExtensionsActivity(config.cameraId, config.extensionMode)
 
         with(activityScenario) {
             use {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
index 2ba7a62..f689467 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsActivityTest.kt
@@ -30,6 +30,7 @@
 import androidx.camera.integration.extensions.util.waitForImageSavedIdle
 import androidx.camera.integration.extensions.util.waitForPreviewIdle
 import androidx.camera.integration.extensions.utils.Camera2ExtensionsUtil.isCamera2ExtensionModeSupported
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CoreAppTestUtil
 import androidx.camera.testing.LabTestRule
@@ -58,10 +59,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsActivityTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsActivityTest(private val config: CameraIdExtensionModePair) {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @get:Rule
@@ -101,7 +99,7 @@
         @ClassRule
         @JvmField val stressTest = StressTestRule()
 
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -110,8 +108,7 @@
     @Test
     fun checkPreviewUpdated() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -125,8 +122,7 @@
     @Test
     fun canCaptureSingleImage() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -140,8 +136,7 @@
     @Test
     fun checkPreviewUpdated_afterPauseResume() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -162,8 +157,7 @@
     @Test
     fun canCaptureImage_afterPauseResume() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+           config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -187,8 +181,7 @@
     @Test
     fun canCaptureMultipleImages() {
         val activityScenario = launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-            cameraId,
-            extensionMode
+            config
         )
         with(activityScenario) { // Launches activity
             use { // Ensures that ActivityScenario is cleaned up properly
@@ -201,9 +194,9 @@
     }
 
     private fun launchCamera2ExtensionsActivityAndWaitForCaptureSessionConfigured(
-        cameraId: String,
-        extensionMode: Int
+        config: CameraIdExtensionModePair
     ): ActivityScenario<Camera2ExtensionsActivity> {
+        val (cameraId, extensionMode) = config
         val context = ApplicationProvider.getApplicationContext<Context>()
         assumeTrue(isCamera2ExtensionModeSupported(context, cameraId, extensionMode))
         val intent = context.packageManager
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt
index c42d674..8c9f89d 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsOpenCloseStressTest.kt
@@ -21,6 +21,7 @@
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.assertCanOpenExtensionsSession
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.StressTestRule
 import androidx.test.core.app.ApplicationProvider
@@ -38,10 +39,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsOpenCloseStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsOpenCloseStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -51,7 +49,7 @@
         @ClassRule
         @JvmField val stressTest = StressTestRule()
 
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -65,6 +63,7 @@
     }
     @Test
     fun openCloseExtensionSession(): Unit = runBlocking {
+        val (cameraId, extensionMode) = config
         repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
             assertCanOpenExtensionsSession(cameraManager, cameraId, extensionMode)
         }
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt
index 9756e72..bc0dfdd 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchCameraStressTest.kt
@@ -22,6 +22,7 @@
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.assertCanOpenExtensionsSession
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.findNextSupportedCameraId
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.StressTestRule
 import androidx.test.core.app.ApplicationProvider
@@ -39,10 +40,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsSwitchCameraStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsSwitchCameraStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -52,7 +50,7 @@
         @ClassRule
         @JvmField val stressTest = StressTestRule()
 
-        @Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        @Parameterized.Parameters(name = "config = {0}")
         @JvmStatic
         fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
     }
@@ -67,6 +65,7 @@
 
     @Test
     fun switchCameras(): Unit = runBlocking {
+        val (cameraId, extensionMode) = config
         val nextCameraId = findNextSupportedCameraId(context, cameraId, extensionMode)
         assumeTrue(nextCameraId != null)
         repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt
index 47b7114..6cffd8f 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsSwitchModeStressTest.kt
@@ -22,6 +22,7 @@
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.EXTENSION_NOT_FOUND
 import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.findNextEffectMode
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.StressTestRule
 import androidx.test.core.app.ApplicationProvider
@@ -39,10 +40,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 @SdkSuppress(minSdkVersion = 31)
-class Camera2ExtensionsSwitchModeStressTest(
-    private val cameraId: String,
-    private val extensionMode: Int
-) {
+class Camera2ExtensionsSwitchModeStressTest(private val config: CameraIdExtensionModePair) {
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
         CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
@@ -68,6 +66,7 @@
 
     @Test
     fun switchModes(): Unit = runBlocking {
+        val (cameraId, extensionMode) = config
         val nextMode = findNextEffectMode(context, cameraId, extensionMode)
         assumeTrue(nextMode != EXTENSION_NOT_FOUND)
         repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
index 5a32a44..18051b9 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
@@ -37,6 +37,7 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.integration.extensions.utils.Camera2ExtensionsUtil.AVAILABLE_CAMERA2_EXTENSION_MODES
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.LabTestRule
 import androidx.camera.testing.SurfaceTextureProvider
@@ -73,12 +74,10 @@
      * Gets a list of all camera id and extension mode combinations.
      */
     @JvmStatic
-    fun getAllCameraIdExtensionModeCombinations(): List<Array<Any>> =
-        arrayListOf<Array<Any>>().apply {
-            CameraUtil.getBackwardCompatibleCameraIdListOrThrow().forEach { cameraId ->
-                AVAILABLE_CAMERA2_EXTENSION_MODES.forEach { mode ->
-                    add(arrayOf(cameraId, mode))
-                }
+    fun getAllCameraIdExtensionModeCombinations(): List<CameraIdExtensionModePair> =
+        CameraUtil.getBackwardCompatibleCameraIdListOrThrow().flatMap { cameraId ->
+            AVAILABLE_CAMERA2_EXTENSION_MODES.map { extensionMode ->
+                CameraIdExtensionModePair(cameraId, extensionMode)
             }
         }
 
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
index 70f7440..09a7be9 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
@@ -49,6 +49,7 @@
 import androidx.camera.extensions.internal.Version
 import androidx.camera.integration.extensions.CameraExtensionsActivity
 import androidx.camera.integration.extensions.IntentExtraKey
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil.createCameraSelectorById
 import androidx.camera.integration.extensions.utils.ExtensionModeUtil
 import androidx.camera.integration.extensions.utils.ExtensionModeUtil.AVAILABLE_EXTENSION_MODES
@@ -66,12 +67,10 @@
      * Gets a list of all camera id and extension mode combinations.
      */
     @JvmStatic
-    fun getAllCameraIdExtensionModeCombinations(): List<Array<Any>> =
-        arrayListOf<Array<Any>>().apply {
-            CameraUtil.getBackwardCompatibleCameraIdListOrThrow().forEach { cameraId ->
-                ExtensionModeUtil.AVAILABLE_EXTENSION_MODES.forEach { mode ->
-                    add(arrayOf(cameraId, mode))
-                }
+    fun getAllCameraIdExtensionModeCombinations(): List<CameraIdExtensionModePair> =
+        CameraUtil.getBackwardCompatibleCameraIdListOrThrow().flatMap { cameraId ->
+            ExtensionModeUtil.AVAILABLE_EXTENSION_MODES.map { extensionMode ->
+                CameraIdExtensionModePair(cameraId, extensionMode)
             }
         }
 
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/CameraIdExtensionModePair.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/CameraIdExtensionModePair.kt
new file mode 100644
index 0000000..629e678
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/CameraIdExtensionModePair.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.utils
+
+/**
+ * Represents a pair of Camera ID and Camera Extension Mode type
+ */
+data class CameraIdExtensionModePair(val cameraId: String, val extensionMode: Int)