Merge "Use java Jpeg encoding instead of native codes / fix some issues." into main am: a0df61244f am: 112a2cec91

Original change: https://android-review.googlesource.com/c/platform/frameworks/ex/+/2462898

Change-Id: I042a6bf4f9505a7349b0133b3db4bf0d8b65b5e2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/camera2/extensions/advancedSample/Android.bp b/camera2/extensions/advancedSample/Android.bp
index d51f6e6..8efae6f 100644
--- a/camera2/extensions/advancedSample/Android.bp
+++ b/camera2/extensions/advancedSample/Android.bp
@@ -23,9 +23,11 @@
         "androidx.annotation_annotation",
         "androidx.exifinterface_exifinterface-nodeps"
     ],
+    exclude_kotlinc_generated_files: true,
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     vendor: true,
+    jarjar_rules: "jarjar-rules.txt",
 }
 
 prebuilt_etc {
diff --git a/camera2/extensions/advancedSample/jarjar-rules.txt b/camera2/extensions/advancedSample/jarjar-rules.txt
new file mode 100644
index 0000000..7442cef
--- /dev/null
+++ b/camera2/extensions/advancedSample/jarjar-rules.txt
@@ -0,0 +1 @@
+rule kotlin.** androidx.camera.extensions.impl.advanced.@0
\ No newline at end of file
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index ccb0dac..bd60570 100755
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -113,4 +118,23 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index 100f665..0c4577a 100755
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index 2d26639..50c8040 100755
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -112,4 +117,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index bc3e48d..1f50174 100755
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 66c5839..ee777cf 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -113,4 +118,23 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index ff58862..1dc5ed7 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -91,4 +91,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
index 3eee146..f4719b8 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Surface;
 
 import java.util.Map;
@@ -46,6 +47,29 @@
     void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
 
     /**
+     * Informs the CaptureProcessorImpl where it should write the postview output to.
+     * This will only be invoked once if a valid postview surface was set.
+     *
+     * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface}
+     *                that the CaptureProcessorImpl should write data into.
+     * @since 1.4
+     */
+    void onPostviewOutputSurface(Surface surface);
+
+    /**
+     * Invoked when the Camera Framework changes the configured output resolution for
+     * still capture and postview.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input for still capture and postview to be at the specified resolutions.
+     *
+     * @param size for the surface for still capture.
+     * @param postviewSize for the surface for postview.
+     * @since 1.4
+     */
+    void onResolutionUpdate(Size size, Size postviewSize);
+
+    /**
      * Process a set images captured that were requested.
      *
      * <p> The result of the processing step should be written to the {@link Surface} that was
@@ -63,4 +87,30 @@
      */
     void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
             ProcessResultImpl resultCallback, Executor executor);
+
+    /**
+     * Process a set images captured that were requested for both postview and
+     * still capture.
+     *
+     * <p> This processing method will be called if a postview was requested, therefore the
+     * processed postview should be written to the
+     * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}.
+     * The final result of the processing step should be written to the {@link Surface} that was
+     * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available
+     * before the capture, it should be processed and written to the surface before
+     * the final capture is processed.
+     *
+     * @param results             The map of {@link ImageFormat#YUV_420_888} format images and
+     *                            metadata to process. The {@link Image} that are contained within
+     *                            the map will become invalid after this method completes, so no
+     *                            references to them should be kept.
+     * @param resultCallback      Capture result callback to be called once the capture result
+     *                            values of the processed image are ready.
+     * @param executor            The executor to run the callback on. If null then the callback
+     *                            will run on any arbitrary executor.
+     * @throws RuntimeException   if postview feature is not supported
+     * @since 1.4
+     */
+    void processWithPostview(Map<Integer, Pair<Image, TotalCaptureResult>> results,
+            ProcessResultImpl resultCallback, Executor executor);
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
index 2879568..3a88ab2 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
@@ -80,4 +80,21 @@
      * @return The request information to customize the session.
      */
     CaptureStageImpl onDisableSession();
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * initialized and must return a valid camera session type
+     * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType}
+     * to be used to configure camera capture session. Both the preview and the image capture
+     * extender must return the same session type value for a specific extension type. If there
+     * is inconsistency between the session type values from preview and image extenders, then
+     * the session configuration will fail.
+     *
+     *
+     * @since 1.4
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     */
+    int onSessionType();
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
index e3dc993..1f28572 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -29,7 +29,7 @@
  */
 public class ExtensionVersionImpl {
     private static final String TAG = "ExtenderVersionImpl";
-    private static final String VERSION = "1.3.0";
+    private static final String VERSION = "1.4.0";
 
     /**
      * @hide
@@ -71,4 +71,4 @@
     public boolean isAdvancedExtenderImplemented() {
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index f1191dc..f3fd2f3 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -112,4 +117,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index 0eb4a61..af48464 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 88bd105..70c1804 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -85,6 +85,21 @@
     List<Pair<Integer, Size[]>> getSupportedResolutions();
 
     /**
+     * Returns the customized supported postview resolutions for a still capture using
+     * its size.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device.
+     *
+     * @return the customized supported resolutions, or null to support all sizes retrieved from
+     *         {@link android.hardware.camera2.params.StreamConfigurationMap}.
+     * @since 1.4
+     */
+    List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize);
+
+    /**
      * Returns the estimated capture latency range in milliseconds for the target capture
      * resolution.
      *
@@ -159,4 +174,44 @@
      * @since 1.3
      */
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
+
+    /**
+     * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be
+     * called after the camera capture session is initialized and camera preview is enabled.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process}
+     * as the second pair field. Both first and second fields will be in milliseconds. The total
+     * still capture latency will be the sum of both the first and second values.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index c8ac978..6f0eaef 100755
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -112,4 +117,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index a5809f6..825994f 100755
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java
index d0e3605..0e15445 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java
@@ -25,7 +25,6 @@
 /**
  * Allows clients to receive information about the capture result values of processed frames.
  *
- * @since 1.3
  */
 @SuppressLint("UnknownNullness")
 public interface ProcessResultImpl {
@@ -40,6 +39,22 @@
      *                             must also be passed as part of this callback. Both Camera2 and
      *                             CameraX guarantee that those two settings and results are always
      *                             supported and applied by the corresponding framework.
+     * @since 1.3
      */
     void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result);
