Camera: Plumb through stream mirroring mode

The stream mirroring mode is used to override the default flip for front
facing camera.

Test: Camera CTS
Bug: 200309079
Change-Id: I161875ccf33267686d54ba3c1c94b597032bd4e8
diff --git a/camera/CameraUtils.cpp b/camera/CameraUtils.cpp
index af3c492..3473780 100644
--- a/camera/CameraUtils.cpp
+++ b/camera/CameraUtils.cpp
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 
 #include <camera/CameraUtils.h>
+#include <camera/camera2/OutputConfiguration.h>
 #include <media/hardware/HardwareAPI.h>
 
 #include <android-base/properties.h>
@@ -31,7 +32,7 @@
 const char *kCameraServiceDisabledProperty = "config.disable_cameraservice";
 
 status_t CameraUtils::getRotationTransform(const CameraMetadata& staticInfo,
-                /*out*/int32_t* transform) {
+        int mirrorMode, /*out*/int32_t* transform) {
     ALOGV("%s", __FUNCTION__);
 
     if (transform == NULL) {
@@ -55,9 +56,18 @@
 
     int32_t& flags = *transform;
 
-    bool mirror = (entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT);
+    int32_t mirror = 0;
+    if (mirrorMode == OutputConfiguration::MIRROR_MODE_AUTO &&
+            entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT) {
+        mirror = NATIVE_WINDOW_TRANSFORM_FLIP_H;
+    } else if (mirrorMode == OutputConfiguration::MIRROR_MODE_H) {
+        mirror = NATIVE_WINDOW_TRANSFORM_FLIP_H;
+    } else if (mirrorMode == OutputConfiguration::MIRROR_MODE_V) {
+        mirror = NATIVE_WINDOW_TRANSFORM_FLIP_V;
+    }
+
     int orientation = entry.data.i32[0];
-    if (!mirror) {
+    if (mirror == 0) {
         switch (orientation) {
             case 0:
                 flags = 0;
@@ -77,25 +87,25 @@
                 return INVALID_OPERATION;
         }
     } else {
-        // Front camera needs to be horizontally flipped for mirror-like behavior.
+        // - Front camera needs to be horizontally flipped for mirror-like behavior.
+        // - Application-specified mirroring needs to be applied.
         // Note: Flips are applied before rotates; using XOR here as some of these flags are
         // composed in terms of other flip/rotation flags, and are not bitwise-ORable.
         switch (orientation) {
             case 0:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H;
+                flags = mirror;
                 break;
             case 90:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
+                flags = mirror ^
                         NATIVE_WINDOW_TRANSFORM_ROT_270;
                 break;
             case 180:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
+                flags = mirror ^
                         NATIVE_WINDOW_TRANSFORM_ROT_180;
                 break;
             case 270:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
+                flags = mirror ^
                         NATIVE_WINDOW_TRANSFORM_ROT_90;
-
                 break;
             default:
                 ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index a16e747..4300f9a 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -89,6 +89,10 @@
     return mTimestampBase;
 }
 
+int OutputConfiguration::getMirrorMode() const {
+    return mMirrorMode;
+}
+
 OutputConfiguration::OutputConfiguration() :
         mRotation(INVALID_ROTATION),
         mSurfaceSetID(INVALID_SET_ID),
@@ -100,7 +104,8 @@
         mIsMultiResolution(false),
         mDynamicRangeProfile(ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD),
         mStreamUseCase(ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT),
-        mTimestampBase(TIMESTAMP_BASE_DEFAULT) {
+        mTimestampBase(TIMESTAMP_BASE_DEFAULT),
+        mMirrorMode(MIRROR_MODE_AUTO) {
 }
 
 OutputConfiguration::OutputConfiguration(const android::Parcel& parcel) :
@@ -199,6 +204,12 @@
         return err;
     }
 
+    int mirrorMode = MIRROR_MODE_AUTO;
+    if ((err = parcel->readInt32(&mirrorMode)) != OK) {
+        ALOGE("%s: Failed to read mirroring mode from parcel", __FUNCTION__);
+        return err;
+    }
+
     mRotation = rotation;
     mSurfaceSetID = setID;
     mSurfaceType = surfaceType;
@@ -209,6 +220,7 @@
     mIsMultiResolution = isMultiResolution != 0;
     mStreamUseCase = streamUseCase;
     mTimestampBase = timestampBase;
+    mMirrorMode = mirrorMode;
     for (auto& surface : surfaceShims) {
         ALOGV("%s: OutputConfiguration: %p, name %s", __FUNCTION__,
                 surface.graphicBufferProducer.get(),
@@ -220,9 +232,11 @@
     mDynamicRangeProfile = dynamicProfile;
 
     ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d,"
-          " physicalCameraId = %s, isMultiResolution = %d, streamUseCase = %d, timestampBase = %d",
+          " physicalCameraId = %s, isMultiResolution = %d, streamUseCase = %d, timestampBase = %d,"
+          " mirrorMode = %d",
           __FUNCTION__, mRotation, mSurfaceSetID, mSurfaceType,
-          String8(mPhysicalCameraId).string(), mIsMultiResolution, mStreamUseCase, timestampBase);
+          String8(mPhysicalCameraId).string(), mIsMultiResolution, mStreamUseCase, timestampBase,
+          mMirrorMode);
 
     return err;
 }
@@ -240,6 +254,7 @@
     mDynamicRangeProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD;
     mStreamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
     mTimestampBase = TIMESTAMP_BASE_DEFAULT;
+    mMirrorMode = MIRROR_MODE_AUTO;
 }
 
 OutputConfiguration::OutputConfiguration(
@@ -251,7 +266,8 @@
     mPhysicalCameraId(physicalCameraId), mIsMultiResolution(false),
     mDynamicRangeProfile(ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD),
     mStreamUseCase(ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT),
-    mTimestampBase(TIMESTAMP_BASE_DEFAULT) { }
+    mTimestampBase(TIMESTAMP_BASE_DEFAULT),
+    mMirrorMode(MIRROR_MODE_AUTO) { }
 
 status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
 
@@ -307,6 +323,9 @@
     err = parcel->writeInt32(mTimestampBase);
     if (err != OK) return err;
 
+    err = parcel->writeInt32(mMirrorMode);
+    if (err != OK) return err;
+
     return OK;
 }
 
diff --git a/camera/include/camera/CameraUtils.h b/camera/include/camera/CameraUtils.h
index a397ccd..31d25e7 100644
--- a/camera/include/camera/CameraUtils.h
+++ b/camera/include/camera/CameraUtils.h
@@ -37,10 +37,13 @@
          * metadata.  This is based on the sensor orientation and lens facing
          * attributes of the camera device.
          *
+         * If mirrorMode is not AUTO, it will be used to override the lens
+         * facing based mirror.
+         *
          * Returns OK on success, or a negative error code.
          */
         static status_t getRotationTransform(const CameraMetadata& staticInfo,
-                /*out*/int32_t* transform);
+                int mirrorMode, /*out*/int32_t* transform);
 
         /**
          * Check if the image data is VideoNativeHandleMetadata, that contains a native handle.
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 9a91cc0..d7abfba 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -38,13 +38,19 @@
         SURFACE_TYPE_SURFACE_VIEW = 0,
         SURFACE_TYPE_SURFACE_TEXTURE = 1
     };
-    enum TimestampBaseByte {
+    enum TimestampBaseType {
         TIMESTAMP_BASE_DEFAULT = 0,
         TIMESTAMP_BASE_SENSOR = 1,
         TIMESTAMP_BASE_MONOTONIC = 2,
         TIMESTAMP_BASE_REALTIME = 3,
         TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = 4
     };
+    enum MirrorModeType {
+        MIRROR_MODE_AUTO = 0,
+        MIRROR_MODE_NONE = 1,
+        MIRROR_MODE_H = 2,
+        MIRROR_MODE_V = 3,
+    };
 
     const std::vector<sp<IGraphicBufferProducer>>& getGraphicBufferProducers() const;
     int                        getRotation() const;
@@ -59,6 +65,7 @@
     bool                       isMultiResolution() const;
     int                        getStreamUseCase() const;
     int                        getTimestampBase() const;
+    int                        getMirrorMode() const;
 
     // set of sensor pixel mode resolutions allowed {MAX_RESOLUTION, DEFAULT_MODE};
     const std::vector<int32_t>&            getSensorPixelModesUsed() const;
@@ -103,7 +110,8 @@
                 sensorPixelModesUsedEqual(other) &&
                 mDynamicRangeProfile == other.mDynamicRangeProfile &&
                 mStreamUseCase == other.mStreamUseCase &&
-                mTimestampBase == other.mTimestampBase);
+                mTimestampBase == other.mTimestampBase &&
+                mMirrorMode == other.mMirrorMode);
     }
     bool operator != (const OutputConfiguration& other) const {
         return !(*this == other);
@@ -149,6 +157,9 @@
         if (mTimestampBase != other.mTimestampBase) {
             return mTimestampBase < other.mTimestampBase;
         }
+        if (mMirrorMode != other.mMirrorMode) {
+            return mMirrorMode < other.mMirrorMode;
+        }
         return gbpsLessThan(other);
     }
 
@@ -176,6 +187,7 @@
     int                        mDynamicRangeProfile;
     int                        mStreamUseCase;
     int                        mTimestampBase;
+    int                        mMirrorMode;
 };
 } // namespace params
 } // namespace camera2
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index d26b730..16ea67d 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -862,6 +862,7 @@
     int dynamicRangeProfile = outputConfiguration.getDynamicRangeProfile();
     int streamUseCase = outputConfiguration.getStreamUseCase();
     int timestampBase = outputConfiguration.getTimestampBase();
+    int mirrorMode = outputConfiguration.getMirrorMode();
 
     res = SessionConfigurationUtils::checkSurfaceType(numBufferProducers, deferredConsumer,
             outputConfiguration.getSurfaceType());
@@ -906,7 +907,7 @@
         res = SessionConfigurationUtils::createSurfaceFromGbp(streamInfo,
                 isStreamInfoValid, surface, bufferProducer, mCameraIdStr,
                 mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed, dynamicRangeProfile,
-                streamUseCase, timestampBase);
+                streamUseCase, timestampBase, mirrorMode);
 
         if (!res.isOk())
             return res;
@@ -953,7 +954,7 @@
                 &streamId, physicalCameraId, streamInfo.sensorPixelModesUsed, &surfaceIds,
                 outputConfiguration.getSurfaceSetID(), isShared, isMultiResolution,
                 /*consumerUsage*/0, streamInfo.dynamicRangeProfile, streamInfo.streamUseCase,
-                streamInfo.timestampBase);
+                streamInfo.timestampBase, streamInfo.mirrorMode);
     }
 
     if (err != OK) {
@@ -979,7 +980,7 @@
                   streamInfo.height, streamInfo.format);
 
         // Set transform flags to ensure preview to be rotated correctly.
