[Extensions] Set the sessionType in SessionConfig from the v1.4 OEM implementation.

- Update Client extensions-interface verion to v1.4

- For Advanced Extender, get the sessionType information from OEMs
  and set it to the SessionConfig.

- For Basic Extender, set sessionType to REGULAR

Bug: 306663504
Test: AdvancedSessionProcessorTest
Change-Id: I3c7c31f8a008833239a7b48f1814bc81628bf608
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
index 12bf312..50f5c4a 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
@@ -69,6 +69,15 @@
     }
 
     /**
+     * Sets the session type for the session.
+     */
+    @NonNull
+    public Camera2SessionConfigImplBuilder setSessionType(int sessionType) {
+        mSessionType = sessionType;
+        return this;
+    }
+
+    /**
      * Gets the session template id.
      */
     public int getSessionTemplateId() {
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
index d780b54..28563e3 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
@@ -41,6 +41,7 @@
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
 import android.hardware.camera2.TotalCaptureResult
+import android.hardware.camera2.params.SessionConfiguration
 import android.media.ImageReader
 import android.media.ImageWriter
 import android.os.Build
@@ -50,6 +51,8 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.camera2.impl.Camera2ImplConfig
+import androidx.camera.camera2.internal.Camera2CameraInfoImpl
+import androidx.camera.camera2.internal.compat.CameraManagerCompat
 import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat
 import androidx.camera.camera2.interop.Camera2CameraInfo
 import androidx.camera.core.CameraFilter
@@ -66,6 +69,7 @@
 import androidx.camera.core.impl.ExtendedCameraConfigProviderStore
 import androidx.camera.core.impl.Identifier
 import androidx.camera.core.impl.MutableOptionsBundle
+import androidx.camera.core.impl.OutputSurface
 import androidx.camera.core.impl.SessionProcessor
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl
@@ -387,6 +391,51 @@
             .isEqualTo(physicalCameraId)
     }
 
+    private fun createOutputSurface(width: Int, height: Int, format: Int): OutputSurface {
+        val captureImageReader = ImageReader.newInstance(width, height, format, 1);
+        return OutputSurface.create(captureImageReader.surface, Size(width, height), format)
+    }
+
+    @Test
+    fun canSetSessionTypeFromOemImpl() {
+        assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
+        // 1. Arrange.
+        val sessionTypeToVerify = 4;
+        val fakeSessionProcessImpl = FakeSessionProcessImpl()
+        fakeSessionProcessImpl.sessionType = sessionTypeToVerify;
+        val advancedSessionProcessor = AdvancedSessionProcessor(fakeSessionProcessImpl,
+            emptyList(), context)
+        val fakeCameraInfo = Camera2CameraInfoImpl("0", CameraManagerCompat.from(context));
+        val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888);
+        val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG);
+
+        // 2. Act.
+        val sessionConfig = advancedSessionProcessor
+            .initSession(fakeCameraInfo, previewOutputSurface, imageCaptureSurface, null);
+
+        // 3. Assert.
+        assertThat(sessionConfig.sessionType).isEqualTo(sessionTypeToVerify)
+    }
+
+    @Test
+    fun defaultSessionType() {
+        // 1. Arrange.
+        val fakeSessionProcessImpl = FakeSessionProcessImpl()
+        fakeSessionProcessImpl.sessionType = -1;
+        val advancedSessionProcessor = AdvancedSessionProcessor(fakeSessionProcessImpl,
+            emptyList(), context)
+        val fakeCameraInfo = Camera2CameraInfoImpl("0", CameraManagerCompat.from(context));
+        val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888);
+        val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG);
+
+        // 2. Act.
+        val sessionConfig = advancedSessionProcessor
+            .initSession(fakeCameraInfo, previewOutputSurface, imageCaptureSurface, null);
+
+        // 3. Assert.
+        assertThat(sessionConfig.sessionType).isEqualTo(SessionConfiguration.SESSION_REGULAR)
+    }
+
     /**
      * Verify if the given use cases have expected output.
      * 1) Preview frame is received
@@ -474,6 +523,7 @@
  * to allow tests to access the [RequestProcessorImpl] to receive the Image for
  * [ImageReaderOutputConfigImpl].
  */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 class FakeSessionProcessImpl(
     var previewConfigBlock: (OutputSurfaceImpl) -> Camera2OutputConfigImpl = { outputSurfaceImpl ->
         Camera2OutputConfigImplBuilder
@@ -497,6 +547,7 @@
     private var startTriggerParametersDeferred =
         CompletableDeferred<MutableMap<CaptureRequest.Key<*>, Any>>()
 
+    var sessionType: Int = -1;
     override fun initSession(
         cameraId: String,
         cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
@@ -522,6 +573,10 @@
             addOutputConfig(captureOutputConfig)
             analysisOutputConfig?.let { addOutputConfig(it) }
         }
+
+        if (ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)) {
+            sessionBuilder.setSessionType(sessionType);
+        }
         return sessionBuilder.build()
     }
 
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
index b082271..c26ed45 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
@@ -25,6 +25,7 @@
 import android.hardware.camera2.TotalCaptureResult
 import android.hardware.camera2.params.SessionConfiguration
 import android.media.Image