+
+    /**
+     * Capture progress callback that needs to be called when the process capture is
+     * ongoing and includes the estimated progress of the processing.
+     *
+     * <p>Extensions must ensure that they always call this callback with monotonically increasing
+     * values.</p>
+     *
+     * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+     * callback is expected to be called once when processing is done with value 100.</p>
+     *
+     * @param progress             Value between 0 and 100.
+     * @since 1.4
+     */
+    void onCaptureProcessProgressed(int progress);
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
index f5335e0..abcbf5f 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
@@ -124,6 +124,17 @@
     Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId);
 
     /**
+     * Returns supported output format/size map for postview image. OEM is required to support
+     * both JPEG and YUV_420_888 format output.
+     *
+     * <p>The surface created with this supported format/size could configure
+     * intermediate surfaces(YUV/RAW..) and write the output to the output surface.</p>
+     *
+     * @since 1.4
+     */
+    Map<Integer, List<Size>> getSupportedPostviewResolutions(Size captureSize);
+
+    /**
      * Returns supported output sizes for Image Analysis (YUV_420_888 format).
      *
      * <p>OEM can optionally support a YUV surface for ImageAnalysis along with Preview/ImageCapture
@@ -185,4 +196,24 @@
      * @since 1.3
      */
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
+
+    /**
+     * Advertise support for {@link SessionProcessorImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java
index 38b4d96..4a77d6a 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java
@@ -103,6 +103,11 @@
     }
 
     @Override
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(Size captureSize) {
+        return new HashMap<>();
+    }
+
+    @Override
     public List<Size> getSupportedYuvAnalysisResolutions(
             String cameraId) {
         return null;
@@ -148,14 +153,12 @@
         public Camera2SessionConfigImpl initSession(@NonNull String cameraId,
                 @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
                 @NonNull Context context,
-                @NonNull OutputSurfaceImpl previewSurfaceConfig,
-                @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig,
-                @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig) {
+                @NonNull OutputSurfaceConfigurationImpl surfaceConfigs) {
 
             Log.d(TAG, "initSession cameraId=" + cameraId);
 
-            mPreviewOutputSurfaceConfig = previewSurfaceConfig;
-            mCaptureOutputSurfaceConfig = imageCaptureSurfaceConfig;
+            mPreviewOutputSurfaceConfig = surfaceConfigs.getPreviewOutputSurface();
+            mCaptureOutputSurfaceConfig = surfaceConfigs.getImageCaptureOutputSurface();
 
             Camera2SessionConfigImplBuilder builder =
                     new Camera2SessionConfigImplBuilder()
@@ -167,7 +170,7 @@
 
                 previewOutputConfigBuilder =
                         Camera2OutputConfigImplBuilder.newSurfaceConfig(
-                                previewSurfaceConfig.getSurface());
+                            mPreviewOutputSurfaceConfig.getSurface());
 
                 mPreviewOutputConfig = previewOutputConfigBuilder.build();
 
@@ -180,7 +183,7 @@
 
                 captureOutputConfigBuilder =
                         Camera2OutputConfigImplBuilder.newImageReaderConfig(
-                                imageCaptureSurfaceConfig.getSize(),
+                                mCaptureOutputSurfaceConfig.getSize(),
                                 ImageFormat.YUV_420_888,
                                 BASIC_CAPTURE_PROCESS_MAX_IMAGES);
 
@@ -194,6 +197,24 @@
             return builder.build();
         }
 
+        @Override
+        public Camera2SessionConfigImpl initSession(@NonNull String cameraId,
+                @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+                @NonNull Context context,
+                @NonNull OutputSurfaceImpl previewSurfaceConfig,
+                @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig,
+                @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig) {
+
+            // Since this sample impl uses version 1.4, the other initSession method will be
+            // called. This is just a sample for earlier versions if wanting to redirect this call.
+            OutputSurfaceConfigurationImplImpl surfaceConfigs =
+                    new OutputSurfaceConfigurationImplImpl(previewSurfaceConfig,
+                    imageCaptureSurfaceConfig, imageAnalysisSurfaceConfig,
+                    null /*postviewSurfaceConfig*/);
+
+            return initSession(cameraId, cameraCharacteristicsMap, context, surfaceConfigs);
+        }
+
         protected void addSessionParameter(Camera2SessionConfigImplBuilder builder) {
             // default empty implementation
         }
@@ -430,6 +451,11 @@
         }
 
         @Override
+        public int startCaptureWithPostview(@NonNull CaptureCallback captureCallback) {
+            return startCapture(captureCallback);
+        }
+
+        @Override
         public int startCapture(@NonNull CaptureCallback captureCallback) {
             List<RequestProcessorImpl.Request> requestList = new ArrayList<>();
             addCaptureRequestParameters(requestList);
@@ -477,6 +503,7 @@
                 @Override
                 public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
                     captureCallback.onCaptureSequenceCompleted(seqId);
+                    captureCallback.onCaptureProcessProgressed(100);
                 }
 
                 @Override
@@ -592,6 +619,48 @@
         public void abortCapture(int captureSequenceId) {
 
         }
+
+        @Override
+        public Pair<Long, Long> getRealtimeCaptureLatency() {
+            return null;
+        }
+    }
+
+    public static class OutputSurfaceConfigurationImplImpl implements OutputSurfaceConfigurationImpl {
+        private OutputSurfaceImpl mOutputPreviewSurfaceImpl;
+        private OutputSurfaceImpl mOutputImageCaptureSurfaceImpl;
+        private OutputSurfaceImpl mOutputImageAnalysisSurfaceImpl;
+        private OutputSurfaceImpl mOutputPostviewSurfaceImpl;
+
+        public OutputSurfaceConfigurationImplImpl(OutputSurfaceImpl previewSurfaceConfig,
+                OutputSurfaceImpl imageCaptureSurfaceConfig,
+                OutputSurfaceImpl imageAnalysisSurfaceConfig,
+                OutputSurfaceImpl postviewSurfaceConfig) {
+            mOutputPreviewSurfaceImpl = previewSurfaceConfig;
+            mOutputImageCaptureSurfaceImpl = imageCaptureSurfaceConfig;
+            mOutputImageAnalysisSurfaceImpl = imageAnalysisSurfaceConfig;
+            mOutputPostviewSurfaceImpl = postviewSurfaceConfig;
+        }
+
+        @Override
+        public OutputSurfaceImpl getPreviewOutputSurface() {
+            return mOutputPreviewSurfaceImpl;
+        }
+
+        @Override
+        public OutputSurfaceImpl getImageCaptureOutputSurface() {
+            return mOutputImageCaptureSurfaceImpl;
+        }
+
+        @Override
+        public OutputSurfaceImpl getImageAnalysisOutputSurface() {
+            return mOutputImageAnalysisSurfaceImpl;
+        }
+
+        @Override
+        public OutputSurfaceImpl getPostviewOutputSurface() {
+            return mOutputPostviewSurfaceImpl;
+        }
     }
 
     @Override
@@ -610,4 +679,14 @@
                 CaptureResult.JPEG_ORIENTATION};
         return Arrays.asList(CAPTURE_RESULT_SET);
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        return true;
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        return false;
+    }
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
index 47a0d22..6fb45bc 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
@@ -43,4 +43,15 @@
      * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}.
      */
     int getSessionTemplateId();
+
+    /**
+     * Retrieves the session type to be used when initializing the
+     * {@link android.hardware.camera2.CameraCaptureSession}.
+     *
+     * @since 1.4
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     */
+    int getSessionType();
 }
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
index a301166..dc1fecc 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
@@ -20,6 +20,7 @@
 import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -32,6 +33,7 @@
 @SuppressLint("UnknownNullness")
 public class Camera2SessionConfigImplBuilder {
     private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW;
+    private int mSessionType = SessionConfiguration.SESSION_REGULAR;
     Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>();
     List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>();
 
@@ -86,6 +88,13 @@
     }
 
     /**
+     * Gets the camera capture session type.
+     */
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    /**
      * Builds a {@link Camera2SessionConfigImpl} instance.
      */
     public Camera2SessionConfigImpl build() {
@@ -95,6 +104,7 @@
     private static class Camera2SessionConfigImplImpl implements
             Camera2SessionConfigImpl {
         int mSessionTemplateId;
+        int mSessionType;
         Map<CaptureRequest.Key<?>, Object> mSessionParameters;
         List<Camera2OutputConfigImpl> mCamera2OutputConfigs;
 
@@ -102,6 +112,7 @@
             mSessionTemplateId = builder.getSessionTemplateId();
             mSessionParameters = builder.getSessionParameters();
             mCamera2OutputConfigs = builder.getCamera2OutputConfigs();
+            mSessionType = builder.getSessionType();
         }
 
         @Override
@@ -118,6 +129,11 @@
         public int getSessionTemplateId() {
             return mSessionTemplateId;
         }
+
+        @Override
+        public int getSessionType() {
+            return mSessionType;
+        }
     }
 }
 
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
index 6d9e013..d8b9928 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
@@ -176,6 +176,7 @@
                 @Override
                 public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
                     captureCallback.onCaptureSequenceCompleted(seqId);