-        res = setStreamTransformLocked(streamId);
+        res = setStreamTransformLocked(streamId, streamInfo.mirrorMode);
 
         // Fill in mHighResolutionCameraIdToStreamIdSet map
         const String8 &cameraIdUsed =
@@ -1049,7 +1050,8 @@
             outputConfiguration.getSurfaceSetID(), isShared,
             outputConfiguration.isMultiResolution(), consumerUsage,
             outputConfiguration.getDynamicRangeProfile(),
-            outputConfiguration.getStreamUseCase());
+            outputConfiguration.getStreamUseCase(),
+            outputConfiguration.getMirrorMode());
 
     if (err != OK) {
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -1065,14 +1067,15 @@
                         overriddenSensorPixelModesUsed,
                         outputConfiguration.getDynamicRangeProfile(),
                         outputConfiguration.getStreamUseCase(),
-                        outputConfiguration.getTimestampBase()));
+                        outputConfiguration.getTimestampBase(),
+                        outputConfiguration.getMirrorMode()));
 
         ALOGV("%s: Camera %s: Successfully created a new stream ID %d for a deferred surface"
                 " (%d x %d) stream with format 0x%x.",
               __FUNCTION__, mCameraIdStr.string(), streamId, width, height, format);
 
         // Set transform flags to ensure preview to be rotated correctly.