+import android.media.ImageReader
 import android.media.ImageWriter
 import android.os.Build
 import android.os.Handler
@@ -33,6 +34,8 @@
 import android.util.Size
 import android.view.Surface
 import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.internal.Camera2CameraInfoImpl
+import androidx.camera.camera2.internal.compat.CameraManagerCompat
 import androidx.camera.camera2.interop.Camera2CameraInfo
 import androidx.camera.camera2.interop.Camera2Interop
 import androidx.camera.core.Camera
@@ -207,6 +210,61 @@
         )
     }
 
+    private fun createOutputSurface(width: Int, height: Int, format: Int): OutputSurface {
+        val captureImageReader = ImageReader.newInstance(width, height, format, 1);
+        return OutputSurface.create(captureImageReader.surface, Size(width, height), format)
+    }
+
+    @Test
+    fun canSetSessionTypeFromOem() {
+        assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4));
+        val sessionTypeToVerify = 4
+        fakeCaptureExtenderImpl.sessionType = sessionTypeToVerify
+        fakePreviewExtenderImpl.sessionType = sessionTypeToVerify
+
+        val fakeCameraInfo = Camera2CameraInfoImpl("0", CameraManagerCompat.from(context));
+        val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888);
+        val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG);
+
+        val sessionConfig = basicExtenderSessionProcessor.initSession(
+            fakeCameraInfo, previewOutputSurface, imageCaptureSurface, null)
+
+        assertThat(sessionConfig.sessionType).isEqualTo(sessionTypeToVerify)
+    }
+
+    @Test
+    fun setDifferentSessionTypes_throwException() {
+        assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4));
+        fakeCaptureExtenderImpl.sessionType = 2
+        fakePreviewExtenderImpl.sessionType = 3
+
+        val fakeCameraInfo = Camera2CameraInfoImpl("0", CameraManagerCompat.from(context));
+        val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888);
+        val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG);
+
+        assertThrows<IllegalArgumentException> {
+             basicExtenderSessionProcessor.initSession(
+                fakeCameraInfo, previewOutputSurface, imageCaptureSurface, null
+            )
+        }
+    }
+
+    @Test
+    fun defaultSessionType() {
+        assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4));
+        fakeCaptureExtenderImpl.sessionType = -1
+        fakePreviewExtenderImpl.sessionType = -1
+
+        val fakeCameraInfo = Camera2CameraInfoImpl("0", CameraManagerCompat.from(context));
+        val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888);
+        val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG);
+
+        val sessionConfig = basicExtenderSessionProcessor.initSession(
+            fakeCameraInfo, previewOutputSurface, imageCaptureSurface, null)
+
+        assertThat(sessionConfig.sessionType).isEqualTo(SessionConfiguration.SESSION_REGULAR)
+    }
+
     @Test
     fun imageCaptureError(): Unit = runBlocking {
         assumeTrue(hasCaptureProcessor)
@@ -800,6 +858,7 @@
     ) : PreviewExtenderImpl, FakeExtenderStateListener() {
         var fakePreviewImageProcessorImpl: FakePreviewImageProcessorImpl? = null
         var fakeRequestUpdateProcessor: FakeRequestUpdateProcessor? = null
+        var sessionType: Int = -1
 
         init {
             when (processorType) {
@@ -843,6 +902,10 @@
             _captureStage = captureStage
         }
 
+        override fun onSessionType(): Int {
+            return sessionType
+        }
+
         override fun getProcessorType() = processorType
         override fun getProcessor() =
             when (processorType) {
@@ -869,6 +932,7 @@
                 null
             }
         }
+        var sessionType = -1
 
         override fun isExtensionAvailable(
             cameraId: String,
@@ -909,7 +973,7 @@
         }
 
         override fun onSessionType(): Int {
-            return SessionConfiguration.SESSION_REGULAR
+            return sessionType
         }
 
         override fun getSupportedPostviewResolutions(captureSize: Size):
@@ -922,7 +986,7 @@
         }
 
         override fun getRealtimeCaptureLatency(): Pair<Long, Long>? {
-            return null;
+            return null
         }
 
         override fun isPostviewAvailable(): Boolean {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ClientVersion.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ClientVersion.java
index 931d754..d4c9a0b 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ClientVersion.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ClientVersion.java
@@ -27,7 +27,7 @@
 public class ClientVersion {
     // Current version of vendor library implementation that the CameraX extension supports. This
     // needs to be increased along with the version of vendor library interface.
-    private static ClientVersion sCurrent = new ClientVersion("1.3.0");
+    private static ClientVersion sCurrent = new ClientVersion("1.4.0");
 
     @NonNull
     public static ClientVersion getCurrentVersion() {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
index 85c344d..c2adbf76e 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.media.Image;
 import android.util.Pair;
 import android.util.Size;
@@ -113,6 +114,14 @@
         }
         camera2SessionConfigBuilder
                 .setSessionTemplateId(sessionConfigImpl.getSessionTemplateId());
+        if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)
+                && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)) {
+            int sessionType = sessionConfigImpl.getSessionType();
+            if (sessionType == -1) { // -1 means using default value
+                sessionType = SessionConfiguration.SESSION_REGULAR;
+            }
+            camera2SessionConfigBuilder.setSessionType(sessionType);
+        }
         return camera2SessionConfigBuilder.build();
     }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