+                    captureCallback.onCaptureProcessProgressed(100);
                 }
 
                 @Override
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
index faf741c..9a9727b 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
@@ -86,14 +86,12 @@
         public Camera2SessionConfigImpl initSession(@NonNull String cameraId,
                 @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
                 @NonNull Context context,
-                @NonNull OutputSurfaceImpl previewSurfaceConfig,
-                @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig,
-                @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig) {
+                @NonNull OutputSurfaceConfigurationImpl surfaceConfigs) {
 
             Log.d(TAG, "initSession cameraId=" + cameraId);
 
-            mPreviewOutputSurfaceConfig = previewSurfaceConfig;
-            mCaptureOutputSurfaceConfig = imageCaptureSurfaceConfig;
+            mPreviewOutputSurfaceConfig = surfaceConfigs.getPreviewOutputSurface();
+            mCaptureOutputSurfaceConfig = surfaceConfigs.getImageCaptureOutputSurface();
 
             Camera2SessionConfigImplBuilder builder =
                     new Camera2SessionConfigImplBuilder()
@@ -105,7 +103,7 @@
 
                 previewOutputConfigBuilder =
                         Camera2OutputConfigImplBuilder.newImageReaderConfig(
-                                previewSurfaceConfig.getSize(),
+                                mPreviewOutputSurfaceConfig.getSize(),
                                 ImageFormat.YUV_420_888,
                                 BASIC_CAPTURE_PROCESS_MAX_IMAGES);
 
@@ -120,7 +118,7 @@
 
                 captureOutputConfigBuilder =
                         Camera2OutputConfigImplBuilder.newImageReaderConfig(
-                                imageCaptureSurfaceConfig.getSize(),
+                                mCaptureOutputSurfaceConfig.getSize(),
                                 ImageFormat.YUV_420_888,
                                 BASIC_CAPTURE_PROCESS_MAX_IMAGES);
 
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
new file mode 100644
index 0000000..ca3832e
--- /dev/null
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
@@ -0,0 +1,35 @@
+/*
+ * 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.impl.advanced;
+
+import android.annotation.SuppressLint;
+
+/**
+ * For specifying the output surface configurations for the extension.
+ *
+ * since 1.4
+ */
+@SuppressLint("UnknownNullness")
+public interface OutputSurfaceConfigurationImpl {
+    public OutputSurfaceImpl getPreviewOutputSurface();
+
+    public OutputSurfaceImpl getImageCaptureOutputSurface();
+
+    public OutputSurfaceImpl getImageAnalysisOutputSurface();
+
+    public OutputSurfaceImpl getPostviewOutputSurface();
+}
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
index fabfc2b..8dbfadc 100644
--- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
+++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
+import android.util.Pair;
 import android.view.Surface;
 
 import java.util.Map;
@@ -64,9 +65,59 @@
      * preparing a CameraCaptureSession. After initSession() is called, the camera ID,
      * cameraCharacteristics and context will not change until deInitSession() has been called.
      *
-     * <p>CameraX specifies the output surface configurations for preview, image capture and image
-     * analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which consists of a
-     * list of {@link Camera2OutputConfigImpl} and session parameters. The
+     * <p>CameraX / Camera2 specifies the output surface configurations for preview using
+     * {@link OutputSurfaceConfigurationImpl#getPreviewOutputSurface}, image capture using
+     * {@link OutputSurfaceConfigurationImpl#getImageCaptureOutputSurface}, and image analysis
+     * [optional] using {@link OutputSurfaceConfigurationImpl#getImageAnalysisOutputSurface}.
+     * And OEM returns a {@link Camera2SessionConfigImpl} which consists of a list of
+     * {@link Camera2OutputConfigImpl} and session parameters. The {@link Camera2SessionConfigImpl}
+     * will be used to configure the CameraCaptureSession.
+     *
+     * <p>OEM is responsible for outputting correct camera images output to these output surfaces.
+     * OEM can have the following options to enable the output:
+     * <pre>
+     * (1) Add these output surfaces in CameraCaptureSession directly using
+     * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in
+     * HAL.
+     *
+     * (2) Use surface sharing with other surface by calling
+     * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)}
+     * to add the output surface to the other {@link Camera2OutputConfigImpl}.
+     *
+     * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output
+     * surface. The output surface won't be contained in the returned
+     * {@link Camera2SessionConfigImpl}.
+     * </pre>
+     *
+     * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder}
+     * implementations are provided in the stub for OEM to construct the
+     * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances.
+     *
+     * @param surfaceConfigs contains output surfaces for preview, image capture, and an
+     *                       optional output config for image analysis (YUV_420_888).
+     * @return a {@link Camera2SessionConfigImpl} consisting of a list of
+     * {@link Camera2OutputConfigImpl} and session parameters which will decide the
+     * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the
+     * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any
+     * supported or mandatory stream combination BUT OEM must ensure this list will always
+     * produce a valid camera capture session.
+     *
+     * @since 1.4
+     */
+    Camera2SessionConfigImpl initSession(
+            String cameraId,
+            Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+            Context context,
+            OutputSurfaceConfigurationImpl surfaceConfigs);
+
+    /**
+     * Initializes the session for the extension. This is where the OEMs allocate resources for
+     * preparing a CameraCaptureSession. After initSession() is called, the camera ID,
+     * cameraCharacteristics and context will not change until deInitSession() has been called.
+     *
+     * <p>CameraX / Camera2 specifies the output surface configurations for preview, image capture
+     * and image analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which
+     * consists of a list of {@link Camera2OutputConfigImpl} and session parameters. The
      * {@link Camera2SessionConfigImpl} will be used to configure the CameraCaptureSession.
      *
      * <p>OEM is responsible for outputting correct camera images output to these output surfaces.
@@ -173,6 +224,26 @@
     void stopRepeating();
 
     /**
+     * Start a multi-frame capture with a postview. {@link #startCapture(CaptureCallback)}
+     * will be used for captures without a postview request.
+     *
+     * Postview will be available before the capture. Upon postview completion,
+     * {@code OnImageAvailableListener#onImageAvailable} will be called on the ImageReader
+     * that creates the postview output surface. When the capture is completed,
+     * {@link CaptureCallback#onCaptureSequenceCompleted} is called and
+     * {@code OnImageAvailableListener#onImageAvailable} will also be called on the ImageReader
+     * that creates the image capture output surface.
+     *
+     * <p>Only one capture can perform at a time. Starting a capture when another capture is
+     * running will cause onCaptureFailed to be called immediately.
+     *
+     * @param callback a callback to report the status.
+     * @return the id of the capture sequence.
+     * @since 1.4
+     */
+    int startCaptureWithPostview(CaptureCallback callback);
+
+    /**
      * Start a multi-frame capture.
      *
      * When the capture is completed, {@link CaptureCallback#onCaptureSequenceCompleted}
@@ -193,6 +264,29 @@
     void abortCapture(int captureSequenceId);
 
     /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link AdvancedExtenderImpl#getEstimatedCaptureLatencyRange} this method is
+     * guaranteed to be called after {@link #onCaptureSessionStart}.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field. This is the time between {@link #onCaptureStarted} and
+     * {@link #onCaptureProcessStarted}. The second field value includes the estimated
+     * post-processing latency. This is the time between {@link #onCaptureProcessStarted} until
+     * the processed frame returns back to the client registered surface.
+     * Both first and second values will be in milliseconds. The total still capture latency will be
+     * the sum of both the first and second values of the pair.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
      * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and
      * {@link #startRepeating(CaptureCallback)}.
      */
@@ -277,5 +371,20 @@
          */
         void onCaptureCompleted(long timestamp, int captureSequenceId,
                 Map<CaptureResult.Key, Object> result);
+
+        /**
+         * Capture progress callback that needs to be called when the process capture is
+         * ongoing and includes the estimated progress of the processing.
+         *
+         * <p>Extensions must ensure that they always call this callback with monotonically
+         * increasing values.</p>
+         *
+         * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+         * callback is expected to be called once when processing is done with value 100.</p>
+         *
+         * @param progress             Value between 0 and 100.
+         * @since 1.4
+         */
+        void onCaptureProcessProgressed(int progress);
     }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index 5f6cb2b..30a79a6 100755
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -20,6 +20,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.media.ImageWriter;
 import android.os.Build;