-        res = setStreamTransformLocked(streamId);
+        res = setStreamTransformLocked(streamId, outputConfiguration.getMirrorMode());
 
         *newStreamId = streamId;
         // Fill in mHighResolutionCameraIdToStreamIdSet
@@ -1086,7 +1089,7 @@
     return res;
 }
 
-binder::Status CameraDeviceClient::setStreamTransformLocked(int streamId) {
+binder::Status CameraDeviceClient::setStreamTransformLocked(int streamId, int mirrorMode) {
     int32_t transform = 0;
     status_t err;
     binder::Status res;
@@ -1095,7 +1098,7 @@
         return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
     }
 
-    err = getRotationTransformLocked(&transform);
+    err = getRotationTransformLocked(mirrorMode, &transform);
     if (err != OK) {
         // Error logged by getRotationTransformLocked.
         return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION,
@@ -1256,6 +1259,7 @@
     int streamUseCase = outputConfiguration.getStreamUseCase();
     int timestampBase = outputConfiguration.getTimestampBase();
     int dynamicRangeProfile = outputConfiguration.getDynamicRangeProfile();
+    int mirrorMode = outputConfiguration.getMirrorMode();
 
     for (size_t i = 0; i < newOutputsMap.size(); i++) {
         OutputStreamInfo outInfo;
@@ -1263,7 +1267,7 @@
         res = SessionConfigurationUtils::createSurfaceFromGbp(outInfo,
                 /*isStreamInfoValid*/ false, surface, newOutputsMap.valueAt(i), mCameraIdStr,
                 mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed, dynamicRangeProfile,
-                streamUseCase, timestampBase);
+                streamUseCase, timestampBase, mirrorMode);
         if (!res.isOk())
             return res;
 
@@ -1623,6 +1627,7 @@
     int dynamicRangeProfile = outputConfiguration.getDynamicRangeProfile();
     int streamUseCase= outputConfiguration.getStreamUseCase();
     int timestampBase = outputConfiguration.getTimestampBase();
+    int mirrorMode = outputConfiguration.getMirrorMode();
     for (auto& bufferProducer : bufferProducers) {
         // Don't create multiple streams for the same target surface
         ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer));
@@ -1636,7 +1641,7 @@
         res = SessionConfigurationUtils::createSurfaceFromGbp(mStreamInfoMap[streamId],
                 true /*isStreamInfoValid*/, surface, bufferProducer, mCameraIdStr,
                 mDevice->infoPhysical(physicalId), sensorPixelModesUsed, dynamicRangeProfile,
-                streamUseCase, timestampBase);
+                streamUseCase, timestampBase, mirrorMode);
 
         if (!res.isOk())
             return res;
