Merge "Camera: Add OutputConfiguration.setMirrorMode(Surface, mirrorMode)" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 46a864e..ff068f1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20277,6 +20277,7 @@
     method public long getDynamicRangeProfile();
     method public int getMaxSharedSurfaceCount();
     method public int getMirrorMode();
+    method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public int getMirrorMode(@NonNull android.view.Surface);
     method public long getStreamUseCase();
     method @Nullable public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
@@ -20287,6 +20288,7 @@
     method public void removeSurface(@NonNull android.view.Surface);
     method public void setDynamicRangeProfile(long);
     method public void setMirrorMode(int);
+    method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public void setMirrorMode(@NonNull android.view.Surface, int);
     method public void setPhysicalCameraId(@Nullable String);
     method public void setReadoutTimestampEnabled(boolean);
     method public void setStreamUseCase(long);
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index ebcc371..22dbf5b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -43,6 +43,7 @@
 import android.media.ImageReader;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -596,6 +597,10 @@
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
         mTimestampBase = TIMESTAMP_BASE_DEFAULT;
         mMirrorMode = MIRROR_MODE_AUTO;
+        mMirrorModeForSurfaces = new IntArray();
+        if (Flags.mirrorModeSharedSurfaces()) {
+            mMirrorModeForSurfaces.add(mMirrorMode);
+        }
         mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
         mUsage = 0;
@@ -827,6 +832,7 @@
 
         mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
         mSurfaces = new ArrayList<Surface>();
+        mMirrorModeForSurfaces = new IntArray();
         mRotation = ROTATION_0;
         mConfiguredSize = surfaceSize;
         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
@@ -971,6 +977,9 @@
         mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
         mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+        mTimestampBase = TIMESTAMP_BASE_DEFAULT;
+        mMirrorMode = MIRROR_MODE_AUTO;
+        mMirrorModeForSurfaces = new IntArray();
         mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
         mUsage = usage;
@@ -1239,6 +1248,9 @@
         }
 
         mSurfaces.add(surface);
+        if (Flags.mirrorModeSharedSurfaces()) {
+            mMirrorModeForSurfaces.add(mMirrorMode);
+        }
     }
 
     /**
@@ -1266,9 +1278,16 @@
             throw new IllegalArgumentException(
                     "Cannot remove surface associated with this output configuration");
         }
-        if (!mSurfaces.remove(surface)) {
+
+        int surfaceIndex = mSurfaces.indexOf(surface);
+        if (surfaceIndex == -1) {
             throw new IllegalArgumentException("Surface is not part of this output configuration");
         }
+
+        mSurfaces.remove(surfaceIndex);
+        if (Flags.mirrorModeSharedSurfaces()) {
+            mMirrorModeForSurfaces.remove(surfaceIndex);
+        }
     }
 
     /**
@@ -1405,6 +1424,11 @@
      * ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
      * needed for such outputs, the application needs to mirror the image buffers itself before
      * passing them onward.</p>
+     *
+     * <p>Starting from Android 16, this function sets the mirror modes for all of the output
+     * surfaces contained within this OutputConfiguration. To set the mirror mode for a particular
+     * output surface, the application can call {@link #setMirrorMode(Surface, int)}. Prior to
+     * Android 16, this function is only applicable if surface sharing is not enabled.</p>
      */
     public void setMirrorMode(@MirrorMode int mirrorMode) {
         // Verify that the value is in range
@@ -1413,6 +1437,9 @@
             throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
         }
         mMirrorMode = mirrorMode;