index 0a8ca6c..bb1b67d 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.util.Pair;
 
 import androidx.annotation.GuardedBy;
@@ -186,6 +187,19 @@
                         .addOutputConfig(mCaptureOutputConfig)
                         .setSessionTemplateId(CameraDevice.TEMPLATE_PREVIEW);
 
+        if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)
+                && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)) {
+            int previewSessionType = mPreviewExtenderImpl.onSessionType();
+            int captureSessionType = mImageCaptureExtenderImpl.onSessionType();
+            Preconditions.checkArgument(previewSessionType == captureSessionType,
+                    "Needs same session type in both PreviewExtenderImpl and "
+                            + "ImageCaptureExtenderImpl");
+            if (previewSessionType == -1) { // -1 means using default value
+                previewSessionType = SessionConfiguration.SESSION_REGULAR;
+            }
+            builder.setSessionType(previewSessionType);
+        }
+
         if (mAnalysisOutputConfig != null) {
             builder.addOutputConfig(mAnalysisOutputConfig);
         }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfig.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfig.java
index 4ba35a5..24b0eec2 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfig.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfig.java
@@ -17,6 +17,7 @@
 package androidx.camera.extensions.internal.sessionprocessor;
 
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -47,4 +48,11 @@
      * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}.
      */
     int getSessionTemplateId();
+
+    /**
+     * Gets the session type
+     */
+    default int getSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfigBuilder.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfigBuilder.java
index 57baff7..08950dc 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfigBuilder.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfigBuilder.java
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -35,6 +36,7 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 class Camera2SessionConfigBuilder {
     private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW;
+    private int mSessionType = SessionConfiguration.SESSION_REGULAR;
     private Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>();
     private List<Camera2OutputConfig> mCamera2OutputConfigs = new ArrayList<>();
 
@@ -71,6 +73,15 @@
     }
 
     /**
+     * Sets the session type for the session.
+     */
+    @NonNull
+    Camera2SessionConfigBuilder setSessionType(int sessionType) {
+        mSessionType = sessionType;
+        return this;
+    }
+
+    /**
      * Gets the session template id.
      */
     int getSessionTemplateId() {
@@ -98,18 +109,22 @@
      */
     @NonNull
     Camera2SessionConfig build() {
-        return new SessionConfigImpl(mSessionTemplateId, mSessionParameters, mCamera2OutputConfigs);
+        return new SessionConfigImpl(
+                mSessionTemplateId, mSessionType, mSessionParameters, mCamera2OutputConfigs);
     }
 
     private static class SessionConfigImpl implements Camera2SessionConfig {
         private final int mSessionTemplateId;
+        private final int mSessionType;
         private final Map<CaptureRequest.Key<?>, Object> mSessionParameters;
         private final List<Camera2OutputConfig> mCamera2OutputConfigs;
 
         SessionConfigImpl(int sessionTemplateId,
+                int sessionType,
                 Map<CaptureRequest.Key<?>, Object> sessionParameters,
                 List<Camera2OutputConfig> camera2OutputConfigs) {
             mSessionTemplateId = sessionTemplateId;
+            mSessionType = sessionType;
             mSessionParameters = sessionParameters;
             mCamera2OutputConfigs = camera2OutputConfigs;
         }
@@ -130,5 +145,10 @@
         public int getSessionTemplateId() {
             return mSessionTemplateId;
         }
+
+        @Override
+        public int getSessionType() {
+            return mSessionType;
+        }
     }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java
index f02f5fa..d57f6a9 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java
@@ -220,6 +220,7 @@
         }
         sessionConfigBuilder.setImplementationOptions(camera2ConfigurationBuilder.build());
         sessionConfigBuilder.setTemplateType(camera2SessionConfig.getSessionTemplateId());
+        sessionConfigBuilder.setSessionType(camera2SessionConfig.getSessionType());
 
         mImageReaderHandlerThread = new HandlerThread(
                 CameraXThreads.TAG + "extensions_image_reader");
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
index d1bb69a..5843dc0 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
@@ -69,6 +69,15 @@
     }
 
     /**
+     * Sets the session type for the session.
+     */
+    @NonNull
+    public Camera2SessionConfigImplBuilder setSessionType(int sessionType) {
+        mSessionType = sessionType;
+        return this;
+    }
+
+    /**
      * Gets the session template id.
      */
     public int getSessionTemplateId() {