@@ -2118,11 +2123,12 @@
     return true;
 }
 
-status_t CameraDeviceClient::getRotationTransformLocked(int32_t* transform) {
+status_t CameraDeviceClient::getRotationTransformLocked(int mirrorMode,
+        int32_t* transform) {
     ALOGV("%s: begin", __FUNCTION__);
 
     const CameraMetadata& staticInfo = mDevice->info();
-    return CameraUtils::getRotationTransform(staticInfo, transform);
+    return CameraUtils::getRotationTransform(staticInfo, mirrorMode, transform);
 }
 
 binder::Status CameraDeviceClient::mapRequestTemplate(int templateId,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 1b0c61a..dad10a7 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -227,7 +227,7 @@
     virtual void          detachDevice();
 
     // Calculate the ANativeWindow transform from android.sensor.orientation
-    status_t              getRotationTransformLocked(/*out*/int32_t* transform);
+    status_t              getRotationTransformLocked(int mirrorMode, /*out*/int32_t* transform);
 
     bool isUltraHighResolutionSensor(const String8 &cameraId);
 
@@ -287,7 +287,7 @@
 
     // Set the stream transform flags to automatically rotate the camera stream for preview use
     // cases.
-    binder::Status setStreamTransformLocked(int streamId);
+    binder::Status setStreamTransformLocked(int streamId, int mirrorMode);
 
     // Utility method to insert the surface into SurfaceMap
     binder::Status insertGbpLocked(const sp<IGraphicBufferProducer>& gbp,
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index baa21f0..bde0693 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -185,7 +185,8 @@
             uint64_t consumerUsage = 0,
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT) = 0;
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO) = 0;
 
     /**
      * Create an output stream of the requested size, format, rotation and
@@ -205,7 +206,8 @@
             uint64_t consumerUsage = 0,
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT) = 0;
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO) = 0;
 
     /**
      * Create an input stream of width, height, and format.
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index f31241c..e4338b9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -980,7 +980,8 @@
             const String8& physicalCameraId,
             const std::unordered_set<int32_t> &sensorPixelModesUsed,
             std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
-            uint64_t consumerUsage, int dynamicRangeProfile, int streamUseCase, int timestampBase) {
+            uint64_t consumerUsage, int dynamicRangeProfile, int streamUseCase, int timestampBase,
+            int mirrorMode) {
     ATRACE_CALL();
 
     if (consumer == nullptr) {
@@ -994,7 +995,7 @@
     return createStream(consumers, /*hasDeferredConsumer*/ false, width, height,
             format, dataSpace, rotation, id, physicalCameraId, sensorPixelModesUsed, surfaceIds,
             streamSetId, isShared, isMultiResolution, consumerUsage, dynamicRangeProfile,
-            streamUseCase, timestampBase);
+            streamUseCase, timestampBase, mirrorMode);
 }
 
 static bool isRawFormat(int format) {
@@ -1014,7 +1015,8 @@
         android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
         const String8& physicalCameraId, const std::unordered_set<int32_t> &sensorPixelModesUsed,
         std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
-        uint64_t consumerUsage, int dynamicRangeProfile, int streamUseCase, int timestampBase) {
+        uint64_t consumerUsage, int dynamicRangeProfile, int streamUseCase, int timestampBase,
+        int mirrorMode) {
     ATRACE_CALL();
 
     Mutex::Autolock il(mInterfaceLock);
@@ -1022,10 +1024,10 @@
     Mutex::Autolock l(mLock);
     ALOGV("Camera %s: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d"
             " consumer usage %" PRIu64 ", isShared %d, physicalCameraId %s, isMultiResolution %d"
-            " dynamicRangeProfile %d, streamUseCase %d, timestampBase %d",
+            " dynamicRangeProfile %d, streamUseCase %d, timestampBase %d, mirrorMode %d",
             mId.string(), mNextStreamId, width, height, format, dataSpace, rotation,
             consumerUsage, isShared, physicalCameraId.string(), isMultiResolution,
-            dynamicRangeProfile, streamUseCase, timestampBase);
+            dynamicRangeProfile, streamUseCase, timestampBase, mirrorMode);
 
     status_t res;
     bool wasActive = false;