@@ -116,6 +117,26 @@
                     }
 
                     @Override
+                    public void onPostviewOutputSurface(Surface surface) {
+
+                    }
+
+                    @Override
+                    public void processWithPostview(
+                            Map<Integer, Pair<Image, TotalCaptureResult>> results,
+                            ProcessResultImpl resultCallback, Executor executor) {
+                        if (!isPostviewAvailable()) {
+                            throw new RuntimeException("The extension doesn't support postview");
+                        }
+
+                        if (resultCallback != null) {
+                            process(results, resultCallback, executor);
+                        } else {
+                            process(results);
+                        }
+                    }
+
+                    @Override
                     public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
                             ProcessResultImpl resultCallback, Executor executor) {
                         throw new RuntimeException("The extension doesn't support capture " +
@@ -164,6 +185,11 @@
                     }
 
                     @Override
+                    public void onResolutionUpdate(Size size, Size postviewSize) {
+
+                    }
+
+                    @Override
                     public void onImageFormatUpdate(int imageFormat) {
 
                     }
@@ -244,6 +270,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        return new ArrayList<>();
+    }
+
+    @Override
     public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) {
         return null;
     }
@@ -257,4 +288,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         return new ArrayList<>();
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        return false;
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        return null;
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        return false;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index 5018df8..aa95a4a 100755
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.util.Pair;
 import android.util.Size;
 
@@ -162,4 +163,9 @@
 
         return captureStage;
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index 5c9b2d3..c9b2420 100755
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
 import android.media.ImageWriter;
@@ -118,6 +119,26 @@
                     }
 
                     @Override
+                    public void onPostviewOutputSurface(Surface surface) {
+
+                    }
+
+                    @Override
+                    public void processWithPostview(
+                            Map<Integer, Pair<Image, TotalCaptureResult>> results,
+                            ProcessResultImpl resultCallback, Executor executor) {
+                        if (!isPostviewAvailable()) {
+                            throw new RuntimeException("The extension doesn't support postview");
+                        }
+
+                        if (resultCallback != null) {
+                            process(results, resultCallback, executor);
+                        } else {
+                            process(results);
+                        }
+                    }
+
+                    @Override
                     public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
                             ProcessResultImpl resultCallback, Executor executor) {
                         throw new RuntimeException("The extension doesn't support capture " +
@@ -166,6 +187,11 @@
                     }
 
                     @Override
+                    public void onResolutionUpdate(Size size, Size postviewSize) {
+
+                    }
+
+                    @Override
                     public void onImageFormatUpdate(int imageFormat) {
 
                     }
@@ -267,6 +293,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        return new ArrayList<>();
+    }
+
+    @Override
     public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) {
         return null;
     }
@@ -280,4 +311,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         return new ArrayList<>();
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        return false;
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        return null;
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        return false;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index fcc78d5..2ef357c 100755
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -19,6 +19,7 @@
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.util.Pair;
 import android.util.Size;
@@ -183,4 +184,9 @@
 
         return captureStage;
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 5c3882b..a72deef 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -16,10 +16,13 @@
 package androidx.camera.extensions.impl;
 
 import android.content.Context;
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
 import android.media.ImageWriter;
 import android.os.Build;
@@ -51,6 +54,8 @@
     private static final int SESSION_STAGE_ID = 101;
     private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_SHADE;
 
+    private CameraCharacteristics mCameraCharacteristics;
+
     /**
      * @hide
      */
@@ -62,6 +67,7 @@
      */
     @Override
     public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        mCameraCharacteristics = cameraCharacteristics;
     }
 
     /**
@@ -104,6 +110,7 @@
         CaptureProcessorImpl captureProcessor =
                 new CaptureProcessorImpl() {
                     private ImageWriter mImageWriter;
+                    private ImageWriter mImageWriterPostview;
 
                     @Override
                     public void onOutputSurface(Surface surface, int imageFormat) {
@@ -113,6 +120,61 @@
                     }
 
                     @Override
+                    public void onPostviewOutputSurface(Surface surface) {
+                        if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) &&
+                                mImageWriterPostview == null) {
+                            mImageWriterPostview = ImageWriter.newInstance(surface, 1);
+                        }
+                    }
+
+                    @Override
+                    public void processWithPostview(
+                            Map<Integer, Pair<Image, TotalCaptureResult>> results,
+                            ProcessResultImpl resultCallback, Executor executor) {
+
+                        Pair<Image, TotalCaptureResult> result = results.get(DEFAULT_STAGE_ID);
+                        if (result == null) {
+                            Log.w(TAG,
+                                    "Unable to process since images does not contain all " +
+                                    "stages.");
+                            return;
+                        } else {
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                                Image image = mImageWriterPostview.dequeueInputImage();
+
+                                // Postview processing here
+                                ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer();
+                                ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer();
+                                ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer();
+
+                                // For sample, allocate empty buffer to match postview size
+                                yByteBuffer.put(ByteBuffer.allocate(image.getPlanes()[0]
+                                        .getBuffer().capacity()));
+                                uByteBuffer.put(ByteBuffer.allocate(image.getPlanes()[2]
+                                        .getBuffer().capacity()));
+                                vByteBuffer.put(ByteBuffer.allocate(image.getPlanes()[1]
+                                        .getBuffer().capacity()));
+                                Long sensorTimestamp =
+                                        result.second.get(CaptureResult.SENSOR_TIMESTAMP);
+                                if (sensorTimestamp != null) {
+                                    image.setTimestamp(sensorTimestamp);
+                                } else {
+                                    Log.e(TAG, "Sensor timestamp absent using default!");
+                                }
+
+                                mImageWriterPostview.queueInputImage(image);
+                            }
+                        }
+
+                        // Process still capture
+                        if (resultCallback != null) {
+                            process(results, resultCallback, executor);
+                        } else {
+                            process(results);
+                        }
+                    }
+
+                    @Override
                     public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
                             ProcessResultImpl resultCallback, Executor executor) {
                         Pair<Image, TotalCaptureResult> result = results.get(DEFAULT_STAGE_ID);
@@ -241,6 +303,11 @@
                     }
 
                     @Override
+                    public void onResolutionUpdate(Size size, Size postviewSize) {
+
+                    }
+
+                    @Override
                     public void onImageFormatUpdate(int imageFormat) {
 
                     }
@@ -317,7 +384,59 @@
      */
     @Override
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
-        return null;
+        List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+
+        StreamConfigurationMap map =
+                mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+        if (map != null) {
+            // The sample implementation only retrieves originally supported resolutions from
+            // CameraCharacteristics for JPEG and YUV_420_888 formats to return.
+            Size[] outputSizes = map.getOutputSizes(ImageFormat.JPEG);
+
+            if (outputSizes != null) {
+                formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, outputSizes));
+            }
+
+            outputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);
+
+            if (outputSizes != null) {
+                formatResolutionsPairList.add(Pair.create(ImageFormat.YUV_420_888, outputSizes));
+            }
+        }
+
+        return formatResolutionsPairList;
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        // Sample for supported postview sizes, returns subset of supported resolutions for
+        // still capture that are less than its size and match the aspect ratio
+        List<Pair<Integer, Size[]>> res = new ArrayList<>();
+        List<Pair<Integer, Size[]>> captureSupportedResolutions = getSupportedResolutions();
+        float targetAr = ((float) captureSize.getWidth()) / captureSize.getHeight();
+
+        for (Pair<Integer, Size[]> elem : captureSupportedResolutions) {
+            Integer currFormat = elem.first;
+            Size[] currFormatSizes = elem.second;
+            List<Size> postviewSizes = new ArrayList<>();
+
+            for (Size s : currFormatSizes) {
+                if ((s.equals(captureSize)) || (s.getWidth() > captureSize.getWidth())
+                        || (s.getHeight() > captureSize.getHeight())) continue;
+                float currentAr = ((float) s.getWidth()) / s.getHeight();
+                if (Math.abs(targetAr - currentAr) < 0.01) {
+                    postviewSizes.add(s);
+                }
+            }
+
+            if (!postviewSizes.isEmpty()) {
+                res.add(new Pair<Integer, Size[]>(currFormat,
+                        postviewSizes.toArray(new Size[postviewSizes.size()])));
+            }
+        }
+
+        return res;
     }
 
     @Override