+        for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+            mMirrorModeForSurfaces.set(j, mirrorMode);
+        }
     }
 
     /**
@@ -1428,6 +1455,72 @@
     }
 
     /**
+     * Set the mirroring mode for a surface belonging to this OutputConfiguration
+     *
+     * <p>This function is identical to {@link #setMirroMode(int)} if {@code surface} is
+     * the only surface belonging to this OutputConfiguration.</p>
+     *
+     * <p>If this OutputConfiguration contains a deferred surface, the application can either
+     * call {@link #setMirrorMode(int)}, or call this function after calling {@link #addSurface}.
+     * </p>
+     *
+     * <p>If this OutputConfiguration contains shared surfaces, the application can set
+     * different mirroring modes for different surfaces.</p>
+     *
+     * <p>For efficiency, the mirror effect is applied as a transform flag, so it is only effective
+     * in some outputs. It works automatically for SurfaceView and TextureView outputs. For manual
+     * use of SurfaceTexture, it is reflected in the value of
+     * {@link android.graphics.SurfaceTexture#getTransformMatrix}. For other end points, such as
+     * ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
+     * needed for such outputs, the application needs to mirror the image buffers itself before
+     * passing them onward.</p>
+     *
+     * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+     *                                  OutputConfiguration, or the {@code mirrorMode} value is
+     *                                  not valid.
+     */
+    @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+    public void setMirrorMode(@NonNull Surface surface, @MirrorMode int mirrorMode) {
+        checkNotNull(surface, "Surface must not be null");
+        // Verify that the value is in range
+        if (mirrorMode < MIRROR_MODE_AUTO || mirrorMode > MIRROR_MODE_V) {
+            throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
+        }
+        int surfaceIndex = mSurfaces.indexOf(surface);
+        if (surfaceIndex == -1) {
+            throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+        }
+
+        mMirrorModeForSurfaces.set(surfaceIndex, mirrorMode);
+    }
+
+    /**
+     * Get the current mirroring mode for an output surface
+     *
+     * <p>If no {@link #setMirrorMode} is called first, this function returns
+     * {@link #MIRROR_MODE_AUTO}.</p>
+     *
+     * <p>If only {@link #setMirrorMode(int)} is called, the mirroring mode set by that
+     * function will be returned here as long as the {@code surface} belongs to this
+     * output configuration.</p>
+     *
+     * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+     *                                  OutputConfiguration.
+     *
+     * @return The mirroring mode for the specified output surface
+     */
+    @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+    public @MirrorMode int getMirrorMode(@NonNull Surface surface) {
+        checkNotNull(surface, "Surface must not be null");
+
+        int surfaceIndex = mSurfaces.indexOf(surface);
+        if (surfaceIndex == -1) {
+            throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+        }
+        return mMirrorModeForSurfaces.get(surfaceIndex);
+    }
+
+    /**
      * Use the camera sensor's readout time for the image timestamp.
      *
      * <p>The start of the camera sensor readout after exposure. For a rolling shutter camera
@@ -1491,6 +1584,7 @@
         this.mStreamUseCase = other.mStreamUseCase;
         this.mTimestampBase = other.mTimestampBase;
         this.mMirrorMode = other.mMirrorMode;
+        this.mMirrorModeForSurfaces = other.mMirrorModeForSurfaces.clone();
         this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
         this.mUsage = other.mUsage;
     }
@@ -1520,6 +1614,7 @@
 
         int timestampBase = source.readInt();
         int mirrorMode = source.readInt();
+        int[] mirrorModeForSurfaces = source.createIntArray();
         boolean readoutTimestampEnabled = source.readInt() == 1;
         int format = source.readInt();
         int dataSpace = source.readInt();
@@ -1531,7 +1626,6 @@
         mConfiguredSize = new Size(width, height);
         mIsDeferredConfig = isDeferred;
         mIsShared = isShared;
-        mSurfaces = surfaces;
         mUsage = 0;
         if (mSurfaces.size() > 0) {
             mSurfaceType = SURFACE_TYPE_UNKNOWN;
@@ -1560,6 +1654,7 @@
         mStreamUseCase = streamUseCase;
         mTimestampBase = timestampBase;
         mMirrorMode = mirrorMode;
+        mMirrorModeForSurfaces = IntArray.wrap(mirrorModeForSurfaces);
         mReadoutTimestampEnabled = readoutTimestampEnabled;
     }
 
@@ -1706,6 +1801,7 @@
         dest.writeLong(mStreamUseCase);
         dest.writeInt(mTimestampBase);
         dest.writeInt(mMirrorMode);
+        dest.writeIntArray(mMirrorModeForSurfaces.toArray());
         dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
         dest.writeInt(mConfiguredFormat);
         dest.writeInt(mConfiguredDataspace);
@@ -1756,6 +1852,16 @@
                     return false;
                 }
             }
+            if (Flags.mirrorModeSharedSurfaces()) {
+                if (mMirrorModeForSurfaces.size() != other.mMirrorModeForSurfaces.size()) {
+                    return false;
+                }
+                for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+                    if (mMirrorModeForSurfaces.get(j) != other.mMirrorModeForSurfaces.get(j)) {
+                        return false;
+                    }
+                }
+            }
             int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
             for (int i = 0;  i < minLen; i++) {
                 if (mSurfaces.get(i) != other.mSurfaces.get(i))
@@ -1799,8 +1905,9 @@
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                     mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                     mDynamicRangeProfile, mColorSpace, mStreamUseCase,
-                    mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
-                    Long.hashCode(mUsage));
+                    mTimestampBase, mMirrorMode,
+                    HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+                    mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
         }
 
         return HashCodeHelpers.hashCode(
@@ -1810,7 +1917,9 @@
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                 mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                 mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
-                mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
+                mMirrorMode, HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+                mReadoutTimestampEnabled ? 1 : 0,
+                Long.hashCode(mUsage));
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -1852,6 +1961,8 @@
     private int mTimestampBase;
     // Mirroring mode
     private int mMirrorMode;
+    // Per-surface mirror modes
+    private IntArray mMirrorModeForSurfaces;
     // readout timestamp
     private boolean mReadoutTimestampEnabled;
     // Whether the timestamp base is set to READOUT_SENSOR