@@ -1095,7 +1097,7 @@
                 width, height, blobBufferSize, format, dataSpace, rotation,
                 mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                 isMultiResolution, dynamicRangeProfile, streamUseCase, mDeviceTimeBaseIsRealtime,
-                timestampBase);
+                timestampBase, mirrorMode);
     } else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
         bool maxResolution =
                 sensorPixelModesUsed.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
@@ -1110,25 +1112,25 @@
                 width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
                 mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                 isMultiResolution, dynamicRangeProfile, streamUseCase, mDeviceTimeBaseIsRealtime,
-                timestampBase);
+                timestampBase, mirrorMode);
     } else if (isShared) {
         newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
                 width, height, format, consumerUsage, dataSpace, rotation,
                 mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                 mUseHalBufManager, dynamicRangeProfile, streamUseCase, mDeviceTimeBaseIsRealtime,
-                timestampBase);
+                timestampBase, mirrorMode);
     } else if (consumers.size() == 0 && hasDeferredConsumer) {
         newStream = new Camera3OutputStream(mNextStreamId,
                 width, height, format, consumerUsage, dataSpace, rotation,
                 mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                 isMultiResolution, dynamicRangeProfile, streamUseCase, mDeviceTimeBaseIsRealtime,
-                timestampBase);
+                timestampBase, mirrorMode);
     } else {
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, format, dataSpace, rotation,
                 mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                 isMultiResolution, dynamicRangeProfile, streamUseCase, mDeviceTimeBaseIsRealtime,
-                timestampBase);
+                timestampBase, mirrorMode);
     }
 
     size_t consumerCount = consumers.size();
@@ -1247,7 +1249,7 @@
         CLOGE("Stream %d does not exist", id);
         return BAD_VALUE;
     }
-    return stream->setTransform(transform);
+    return stream->setTransform(transform, false /*mayChangeMirror*/);
 }
 
 status_t Camera3Device::deleteStream(int id) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 0f5e65b..5f0a296 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -136,7 +136,8 @@
             int dynamicRangeProfile =
             ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT) override;
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO) override;
 
     status_t createStream(const std::vector<sp<Surface>>& consumers,
             bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
@@ -150,7 +151,8 @@
             int dynamicRangeProfile =
             ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT) override;
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO) override;
 
     status_t createInputStream(
             uint32_t width, uint32_t height, int format, bool isMultiResolution,
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.cpp b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
index 61e43cb..19afd69 100644
--- a/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
@@ -76,7 +76,7 @@
     Camera3IOStreamBase::dump(fd, args);
 }
 