@@ -341,4 +460,24 @@
             CaptureResult.FLASH_STATE};
         return Arrays.asList(CAPTURE_RESULT_SET);
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        return false;
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        return null;
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        return true;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index 45c7f47..ace54c8 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -19,6 +19,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.util.Pair;
 import android.util.Size;
 import android.view.Surface;
@@ -202,4 +203,9 @@
 
         return captureStage;
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
index 3eee146..f4719b8 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Surface;
 
 import java.util.Map;
@@ -46,6 +47,29 @@
     void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
 
     /**
+     * Informs the CaptureProcessorImpl where it should write the postview output to.
+     * This will only be invoked once if a valid postview surface was set.
+     *
+     * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface}
+     *                that the CaptureProcessorImpl should write data into.
+     * @since 1.4
+     */
+    void onPostviewOutputSurface(Surface surface);
+
+    /**
+     * Invoked when the Camera Framework changes the configured output resolution for
+     * still capture and postview.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input for still capture and postview to be at the specified resolutions.
+     *
+     * @param size for the surface for still capture.
+     * @param postviewSize for the surface for postview.
+     * @since 1.4
+     */
+    void onResolutionUpdate(Size size, Size postviewSize);
+
+    /**
      * Process a set images captured that were requested.
      *
      * <p> The result of the processing step should be written to the {@link Surface} that was
@@ -63,4 +87,30 @@
      */
     void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
             ProcessResultImpl resultCallback, Executor executor);
+
+    /**
+     * Process a set images captured that were requested for both postview and
+     * still capture.
+     *
+     * <p> This processing method will be called if a postview was requested, therefore the
+     * processed postview should be written to the
+     * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}.
+     * The final result of the processing step should be written to the {@link Surface} that was
+     * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available
+     * before the capture, it should be processed and written to the surface before
+     * the final capture is processed.
+     *
+     * @param results             The map of {@link ImageFormat#YUV_420_888} format images and
+     *                            metadata to process. The {@link Image} that are contained within
+     *                            the map will become invalid after this method completes, so no
+     *                            references to them should be kept.
+     * @param resultCallback      Capture result callback to be called once the capture result
+     *                            values of the processed image are ready.
+     * @param executor            The executor to run the callback on. If null then the callback
+     *                            will run on any arbitrary executor.
+     * @throws RuntimeException   if postview feature is not supported
+     * @since 1.4
+     */
+    void processWithPostview(Map<Integer, Pair<Image, TotalCaptureResult>> results,
+            ProcessResultImpl resultCallback, Executor executor);
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
index f926cff..23570c4 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
@@ -84,4 +84,21 @@
      * @hide
      */
     CaptureStageImpl onDisableSession();
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * initialized and must return a valid camera session type
+     * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType}
+     * to be used to configure camera capture session. Both the preview and the image capture
+     * extender must return the same session type value for a specific extension type. If there
+     * is inconsistency between the session type values from preview and image extenders, then
+     * the session configuration will fail.
+     *
+     *
+     * @since 1.4
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     */
+    int onSessionType();
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
index af147ed..75739ce 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -29,7 +29,7 @@
  */
 public class ExtensionVersionImpl {
     private static final String TAG = "ExtenderVersionImpl";
-    private static final String VERSION = "1.3.0";
+    private static final String VERSION = "1.4.0";
 
     /**
      * @hide
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index 57b7fe6..2523263 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -17,10 +17,12 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.media.Image;
 import android.media.ImageWriter;
 import android.os.Build;
@@ -130,6 +132,26 @@
                     }
 
                     @Override
+                    public void onPostviewOutputSurface(Surface surface) {
+
+                    }
+
+                    @Override
+                    public void processWithPostview(
+                            Map<Integer, Pair<Image, TotalCaptureResult>> results,
+                            ProcessResultImpl resultCallback, Executor executor) {
+                        if (!isPostviewAvailable()) {
+                            throw new RuntimeException("The extension doesn't support postview");
+                        }
+
+                        if (resultCallback != null) {
+                            process(results, resultCallback, executor);
+                        } else {
+                            process(results);
+                        }
+                    }
+
+                    @Override
                     public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
                             ProcessResultImpl resultCallback, Executor executor) {
                         Pair<Image, TotalCaptureResult> result = results.get(NORMAL_STAGE_ID);
@@ -184,12 +206,25 @@
                                             jpegOrientation));
                                 }
 
+                                Integer strength = result.second.get(
+                                        CaptureResult.EXTENSION_STRENGTH);
+                                if (strength != null) {
+                                    captureResults.add(new Pair<>(CaptureResult.EXTENSION_STRENGTH,
+                                            strength));
+                                }
+
+                                captureResults.add(new Pair<>(CaptureResult.EXTENSION_CURRENT_TYPE,
+                                            CameraExtensionCharacteristics.EXTENSION_HDR));
+
                                 if (executor != null) {
                                     executor.execute(() -> resultCallback.onCaptureCompleted(
                                             shutterTimestamp, captureResults));
+                                    executor.execute(() ->
+                                            resultCallback.onCaptureProcessProgressed(100));
                                 } else {
                                     resultCallback.onCaptureCompleted(shutterTimestamp,
                                             captureResults);
+                                    resultCallback.onCaptureProcessProgressed(100);
                                 }
                             }
                         }
@@ -264,6 +299,11 @@
                     }
 
                     @Override
+                    public void onResolutionUpdate(Size size, Size postviewSize) {
+
+                    }
+
+                    @Override
                     public void onImageFormatUpdate(int imageFormat) {
 
                     }
@@ -332,6 +372,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        return new ArrayList<>();
+    }
+
+    @Override
     public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) {
         return null;
     }
@@ -340,7 +385,7 @@
     public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
         final CaptureRequest.Key [] CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_ZOOM_RATIO,
             CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_REGIONS,
-            CaptureRequest.CONTROL_AF_TRIGGER};
+            CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.EXTENSION_STRENGTH};
         return Arrays.asList(CAPTURE_REQUEST_SET);
     }
 
@@ -348,7 +393,28 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         final CaptureResult.Key [] CAPTURE_RESULT_SET = {CaptureResult.CONTROL_ZOOM_RATIO,
             CaptureResult.CONTROL_AF_MODE, CaptureResult.CONTROL_AF_REGIONS,
-            CaptureResult.CONTROL_AF_TRIGGER, CaptureResult.CONTROL_AF_STATE};
+            CaptureResult.CONTROL_AF_TRIGGER, CaptureResult.CONTROL_AF_STATE,
+            CaptureResult.EXTENSION_CURRENT_TYPE, CaptureResult.EXTENSION_STRENGTH};
         return Arrays.asList(CAPTURE_RESULT_SET);
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        return true;
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        return null;
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        return false;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index 78b0a9d..7777bfa 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -18,15 +18,18 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.media.ImageWriter;
 import android.media.Image;
 import android.util.Pair;
 import android.util.Size;
 import android.view.Surface;
 
+import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.concurrent.Executor;
 import java.util.List;
@@ -43,8 +46,6 @@
 public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl {
     private static final int DEFAULT_STAGE_ID = 0;
 
-    ImageWriter mWriter;
-
     /**
      * @hide
      */
@@ -109,17 +110,32 @@
         return null;
     }
 
