Merge "MediaCodec: add onMetricsFlushed callback API" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 9a5e0de..d6b8a51 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -22663,6 +22663,7 @@
     method public void onCryptoError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CryptoException);
     method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException);
     method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int);
+    method @FlaggedApi("android.media.codec.subsession_metrics") public void onMetricsFlushed(@NonNull android.media.MediaCodec, @NonNull android.os.PersistableBundle);
     method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo);
     method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void onOutputBuffersAvailable(@NonNull android.media.MediaCodec, int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>);
     method public abstract void onOutputFormatChanged(@NonNull android.media.MediaCodec, @NonNull android.media.MediaFormat);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index e575dae..2ae89d3 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -18,6 +18,7 @@
 
 import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
 import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_SUBSESSION_METRICS;
 
 import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
 
@@ -890,7 +891,7 @@
  any start codes), and submit it as a <strong>regular</strong> input buffer.
  <p>
  You will receive an {@link #INFO_OUTPUT_FORMAT_CHANGED} return value from {@link
- #dequeueOutputBuffer dequeueOutputBuffer} or a {@link Callback#onOutputBufferAvailable
+ #dequeueOutputBuffer dequeueOutputBuffer} or a {@link Callback#onOutputFormatChanged
  onOutputFormatChanged} callback just after the picture-size change takes place and before any
  frames with the new size have been returned.
  <p class=note>
@@ -1835,6 +1836,13 @@
     private static final int CB_CRYPTO_ERROR = 6;
     private static final int CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7;
 
+    /**
+     * Callback ID for when the metrics for this codec have been flushed due to
+     * the start of a new subsession. The associated Java Message object will
+     * contain the flushed metrics as a PersistentBundle in the obj field.
+     */
+    private static final int CB_METRICS_FLUSHED = 8;
+
     private class EventHandler extends Handler {
         private MediaCodec mCodec;
 
@@ -2007,6 +2015,15 @@
                     break;
                 }
 
+                case CB_METRICS_FLUSHED:
+                {
+
+                    if (GetFlag(() -> android.media.codec.Flags.subsessionMetrics())) {
+                        mCallback.onMetricsFlushed(mCodec, (PersistableBundle)msg.obj);
+                    }
+                    break;
+                }
+
                 default:
                 {
                     break;
@@ -4958,14 +4975,24 @@
     public native final String getCanonicalName();
 
     /**
-     *  Return Metrics data about the current codec instance.
+     * Return Metrics data about the current codec instance.
+     * <p>
+     * Call this method after configuration, during execution, or after
+     * the codec has been already stopped.
+     * <p>
+     * Beginning with {@link android.os.Build.VERSION_CODES#B}
+     * this method can be used to get the Metrics data prior to an error.
+     * (e.g. in {@link Callback#onError} or after a method throws
+     * {@link MediaCodec.CodecException}.) Before that, the Metrics data was
+     * cleared on error, resulting in a null return value.
      *
      * @return a {@link PersistableBundle} containing the set of attributes and values
      * available for the media being handled by this instance of MediaCodec
      * The attributes are descibed in {@link MetricsConstants}.
      *
      * Additional vendor-specific fields may also be present in
-     * the return value.
+     * the return value. Returns null if there is no Metrics data.
+     *
      */
     public PersistableBundle getMetrics() {
         PersistableBundle bundle = native_getMetrics();
@@ -5692,6 +5719,27 @@
          */
         public abstract void onOutputFormatChanged(
                 @NonNull MediaCodec codec, @NonNull MediaFormat format);
+
+        /**
+         * Called when the metrics for this codec have been flushed due to the
+         * start of a new subsession.
+         * <p>
+         * This can happen when the codec is reconfigured after stop(), or
+         * mid-stream e.g. if the video size changes. When this happens, the
+         * metrics for the previous subsession are flushed, and
+         * {@link MediaCodec#getMetrics} will return the metrics for the
+         * new subsession. This happens just before the {@link Callback#onOutputFormatChanged}
+         * event, so this <b>optional</b> callback is provided to be able to
+         * capture the final metrics for the previous subsession.
+         *
+         * @param codec The MediaCodec object.
+         * @param metrics The flushed metrics for this codec.
+         */
+        @FlaggedApi(FLAG_SUBSESSION_METRICS)
+        public void onMetricsFlushed(
+                @NonNull MediaCodec codec, @NonNull PersistableBundle metrics) {
+            // default implementation ignores this callback.
+        }
     }
 
     private void postEventFromNative(