-status_t Camera3FakeStream::setTransform(int) {
+status_t Camera3FakeStream::setTransform(int, bool) {
     ATRACE_CALL();
     // Do nothing
     return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.h b/services/camera/libcameraservice/device3/Camera3FakeStream.h
index df19c3d..48e44dc 100644
--- a/services/camera/libcameraservice/device3/Camera3FakeStream.h
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.h
@@ -52,7 +52,7 @@
 
     virtual void     dump(int fd, const Vector<String16> &args) const;
 
-    status_t         setTransform(int transform);
+    status_t         setTransform(int transform, bool mayChangeMirror);
 
     virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index a82d19b..9574309 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -47,7 +47,8 @@
         nsecs_t timestampOffset, const String8& physicalCameraId,
         const std::unordered_set<int32_t> &sensorPixelModesUsed,
         int setId, bool isMultiResolution, int dynamicRangeProfile,
-        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase) :
+        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase,
+        int mirrorMode) :
         Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
                             /*maxSize*/0, format, dataSpace, rotation,
                             physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution,
@@ -60,6 +61,7 @@
         mTimestampOffset(timestampOffset),
         mConsumerUsage(0),
         mDropBuffers(false),
+        mMirrorMode(mirrorMode),
         mDequeueBufferLatency(kDequeueLatencyBinSize) {
 
     if (mConsumer == NULL) {
@@ -78,7 +80,8 @@
         nsecs_t timestampOffset, const String8& physicalCameraId,
         const std::unordered_set<int32_t> &sensorPixelModesUsed,
         int setId, bool isMultiResolution, int dynamicRangeProfile,
-        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase) :
+        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase,
+        int mirrorMode) :
         Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height, maxSize,
                             format, dataSpace, rotation, physicalCameraId, sensorPixelModesUsed,
                             setId, isMultiResolution, dynamicRangeProfile, streamUseCase,
@@ -90,6 +93,7 @@
         mTimestampOffset(timestampOffset),
         mConsumerUsage(0),
         mDropBuffers(false),
+        mMirrorMode(mirrorMode),
         mDequeueBufferLatency(kDequeueLatencyBinSize) {
 
     if (format != HAL_PIXEL_FORMAT_BLOB && format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
@@ -114,7 +118,8 @@
         const String8& physicalCameraId,
         const std::unordered_set<int32_t> &sensorPixelModesUsed,
         int setId, bool isMultiResolution, int dynamicRangeProfile,
-        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase) :
+        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase,
+        int mirrorMode) :
         Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
                             /*maxSize*/0, format, dataSpace, rotation,
                             physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution,
@@ -127,6 +132,7 @@
         mTimestampOffset(timestampOffset),
         mConsumerUsage(consumerUsage),
         mDropBuffers(false),
+        mMirrorMode(mirrorMode),
         mDequeueBufferLatency(kDequeueLatencyBinSize) {
     // Deferred consumer only support preview surface format now.
     if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
@@ -158,7 +164,8 @@
                                          uint64_t consumerUsage, nsecs_t timestampOffset,
                                          int setId, bool isMultiResolution,
                                          int dynamicRangeProfile, int streamUseCase,
-                                         bool deviceTimeBaseIsRealtime, int timestampBase) :
+                                         bool deviceTimeBaseIsRealtime, int timestampBase,
+                                         int mirrorMode) :
         Camera3IOStreamBase(id, type, width, height,
                             /*maxSize*/0,
                             format, dataSpace, rotation,
@@ -171,6 +178,7 @@
         mTimestampOffset(timestampOffset),
         mConsumerUsage(consumerUsage),
         mDropBuffers(false),
+        mMirrorMode(mirrorMode),
         mDequeueBufferLatency(kDequeueLatencyBinSize) {
 
     bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
@@ -379,7 +387,7 @@
                 return res;
             }
         } else {
-            setTransform(transform);
+            setTransform(transform, true/*mayChangeMirror*/);
             res = native_window_set_buffers_timestamp(mConsumer.get(), t);
             if (res != OK) {
                 ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
@@ -426,9 +434,15 @@
         "      DequeueBuffer latency histogram:");
 }
 