-    private PreviewImageProcessorImpl mProcessor = new PreviewImageProcessorImpl() {
+    private HdrPreviewProcessor mProcessor = new HdrPreviewProcessor();
+
+    private static class HdrPreviewProcessor implements PreviewImageProcessorImpl, Closeable {
         Surface mSurface;
         int mFormat = -1;
+        final Object mLock = new Object(); // Synchronize access to 'mWriter'
+        ImageWriter mWriter;
 
-        private void setWindowSurface() {
-            if (mSurface != null && mFormat >= 0) {
+        public void close() {
+            synchronized(mLock) {
                 if (mWriter != null) {
                     mWriter.close();
+                    mWriter = null;
                 }
+            }
+        }
 
-                mWriter = ImageWriter.newInstance(mSurface, 2, mFormat);
+        private void setWindowSurface() {
+            synchronized(mLock) {
+                if (mSurface != null && mFormat >= 0) {
+                    if (mWriter != null) {
+                        mWriter.close();
+                    }
+
+                    mWriter = ImageWriter.newInstance(mSurface, 2, mFormat);
+                }
             }
         }
 
@@ -132,7 +148,11 @@
 
         @Override
         public void process(Image image, TotalCaptureResult result) {
-            mWriter.queueInputImage(image);
+            synchronized(mLock) {
+                if (mWriter != null) {
+                    mWriter.queueInputImage(image);
+                }
+            }
         }
 
         @Override
@@ -175,6 +195,14 @@
                                 jpegOrientation));
                     }
 
+                    Integer strength = result.get(CaptureResult.EXTENSION_STRENGTH);
+                    if (strength != null) {
+                        captureResults.add(new Pair<>(CaptureResult.EXTENSION_STRENGTH, strength));
+                    }
+
+                    captureResults.add(new Pair<>(CaptureResult.EXTENSION_CURRENT_TYPE,
+                                CameraExtensionCharacteristics.EXTENSION_HDR));
+
                     if (executor != null) {
                         executor.execute(() -> resultCallback.onCaptureCompleted(shutterTimestamp,
                                 captureResults));
@@ -209,10 +237,7 @@
      */
     @Override
     public void onDeInit() {
-        if (mWriter != null) {
-            mWriter.close();
-            mWriter = null;
-        }
+        mProcessor.close();
     }
 
     /**
@@ -238,4 +263,9 @@
     public CaptureStageImpl onDisableSession() {
         return null;
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index c27a5db..37e7baf 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -91,6 +91,21 @@
     List<Pair<Integer, Size[]>> getSupportedResolutions();
 
     /**
+     * Returns the customized supported postview resolutions for a still capture using
+     * its size.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device.
+     *
+     * @return the customized supported resolutions, or null to support all sizes retrieved from
+     *         {@link android.hardware.camera2.params.StreamConfigurationMap}.
+     * @since 1.4
+     */
+    List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize);
+
+    /**
      * Returns the estimated capture latency range in milliseconds for the target capture
      * resolution.
      *
@@ -138,5 +153,44 @@
      * @since 1.3
      */
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
-}
 
+    /**
+     * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be
+     * called after the camera capture session is initialized and camera preview is enabled.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process}
+     * as the second pair field. Both first and second fields will be in milliseconds. The total
+     * still capture latency will be the sum of both the first and second values.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index f0821ed..e3317d9 100755
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -20,6 +20,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.media.ImageWriter;
 import android.os.Build;
@@ -116,6 +117,26 @@
                     }
 
                     @Override
+                    public void onPostviewOutputSurface(Surface surface) {
+
+                    }
+
+                    @Override
+                    public void processWithPostview(
+                            Map<Integer, Pair<Image, TotalCaptureResult>> results,
+                            ProcessResultImpl resultCallback, Executor executor) {
+                        if (!isPostviewAvailable()) {
+                            throw new RuntimeException("The extension doesn't support postview");
+                        }
+
+                        if (resultCallback != null) {
+                            process(results, resultCallback, executor);
+                        } else {
+                            process(results);
+                        }
+                    }
+
+                    @Override
                     public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
                             ProcessResultImpl resultCallback, Executor executor) {
                         throw new RuntimeException("The extension doesn't support capture " +
@@ -164,6 +185,11 @@
                     }
 
                     @Override
+                    public void onResolutionUpdate(Size size, Size postviewSize) {
+
+                    }
+
+                    @Override
                     public void onImageFormatUpdate(int imageFormat) {
 
                     }
@@ -244,6 +270,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        return new ArrayList<>();
+    }
+
+    @Override
     public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) {
         return null;
     }
@@ -257,4 +288,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         return new ArrayList<>();
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        return false;
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        return null;
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        return false;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index e29abec..46be86a 100755
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.util.Pair;
 import android.util.Size;
 
@@ -162,4 +163,9 @@
 
         return captureStage;
     }
+
+    @Override
+    public int onSessionType() {
+        return SessionConfiguration.SESSION_REGULAR;
+    }
 }
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java
index d4c2014..518942e 100644
--- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java
@@ -24,7 +24,6 @@
 /**
  * Allows clients to receive information about the capture result values of processed frames.
  *
- * @since 1.3
  */
 public interface ProcessResultImpl {
     /**
@@ -38,6 +37,23 @@
      *                             must also be passed as part of this callback. Both Camera2 and
      *                             CameraX guarantee that those two settings and results are always
      *                             supported and applied by the corresponding framework.
+     * @since 1.3
      */
     void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result);
+
+
+    /**
+     * Capture progress callback that needs to be called when the process capture is
+     * ongoing and includes the estimated progress of the processing.
+     *
+     * <p>Extensions must ensure that they always call this callback with monotonically increasing
+     * values.</p>
+     *
+     * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+     * callback is expected to be called once when processing is done with value 100.</p>
+     *
+     * @param progress             Value between 0 and 100.
+     * @since 1.4
+     */
+    void onCaptureProcessProgressed(int progress);
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index ccb0dac..bd60570 100755
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -113,4 +118,23 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index 100f665..0c4577a 100755
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index 2d26639..50c8040 100755
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -112,4 +117,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index bc3e48d..1f50174 100755
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 66c5839..ee777cf 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -113,4 +118,23 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index ff58862..1dc5ed7 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -91,4 +91,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
index 3eee146..f4719b8 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Surface;
 
 import java.util.Map;
@@ -46,6 +47,29 @@
     void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
 
     /**
+     * Informs the CaptureProcessorImpl where it should write the postview output to.
+     * This will only be invoked once if a valid postview surface was set.
+     *
+     * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface}
+     *                that the CaptureProcessorImpl should write data into.
+     * @since 1.4
+     */
+    void onPostviewOutputSurface(Surface surface);
+
+    /**
+     * Invoked when the Camera Framework changes the configured output resolution for
+     * still capture and postview.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input for still capture and postview to be at the specified resolutions.
+     *
+     * @param size for the surface for still capture.
+     * @param postviewSize for the surface for postview.
+     * @since 1.4
+     */
+    void onResolutionUpdate(Size size, Size postviewSize);
+
+    /**
      * Process a set images captured that were requested.
      *
      * <p> The result of the processing step should be written to the {@link Surface} that was
@@ -63,4 +87,30 @@
      */
     void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
             ProcessResultImpl resultCallback, Executor executor);
