Merge Android 12

Bug: 202323961
Merged-In: Ied4e7a153b46e23aaca665b7812fafb2449357e2
Change-Id: I8b3b281b084629ad4a01afd87b4b7d1c92a990c5
diff --git a/camera2/extensions/README.android b/camera2/extensions/README.android
new file mode 100644
index 0000000..8c1a70a
--- /dev/null
+++ b/camera2/extensions/README.android
@@ -0,0 +1,12 @@
+Library Name: CameraX Extension stub and sample
+License: Apache 2
+Description: The CameraX Extension stub library along with a very basic sample
+implementation. The stub includes extension API versions 1.2.0, 1.1.0 and older.
+
+Local patches
+-------------
+- No changes to the stubs
+- Minor modifications to the sample implementation switching effects to white balance modes
+- The HDR preview processor doesn't use GL to render to the registered output surface instead
+  it will just attach the incoming image
+
diff --git a/camera2/extensions/sample/Android.bp b/camera2/extensions/sample/Android.bp
new file mode 100644
index 0000000..7029175
--- /dev/null
+++ b/camera2/extensions/sample/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "androidx.camera.extensions.impl",
+    installable: true,
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "current",
+    vendor: true,
+}
+
+prebuilt_etc {
+    name: "sample_camera_extensions.xml",
+    src: "camera_extensions.xml",
+    sub_dir: "permissions",
+    vendor: true,
+}
diff --git a/camera2/extensions/sample/camera_extensions.xml b/camera2/extensions/sample/camera_extensions.xml
new file mode 100644
index 0000000..3f8c637
--- /dev/null
+++ b/camera2/extensions/sample/camera_extensions.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<permissions>
+    <library name="androidx.camera.extensions.impl"
+            file="/vendor/framework/androidx.camera.extensions.impl.jar"/>
+</permissions>
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
new file mode 100755
index 0000000..cabcaae
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation for auto image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    private static final String TAG = "AutoICExtender";
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_DAYLIGHT;
+
+    /**
+     * @hide
+     */
+    public AutoImageCaptureExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        return false;
+    }
+
+    public boolean isExtensionAvailableOriginal(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Requires API 23 for ImageWriter
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            return false;
+        }
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        // Placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+        List<CaptureStageImpl> captureStages = new ArrayList<>();
+        captureStages.add(captureStage);
+        return captureStages;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        CaptureProcessorImpl captureProcessor =
+                new CaptureProcessorImpl() {
+                    private ImageWriter mImageWriter;
+
+                    @Override
+                    public void onOutputSurface(Surface surface, int imageFormat) {
+                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                            mImageWriter = ImageWriter.newInstance(surface, 1);
+                        }
+                    }
+
+                    @Override
+                    public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results) {
+                        Log.d(TAG, "Started auto CaptureProcessor");
+
+                        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 = mImageWriter.dequeueInputImage();
+
+                                // Do processing here
+                                ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer();
+                                ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer();
+                                ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer();
+
+                                // Sample here just simply copy/paste the capture image result
+                                yByteBuffer.put(result.first.getPlanes()[0].getBuffer());
+                                uByteBuffer.put(result.first.getPlanes()[2].getBuffer());
+                                vByteBuffer.put(result.first.getPlanes()[1].getBuffer());
+
+                                mImageWriter.queueInputImage(image);
+                            }
+                        }
+
+                        // Close all input images
+                        for (Pair<Image, TotalCaptureResult> imageDataPair : results.values()) {
+                            imageDataPair.first.close();
+                        }
+
+                        Log.d(TAG, "Completed auto CaptureProcessor");
+                    }
+
+                    @Override
+                    public void onResolutionUpdate(Size size) {
+
+                    }
+
+                    @Override
+                    public void onImageFormatUpdate(int imageFormat) {
+
+                    }
+                };
+        return captureProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getMaxCaptureStage() {
+        return 3;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+}
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
new file mode 100755
index 0000000..5018df8
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+import android.util.Size;
+
+import java.util.List;
+
+/**
+ * Implementation for auto preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class AutoPreviewExtenderImpl implements PreviewExtenderImpl {
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_DAYLIGHT;
+
+    /**
+     * @hide
+     */
+    public AutoPreviewExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        return false;
+    }
+
+    public boolean isExtensionAvailableOriginal(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Implement the logic to check whether the extension function is supported or not.
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorType getProcessorType() {
+        return ProcessorType.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorImpl getProcessor() {
+        return RequestUpdateProcessorImpls.noUpdateProcessor();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+}
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
new file mode 100755
index 0000000..7297314
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation for beauty image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    private static final String TAG = "BeautyICExtender";
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_TWILIGHT;
+
+    private CameraCharacteristics mCameraCharacteristics;
+
+    public BeautyImageCaptureExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        mCameraCharacteristics = cameraCharacteristics;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        return false;
+    }
+
+    public boolean isExtensionAvailableOriginal(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Requires API 23 for ImageWriter
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            return false;
+        }
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        // Placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+        List<CaptureStageImpl> captureStages = new ArrayList<>();
+        captureStages.add(captureStage);
+        return captureStages;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        CaptureProcessorImpl captureProcessor =
+                new CaptureProcessorImpl() {
+                    private ImageWriter mImageWriter;
+
+                    @Override
+                    public void onOutputSurface(Surface surface, int imageFormat) {
+                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                            mImageWriter = ImageWriter.newInstance(surface, 1);
+                        }
+                    }
+
+                    @Override
+                    public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results) {
+                        Log.d(TAG, "Started beauty CaptureProcessor");
+
+                        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 = mImageWriter.dequeueInputImage();
+
+                                // Do processing here
+                                ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer();
+                                ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer();
+                                ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer();
+
+                                // Sample here just simply copy/paste the capture image result
+                                yByteBuffer.put(result.first.getPlanes()[0].getBuffer());
+                                uByteBuffer.put(result.first.getPlanes()[2].getBuffer());
+                                vByteBuffer.put(result.first.getPlanes()[1].getBuffer());
+
+                                mImageWriter.queueInputImage(image);
+                            }
+                        }
+
+                        // Close all input images
+                        for (Pair<Image, TotalCaptureResult> imageDataPair : results.values()) {
+                            imageDataPair.first.close();
+                        }
+
+                        Log.d(TAG, "Completed beauty CaptureProcessor");
+                    }
+
+                    @Override
+                    public void onResolutionUpdate(Size size) {
+
+                    }
+
+                    @Override
+                    public void onImageFormatUpdate(int imageFormat) {
+
+                    }
+                };
+        return captureProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getMaxCaptureStage() {
+        return 3;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        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;
+    }
+}
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
new file mode 100755
index 0000000..fcc78d5
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Pair;
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation for beauty preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl {
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_TWILIGHT;
+
+    /**
+     * @hide
+     */
+    private CameraCharacteristics mCameraCharacteristics;
+
+    /**
+     * @hide
+     */
+    public BeautyPreviewExtenderImpl() {
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        mCameraCharacteristics = cameraCharacteristics;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        return false;
+    }
+
+    public boolean isExtensionAvailableOriginal(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Implement the logic to check whether the extension function is supported or not.
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorType getProcessorType() {
+        return ProcessorType.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorImpl getProcessor() {
+        return RequestUpdateProcessorImpls.noUpdateProcessor();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        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 PRIVATE format to return.
+            Size[] outputSizes = map.getOutputSizes(ImageFormat.PRIVATE);
+
+            if (outputSizes != null) {
+                formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, outputSizes));
+            }
+        }
+
+        return formatResolutionsPairList;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+}
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
new file mode 100644
index 0000000..db57461
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation for bokeh image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    private static final String TAG = "BokehICExtender";
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_SHADE;
+
+    /**
+     * @hide
+     */
+    public BokehImageCaptureExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Requires API 23 for ImageWriter
+        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
+            return false;
+        }
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        // Placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+        List<CaptureStageImpl> captureStages = new ArrayList<>();
+        captureStages.add(captureStage);
+        return captureStages;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        CaptureProcessorImpl captureProcessor =
+                new CaptureProcessorImpl() {
+                    private ImageWriter mImageWriter;
+
+                    @Override
+                    public void onOutputSurface(Surface surface, int imageFormat) {
+                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                            mImageWriter = ImageWriter.newInstance(surface, 1);
+                        }
+                    }
+
+                    @Override
+                    public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results) {
+                        Log.d(TAG, "Started bokeh CaptureProcessor");
+
+                        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 (android.os.Build.VERSION.SDK_INT
+                                    >= android.os.Build.VERSION_CODES.M) {
+                                Image image = mImageWriter.dequeueInputImage();
+
+                                // Do processing here
+                                ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer();
+                                ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer();
+                                ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer();
+
+                                // Sample here just simply copy/paste the capture image result
+                                yByteBuffer.put(result.first.getPlanes()[0].getBuffer());
+                                uByteBuffer.put(result.first.getPlanes()[2].getBuffer());
+                                vByteBuffer.put(result.first.getPlanes()[1].getBuffer());
+
+                                mImageWriter.queueInputImage(image);
+                            }
+                        }
+
+                        // Close all input images
+                        for (Pair<Image, TotalCaptureResult> imageDataPair : results.values()) {
+                            imageDataPair.first.close();
+                        }
+
+                        Log.d(TAG, "Completed bokeh CaptureProcessor");
+                    }
+
+                    @Override
+                    public void onResolutionUpdate(Size size) {
+
+                    }
+
+                    @Override
+                    public void onImageFormatUpdate(int imageFormat) {
+
+                    }
+                };
+        return captureProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getMaxCaptureStage() {
+        return 3;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+}
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
new file mode 100644
index 0000000..8c43d34
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.util.List;
+
+/**
+ * Implementation for bokeh preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class BokehPreviewExtenderImpl implements PreviewExtenderImpl {
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_SHADE;
+
+    SettableCaptureStage mCaptureStage;
+
+    /**
+     * @hide
+     */
+    public BokehPreviewExtenderImpl() {}
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        mCaptureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        mCaptureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE,
+                CaptureRequest.CONTROL_AWB_MODE_AUTO);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Implement the logic to check whether the extension function is supported or not.
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        return mCaptureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorType getProcessorType() {
+        return ProcessorType.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY;
+    }
+
+    /**
+     * @hide
+     */
+    // Switches effect every 90 frames
+    private RequestUpdateProcessorImpl mRequestUpdateProcessor = new RequestUpdateProcessorImpl() {
+        private int mFrameCount = 0;
+        private Integer mWBMode = CaptureRequest.CONTROL_AWB_MODE_AUTO;
+
+        @Override
+        public CaptureStageImpl process(TotalCaptureResult result) {
+            mFrameCount++;
+            if (mFrameCount % 90 == 0) {
+                mCaptureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+                switch (mWBMode) {
+                    case CaptureRequest.CONTROL_AWB_MODE_AUTO:
+                        mWBMode = MODE;
+                        break;
+                    case MODE:
+                        mWBMode = CaptureRequest.CONTROL_AWB_MODE_AUTO;
+                        break;
+                    default:
+                }
+                mCaptureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE,
+                        mWBMode);
+                mFrameCount = 0;
+
+                return mCaptureStage;
+            }
+
+            return null;
+        }
+
+        @Override
+        public void onOutputSurface(Surface surface, int imageFormat) {}
+
+        @Override
+        public void onResolutionUpdate(Size size) {}
+
+        @Override
+        public void onImageFormatUpdate(int imageFormat) {}
+    };
+
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorImpl getProcessor() {
+        return mRequestUpdateProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CameraCharacteristicAvailability.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CameraCharacteristicAvailability.java
new file mode 100644
index 0000000..0b51c7c
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CameraCharacteristicAvailability.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 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;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * A utility class to check the availabilities of camera characteristics.
+ */
+final class CameraCharacteristicAvailability {
+    private static final String TAG = "CharacteristicAbility";
+
+    private CameraCharacteristicAvailability() {
+    }
+
+    /**
+     * Check if the given white balance mode id is available in the camera characteristics.
+     *
+     * @param cameraCharacteristics the camera characteristics.
+     * @param mode white balance mode id.
+     * @return {@code true} if the given white balance mode id is available in the camera
+     *                      characteristics.
+     * {@code false} otherwise.
+     */
+    static boolean isWBModeAvailable(CameraCharacteristics cameraCharacteristics,
+            int mode) {
+        int[] availableModes = cameraCharacteristics.get(
+                CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
+        if (availableModes == null) {
+            Log.d(TAG, "No CONTROL_AWB_AVAILABLE_MODES info");
+            return false;
+        }
+
+        for (int availableMode : availableModes) {
+            if (availableMode == mode) {
+                return true;
+            }
+        }
+        Log.d(TAG, "wb mode: " + mode + " is not in available list "
+                + Arrays.toString(availableModes));
+        return false;
+    }
+
+    /**
+     * Check if the given effect id is available in the camera characteristics.
+     *
+     * @param cameraCharacteristics the camera characteristics.
+     * @param effect the effect id.
+     * @return {@code true} if the given effect id is available in the camera characteristics.
+     * {@code false} otherwise.
+     */
+    static boolean isEffectAvailable(CameraCharacteristics cameraCharacteristics,
+            int effect) {
+        int[] availableEffects = cameraCharacteristics.get(
+                CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS);
+        if (availableEffects == null) {
+            Log.d(TAG, "No CONTROL_AVAILABLE_EFFECTS info");
+            return false;
+        }
+
+        for (int availableEffect : availableEffects) {
+            if (availableEffect == effect) {
+                return true;
+            }
+        }
+        Log.d(TAG, "effect: " + effect + " is not in available list "
+                + Arrays.toString(availableEffects));
+        return false;
+    }
+}
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
new file mode 100644
index 0000000..1a257e2
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 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;
+
+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;
+
+/**
+ * The interface for processing a set of {@link Image}s that have captured.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface CaptureProcessorImpl {
+    /**
+     * This gets called to update where the CaptureProcessor should write the output of {@link
+     * #process(Map)}.
+     *
+     * @param surface The {@link Surface} that the CaptureProcessor should write data into.
+     * @param imageFormat The format of that the surface expects.
+     * @hide
+     */
+    void onOutputSurface(Surface surface, int imageFormat);
+
+    /**
+     * Process a set images captured that were requested.
+     *
+     * <p> The result of the processing step should be written to the {@link Surface} that was
+     * received by {@link #onOutputSurface(Surface, int)}.
+     *
+     * @param results The map of 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.
+     * @hide
+     */
+    void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
+
+    /**
+     * This callback will be invoked when CameraX changes the configured input resolution. After
+     * this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as input
+     * to be at the specified resolution.
+     *
+     * @param size for the surface.
+     * @hide
+     */
+    void onResolutionUpdate(Size size);
+
+    /**
+     * This callback will be invoked when CameraX changes the configured input image format.
+     * After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input to have the specified image format.
+     *
+     * @param imageFormat for the surface.
+     * @hide
+     */
+    void onImageFormatUpdate(int imageFormat);
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java
new file mode 100644
index 0000000..268a49d
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+
+import java.util.List;
+
+/**
+ * The set of parameters that defines a single capture that will be sent to the camera.
+ *
+ * @since 1.0
+     * @hide
+ */
+public interface CaptureStageImpl {
+    /** Returns the identifier for the {@link CaptureStageImpl}. */
+    /**
+     * @hide
+     */
+    int getId();
+
+    /**
+     * Returns the set of {@link CaptureRequest.Key} and the corresponding values that will be
+     * set for a single {@link CaptureRequest}.
+     */
+    /**
+     * @hide
+     */
+    List<Pair<CaptureRequest.Key, Object>> getParameters();
+}
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
new file mode 100644
index 0000000..f926cff
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+
+/**
+ * Provides interfaces that the OEM needs to implement to handle the state change.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface ExtenderStateListener {
+
+    /**
+     * Notify to initialize the extension. This will be called after bindToLifeCycle. This is
+     * where the use case is started and would be able to allocate resources here. After onInit() is
+     * called, the camera ID, cameraCharacteristics and context will not change until onDeInit()
+     * has been called.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @param context The {@link Context} used for CameraX.
+     * @hide
+     */
+    void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, Context context);
+
+    /**
+     * Notify to de-initialize the extension. This callback will be invoked after unbind.
+     * After onDeInit() was called, it is expected that the camera ID, cameraCharacteristics will
+     * no longer hold, this should be where to clear all resources allocated for this use case.
+     * @hide
+     */
+    void onDeInit();
+
+    /**
+     * This will be invoked before creating a
+     * {@link android.hardware.camera2.CameraCaptureSession}. The {@link CaptureRequest}
+     * parameters returned via {@link CaptureStageImpl} will be passed to the camera device as
+     * part of the capture session initialization via setSessionParameters(). The valid parameter
+     * is a subset of the available capture request parameters.
+     *
+     * @return The request information to set the session wide camera parameters.
+     * @hide
+     */
+    CaptureStageImpl onPresetSession();
+
+    /**
+     * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession}
+     * has been created. The {@link CaptureRequest} parameters returned via
+     * {@link CaptureStageImpl} will be used to generate a single request to the current
+     * configured {@link CameraDevice}. The generated request will be submitted to camera before
+     * processing other single requests.
+     *
+     * @return The request information to create a single capture request to camera device.
+     * @hide
+     */
+    CaptureStageImpl onEnableSession();
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * closed. The {@link CaptureRequest} parameters returned via {@link CaptureStageImpl} will
+     * be used to generate a single request to the currently configured {@link CameraDevice}. The
+     * generated request will be submitted to camera before the CameraCaptureSession is closed.
+     *
+     * @return The request information to customize the session.
+     * @hide
+     */
+    CaptureStageImpl onDisableSession();
+}
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
new file mode 100644
index 0000000..76cc2c4
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 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;
+
+import android.util.Log;
+
+/**
+ * Implementation for extension version check.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public class ExtensionVersionImpl {
+    private static final String TAG = "ExtenderVersionImpl";
+    private static final String VERSION = "1.1.0";
+
+    /**
+     * @hide
+     */
+    public ExtensionVersionImpl() {
+    }
+
+    /**
+     * Provide the current CameraX extension library version to vendor library and vendor would
+     * need to return the supported version for this device. If the returned version is not
+     * supported by CameraX library, the Preview and ImageCapture would not be able to enable the
+     * specific effects provided by the vendor.
+     *
+     * <p>CameraX library provides the Semantic Versioning string in a form of
+     * MAJOR.MINOR.PATCH-description
+     * We will increment the
+     * MAJOR version when make incompatible API changes,
+     * MINOR version when add functionality in a backwards-compatible manner, and
+     * PATCH version when make backwards-compatible bug fixes. And the description can be ignored.
+     *
+     * <p>Vendor library should provide MAJOR.MINOR.PATCH to CameraX. The MAJOR and MINOR
+     * version is used to map to the version of CameraX that it supports, and CameraX extension
+     * would only available when MAJOR version is matched with CameraX current version. The PATCH
+     * version does not indicate compatibility. The patch version should be incremented whenever
+     * the vendor library makes bug fixes or updates to the algorithm.
+     *
+     * @param version the version of CameraX library formatted as MAJOR.MINOR.PATCH-description.
+     * @return the version that vendor supported in this device. The MAJOR.MINOR.PATCH format
+     * should be used.
+     */
+    /**
+     * @hide
+     */
+    public String checkApiVersion(String version) {
+        Log.d(TAG, "Extension device library version " + VERSION);
+        return VERSION;
+    }
+}
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
new file mode 100644
index 0000000..d6f017f
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementation for HDR image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    private static final String TAG = "HdrImageCaptureExtender";
+    private static final int UNDER_STAGE_ID = 0;
+    private static final int NORMAL_STAGE_ID = 1;
+    private static final int OVER_STAGE_ID = 2;
+    private static final int SESSION_STAGE_ID = 101;
+
+    /**
+     * @hide
+     */
+    public HdrImageCaptureExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Requires API 23 for ImageWriter
+        return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        // Under exposed capture stage
+        SettableCaptureStage captureStageUnder = new SettableCaptureStage(UNDER_STAGE_ID);
+        // Turn off AE so that ISO sensitivity can be controlled
+        captureStageUnder.addCaptureRequestParameters(CaptureRequest.CONTROL_AE_MODE,
+                CaptureRequest.CONTROL_AE_MODE_OFF);
+        captureStageUnder.addCaptureRequestParameters(CaptureRequest.SENSOR_EXPOSURE_TIME,
+                TimeUnit.MILLISECONDS.toNanos(8));
+
+        // Normal exposed capture stage
+        SettableCaptureStage captureStageNormal = new SettableCaptureStage(NORMAL_STAGE_ID);
+        captureStageNormal.addCaptureRequestParameters(CaptureRequest.SENSOR_EXPOSURE_TIME,
+                TimeUnit.MILLISECONDS.toNanos(16));
+
+        // Over exposed capture stage
+        SettableCaptureStage captureStageOver = new SettableCaptureStage(OVER_STAGE_ID);
+        captureStageOver.addCaptureRequestParameters(CaptureRequest.SENSOR_EXPOSURE_TIME,
+                TimeUnit.MILLISECONDS.toNanos(32));
+
+        List<CaptureStageImpl> captureStages = new ArrayList<>();
+        captureStages.add(captureStageUnder);
+        captureStages.add(captureStageNormal);
+        captureStages.add(captureStageOver);
+        return captureStages;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        CaptureProcessorImpl captureProcessor =
+                new CaptureProcessorImpl() {
+                    private ImageWriter mImageWriter;
+
+                    @Override
+                    public void onOutputSurface(Surface surface, int imageFormat) {
+                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                            mImageWriter = ImageWriter.newInstance(surface, 1);
+                        }
+                    }
+
+                    @Override
+                    public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results) {
+                        Log.d(TAG, "Started HDR CaptureProcessor");
+
+                        // Check for availability of all requested images
+                        if (!results.containsKey(UNDER_STAGE_ID)) {
+                            Log.w(TAG,
+                                    "Unable to process since images does not contain "
+                                            + "underexposed image.");
+                            return;
+                        }
+
+                        if (!results.containsKey(NORMAL_STAGE_ID)) {
+                            Log.w(TAG,
+                                    "Unable to process since images does not contain normal "
+                                            + "exposed image.");
+                            return;
+                        }
+
+                        if (!results.containsKey(OVER_STAGE_ID)) {
+                            Log.w(TAG,
+                                    "Unable to process since images does not contain "
+                                            + "overexposed image.");
+                            return;
+                        }
+
+                        // Do processing of images, our placeholder logic just copies the first
+                        // Image into the output buffer.
+                        List<Pair<Image, TotalCaptureResult>> imageDataPairs = new ArrayList<>(
+                                results.values());
+                        Image image = null;
+                        if (android.os.Build.VERSION.SDK_INT
+                                >= android.os.Build.VERSION_CODES.M) {
+                            image = mImageWriter.dequeueInputImage();
+
+                            // Do processing here
+                            ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer();
+                            ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer();
+                            ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer();
+
+                            // Sample here just simply return the normal image result
+                            yByteBuffer.put(imageDataPairs.get(1).first.getPlanes()[0].getBuffer());
+                            uByteBuffer.put(imageDataPairs.get(1).first.getPlanes()[2].getBuffer());
+                            vByteBuffer.put(imageDataPairs.get(1).first.getPlanes()[1].getBuffer());
+
+                            mImageWriter.queueInputImage(image);
+                        }
+
+                        // Close all input images
+                        for (Pair<Image, TotalCaptureResult> imageDataPair : imageDataPairs) {
+                            imageDataPair.first.close();
+                        }
+
+                        Log.d(TAG, "Completed HDR CaptureProcessor");
+                    }
+
+                    @Override
+                    public void onResolutionUpdate(Size size) {
+
+                    }
+
+                    @Override
+                    public void onImageFormatUpdate(int imageFormat) {
+
+                    }
+                };
+        return captureProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getMaxCaptureStage() {
+        return 4;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+}
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
new file mode 100644
index 0000000..2a47109
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.ImageWriter;
+import android.media.Image;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.util.List;
+
+/**
+ * Implementation for HDR preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl {
+    private static final int DEFAULT_STAGE_ID = 0;
+
+    ImageWriter mWriter;
+
+    /**
+     * @hide
+     */
+    public HdrPreviewExtenderImpl() { }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Implement the logic to check whether the extension function is supported or not.
+        return true;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorType getProcessorType() {
+        return ProcessorType.PROCESSOR_TYPE_IMAGE_PROCESSOR;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorImpl getProcessor() {
+        return mProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    private PreviewImageProcessorImpl mProcessor = new PreviewImageProcessorImpl() {
+        Surface mSurface;
+        int mFormat = -1;
+
+        private void setWindowSurface() {
+            if (mSurface != null && mFormat >= 0) {
+                if (mWriter != null) {
+                    mWriter.close();
+                }
+
+                mWriter = ImageWriter.newInstance(mSurface, 2, mFormat);
+            }
+        }
+
+        @Override
+        public void onOutputSurface(Surface surface, int imageFormat) {
+            mSurface = surface;
+            mFormat = imageFormat;
+            setWindowSurface();
+        }
+
+        @Override
+        public void process(Image image, TotalCaptureResult result) {
+            mWriter.queueInputImage(image);
+        }
+
+        @Override
+        public void onResolutionUpdate(Size size) {
+        }
+
+        @Override
+        public void onImageFormatUpdate(int imageFormat) {
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+        if (mWriter != null) {
+            mWriter.close();
+            mWriter = null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        return null;
+    }
+}
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
new file mode 100644
index 0000000..b70038f
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 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;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import java.util.List;
+
+/**
+ * Provides abstract methods that the OEM needs to implement to enable extensions for image capture.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface ImageCaptureExtenderImpl extends ExtenderStateListener {
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     * @hide
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * Initializes the extender to be used with the specified camera.
+     *
+     * <p>This should be called before any other method on the extender. The exception is {@link
+     * #isExtensionAvailable(String, CameraCharacteristics)}.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @hide
+     */
+    void init(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * The processing that will be done on a set of captures to create and image with the effect.
+     * @hide
+     */
+    CaptureProcessorImpl getCaptureProcessor();
+
+    /**
+     * @hide
+     */
+    /** The set of captures that are needed to create an image with the effect. */
+    List<CaptureStageImpl> getCaptureStages();
+
+    /**
+     * Returns the maximum size of the list returned by {@link #getCaptureStages()}.
+     * @return the maximum count.
+     * @hide
+     */
+    int getMaxCaptureStage();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <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. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     * @hide
+     */
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
+}
+
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/InitializerImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/InitializerImpl.java
new file mode 100644
index 0000000..fe2afd5
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/InitializerImpl.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * Used for initializing the extensions library.
+ *
+ * @since 1.1
+ * @hide
+ */
+public class InitializerImpl {
+    private InitializerImpl() {
+    }
+
+    private static final String TAG = "InitializerImpl";
+    /**
+     * An unknown error has occurred.
+     * @hide
+     */
+    public static final int ERROR_UNKNOWN = 0;
+    /**
+     * Error reported if the application version of extensions is incompatible with the on device
+     * library version.
+     * @hide
+     */
+    public static final int ERROR_INITIALIZE_VERSION_INCOMPATIBLE = 1;
+    private static Executor sExecutor = Executors.newSingleThreadExecutor();
+
+    /**
+     * Initializes the {@link Context}.
+     *
+     * <p>Before this call has been made no calls to the extensions library should be made except
+     * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+     *
+     * @param version  The version of the extension used by the application.
+     * @param context  The {@link Context} of the calling application.
+     * @param executor The executor to run the callback on. If null then the callback will run on
+     *                 any arbitrary executor.
+     * @hide
+     */
+    public static void init(String version, Context context,
+            OnExtensionsInitializedCallback callback, Executor executor) {
+        Log.d(TAG, "initializing extensions");
+        if (executor == null) {
+            sExecutor.execute(callback::onSuccess);
+        } else {
+            executor.execute(callback::onSuccess);
+        }
+    }
+
+    /**
+     * Deinitializes the extensions to release resources.
+     *
+     * <p>After this call has been made no calls to the extensions library should be made except
+     * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+     *
+     * @param executor The executor to run the callback on. If null then the callback will run on
+     *                 any arbitrary executor.
+     * @hide
+     */
+    public static void deinit(OnExtensionsDeinitializedCallback callback,
+            Executor executor) {
+        Log.d(TAG, "deinitializing extensions");
+        if (executor == null) {
+            sExecutor.execute(callback::onSuccess);
+        } else {
+            executor.execute(callback::onSuccess);
+        }
+    }
+
+    /**
+     * Callback that gets called when the library has finished initializing and is ready for used.
+     * @hide
+     */
+    public interface OnExtensionsInitializedCallback {
+        /** Called if the library successfully initializes. */
+        void onSuccess();
+
+        /**
+         * Called if the library is unable to successfully initialize.
+         *
+         * @param error The reason for failing to initialize.
+         */
+        void onFailure(int error);
+    }
+
+    /**
+     * Callback that gets called when the library has finished deinitialized.
+     *
+     * <p> Once this interface has been called then
+     * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} can be called
+     * again regardless of whether or not the deinitialization has succeeded or failed.
+     * @hide
+     */
+    public interface OnExtensionsDeinitializedCallback {
+        /** Called if the library successfully deinitializes. */
+        void onSuccess();
+
+        /**
+         * Called if the library encountered some error during the deinitialization.
+         *
+         * <p>Even if the library fails to deinitialize it is now valid for {@link
+         * #init(String, Context, OnExtensionsInitializedCallback, Executor)} to be called
+         * again.
+         *
+         * @param error The reason for failing to deinitialize.
+         */
+        void onFailure(int error);
+    }
+}
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
new file mode 100755
index 0000000..3d00a46
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation for night image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class NightImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    private static final String TAG = "NightICExtender";
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_INCANDESCENT;
+
+    /**
+     * @hide
+     */
+    public NightImageCaptureExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        return false;
+    }
+
+    public boolean isExtensionAvailableOriginal(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Requires API 23 for ImageWriter
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            return false;
+        }
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        // Placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+        List<CaptureStageImpl> captureStages = new ArrayList<>();
+        captureStages.add(captureStage);
+        return captureStages;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        CaptureProcessorImpl captureProcessor =
+                new CaptureProcessorImpl() {
+                    private ImageWriter mImageWriter;
+
+                    @Override
+                    public void onOutputSurface(Surface surface, int imageFormat) {
+                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                            mImageWriter = ImageWriter.newInstance(surface, 1);
+                        }
+                    }
+
+                    @Override
+                    public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results) {
+                        Log.d(TAG, "Started night CaptureProcessor");
+
+                        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 = mImageWriter.dequeueInputImage();
+
+                                // Do processing here
+                                ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer();
+                                ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer();
+                                ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer();
+
+                                // Sample here just simply copy/paste the capture image result
+                                yByteBuffer.put(result.first.getPlanes()[0].getBuffer());
+                                uByteBuffer.put(result.first.getPlanes()[2].getBuffer());
+                                vByteBuffer.put(result.first.getPlanes()[1].getBuffer());
+
+                                mImageWriter.queueInputImage(image);
+                            }
+                        }
+
+                        // Close all input images
+                        for (Pair<Image, TotalCaptureResult> imageDataPair : results.values()) {
+                            imageDataPair.first.close();
+                        }
+
+                        Log.d(TAG, "Completed night CaptureProcessor");
+                    }
+
+                    @Override
+                    public void onResolutionUpdate(Size size) {
+
+                    }
+
+                    @Override
+                    public void onImageFormatUpdate(int imageFormat) {
+
+                    }
+                };
+        return captureProcessor;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getMaxCaptureStage() {
+        return 3;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+}
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
new file mode 100755
index 0000000..e29abec
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+import android.util.Size;
+
+import java.util.List;
+
+/**
+ * Implementation for night preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers
+ * don't need to implement this, unless this is used for related testing usage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public final class NightPreviewExtenderImpl implements PreviewExtenderImpl {
+    private static final int DEFAULT_STAGE_ID = 0;
+    private static final int SESSION_STAGE_ID = 101;
+    private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_INCANDESCENT;
+
+    /**
+     * @hide
+     */
+    public NightPreviewExtenderImpl() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        return false;
+    }
+
+    public boolean isExtensionAvailableOriginal(String cameraId,
+            CameraCharacteristics cameraCharacteristics) {
+        // Implement the logic to check whether the extension function is supported or not.
+
+        if (cameraCharacteristics == null) {
+            return false;
+        }
+
+        return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, MODE);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(DEFAULT_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorType getProcessorType() {
+        return ProcessorType.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ProcessorImpl getProcessor() {
+        return RequestUpdateProcessorImpls.noUpdateProcessor();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void onDeInit() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        // Set the necessary CaptureRequest parameters via CaptureStage, here we use some
+        // placeholder set of CaptureRequest.Key values
+        SettableCaptureStage captureStage = new SettableCaptureStage(SESSION_STAGE_ID);
+        captureStage.addCaptureRequestParameters(CaptureRequest.CONTROL_AWB_MODE, MODE);
+
+        return captureStage;
+    }
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
new file mode 100644
index 0000000..6b7be85
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 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;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+
+import java.util.List;
+
+/**
+ * Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface PreviewExtenderImpl extends ExtenderStateListener {
+    /** The different types of the preview processing. */
+    enum ProcessorType {
+        /** Processor which only updates the {@link CaptureStageImpl}. */
+        PROCESSOR_TYPE_REQUEST_UPDATE_ONLY,
+        /** Processor which updates the received {@link android.media.Image}. */
+        PROCESSOR_TYPE_IMAGE_PROCESSOR,
+        /** No processor, only a {@link CaptureStageImpl} is defined. */
+        PROCESSOR_TYPE_NONE
+    }
+
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * Initializes the extender to be used with the specified camera.
+     *
+     * <p>This should be called before any other method on the extender. The exception is {@link
+     * #isExtensionAvailable(String, CameraCharacteristics)}.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     */
+    void init(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * The set of parameters required to produce the effect on the preview stream.
+     *
+     * <p> This will be the initial set of parameters used for the preview
+     * {@link android.hardware.camera2.CaptureRequest}. If the {@link ProcessorType} is defined as
+     * {@link ProcessorType#PROCESSOR_TYPE_REQUEST_UPDATE_ONLY} then this will be updated when
+     * the {@link RequestUpdateProcessorImpl#process(TotalCaptureResult)} from {@link
+     * #getProcessor()} has been called, this should be updated to reflect the new {@link
+     * CaptureStageImpl}. If the processing step returns a {@code null}, meaning the required
+     * parameters has not changed, then calling this will return the previous non-null value.
+     */
+    CaptureStageImpl getCaptureStage();
+
+    /** The type of preview processing to use. */
+    ProcessorType getProcessorType();
+
+    /**
+     * Returns a processor which only updates the {@link CaptureStageImpl}.
+     *
+     * <p>The type of processor is dependent on the return of {@link #getProcessorType()}. The
+     * type of ProcessorImpl returned will be according to the following table.
+     *
+     * <table>
+     * <tr><th> ProcessorType </th> <th> ProcessorImpl </th> </tr>
+     * <tr><td> PROCESSOR_TYPE_REQUEST_UPDATE_ONLY </td> <td> RequestUpdateProcessorImpl </td> </tr>
+     * <tr><td> PROCESSOR_TYPE_IMAGE_PROCESSOR </td> <td> PreviewImageProcessorImpl </td> </tr>
+     * <tr><td> PROCESSOR_TYPE_NONE </td> <td> null </td> </tr>
+     * </table>
+     */
+    ProcessorImpl getProcessor();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <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. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
new file mode 100644
index 0000000..3c750db
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+
+/**
+ * Processing a single {@link Image} and {@link TotalCaptureResult} to produce an output to a
+ * stream.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface PreviewImageProcessorImpl extends ProcessorImpl {
+    /**
+     * Processes the requested image capture.
+     *
+     * <p> The result of the processing step should be written to the {@link android.view.Surface}
+     * that was received by {@link ProcessorImpl#onOutputSurface(android.view.Surface, int)}.
+     *
+     * @param image The image to process. This will be invalid after the method completes so no
+     *              reference to it should be kept.
+     * @param result The metadata associated with the image to process.
+     * @hide
+     */
+    void process(Image image, TotalCaptureResult result);
+}
+
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessorImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessorImpl.java
new file mode 100644
index 0000000..c97e6ed
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessorImpl.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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;
+
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * Processes an input image stream and produces an output image stream.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface ProcessorImpl {
+    /**
+     * Updates where the ProcessorImpl should write the output to.
+     *
+     * @param surface     The {@link Surface} that the ProcessorImpl should write data into.
+     * @param imageFormat The format of that the surface expects.
+     */
+    void onOutputSurface(Surface surface, int imageFormat);
+
+    /**
+     * Invoked when CameraX changes the configured output resolution.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input to be at the specified resolution.
+     *
+     * @param size for the surface.
+     */
+    void onResolutionUpdate(Size size);
+
+    /**
+     * Invoked when CameraX changes the configured input image format.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input to have the specified image format.
+     *
+     * @param imageFormat for the surface.
+     */
+    void onImageFormatUpdate(int imageFormat);
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java
new file mode 100644
index 0000000..4edf05f
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.TotalCaptureResult;
+
+/**
+ * Processes a {@link TotalCaptureResult} to update a CaptureStage.
+ *
+ * @since 1.0
+ * @hide
+ */
+public interface RequestUpdateProcessorImpl extends ProcessorImpl {
+    /**
+     * Process the {@link TotalCaptureResult} to update the {@link CaptureStageImpl}
+     *
+     * @param result The metadata associated with the image. Can be null if the image and meta have
+     *               not been synced.
+     * @return The updated parameters used for the repeating requests. If this is {@code null} then
+     * the previous parameters will be used.
+     */
+    CaptureStageImpl process(TotalCaptureResult result);
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpls.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpls.java
new file mode 100644
index 0000000..afbad00
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpls.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.TotalCaptureResult;
+import android.util.Size;
+import android.view.Surface;
+
+class RequestUpdateProcessorImpls {
+    private static final RequestUpdateProcessorImpl sNoUpdateProcessor =
+            new RequestUpdateProcessorImpl() {
+                @Override
+                public CaptureStageImpl process(TotalCaptureResult result) {
+                    return null;
+                }
+
+                @Override
+                public void onOutputSurface(Surface surface, int imageFormat) {}
+
+                @Override
+                public void onResolutionUpdate(Size size) {}
+
+                @Override
+                public void onImageFormatUpdate(int imageFormat) {}
+            };
+
+    static RequestUpdateProcessorImpl noUpdateProcessor() {
+        return sNoUpdateProcessor;
+    }
+
+    private RequestUpdateProcessorImpls() {
+    }
+}
diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/SettableCaptureStage.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/SettableCaptureStage.java
new file mode 100644
index 0000000..7d5dcb1
--- /dev/null
+++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/SettableCaptureStage.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+final class SettableCaptureStage implements CaptureStageImpl {
+    private final int mId;
+
+    private Map<CaptureRequest.Key, Object> mCaptureRequestKeyValueMap = new HashMap<>();
+
+    /**
+     * Constructor for a {@link CaptureStageImpl} with specific identifier.
+     *
+     * <p>After this {@link CaptureStageImpl} is applied to a single capture operation,
+     * developers can
+     * retrieve the {@link android.media.Image} object with the identifier.
+     *
+     * @param id The identifier for the {@link CaptureStageImpl}.
+     */
+    SettableCaptureStage(int id) {
+        mId = id;
+    }
+
+    /**
+     * @hide
+     */
+    /** Returns the identifier for the {@link CaptureStageImpl}. */
+    @Override
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<Pair<CaptureRequest.Key, Object>> getParameters() {
+        List<Pair<CaptureRequest.Key, Object>> parameters = new ArrayList<>();
+
+        for (Map.Entry<CaptureRequest.Key, Object> entry : mCaptureRequestKeyValueMap.entrySet()) {
+            parameters.add(Pair.create(entry.getKey(), entry.getValue()));
+        }
+
+        return parameters;
+    }
+
+    /**
+     * Adds necessary {@link CaptureRequest.Key} settings into the {@link CaptureStageImpl} object.
+     */
+    <T> void addCaptureRequestParameters(CaptureRequest.Key<T> key, T value) {
+        mCaptureRequestKeyValueMap.put(key, value);
+    }
+}
diff --git a/camera2/extensions/stub/Android.bp b/camera2/extensions/stub/Android.bp
new file mode 100644
index 0000000..7ebfdea
--- /dev/null
+++ b/camera2/extensions/stub/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "androidx.camera.extensions.stub",
+    installable: true,
+
+    static_libs: ["androidx.annotation_annotation"],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "current",
+}
diff --git a/camera2/extensions/stub/src/main/AndroidManifest.xml b/camera2/extensions/stub/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..24ec9a7
--- /dev/null
+++ b/camera2/extensions/stub/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 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.
+  -->
+<manifest package="androidx.camera.extensions.impl"/>
\ No newline at end of file
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
new file mode 100755
index 0000000..a364d4b
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for auto image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    public AutoImageCaptureExtenderImpl() {}
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public int getMaxCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
+        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
new file mode 100755
index 0000000..100f665
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for auto preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class AutoPreviewExtenderImpl implements PreviewExtenderImpl {
+    public AutoPreviewExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorType getProcessorType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorImpl getProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        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
new file mode 100755
index 0000000..d68d8eb
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for beauty image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    public BeautyImageCaptureExtenderImpl() {}
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public int getMaxCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
+        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
new file mode 100755
index 0000000..bc3e48d
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for beauty preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl {
+    public BeautyPreviewExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorType getProcessorType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorImpl getProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        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
new file mode 100644
index 0000000..b943e0a
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for bokeh image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    public BokehImageCaptureExtenderImpl() {}
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public int getMaxCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
+        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
new file mode 100644
index 0000000..ff58862
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for bokeh preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class BokehPreviewExtenderImpl implements PreviewExtenderImpl {
+    public BokehPreviewExtenderImpl() {}
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorType getProcessorType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorImpl getProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        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
new file mode 100644
index 0000000..90de15a
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 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;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.util.Pair;
+import android.view.Surface;
+
+import java.util.Map;
+
+/**
+ * The interface for processing a set of {@link Image}s that have captured.
+ *
+ * @since 1.0
+ */
+public interface CaptureProcessorImpl extends ProcessorImpl {
+    /**
+     * Process a set images captured that were requested.
+     *
+     * <p> The result of the processing step should be written to the {@link Surface} that was
+     * received by {@link #onOutputSurface(Surface, int)}.
+     *
+     * @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.
+     */
+    void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureStageImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureStageImpl.java
new file mode 100644
index 0000000..c4796c2
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureStageImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+
+import java.util.List;
+
+/**
+ * The set of parameters that defines a single capture that will be sent to the camera.
+ *
+ * @since 1.0
+ */
+public interface CaptureStageImpl {
+    /** Returns the identifier for the {@link CaptureStageImpl}. */
+    int getId();
+
+    /**
+     * Returns the set of {@link CaptureRequest.Key} and the corresponding values that will be
+     * set for a single {@link CaptureRequest}.
+     */
+    List<Pair<CaptureRequest.Key, Object>> getParameters();
+}
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
new file mode 100644
index 0000000..2879568
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
+
+/**
+ * Provides interfaces that the OEM needs to implement to handle the state change.
+ *
+ * @since 1.0
+ */
+public interface ExtenderStateListener {
+
+    /**
+     * Notify to initialize the extension. This will be called after bindToLifeCycle. This is
+     * where the use case is started and would be able to allocate resources here. After onInit() is
+     * called, the camera ID, cameraCharacteristics and context will not change until onDeInit()
+     * has been called.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @param context The {@link Context} used for CameraX.
+     */
+    void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, Context context);
+
+    /**
+     * Notify to de-initialize the extension. This callback will be invoked after unbind.
+     * After onDeInit() was called, it is expected that the camera ID, cameraCharacteristics will
+     * no longer hold, this should be where to clear all resources allocated for this use case.
+     */
+    void onDeInit();
+
+    /**
+     * This will be invoked before creating a
+     * {@link android.hardware.camera2.CameraCaptureSession}. The {@link CaptureRequest}
+     * parameters returned via {@link CaptureStageImpl} will be passed to the camera device as
+     * part of the capture session initialization via
+     * {@link SessionConfiguration#setSessionParameters(CaptureRequest)} which only supported from
+     * API level 28. The valid parameter is a subset of the available capture request parameters.
+     *
+     * @return The request information to set the session wide camera parameters.
+     */
+    CaptureStageImpl onPresetSession();
+
+    /**
+     * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession}
+     * has been created. The {@link CaptureRequest} parameters returned via
+     * {@link CaptureStageImpl} will be used to generate a single request to the current
+     * configured {@link CameraDevice}. The generated request will be submitted to camera before
+     * processing other single requests.
+     *
+     * @return The request information to create a single capture request to camera device.
+     */
+    CaptureStageImpl onEnableSession();
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * closed. The {@link CaptureRequest} parameters returned via {@link CaptureStageImpl} will
+     * be used to generate a single request to the currently configured {@link CameraDevice}. The
+     * generated request will be submitted to camera before the CameraCaptureSession is closed.
+     *
+     * @return The request information to customize the session.
+     */
+    CaptureStageImpl onDisableSession();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
new file mode 100644
index 0000000..7769551
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 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;
+
+/**
+ * Stub implementation for the extension version check.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public class ExtensionVersionImpl {
+    public ExtensionVersionImpl() {
+    }
+
+    /**
+     * Provide the current CameraX extension library version to vendor library and vendor would
+     * need to return the supported version for this device. If the returned version is not
+     * supported by CameraX library, the Preview and ImageCapture would not be able to enable the
+     * specific effects provided by the vendor.
+     *
+     * <p>CameraX library provides the Semantic Versioning string in a form of
+     * MAJOR.MINOR.PATCH-description
+     * We will increment the
+     * MAJOR version when make incompatible API changes,
+     * MINOR version when add functionality in a backwards-compatible manner, and
+     * PATCH version when make backwards-compatible bug fixes. And the description can be ignored.
+     *
+     * <p>Vendor library should provide MAJOR.MINOR.PATCH to CameraX. The MAJOR and MINOR
+     * version is used to map to the version of CameraX that it supports, and CameraX extension
+     * would only available when MAJOR version is matched with CameraX current version. The PATCH
+     * version does not indicate compatibility. The patch version should be incremented whenever
+     * the vendor library makes bug fixes or updates to the algorithm.
+     *
+     * @param version the version of CameraX library formatted as MAJOR.MINOR.PATCH-description.
+     * @return the version that vendor supported in this device. The MAJOR.MINOR.PATCH format
+     * should be used.
+     */
+    public String checkApiVersion(String version) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    /**
+     * Specify whether or not CameraX should invoke the AdvancedExtenderImpl instead of
+     * PreviewExtenderImpl/ImageCaptureExtenderImpl.
+     *
+     * <p>Starting from version 1.2, a set of alternative interfaces called advanced extender for
+     * implementing extensions are provided to OEMs as another option. OEMs can continue using
+     * previous interfaces (PreviewExtenderImpl/ImageCaptureExtenderImpl, also called basic
+     * extender).
+     *
+     * <p>OEMs should return false here if only basic extender is implemented. When returning true,
+     * CameraX will invoke the AdvancedExtenderImpl implementation in advanced package for all
+     * types of extension modes.
+     *
+     * <p>ExtensionVersionImpl, InitializerImpl will still be called for both basic and advanced
+     * extender implementation paths.
+     *
+     * @return true if AdvancedExtenderImpl is implemented
+     * @since 1.2
+     */
+    public boolean isAdvancedExtenderImplemented() {
+        return false;
+    }
+}
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
new file mode 100644
index 0000000..66f4a50
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for HDR image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    public HdrImageCaptureExtenderImpl() {}
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public int getMaxCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
+        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
new file mode 100644
index 0000000..0eb4a61
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for HDR preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl {
+    public HdrPreviewExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorType getProcessorType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorImpl getProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        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
new file mode 100644
index 0000000..571c2e3
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Provides abstract methods that the OEM needs to implement to enable extensions for image capture.
+ *
+ * @since 1.0
+ */
+public interface ImageCaptureExtenderImpl extends ExtenderStateListener {
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * Initializes the extender to be used with the specified camera.
+     *
+     * <p>This should be called before any other method on the extender. The exception is {@link
+     * #isExtensionAvailable(String, CameraCharacteristics)}.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     */
+    void init(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * The processing that will be done on a set of captures to create and image with the effect.
+     */
+    CaptureProcessorImpl getCaptureProcessor();
+
+    /** The set of captures that are needed to create an image with the effect. */
+    List<CaptureStageImpl> getCaptureStages();
+
+    /**
+     * Returns the maximum size of the list returned by {@link #getCaptureStages()}.
+     * @return the maximum count.
+     */
+    int getMaxCaptureStage();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <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. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
+
+    /**
+     * Returns the estimated capture latency range in milliseconds for the target capture
+     * resolution.
+     *
+     * <p>This includes the time spent processing the multi-frame capture request along with any
+     * additional time for encoding of the processed buffer in the framework if necessary.
+     *
+     * @param captureOutputSize size of the capture output surface. If it is null or not in the
+     *                          supported output sizes, maximum capture output size is used for
+     *                          the estimation.
+     * @return the range of estimated minimal and maximal capture latency in milliseconds, or
+     * null if no capture latency info can be provided.
+     * @since 1.2
+     */
+    @Nullable
+    Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize);
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java
new file mode 100644
index 0000000..779a2ee
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Used for initializing the extensions library.
+ *
+ * @since 1.1
+ */
+public class InitializerImpl {
+    private InitializerImpl() {
+    }
+
+    /** An unknown error has occurred. */
+    public static final int ERROR_UNKNOWN = 0;
+
+    /**
+     * Error reported if the application version of extensions is incompatible with the on device
+     * library version.
+     */
+    public static final int ERROR_INITIALIZE_VERSION_INCOMPATIBLE = 1;
+
+    /**
+     * Initializes the {@link Context}.
+     *
+     * <p>Before this call has been made no calls to the extensions library should be made except
+     * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+     *
+     * @param version  The version of the extension used by the application.
+     * @param context  The {@link Context} of the calling application.
+     * @param executor The executor to run the callback on. If null then the callback will run on
+     *                 any arbitrary executor.
+     */
+    public static void init(@NonNull String version, @NonNull Context context,
+            @NonNull OnExtensionsInitializedCallback callback, @Nullable Executor executor) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    /**
+     * Deinitializes the extensions to release resources.
+     *
+     * <p>After this call has been made no calls to the extensions library should be made except
+     * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+     *
+     * @param executor The executor to run the callback on. If null then the callback will run on
+     *                 any arbitrary executor.
+     */
+    public static void deinit(@NonNull OnExtensionsDeinitializedCallback callback,
+            @Nullable Executor executor) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    /**
+     * Callback that gets called when the library has finished initializing and is ready for used.
+     */
+    public interface OnExtensionsInitializedCallback {
+        /** Called if the library successfully initializes. */
+        void onSuccess();
+
+        /**
+         * Called if the library is unable to successfully initialize.
+         *
+         * @param error The reason for failing to initialize.
+         */
+        void onFailure(int error);
+    }
+
+    /**
+     * Callback that gets called when the library has finished deinitialized.
+     *
+     * <p> Once this interface has been called then
+     * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} can be called
+     * again regardless of whether or not the deinitialization has succeeded or failed.
+     */
+    public interface OnExtensionsDeinitializedCallback {
+        /**
+         * Called if the library successfully deinitializes.
+         */
+        void onSuccess();
+
+        /**
+         * Called if the library encountered some error during the deinitialization.
+         *
+         * <p>Even if the library fails to deinitialize it is now valid for
+         * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} to be called
+         * again.
+         *
+         * @param error The reason for failing to deinitialize.
+         */
+        void onFailure(int error);
+    }
+}
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
new file mode 100755
index 0000000..3b39cf1
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for night image capture use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class NightImageCaptureExtenderImpl implements ImageCaptureExtenderImpl {
+    public NightImageCaptureExtenderImpl() {}
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureProcessorImpl getCaptureProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<CaptureStageImpl> getCaptureStages() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public int getMaxCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) {
+        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
new file mode 100755
index 0000000..a5809f6
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 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;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub implementation for night preview use case.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.0
+ */
+public final class NightPreviewExtenderImpl implements PreviewExtenderImpl {
+    public NightPreviewExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @Nullable CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl getCaptureStage() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorType getProcessorType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public ProcessorImpl getProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
+            Context context) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void onDeInit() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onPresetSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onEnableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public CaptureStageImpl onDisableSession() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
new file mode 100644
index 0000000..4324987
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2019 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;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
+ *
+ * @since 1.0
+ */
+public interface PreviewExtenderImpl extends ExtenderStateListener {
+    /** The different types of the preview processing. */
+    enum ProcessorType {
+        /** Processor which only updates the {@link CaptureStageImpl}. */
+        PROCESSOR_TYPE_REQUEST_UPDATE_ONLY,
+        /** Processor which updates the received {@link android.media.Image}. */
+        PROCESSOR_TYPE_IMAGE_PROCESSOR,
+        /** No processor, only a {@link CaptureStageImpl} is defined. */
+        PROCESSOR_TYPE_NONE
+    }
+
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * Initializes the extender to be used with the specified camera.
+     *
+     * <p>This should be called before any other method on the extender. The exception is {@link
+     * #isExtensionAvailable(String, CameraCharacteristics)}.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     */
+    void init(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
+     * The set of parameters required to produce the effect on the preview stream.
+     *
+     * <p> This will be the initial set of parameters used for the preview
+     * {@link android.hardware.camera2.CaptureRequest}. If the {@link ProcessorType} is defined as
+     * {@link ProcessorType#PROCESSOR_TYPE_REQUEST_UPDATE_ONLY} then this will be updated when
+     * the {@link RequestUpdateProcessorImpl#process(TotalCaptureResult)} from {@link
+     * #getProcessor()} has been called, this should be updated to reflect the new {@link
+     * CaptureStageImpl}. If the processing step returns a {@code null}, meaning the required
+     * parameters has not changed, then calling this will return the previous non-null value.
+     */
+    CaptureStageImpl getCaptureStage();
+
+    /** The type of preview processing to use. */
+    ProcessorType getProcessorType();
+
+    /**
+     * Returns a processor which only updates the {@link CaptureStageImpl}.
+     *
+     * <p>The type of processor is dependent on the return of {@link #getProcessorType()}. The
+     * type of ProcessorImpl returned will be according to the following table.
+     *
+     * <table>
+     * <tr><th> ProcessorType </th> <th> ProcessorImpl </th> </tr>
+     * <tr><td> PROCESSOR_TYPE_REQUEST_UPDATE_ONLY </td> <td> RequestUpdateProcessorImpl </td> </tr>
+     * <tr><td> PROCESSOR_TYPE_IMAGE_PROCESSOR </td> <td> PreviewImageProcessorImpl </td> </tr>
+     * <tr><td> PROCESSOR_TYPE_NONE </td> <td> null </td> </tr>
+     * </table>
+     */
+    ProcessorImpl getProcessor();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <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. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
new file mode 100644
index 0000000..e7ecaa1
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+
+/**
+ * Processes a single {@link Image} and {@link TotalCaptureResult} to produce an output to a
+ * stream.
+ *
+ * @since 1.0
+ */
+public interface PreviewImageProcessorImpl extends ProcessorImpl {
+    /**
+     * Processes the requested image capture.
+     *
+     * <p> The result of the processing step should be written to the {@link android.view.Surface}
+     * that was received by {@link ProcessorImpl#onOutputSurface(android.view.Surface, int)}.
+     *
+     * @param image  The {@link ImageFormat#YUV_420_888} format image to process. This will be
+     *               invalid after the method completes so no reference to it should be kept.
+     * @param result The metadata associated with the image to process.
+     */
+    void process(Image image, TotalCaptureResult result);
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessorImpl.java
new file mode 100644
index 0000000..6be328b
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessorImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 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;
+
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * Processes an input image stream and produces an output image stream.
+ *
+ * @since 1.0
+ */
+public interface ProcessorImpl {
+    /**
+     * Updates where the ProcessorImpl should write the output to.
+     *
+     * @param surface     The {@link Surface} that the ProcessorImpl should write data into.
+     * @param imageFormat The format of that the surface expects.
+     */
+    void onOutputSurface(Surface surface, int imageFormat);
+
+    /**
+     * Invoked when CameraX changes the configured output resolution.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input to be at the specified resolution.
+     *
+     * @param size for the surface.
+     */
+    void onResolutionUpdate(Size size);
+
+    /**
+     * Invoked when CameraX changes the configured input image format.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input to have the specified image format.
+     *
+     * @param imageFormat for the surface.
+     */
+    void onImageFormatUpdate(int imageFormat);
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java
new file mode 100644
index 0000000..14637d7
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 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;
+
+import android.hardware.camera2.TotalCaptureResult;
+
+/**
+ * Processes a {@link TotalCaptureResult} to update a CaptureStage.
+ *
+ * @since 1.0
+ */
+public interface RequestUpdateProcessorImpl extends ProcessorImpl {
+    /**
+     * Process the {@link TotalCaptureResult} to update the {@link CaptureStageImpl}
+     *
+     * @param result The metadata associated with the image. Can be null if the image and meta have
+     *               not been synced.
+     * @return The updated parameters used for the repeating requests. If this is {@code null} then
+     * the previous parameters will be used.
+     */
+    CaptureStageImpl process(TotalCaptureResult result);
+}
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
new file mode 100644
index 0000000..86424c4
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.camera.extensions.impl.ExtensionVersionImpl;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Advanced OEM contract for implementing Extensions. ImageCapture/Preview Extensions are both
+ * implemented on this interface.
+ *
+ * <p>This advanced OEM contract empowers OEM to gain access to more Camera2 capability. This
+ * includes: (1) Add custom surfaces with specific formats like YUV, RAW, RAW_DEPTH. (2) Access to
+ * the capture request callbacks as well as all the images retrieved of various image formats. (3)
+ * Able to triggers single or repeating request with the capabilities to specify target surfaces,
+ * template id and parameters.
+ *
+ * <p>OEM needs to implement it with class name HdrAdvancedExtenderImpl for HDR,
+ * NightAdvancedExtenderImpl for night mode, BeautyAdvancedExtenderImpl for beauty mode,
+ * BokehAdvancedExtenderImpl for bokeh mode and AutoAdvancedExtenderImpl for auto mode.
+ *
+ * <p>OEMs are required to return true in
+ * {@link ExtensionVersionImpl#isAdvancedExtenderImplemented()} in order to request CameraX to
+ * use advanced extender over basic extender. OEM is okay to implement advanced
+ * extender only Or basic extender only. However the caveat of advanced-only implementation is,
+ * extensions will be unavailable on the apps using interfaces prior to 1.2.
+ *
+ * @since 1.2
+ */
+@SuppressLint("UnknownNullness")
+public interface AdvancedExtenderImpl {
+
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId           The camera2 id string of the camera.
+     * @param characteristicsMap A map consisting of the camera ids and the
+     *                           {@link CameraCharacteristics}s. For every camera, the map
+     *                           contains at least the CameraCharacteristics for the camera id.
+     *                           If the camera is logical camera, it will also contain associated
+     *                           physical camera ids and their CameraCharacteristics.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap);
+
+    /**
+     * Initializes the extender to be used with the specified camera.
+     *
+     * <p>This should be called before any other method on the extender. The exception is {@link
+     * #isExtensionAvailable}.
+     *
+     * @param cameraId           The camera2 id string of the camera.
+     * @param characteristicsMap A map consisting of the camera ids and the
+     *                           {@link CameraCharacteristics}s. For every camera, the map
+     *                           contains at least the CameraCharacteristics for the camera id.
+     *                           If the camera is logical camera, it will also contain associated
+     *                           physical camera ids and their CameraCharacteristics.
+     */
+    void init(String cameraId, Map<String, CameraCharacteristics> characteristicsMap);
+
+    /**
+     * Returns the estimated capture latency range in milliseconds for the
+     * target capture resolution during the calls to
+     * {@link SessionProcessorImpl#startCapture}. This
+     * includes the time spent processing the multi-frame capture request along with any additional
+     * time for encoding of the processed buffer in the framework if necessary.
+     *
+     * @param cameraId          the camera id
+     * @param captureOutputSize size of the capture output surface. If it is null or not in the
+     *                          supported output sizes, maximum capture output size is used for
+     *                          the estimation.
+     * @param imageFormat the image format of the capture output surface.
+     * @return the range of estimated minimal and maximal capture latency in milliseconds.
+     * Returns null if no capture latency info can be provided.
+     */
+    Range<Long> getEstimatedCaptureLatencyRange(String cameraId,
+            Size captureOutputSize, int imageFormat);
+
+    /**
+     * Returns supported output format/size map for preview. The format could be PRIVATE or
+     * YUV_420_888. OEM must support PRIVATE format at least. CameraX will only use resolutions
+     * for preview from the list.
+     *
+     * <p>The preview surface format in the CameraCaptureSession may not be identical to the
+     * supported preview output format returned here. Like in the basic extender interface, the
+     * preview PRIVATE surface could be added to the CameraCaptureSession and OEM processes it in
+     * the HAL. Alternatively OEM can configure a intermediate YUV surface of the same size and
+     * writes the output to the preview output surface.
+     */
+    Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(String cameraId);
+
+    /**
+     * Returns supported output format/size map for image capture. OEM is required to support
+     * both JPEG and YUV_420_888 format output.
+     *
+     * <p>Like in the basic extender interface, the surface created with this supported
+     * format/size could be either added in CameraCaptureSession with HAL processing OR it
+     * configures intermediate surfaces(YUV/RAW..) and writes the output to the output surface.
+     */
+    Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId);
+
+    /**
+     * 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
+     * output surfaces. If imageAnalysis YUV surface is not supported, OEM will return null or
+     * empty list.
+     */
+    List<Size> getSupportedYuvAnalysisResolutions(String cameraId);
+
+    /**
+     * Returns a processor for activating extension sessions. It implements all the interactions
+     * required for starting a extension and cleanup.
+     */
+    SessionProcessorImpl createSessionProcessor();
+}
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
new file mode 100644
index 0000000..7753258
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stub advanced extender implementation for auto.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.2
+ */
+@SuppressLint("UnknownNullness")
+public class AutoAdvancedExtenderImpl implements AdvancedExtenderImpl {
+    public AutoAdvancedExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(
+            String cameraId, Size size, int imageFormat) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Size> getSupportedYuvAnalysisResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public SessionProcessorImpl createSessionProcessor() {
+        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
new file mode 100644
index 0000000..91d8171
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stub advanced extender implementation for beauty.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.2
+ */
+@SuppressLint("UnknownNullness")
+public class BeautyAdvancedExtenderImpl implements AdvancedExtenderImpl {
+    public BeautyAdvancedExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(
+            String cameraId, Size size, int imageFormat) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Size> getSupportedYuvAnalysisResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public SessionProcessorImpl createSessionProcessor() {
+        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
new file mode 100644
index 0000000..b05740d
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stub advanced extender implementation for bokeh.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.2
+ */
+@SuppressLint("UnknownNullness")
+public class BokehAdvancedExtenderImpl implements AdvancedExtenderImpl {
+    public BokehAdvancedExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(
+            String cameraId, Size size, int imageFormat) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Size> getSupportedYuvAnalysisResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public SessionProcessorImpl createSessionProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java
new file mode 100644
index 0000000..68de01b
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021 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;
+
+import java.util.List;
+
+/**
+ * A config representing a {@link android.hardware.camera2.params.OutputConfiguration} where
+ * Surface will be created by the information in this config.
+ */
+@SuppressLint("UnknownNullness")
+public interface Camera2OutputConfigImpl {
+    /**
+     * Gets thd id of this output config. The id can be used to identify the stream in vendor
+     * implementations.
+     */
+    int getId();
+
+    /**
+     * Gets the surface group id. Vendor can use the surface group id to share memory between
+     * Surfaces.
+     */
+    int getSurfaceGroupId();
+
+    /**
+     * Gets the physical camera id. Returns null if not specified.
+     */
+    String getPhysicalCameraId();
+
+    /**
+     * If non-null, enable surface sharing and add the surface constructed by the return
+     * Camera2OutputConfig.
+     */
+    List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java
new file mode 100644
index 0000000..1298d80
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2021 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;
+import android.util.Size;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A builder implementation to help OEM build the {@link Camera2OutputConfigImpl} instance.
+ * Implementation will be provided in the stub.
+ */
+@SuppressLint("UnknownNullness")
+public class Camera2OutputConfigImplBuilder {
+    static AtomicInteger sLastId = new AtomicInteger(0);
+    private OutputConfigImplImpl mOutputConfig;
+    private int mSurfaceGroupId;
+    private String mPhysicalCameraId;
+    private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs;
+
+    private Camera2OutputConfigImplBuilder(OutputConfigImplImpl outputConfig) {
+        mOutputConfig = outputConfig;
+    }
+
+    private int getNextId() {
+        return sLastId.getAndIncrement();
+    }
+
+    /**
+     * Creates a {@link Camera2OutputConfigImpl} that represents a {@link android.media.ImageReader}
+     * with the given parameters.
+     */
+    public static Camera2OutputConfigImplBuilder newImageReaderConfig(
+            Size size, int imageFormat, int maxImages) {
+        return new Camera2OutputConfigImplBuilder(
+                new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages));
+    }
+
+    /**
+     * Creates a {@link Camera2OutputConfigImpl} that represents a MultiResolutionImageReader with
+     * the given parameters.
+     */
+    public static Camera2OutputConfigImplBuilder newMultiResolutionImageReaderConfig(
+            int imageFormat, int maxImages) {
+        return new Camera2OutputConfigImplBuilder(
+                new MultiResolutionImageReaderOutputConfigImplImpl(imageFormat, maxImages));
+    }
+
+    /**
+     * Creates a {@link Camera2OutputConfigImpl} that contains the Surface directly.
+     */
+    public static Camera2OutputConfigImplBuilder newSurfaceConfig(Surface surface) {
+        return new Camera2OutputConfigImplBuilder(new SurfaceOutputConfigImplImpl(surface));
+    }
+
+    /**
+     * Adds a {@link Camera2SessionConfigImpl} to be shared with current config.
+     */
+    public Camera2OutputConfigImplBuilder addSurfaceSharingOutputConfig(
+            Camera2OutputConfigImpl camera2OutputConfig) {
+        if (mSurfaceSharingConfigs == null) {
+            mSurfaceSharingConfigs = new ArrayList<>();
+        }
+
+        mSurfaceSharingConfigs.add(camera2OutputConfig);
+        return this;
+    }
+
+    /**
+     * Sets a physical camera id.
+     */
+    public Camera2OutputConfigImplBuilder setPhysicalCameraId(String physicalCameraId) {
+        mPhysicalCameraId = physicalCameraId;
+        return this;
+    }
+
+    /**
+     * Sets surface group id.
+     */
+    public Camera2OutputConfigImplBuilder setSurfaceGroupId(int surfaceGroupId) {
+        mSurfaceGroupId = surfaceGroupId;
+        return this;
+    }
+
+    /**
+     * Build a {@link Camera2OutputConfigImpl} instance.
+     */
+    public Camera2OutputConfigImpl build() {
+        mOutputConfig.setId(getNextId());
+        mOutputConfig.setPhysicalCameraId(mPhysicalCameraId);
+        mOutputConfig.setSurfaceGroup(mSurfaceGroupId);
+        mOutputConfig.setSurfaceSharingConfigs(mSurfaceSharingConfigs);
+        return mOutputConfig;
+    }
+
+    private static class OutputConfigImplImpl implements Camera2OutputConfigImpl {
+        private int mId;
+        private int mSurfaceGroup;
+        private String mPhysicalCameraId;
+        private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs;
+
+        OutputConfigImplImpl() {
+            mId = -1;
+            mSurfaceGroup = 0;
+            mPhysicalCameraId = null;
+            mSurfaceSharingConfigs = null;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+
+        @Override
+        public int getSurfaceGroupId() {
+            return mSurfaceGroup;
+        }
+
+        @Override
+        public String getPhysicalCameraId() {
+            return mPhysicalCameraId;
+        }
+
+        @Override
+        public List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs() {
+            return mSurfaceSharingConfigs;
+        }
+
+        public void setId(int id) {
+            mId = id;
+        }
+
+        public void setSurfaceGroup(int surfaceGroup) {
+            mSurfaceGroup = surfaceGroup;
+        }
+
+        public void setPhysicalCameraId(String physicalCameraId) {
+            mPhysicalCameraId = physicalCameraId;
+        }
+
+        public void setSurfaceSharingConfigs(
+                List<Camera2OutputConfigImpl> surfaceSharingConfigs) {
+            mSurfaceSharingConfigs = surfaceSharingConfigs;
+        }
+    }
+
+    private static class SurfaceOutputConfigImplImpl extends OutputConfigImplImpl
+            implements SurfaceOutputConfigImpl {
+        private Surface mSurface;
+
+        SurfaceOutputConfigImplImpl(Surface surface) {
+            mSurface = surface;
+        }
+
+        @Override
+        public Surface getSurface() {
+            return mSurface;
+        }
+    }
+
+    private static class ImageReaderOutputConfigImplImpl extends OutputConfigImplImpl
+            implements ImageReaderOutputConfigImpl {
+        private Size mSize;
+        private int mImageFormat;
+        private int mMaxImages;
+
+        ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages) {
+            mSize = size;
+            mImageFormat = imageFormat;
+            mMaxImages = maxImages;
+        }
+
+        @Override
+        public Size getSize() {
+            return mSize;
+        }
+
+        @Override
+        public int getImageFormat() {
+            return mImageFormat;
+        }
+
+        @Override
+        public int getMaxImages() {
+            return mMaxImages;
+        }
+    }
+
+    private static class MultiResolutionImageReaderOutputConfigImplImpl extends OutputConfigImplImpl
+            implements MultiResolutionImageReaderOutputConfigImpl {
+        private int mImageFormat;
+        private int mMaxImages;
+
+        MultiResolutionImageReaderOutputConfigImplImpl(int imageFormat, int maxImages) {
+            mImageFormat = imageFormat;
+            mMaxImages = maxImages;
+        }
+
+        @Override
+        public int getImageFormat() {
+            return mImageFormat;
+        }
+
+        @Override
+        public int getMaxImages() {
+            return mMaxImages;
+        }
+    }
+}
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
new file mode 100644
index 0000000..d121717
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CaptureRequest;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A config representing a {@link android.hardware.camera2.params.SessionConfiguration}
+ */
+@SuppressLint("UnknownNullness")
+public interface Camera2SessionConfigImpl {
+    /**
+     * Returns all the {@link Camera2OutputConfigImpl}s that will be used to create
+     * {@link android.hardware.camera2.params.OutputConfiguration}.
+     */
+    List<Camera2OutputConfigImpl> getOutputConfigs();
+
+    /**
+     * Gets all the parameters to create the session parameters with.
+     */
+    Map<CaptureRequest.Key<?>, Object> getSessionParameters();
+
+    /**
+     * Gets the template id used for creating {@link CaptureRequest}s to be passed in
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}.
+     */
+    int getSessionTemplateId();
+}
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
new file mode 100644
index 0000000..6c052d1
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A builder implementation to help OEM build the {@link Camera2SessionConfigImpl} instance.
+ * Implementation will be provided in the stub.
+ */
+@SuppressLint("UnknownNullness")
+public class Camera2SessionConfigImplBuilder {
+    private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW;
+    Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>();
+    List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>();
+
+    public Camera2SessionConfigImplBuilder() {
+    }
+
+    /**
+     * Adds a output config.
+     */
+    public Camera2SessionConfigImplBuilder addOutputConfig(
+            Camera2OutputConfigImpl outputConfig) {
+        mCamera2OutputConfigs.add(outputConfig);
+        return this;
+    }
+
+    /**
+     * Sets session parameters.
+     */
+    public <T> Camera2SessionConfigImplBuilder addSessionParameter(
+            CaptureRequest.Key<T> key, T value) {
+        mSessionParameters.put(key, value);
+        return this;
+    }
+
+    /**
+     * Sets the template id for session parameters request.
+     */
+    public Camera2SessionConfigImplBuilder setSessionTemplateId(int templateId) {
+        mSessionTemplateId = templateId;
+        return this;
+    }
+
+    /**
+     * Gets the session template id.
+     */
+    public int getSessionTemplateId() {
+        return mSessionTemplateId;
+    }
+
+    /**
+     * Gets the session parameters.
+     */
+    public Map<CaptureRequest.Key<?>, Object> getSessionParameters() {
+        return mSessionParameters;
+    }
+
+    /**
+     * Gets all the output configs.
+     */
+    public List<Camera2OutputConfigImpl> getCamera2OutputConfigs() {
+        return mCamera2OutputConfigs;
+    }
+
+    /**
+     * Builds a {@link Camera2SessionConfigImpl} instance.
+     */
+    public Camera2SessionConfigImpl build() {
+        return new Camera2SessionConfigImplImpl(this);
+    }
+
+    private static class Camera2SessionConfigImplImpl implements
+            Camera2SessionConfigImpl {
+        int mSessionTemplateId;
+        Map<CaptureRequest.Key<?>, Object> mSessionParameters;
+        List<Camera2OutputConfigImpl> mCamera2OutputConfigs;
+
+        Camera2SessionConfigImplImpl(Camera2SessionConfigImplBuilder builder) {
+            mSessionTemplateId = builder.getSessionTemplateId();
+            mSessionParameters = builder.getSessionParameters();
+            mCamera2OutputConfigs = builder.getCamera2OutputConfigs();
+        }
+
+        @Override
+        public List<Camera2OutputConfigImpl> getOutputConfigs() {
+            return mCamera2OutputConfigs;
+        }
+
+        @Override
+        public Map<CaptureRequest.Key<?>, Object> getSessionParameters() {
+            return mSessionParameters;
+        }
+
+        @Override
+        public int getSessionTemplateId() {
+            return mSessionTemplateId;
+        }
+    }
+}
+
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
new file mode 100644
index 0000000..bfd8e9a
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stub advanced extender implementation for hdr.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.2
+ */
+@SuppressLint("UnknownNullness")
+public class HdrAdvancedExtenderImpl implements AdvancedExtenderImpl {
+    public HdrAdvancedExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(
+            String cameraId, Size size, int imageFormat) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Size> getSupportedYuvAnalysisResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public SessionProcessorImpl createSessionProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java
new file mode 100644
index 0000000..ce17c4f
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 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;
+
+/**
+ * A interface to receive and process the upcoming next available Image.
+ *
+ * <p>Implemented by OEM.
+ */
+@SuppressLint("UnknownNullness")
+public interface ImageProcessorImpl {
+    /**
+     * The reference count will be decremented when this method returns. If an extension wants
+     * to hold onto the image it should increment the reference count in this method and
+     * decrement it when the image is no longer needed.
+     *
+     * <p>If OEM is not closing(decrement) the image fast enough, the imageReference passed
+     * in this method might contain null image meaning that the Image was closed to prevent
+     * preview from stalling.
+     *
+     * @param outputConfigId the id of {@link Camera2OutputConfigImpl} which identifies
+     *                       corresponding Surface
+     * @param timestampNs    the timestamp in nanoseconds associated with this image
+     * @param imageReference A reference to the {@link android.media.Image} which might contain
+     *                       null if OEM close(decrement) the image too slowly
+     * @param physicalCameraId used to distinguish which physical camera id the image comes from
+     *                         when the output configuration is
+     *                         MultiResolutionImageReaderOutputConfigImpl. It is also set if
+     *                         physicalCameraId is set in other Camera2OutputConfigImpl types.
+     *
+     */
+    void onNextImageAvailable(
+            int outputConfigId,
+            long timestampNs,
+            ImageReferenceImpl imageReference,
+            String physicalCameraId
+            );
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java
new file mode 100644
index 0000000..ca4dcaf
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 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;
+import android.util.Size;
+
+/**
+ * Surface will be created by constructing a ImageReader.
+ */
+@SuppressLint("UnknownNullness")
+public interface ImageReaderOutputConfigImpl extends Camera2OutputConfigImpl {
+    /**
+     * Returns the size of the surface.
+     */
+    Size getSize();
+
+    /**
+     * Gets the image format of the surface.
+     */
+    int getImageFormat();
+
+    /**
+     * Gets the capacity for TYPE_IMAGEREADER.
+     */
+    int getMaxImages();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java
new file mode 100644
index 0000000..95f2c3b
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 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;
+import android.media.Image;
+
+/**
+ * A Image reference container that enables the Image sharing between Camera2/CameraX and OEM
+ * using reference counting. The wrapped Image will be closed once the reference count
+ * reaches 0.
+ *
+ * <p>Implemented by Camera2/CameraX.
+ */
+@SuppressLint("UnknownNullness")
+public interface ImageReferenceImpl {
+
+    /**
+     * Increment the reference count. Returns true if the value was incremented.
+     * (returns false if the reference count has already reached zero.)
+     */
+    boolean increment();
+
+    /**
+     * Decrement the reference count. Image will be closed if reference count reaches 0.
+     * Returns true if the value was decremented (returns false if the reference count has
+     * already reached zero)
+     */
+    boolean decrement();
+
+    /**
+     * Return the Android image. This object MUST not be closed directly.
+     * Returns null when the reference count is zero.
+     */
+    Image get();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java
new file mode 100644
index 0000000..c3ad61b
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 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;
+
+/**
+ * Surface will be created by constructing a MultiResolutionImageReader.
+ */
+public interface MultiResolutionImageReaderOutputConfigImpl extends Camera2OutputConfigImpl {
+    /**
+     * Gets the image format of the surface.
+     */
+    int getImageFormat();
+
+    /**
+     * Gets the max images of the ImageReader.
+     */
+    int getMaxImages();
+}
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
new file mode 100644
index 0000000..fc05224
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stub advanced extender implementation for night.
+ *
+ * <p>This class should be implemented by OEM and deployed to the target devices.
+ *
+ * @since 1.2
+ */
+@SuppressLint("UnknownNullness")
+public class NightAdvancedExtenderImpl implements AdvancedExtenderImpl {
+    public NightAdvancedExtenderImpl() {
+    }
+
+    @Override
+    public boolean isExtensionAvailable(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public void init(String cameraId,
+            Map<String, CameraCharacteristics> characteristicsMap) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Range<Long> getEstimatedCaptureLatencyRange(
+            String cameraId, Size size, int imageFormat) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public List<Size> getSupportedYuvAnalysisResolutions(
+            String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public SessionProcessorImpl createSessionProcessor() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
new file mode 100644
index 0000000..f692029
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 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;
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * For specifying output surface of the extension.
+ */
+@SuppressLint("UnknownNullness")
+public interface OutputSurfaceImpl {
+    /**
+     * Gets the surface.
+     */
+    Surface getSurface();
+
+    /**
+     * Gets the size.
+     */
+    Size getSize();
+
+    /**
+     * Gets the image format.
+     */
+    int getImageFormat();
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java
new file mode 100644
index 0000000..e70ce37
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2021 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;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A Interface to execute requests.
+ */
+@SuppressLint("UnknownNullness")
+public interface RequestProcessorImpl {
+    /**
+     * Sets a {@link ImageProcessorImpl} to receive {@link ImageReferenceImpl} to process.
+     */
+    void setImageProcessor(int outputconfigId, ImageProcessorImpl imageProcessor);
+
+    /**
+     * Submits a request.
+     * @return the id of the capture sequence or -1 in case the processor encounters a fatal error
+     *         or receives and invalid argument.
+     */
+    int submit(Request request, Callback callback);
+
+    /**
+     * Submits a list of requests.
+     * @return the id of the capture sequence or -1 in case the processor encounters a fatal error
+     *         or receives and invalid argument.
+     */
+    int submit(List<Request> requests, Callback callback);
+
+    /**
+     * Set repeating requests.
+     * @return the id of the capture sequence or -1 in case the processor encounters a fatal error
+     *         or receives and invalid argument.
+     */
+    int setRepeating(Request request, Callback callback);
+
+    /**
+     * Abort captures.
+     */
+    void abortCaptures();
+
+    /**
+     * Stop Repeating.
+     */
+    void stopRepeating();
+
+    /**
+     * A interface representing a capture request configuration used for submitting requests in
+     * {@link RequestProcessorImpl}.
+     */
+    interface Request {
+        /**
+         * Gets the target ids of {@link Camera2OutputConfigImpl} which identifies corresponding
+         * Surface to be the targeted for the request.
+         */
+        List<Integer> getTargetOutputConfigIds();
+
+        /**
+         * Gets all the parameters.
+         */
+        Map<CaptureRequest.Key<?>, Object> getParameters();
+
+        /**
+         * Gets the template id.
+         */
+        Integer getTemplateId();
+    }
+
+    /**
+     * Callback to be invoked during the capture.
+     */
+    interface Callback {
+        void onCaptureStarted(
+                Request request,
+                long frameNumber,
+                long timestamp);
+
+        void onCaptureProgressed(
+                Request request,
+                CaptureResult partialResult);
+
+        void onCaptureCompleted(
+                Request request,
+                TotalCaptureResult totalCaptureResult);
+
+        void onCaptureFailed(
+                Request request,
+                CaptureFailure captureFailure);
+
+        void onCaptureBufferLost(
+                Request request,
+                long frameNumber,
+                int outputStreamId);
+
+        void onCaptureSequenceCompleted(int sequenceId, long frameNumber);
+
+        void onCaptureSequenceAborted(int sequenceId);
+
+    }
+}
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
new file mode 100644
index 0000000..9ac3eeb
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2021 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;
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.view.Surface;
+
+import java.util.Map;
+
+/**
+ * Interface for activating extension sessions.
+ *
+ * <p><pre>
+ * The flow of a extension session.
+ * (1) {@link #initSession}: CameraX prepares streams configuration for creating
+ *     CameraCaptureSession. Output surfaces for Preview, ImageCapture and ImageAnalysis are passed
+ *     in and vendor is responsible for outputting the results to these surfaces.
+ *
+ * (2) {@link #onCaptureSessionStart}: It is called after CameraCaptureSession is configured.
+ *     A {@link RequestProcessorImpl} is passed for vendor to send repeating requests and
+ *     single requests.
+ *
+ * (3) {@link #startRepeating}:  CameraX will call this method to start the repeating request
+ *     after CameraCaptureSession is called.  Vendor should start the repeating request by
+ *     {@link RequestProcessorImpl}. Vendor can also update the repeating request at other
+ *     time other than in this method.
+ *
+ * (4) {@link #setParameters(Map)}: The passed parameters will be attached to the repeating request
+ *     and single requests but vendor can choose to apply some of them only.
+ *
+ * (5) {@link #startCapture(CaptureCallback)}: It is called when apps want to
+ *     start a multi-frame image capture.  {@link CaptureCallback} will be called
+ *     to report the statue and the output image will be written to the capture output surface
+ *     specified in {@link #initSession}.
+ *
+ * (5) {@link #onCaptureSessionEnd}: It is called right BEFORE CameraCaptureSession.close() is
+ *     called.
+ *
+ * (6) {@link #deInitSession}: called when CameraCaptureSession is destroyed
+ * </pre>
+ */
+@SuppressLint("UnknownNullness")
+public interface SessionProcessorImpl {
+    /**
+     * Notify to start the session for the extension. This is where the use case is started and
+     * would be able to allocate resources here. After onInit() 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
+     * {@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
+     * may be 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>To ensure the preview, image capture and image analysis are working properly, OEM is
+     * responsible for setting corresponding {@link Camera2OutputConfigImpl.UsageType} in the
+     * {@link Camera2OutputConfigImpl}. CameraX will examine if all usage types
+     * (USAGE_PREVIEW/USAGE_CAPTURE/USAGE_ANALYSIS) can be found in the returned
+     * {@link Camera2SessionConfigImpl} and throws an {@link IllegalArgumentException} if some
+     * usage type is not found.
+     *
+     * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder}
+     * implementations are provided in the stub for OEM to construct the
+     * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances.
+     *
+     * @param previewSurfaceConfig       output surface for preview
+     * @param imageCaptureSurfaceConfig  output surface for image capture.
+     * @param imageAnalysisSurfaceConfig 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.
+     */
+    Camera2SessionConfigImpl initSession(
+            String cameraId,
+            Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+            Context context,
+            OutputSurfaceImpl previewSurfaceConfig,
+            OutputSurfaceImpl imageCaptureSurfaceConfig,
+            OutputSurfaceImpl imageAnalysisSurfaceConfig);
+
+    /**
+     * Notify to de-initialize the extension. This callback will be invoked after unbind. After
+     * onDeInit() was called, it is expected that the camera ID, cameraCharacteristics will
+     * no longer hold and tear down any resources allocated for this extension. Aborts all pending
+     * captures.
+     */
+    void deInitSession();
+
+    /**
+     * CameraX / Camera2 would call these API’s to pass parameters from the app to the OEM. It’s
+     * expected that the OEM would (eventually) update the repeating request if the keys are
+     * supported. Setting a value to null explicitly un-sets the value.
+     */
+    void setParameters(Map<CaptureRequest.Key<?>, Object> parameters);
+
+    /**
+     * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession}
+     * has been created. {@link RequestProcessorImpl} is passed for OEM to submit single
+     * requests or set repeating requests. This ExtensionRequestProcessor will be valid to use
+     * until onCaptureSessionEnd is called.
+     */
+    void onCaptureSessionStart(RequestProcessorImpl requestProcessor);
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * closed. {@link RequestProcessorImpl} passed in onCaptureSessionStart will no longer
+     * accept any requests after onCaptureSessionEnd() returns.
+     */
+    void onCaptureSessionEnd();
+
+    /**
+     * Starts the repeating request after CameraCaptureSession is called. Vendor should start the
+     * repeating request by {@link RequestProcessorImpl}. Vendor can also update the
+     * repeating request at time other than in this method.
+     *
+     * @param callback a callback to report the status.
+     * @return the id of the capture sequence.
+     */
+    int startRepeating(CaptureCallback callback);
+
+    /**
+     * Stop the repeating request. To prevent OEM from not calling stopRepeating, CameraX will
+     * first stop repeating of current CameraCaptureSession and call this API to signal OEM that
+     * all repeating request should stopped and calling
+     * {@link RequestProcessorImpl#setRepeating(RequestProcessorImpl.Request)} will simply do
+     * nothing.
+     */
+    void stopRepeating();
+
+    /**
+     * Start a multi-frame capture.
+     *
+     * 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.
+     */
+    int startCapture(CaptureCallback callback);
+
+    /**
+     * Abort capture tasks.
+     */
+    void abortCapture(int captureSequenceId);
+
+    /**
+     * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and
+     * {@link #startRepeating(CaptureCallback)}.
+     */
+    interface CaptureCallback {
+        /**
+         * This method is called when the camera device has started capturing the initial input
+         * image.
+         *
+         * For a multi-frame capture, the method is called when the
+         * CameraCaptureSession.CaptureCallback onCaptureStarted of first frame is called and its
+         * timestamp is directly forwarded to timestamp parameter of
+         * this method.
+         *
+         * @param captureSequenceId id of the current capture sequence
+         * @param timestamp         the timestamp at start of capture for repeating
+         *                          request or the timestamp at start of capture of the
+         *                          first frame in a multi-frame capture, in nanoseconds.
+         */
+        void onCaptureStarted(int captureSequenceId, long timestamp);
+
+        /**
+         * This method is called when an image (or images in case of multi-frame
+         * capture) is captured and device-specific extension processing is triggered.
+         *
+         * @param captureSequenceId id of the current capture sequence
+         */
+        void onCaptureProcessStarted(int captureSequenceId);
+
+        /**
+         * This method is called instead of
+         * {@link #onCaptureProcessStarted} when the camera device failed
+         * to produce the required input for the device-specific extension. The
+         * cause could be a failed camera capture request, a failed
+         * capture result or dropped camera frame.
+         *
+         * @param captureSequenceId id of the current capture sequence
+         */
+        void onCaptureFailed(int captureSequenceId);
+
+        /**
+         * This method is called independently of the others in the CaptureCallback, when a capture
+         * sequence finishes.
+         *
+         * <p>In total, there will be at least one
+         * {@link #onCaptureProcessStarted}/{@link #onCaptureFailed}
+         * invocation before this callback is triggered. If the capture
+         * sequence is aborted before any requests have begun processing,
+         * {@link #onCaptureSequenceAborted} is invoked instead.</p>
+         *
+         * @param captureSequenceId id of the current capture sequence
+         */
+        void onCaptureSequenceCompleted(int captureSequenceId);
+
+        /**
+         * This method is called when a capture sequence aborts.
+         *
+         * @param captureSequenceId id of the current capture sequence
+         */
+        void onCaptureSequenceAborted(int captureSequenceId);
+    }
+}
diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java
new file mode 100644
index 0000000..7b8d83c
--- /dev/null
+++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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;
+import android.view.Surface;
+
+/**
+ * Use Surface directly to create the OutputConfiguration.
+ */
+@SuppressLint("UnknownNullness")
+public interface SurfaceOutputConfigImpl extends Camera2OutputConfigImpl {
+    /**
+     * Get the {@link Surface}. It'll return valid surface only when type is TYPE_SURFACE.
+     */
+    Surface getSurface();
+}
diff --git a/camera2/public/src/com/android/ex/camera2/blocking/BlockingExtensionSessionCallback.java b/camera2/public/src/com/android/ex/camera2/blocking/BlockingExtensionSessionCallback.java
new file mode 100644
index 0000000..d191ef7
--- /dev/null
+++ b/camera2/public/src/com/android/ex/camera2/blocking/BlockingExtensionSessionCallback.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2020 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 com.android.ex.camera2.blocking;
+
+import android.hardware.camera2.CameraExtensionSession;
+import android.os.ConditionVariable;
+import android.util.Log;
+
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+import com.android.ex.camera2.utils.StateChangeListener;
+import com.android.ex.camera2.utils.StateWaiter;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+/**
+ * A camera extension session listener that implements blocking operations on session state changes.
+ *
+ * <p>Provides a waiter that can be used to block until the next unobserved state of the
+ * requested type arrives.</p>
+ *
+ * <p>Pass-through all StateCallback changes to the proxy.</p>
+ *
+ * @see #getStateWaiter
+ */
+public class BlockingExtensionSessionCallback extends CameraExtensionSession.StateCallback {
+    /**
+     * Session is configured, ready for captures
+     */
+    public static final int SESSION_CONFIGURED = 0;
+
+    /**
+     * Session has failed to configure, can't do any captures
+     */
+    public static final int SESSION_CONFIGURE_FAILED = 1;
+
+    /**
+     * Session is closed
+     */
+    public static final int SESSION_CLOSED = 2;
+
+    private static final int NUM_STATES = 3;
+
+    /*
+     * Private fields
+     */
+    private static final String TAG = "BlockingExtensionSessionCallback";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final CameraExtensionSession.StateCallback mProxy;
+    private final SessionFuture mSessionFuture = new SessionFuture();
+
+    private final StateWaiter mStateWaiter = new StateWaiter(sStateNames);
+    private final StateChangeListener mStateChangeListener = mStateWaiter.getListener();
+
+    private static final String[] sStateNames = {
+        "SESSION_CONFIGURED",
+        "SESSION_CONFIGURE_FAILED",
+        "SESSION_CLOSED"
+    };
+
+    /**
+     * Create a blocking session listener without forwarding the session listener invocations
+     * to another session listener.
+     */
+    public BlockingExtensionSessionCallback() {
+        mProxy = null;
+    }
+
+    /**
+     * Create a blocking session listener; forward original listener invocations
+     * into {@code listener}.
+     *
+     * @param listener a non-{@code null} listener to forward invocations into
+     *
+     * @throws NullPointerException if {@code listener} was {@code null}
+     */
+    public BlockingExtensionSessionCallback(CameraExtensionSession.StateCallback listener) {
+        if (listener == null) {
+            throw new NullPointerException("listener must not be null");
+        }
+        mProxy = listener;
+    }
+
+    /**
+     * Acquire the state waiter; can be used to block until a set of state transitions have
+     * been reached.
+     *
+     * <p>Only one thread should wait at a time.</p>
+     */
+    public StateWaiter getStateWaiter() {
+        return mStateWaiter;
+    }
+
+    /**
+     * Return session if already have it; otherwise wait until any of the session listener
+     * invocations fire and the session is available.
+     *
+     * <p>Does not consume any of the states from the state waiter.</p>
+     *
+     * @param timeoutMs how many milliseconds to wait for
+     * @return a non-{@code null}
+     * {@link android.example.com.cameraextensionbasic.CameraExtensionSession} instance
+     *
+     * @throws TimeoutRuntimeException if waiting for more than {@long timeoutMs}
+     */
+    public CameraExtensionSession waitAndGetSession(long timeoutMs) {
+        try {
+            return mSessionFuture.get(timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            throw new TimeoutRuntimeException(
+                    String.format("Failed to get session after %s milliseconds", timeoutMs), e);
+        }
+    }
+
+    /*
+     * CameraExtensionSession.StateCallback implementation
+     */
+
+    @Override
+    public void onClosed(CameraExtensionSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) mProxy.onClosed(session);
+        mStateChangeListener.onStateChanged(SESSION_CLOSED);
+    }
+
+    @Override
+    public void onConfigured(CameraExtensionSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) {
+            mProxy.onConfigured(session);
+        }
+        mStateChangeListener.onStateChanged(SESSION_CONFIGURED);
+    }
+
+    @Override
+    public void onConfigureFailed(CameraExtensionSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) {
+            mProxy.onConfigureFailed(session);
+        }
+        mStateChangeListener.onStateChanged(SESSION_CONFIGURE_FAILED);
+    }
+
+    private static class SessionFuture implements Future<CameraExtensionSession> {
+        private volatile CameraExtensionSession mSession;
+        ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
+
+        public void setSession(CameraExtensionSession session) {
+            mSession = session;
+            mCondVar.open();
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return false; // don't allow canceling this task
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return false; // can never cancel this task
+        }
+
+        @Override
+        public boolean isDone() {
+            return mSession != null;
+        }
+
+        @Override
+        public CameraExtensionSession get() {
+            mCondVar.block();
+            return mSession;
+        }
+
+        @Override
+        public CameraExtensionSession get(long timeout, TimeUnit unit) throws TimeoutException {
+            long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
+            if (!mCondVar.block(timeoutMs)) {
+                throw new TimeoutException(
+                        "Failed to receive session after " + timeout + " " + unit);
+            }
+
+            if (mSession == null) {
+                throw new AssertionError();
+            }
+            return mSession;
+        }
+
+    }
+}