-status_t Camera3OutputStream::setTransform(int transform) {
+status_t Camera3OutputStream::setTransform(int transform, bool mayChangeMirror) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
+    if (mMirrorMode != OutputConfiguration::MIRROR_MODE_AUTO && mayChangeMirror) {
+        // If the mirroring mode is not AUTO, do not allow transform update
+        // which may change mirror.
+        return OK;
+    }
+
     return setTransformLocked(transform);
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index c7910b7..80901d6 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -93,7 +93,8 @@
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
             bool deviceTimeBaseIsRealtime = false,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT);
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO);
     /**
      * Set up a stream for formats that have a variable buffer size for the same
      * dimensions, such as compressed JPEG.
@@ -109,7 +110,8 @@
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
             bool deviceTimeBaseIsRealtime = false,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT);
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO);
     /**
      * Set up a stream with deferred consumer for formats that have 2 dimensions, such as
      * RAW and YUV. The consumer must be set before using this stream for output. A valid
@@ -124,7 +126,8 @@
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
             bool deviceTimeBaseIsRealtime = false,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT);
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO);
 
     virtual ~Camera3OutputStream();
 
@@ -138,7 +141,7 @@
      * Set the transform on the output stream; one of the
      * HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants.
      */
-    status_t         setTransform(int transform);
+    status_t         setTransform(int transform, bool mayChangeMirror);
 
     /**
      * Return if this output stream is for video encoding.
@@ -255,7 +258,8 @@
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
             bool deviceTimeBaseIsRealtime = false,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT);
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO);
 
     /**
      * Note that we release the lock briefly in this function
@@ -343,6 +347,8 @@
     std::vector<Surface::BatchBuffer> mBatchedBuffers;
     // ---- End of mBatchLock protected scope ----
 
+    const int mMirrorMode;
+
     /**
      * Internal Camera3Stream interface
      */
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 49f9f62..e44e795 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -34,7 +34,7 @@
      * Set the transform on the output stream; one of the
      * HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants.
      */
-    virtual status_t setTransform(int transform) = 0;
+    virtual status_t setTransform(int transform, bool mayChangeMirror) = 0;
 
     /**
      * Return if this output stream is for video encoding.
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index d8cc685..ab25322 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -532,7 +532,7 @@
                     auto orientation = deviceInfo->second.find(ANDROID_SENSOR_ORIENTATION);
                     if (orientation.count > 0) {
                         ret = CameraUtils::getRotationTransform(deviceInfo->second,
-                                &request.transform);
+                                OutputConfiguration::MIRROR_MODE_AUTO, &request.transform);
                         if (ret != OK) {
                             ALOGE("%s: Failed to calculate current stream transformation: %s (%d)",
                                     __FUNCTION__, strerror(-ret), ret);
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 047897e..9bf7b6a 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -34,11 +34,13 @@
         nsecs_t timestampOffset, const String8& physicalCameraId,
         const std::unordered_set<int32_t> &sensorPixelModesUsed,
         int setId, bool useHalBufManager, int dynamicProfile,
-        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase) :
+        int streamUseCase, bool deviceTimeBaseIsRealtime, int timestampBase,
+        int mirrorMode) :
         Camera3OutputStream(id, CAMERA_STREAM_OUTPUT, width, height,
                             format, dataSpace, rotation, physicalCameraId, sensorPixelModesUsed,
                             consumerUsage, timestampOffset, setId, /*isMultiResolution*/false,
-                            dynamicProfile, streamUseCase, deviceTimeBaseIsRealtime, timestampBase),
+                            dynamicProfile, streamUseCase, deviceTimeBaseIsRealtime, timestampBase,
+                            mirrorMode),
         mUseHalBufManager(useHalBufManager) {
     size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
     if (surfaces.size() > consumerCount) {
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index dc22c42..0061a52 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -44,7 +44,8 @@
             int dynamicProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD,
             int streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
             bool deviceTimeBaseIsRealtime = false,
-            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT);
+            int timestampBase = OutputConfiguration::TIMESTAMP_BASE_DEFAULT,
+            int mirrorMode = OutputConfiguration::MIRROR_MODE_AUTO);
 
     virtual ~Camera3SharedOutputStream();
 
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 8962fac..148e511 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -113,19 +113,21 @@
         int dynamicRangeProfile;
         int streamUseCase;
         int timestampBase;