+
+    /**
+     * Process a set images captured that were requested for both postview and
+     * still capture.
+     *
+     * <p> This processing method will be called if a postview was requested, therefore the
+     * processed postview should be written to the
+     * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}.
+     * The final result of the processing step should be written to the {@link Surface} that was
+     * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available
+     * before the capture, it should be processed and written to the surface before
+     * the final capture is processed.
+     *
+     * @param results             The map of {@link ImageFormat#YUV_420_888} format images and
+     *                            metadata to process. The {@link Image} that are contained within
+     *                            the map will become invalid after this method completes, so no
+     *                            references to them should be kept.
+     * @param resultCallback      Capture result callback to be called once the capture result
+     *                            values of the processed image are ready.
+     * @param executor            The executor to run the callback on. If null then the callback
+     *                            will run on any arbitrary executor.
+     * @throws RuntimeException   if postview feature is not supported
+     * @since 1.4
+     */
+    void processWithPostview(Map<Integer, Pair<Image, TotalCaptureResult>> results,
+            ProcessResultImpl resultCallback, Executor executor);
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
index 2879568..4a3b01c 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
@@ -80,4 +80,20 @@
      * @return The request information to customize the session.
      */
     CaptureStageImpl onDisableSession();
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * initialized and must return a valid camera session type
+     * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType}
+     * to be used to configure camera capture session. Both the preview and the image capture
+     * extender must return the same session type value for a specific extension type. If there
+     * is inconsistency between the session type values from preview and image extenders, then
+     * the session configuration will fail.
+     *
+     * @since 1.4
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     */
+    int onSessionType();
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index f1191dc..f3fd2f3 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -112,4 +117,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index 0eb4a61..af48464 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 88bd105..70c1804 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -85,6 +85,21 @@
     List<Pair<Integer, Size[]>> getSupportedResolutions();
 
     /**
+     * Returns the customized supported postview resolutions for a still capture using
+     * its size.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device.
+     *
+     * @return the customized supported resolutions, or null to support all sizes retrieved from
+     *         {@link android.hardware.camera2.params.StreamConfigurationMap}.
+     * @since 1.4
+     */
+    List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize);
+
+    /**
      * Returns the estimated capture latency range in milliseconds for the target capture
      * resolution.
      *
@@ -159,4 +174,44 @@
      * @since 1.3
      */
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
+
+    /**
+     * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be
+     * called after the camera capture session is initialized and camera preview is enabled.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process}
+     * as the second pair field. Both first and second fields will be in milliseconds. The total
+     * still capture latency will be the sum of both the first and second values.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index c8ac978..6f0eaef 100755
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -95,6 +95,11 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
@@ -112,4 +117,24 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index a5809f6..825994f 100755
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -93,4 +93,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java
index d0e3605..0e15445 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java
@@ -25,7 +25,6 @@
 /**
  * Allows clients to receive information about the capture result values of processed frames.
  *
- * @since 1.3
  */
 @SuppressLint("UnknownNullness")
 public interface ProcessResultImpl {
@@ -40,6 +39,22 @@
      *                             must also be passed as part of this callback. Both Camera2 and
      *                             CameraX guarantee that those two settings and results are always
      *                             supported and applied by the corresponding framework.
+     * @since 1.3
      */
     void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result);
+
+    /**
+     * Capture progress callback that needs to be called when the process capture is
+     * ongoing and includes the estimated progress of the processing.
+     *
+     * <p>Extensions must ensure that they always call this callback with monotonically increasing
+     * values.</p>
+     *
+     * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+     * callback is expected to be called once when processing is done with value 100.</p>
+     *
+     * @param progress             Value between 0 and 100.
+     * @since 1.4
+     */
+    void onCaptureProcessProgressed(int progress);
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
index 465bfe8..d13efc8 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
@@ -124,6 +124,17 @@
     Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId);
 
     /**
+     * Returns supported output format/size map for postview image. OEM is required to support
+     * both JPEG and YUV_420_888 format output.
+     *
+     * <p>The surface created with this supported format/size could configure
+     * intermediate surfaces(YUV/RAW..) and write the output to the output surface.</p>
+     *
+     * @since 1.4
+     */
+    Map<Integer, List<Size>> getSupportedPostviewResolutions(Size captureSize);
+
+    /**
      * Returns supported output sizes for Image Analysis (YUV_420_888 format).
      *
      * <p>OEM can optionally support a YUV surface for ImageAnalysis along with Preview/ImageCapture
@@ -185,4 +196,24 @@
      * @since 1.3
      */
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
+
+    /**
+     * Advertise support for {@link SessionProcessorImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    public boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
index 0d3bd4a..8c3ac11 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
@@ -69,6 +69,12 @@
     }
 
     @Override
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
     public List<Size> getSupportedYuvAnalysisResolutions(
             String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -88,4 +94,14 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
index 1dec326..135306c 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
@@ -69,6 +69,12 @@
     }
 
     @Override
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
     public List<Size> getSupportedYuvAnalysisResolutions(
             String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -88,4 +94,14 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
index bc41b4e..fa4ad0d 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
@@ -69,6 +69,12 @@
     }
 
     @Override
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
     public List<Size> getSupportedYuvAnalysisResolutions(
             String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -88,4 +94,14 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
index d121717..850f0e1 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
@@ -43,4 +43,16 @@
      * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}.
      */
     int getSessionTemplateId();
+
+
+    /**
+     * Retrieves the session type to be used when initializing the
+     * {@link android.hardware.camera2.CameraCaptureSession}.
+     *
+     * @since 1.4
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     */
+    int getSessionType();
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
index a301166..dc1fecc 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
@@ -20,6 +20,7 @@
 import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -32,6 +33,7 @@
 @SuppressLint("UnknownNullness")
 public class Camera2SessionConfigImplBuilder {
     private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW;
+    private int mSessionType = SessionConfiguration.SESSION_REGULAR;
     Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>();
     List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>();
 
@@ -86,6 +88,13 @@
     }
 
     /**
+     * Gets the camera capture session type.
+     */
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    /**
      * Builds a {@link Camera2SessionConfigImpl} instance.
      */
     public Camera2SessionConfigImpl build() {
@@ -95,6 +104,7 @@
     private static class Camera2SessionConfigImplImpl implements
             Camera2SessionConfigImpl {
         int mSessionTemplateId;
+        int mSessionType;
         Map<CaptureRequest.Key<?>, Object> mSessionParameters;
         List<Camera2OutputConfigImpl> mCamera2OutputConfigs;
 
@@ -102,6 +112,7 @@
             mSessionTemplateId = builder.getSessionTemplateId();
             mSessionParameters = builder.getSessionParameters();
             mCamera2OutputConfigs = builder.getCamera2OutputConfigs();
+            mSessionType = builder.getSessionType();
         }
 
         @Override
@@ -118,6 +129,11 @@
         public int getSessionTemplateId() {
             return mSessionTemplateId;
         }
+
+        @Override
+        public int getSessionType() {
+            return mSessionType;
+        }
     }
 }
 
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
index 06157dc..dc5b2b6 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
@@ -70,6 +70,12 @@
     }
 
     @Override
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
     public List<Size> getSupportedYuvAnalysisResolutions(
             String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -89,4 +95,14 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
index 97da5c1..5b0ed8e 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
@@ -69,6 +69,12 @@
     }
 
     @Override
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
     public List<Size> getSupportedYuvAnalysisResolutions(
             String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -88,4 +94,14 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
new file mode 100644
index 0000000..723f0f4
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
@@ -0,0 +1,35 @@
+/*
+ * 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.impl.advanced;
+
+import android.annotation.SuppressLint;
+
+/**
+ * For specifying the output surface configurations for the extension.
+ *
+ * @since 1.4
+ */
+@SuppressLint("UnknownNullness")
+public interface OutputSurfaceConfigurationImpl {
+    public OutputSurfaceImpl getPreviewOutputSurface();
+
+    public OutputSurfaceImpl getImageCaptureOutputSurface();
+
+    public OutputSurfaceImpl getImageAnalysisOutputSurface();
+
+    public OutputSurfaceImpl getPostviewOutputSurface();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
index fabfc2b..0627081 100644
--- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
+import android.util.Pair;
 import android.view.Surface;
 
 import java.util.Map;
@@ -64,9 +65,59 @@
      * preparing a CameraCaptureSession. After initSession() is called, the camera ID,
      * cameraCharacteristics and context will not change until deInitSession() has been called.
      *
-     * <p>CameraX specifies the output surface configurations for preview, image capture and image
-     * analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which consists of a
-     * list of {@link Camera2OutputConfigImpl} and session parameters. The
+     * <p>CameraX / Camera2 specifies the output surface configurations for preview using
+     * {@link OutputSurfaceConfigurationImpl#getPreviewOutputSurface}, image capture using
+     * {@link OutputSurfaceConfigurationImpl#getImageCaptureOutputSurface}, and image analysis
+     * [optional] using {@link OutputSurfaceConfigurationImpl#getImageAnalysisOutputSurface}.
+     * And OEM returns a {@link Camera2SessionConfigImpl} which consists of a list of
+     * {@link Camera2OutputConfigImpl} and session parameters. The {@link Camera2SessionConfigImpl}
+     * will be used to configure the CameraCaptureSession.
+     *
+     * <p>OEM is responsible for outputting correct camera images output to these output surfaces.
+     * OEM can have the following options to enable the output:
+     * <pre>
+     * (1) Add these output surfaces in CameraCaptureSession directly using
+     * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in
+     * HAL.
+     *
+     * (2) Use surface sharing with other surface by calling
+     * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)}
+     * to add the output surface to the other {@link Camera2OutputConfigImpl}.
+     *
+     * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output
+     * surface. The output surface won't be contained in the returned
+     * {@link Camera2SessionConfigImpl}.
+     * </pre>
+     *
+     * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder}
+     * implementations are provided in the stub for OEM to construct the
+     * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances.
+     *
+     * @param surfaceConfigs contains output surfaces for preview, image capture, and an
+     *                       optional output config for image analysis (YUV_420_888).
+     * @return a {@link Camera2SessionConfigImpl} consisting of a list of
+     * {@link Camera2OutputConfigImpl} and session parameters which will decide the
+     * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the
+     * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any
+     * supported or mandatory stream combination BUT OEM must ensure this list will always
+     * produce a valid camera capture session.
+     *
+     * @since 1.4
+     */
+    Camera2SessionConfigImpl initSession(
+            String cameraId,
+            Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+            Context context,
+            OutputSurfaceConfigurationImpl surfaceConfigs);
+
+    /**
+     * Initializes the session for the extension. This is where the OEMs allocate resources for
+     * preparing a CameraCaptureSession. After initSession() is called, the camera ID,
+     * cameraCharacteristics and context will not change until deInitSession() has been called.
+     *
+     * <p>CameraX / Camera 2 specifies the output surface configurations for preview, image capture
+     * and image analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which
+     * consists of a list of {@link Camera2OutputConfigImpl} and session parameters. The
      * {@link Camera2SessionConfigImpl} will be used to configure the CameraCaptureSession.
      *
      * <p>OEM is responsible for outputting correct camera images output to these output surfaces.
@@ -188,11 +239,54 @@
     int startCapture(CaptureCallback callback);
 
     /**
+     * Start a multi-frame capture with a postview. {@link #startCapture(CaptureCallback)}
+     * will be used for captures without a postview request.
+     *
+     * Postview will be available before the capture. Upon postview completion,
+     * {@code OnImageAvailableListener#onImageAvailable} will be called on the ImageReader
+     * that creates the postview output surface. When the capture is completed,
+     * {@link CaptureCallback#onCaptureSequenceCompleted} is called and
+     * {@code OnImageAvailableListener#onImageAvailable} will also be called on the ImageReader
+     * that creates the image capture output surface.
+     *
+     * <p>Only one capture can perform at a time. Starting a capture when another capture is
+     * running will cause onCaptureFailed to be called immediately.
+     *
+     * @param callback a callback to report the status.
+     * @return the id of the capture sequence.
+     * @since 1.4
+     */
+    int startCaptureWithPostview(CaptureCallback callback);
+
+    /**
      * Abort all capture tasks.
      */
     void abortCapture(int captureSequenceId);
 
     /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link AdvancedExtenderImpl#getEstimatedCaptureLatencyRange} this method is
+     * guaranteed to be called after {@link #onCaptureSessionStart}.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field. This is the time between {@link #onCaptureStarted} and
+     * {@link #onCaptureProcessStarted}. The second field value includes the estimated
+     * post-processing latency. This is the time between {@link #onCaptureProcessStarted} until
+     * the processed frame returns back to the client registered surface.
+     * Both first and second values will be in milliseconds. The total still capture latency will be
+     * the sum of both the first and second values of the pair.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
      * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and
      * {@link #startRepeating(CaptureCallback)}.
      */
@@ -277,5 +371,20 @@
          */
         void onCaptureCompleted(long timestamp, int captureSequenceId,
                 Map<CaptureResult.Key, Object> result);
+
+        /**
+         * Capture progress callback that needs to be called when the process capture is
+         * ongoing and includes the estimated progress of the processing.
+         *
+         * <p>Extensions must ensure that they always call this callback with monotonically
+         * increasing values.</p>
+         *
+         * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+         * callback is expected to be called once when processing is done with value 100.</p>
+         *
+         * @param progress             Value between 0 and 100.
+         * @since 1.4
+         */
+        void onCaptureProcessProgressed(int progress);
     }
 }
diff --git a/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java b/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java
index 21014d0..5b5a38d 100644
--- a/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java
+++ b/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java
@@ -89,7 +89,7 @@
         }
     }
 
-    private final CameraManager mManager;
+    protected final CameraManager mManager;
 
     /**
      * Create a new blocking camera manager.
@@ -168,7 +168,7 @@
      * <p>Time out after {@link #OPEN_TIME_OUT_MS} and unblock. Clean up camera if it arrives
      * later.</p>
      */
-    private class OpenListener extends CameraDevice.StateCallback {
+    protected class OpenListener extends CameraDevice.StateCallback {
         private static final int ERROR_UNINITIALIZED = -1;
 
         private final String mCameraId;
@@ -186,9 +186,13 @@
         private boolean mNoReply = true; // Start with no reply until proven otherwise
         private boolean mTimedOut = false;
 
-        OpenListener(CameraManager manager, String cameraId,
-                CameraDevice.StateCallback listener, Handler handler)
-                throws CameraAccessException {
+        protected OpenListener(String cameraId, CameraDevice.StateCallback listener) {
+            mCameraId = cameraId;
+            mProxy = listener;
+        }
+
+        OpenListener(CameraManager manager, String cameraId, CameraDevice.StateCallback listener,
+                Handler handler) throws CameraAccessException {
             mCameraId = cameraId;
             mProxy = listener;
             manager.openCamera(cameraId, this, handler);
@@ -281,7 +285,7 @@
             if (mProxy != null) mProxy.onClosed(camera);
         }
 
-        CameraDevice blockUntilOpen() throws BlockingOpenException {
+        public CameraDevice blockUntilOpen() throws BlockingOpenException {
             /**
              * Block until onOpened, onError, or onDisconnected
              */