+        int mirrorMode;
         OutputStreamInfo() :
             width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
             consumerUsage(0),
             dynamicRangeProfile(ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD),
             streamUseCase(ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT),
-            timestampBase(OutputConfiguration::TIMESTAMP_BASE_DEFAULT) {}
+            timestampBase(OutputConfiguration::TIMESTAMP_BASE_DEFAULT),
+            mirrorMode(OutputConfiguration::MIRROR_MODE_AUTO) {}
         OutputStreamInfo(int _width, int _height, int _format, android_dataspace _dataSpace,
                 uint64_t _consumerUsage, const std::unordered_set<int32_t>& _sensorPixelModesUsed,
-                int _dynamicRangeProfile, int _streamUseCase, int _timestampBase) :
+                int _dynamicRangeProfile, int _streamUseCase, int _timestampBase, int _mirrorMode) :
             width(_width), height(_height), format(_format),
             dataSpace(_dataSpace), consumerUsage(_consumerUsage),
             sensorPixelModesUsed(_sensorPixelModesUsed), dynamicRangeProfile(_dynamicRangeProfile),
-            streamUseCase(_streamUseCase), timestampBase(_timestampBase) {}
+            streamUseCase(_streamUseCase), timestampBase(_timestampBase), mirrorMode(_mirrorMode) {}
 };
 
 /**
diff --git a/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp b/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
index 6135f9e..154e6f5 100644
--- a/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
+++ b/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
@@ -187,7 +187,7 @@
 
 status_t PreviewFrameScheduler::queueBufferToClientLocked(
         const BufferHolder& bufferHolder, nsecs_t timestamp) {
-    mParent.setTransform(bufferHolder.transform);
+    mParent.setTransform(bufferHolder.transform, true/*mayChangeMirror*/);
 
     status_t res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp);
     if (res != OK) {
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 751f24f..e38140b 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -334,7 +334,7 @@
         sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
         const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,
         const std::vector<int32_t> &sensorPixelModesUsed, int dynamicRangeProfile,
-        int streamUseCase, int timestampBase) {
+        int streamUseCase, int timestampBase, int mirrorMode) {
     // bufferProducer must be non-null
     if (gbp == nullptr) {
         String8 msg = String8::format("Camera %s: Surface is NULL", logicalCameraId.string());
@@ -461,6 +461,13 @@
         ALOGE("%s: %s", __FUNCTION__, msg.string());
         return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
     }
+    if (mirrorMode < OutputConfiguration::MIRROR_MODE_AUTO ||
+            mirrorMode > OutputConfiguration::MIRROR_MODE_V) {
+        String8 msg = String8::format("Camera %s: invalid mirroring mode %d",
+                logicalCameraId.string(), mirrorMode);
+        ALOGE("%s: %s", __FUNCTION__, msg.string());
+        return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+    }
 
     if (!isStreamInfoValid) {
         streamInfo.width = width;
@@ -472,6 +479,7 @@
         streamInfo.dynamicRangeProfile = dynamicRangeProfile;
         streamInfo.streamUseCase = streamUseCase;
         streamInfo.timestampBase = timestampBase;
+        streamInfo.mirrorMode = mirrorMode;
         return binder::Status::ok();
     }
     if (width != streamInfo.width) {
@@ -706,6 +714,7 @@
 
         int streamUseCase = it.getStreamUseCase();
         int timestampBase = it.getTimestampBase();
+        int mirrorMode = it.getMirrorMode();
         if (deferredConsumer) {
             streamInfo.width = it.getWidth();
             streamInfo.height = it.getHeight();
@@ -740,7 +749,7 @@
             sp<Surface> surface;
             res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
                     logicalCameraId, metadataChosen, sensorPixelModesUsed, dynamicRangeProfile,
-                    streamUseCase, timestampBase);
+                    streamUseCase, timestampBase, mirrorMode);
 
             if (!res.isOk())
                 return res;
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index 3dcbdbc..1e690e4 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -98,7 +98,7 @@
         sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
         const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,
         const std::vector<int32_t> &sensorPixelModesUsed,  int dynamicRangeProfile,
-        int streamUseCase, int timestampBase);
+        int streamUseCase, int timestampBase, int mirrorMode);
 
 void mapStreamInfo(const camera3::OutputStreamInfo &streamInfo,
         camera3::camera_stream_rotation_t rotation, String8 physicalId, int32_t groupId,