Merge "audio policy: fix sensor privacy listener"
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 936943c..4995dc4 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -587,6 +587,7 @@
     ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
     ANDROID_CONTROL_AE_PRECAPTURE_ID,
     ANDROID_CONTROL_AF_TRIGGER_ID,
+    ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
     ANDROID_DEMOSAIC_MODE,
     ANDROID_EDGE_STRENGTH,
     ANDROID_FLASH_FIRING_POWER,
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 587c431..c0b0052 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2169,7 +2169,7 @@
      * @see ACAMERA_SCALER_CROP_REGION
      */
     ACAMERA_CONTROL_AUTOFRAMING =                               // byte (acamera_metadata_enum_android_control_autoframing_t)
-            ACAMERA_CONTROL_START + 51,
+            ACAMERA_CONTROL_START + 52,
     /**
      * <p>Whether the camera device supports ACAMERA_CONTROL_AUTOFRAMING.</p>
      *
@@ -2185,7 +2185,7 @@
      * <p>Will be <code>false</code> if auto-framing is not available.</p>
      */
     ACAMERA_CONTROL_AUTOFRAMING_AVAILABLE =                     // byte (acamera_metadata_enum_android_control_autoframing_available_t)
-            ACAMERA_CONTROL_START + 52,
+            ACAMERA_CONTROL_START + 53,
     /**
      * <p>Current state of auto-framing.</p>
      *
@@ -2212,7 +2212,7 @@
      * @see ACAMERA_CONTROL_AUTOFRAMING_AVAILABLE
      */
     ACAMERA_CONTROL_AUTOFRAMING_STATE =                         // byte (acamera_metadata_enum_android_control_autoframing_state_t)
-            ACAMERA_CONTROL_START + 53,
+            ACAMERA_CONTROL_START + 54,
     ACAMERA_CONTROL_END,
 
     /**
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 6d3c348..386e42c 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -125,6 +125,7 @@
 AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor *mData) {
     sp<AMessage> format;
     mData->mImpl->getFileFormat(&format);
+    // ignore any error, we want to return the empty format
     return AMediaFormat_fromMsg(&format);
 }
 
@@ -247,7 +248,10 @@
     }
 
     sp<AMessage> format;
-    ex->mImpl->getFileFormat(&format);
+    if (ex->mImpl->getFileFormat(&format) != OK) {
+        android_errorWriteWithInfoLog(0x534e4554, "243222985", -1, nullptr, 0);
+        return NULL;
+    }
     sp<ABuffer> buffer;
     if(!format->findBuffer("pssh", &buffer)) {
         return NULL;
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index a95e874..c0de4e4 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -332,6 +332,9 @@
     if (name == nullptr) {
         return;
     }
+    if (value == nullptr) {
+        return;
+    }
     // AMessage::setString() makes a copy of the string
     format->mFormat->setString(name, value, strlen(value));
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 4b44dcf..d41a7f9 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -69,6 +69,6 @@
 java_defaults {
     name: "MediaBenchmark-defaults",
 
-    min_sdk_version: "28",
+    min_sdk_version: "29",
     target_sdk_version: "30",
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
index eea9914..772a29b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
+++ b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
@@ -20,8 +20,6 @@
     package="com.android.media.benchmark">
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
 
     <application
         tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon"
@@ -31,4 +29,4 @@
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.media.benchmark"
             android:label="Benchmark Media Test"/>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index b222d47..a2af701 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -30,7 +30,7 @@
     compileSdkVersion 30
     defaultConfig {
         applicationId "com.android.media.benchmark"
-        minSdkVersion 28
+        minSdkVersion 29
         targetSdkVersion 30
         versionCode 1
         versionName "1.0"
@@ -73,4 +73,4 @@
     testImplementation 'junit:junit:4.13.2'
     androidTestImplementation 'androidx.test:runner:1.3.0'
     androidTestImplementation 'androidx.test.ext:junit:1.1.2'
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
index 08035c9..1e10b37 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
@@ -2,13 +2,12 @@
 
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
+import android.media.MediaFormat;
 import android.os.Build;
-
 import java.util.ArrayList;
 
 public class CodecUtils {
     private CodecUtils() {}
-
     /**
      * Queries the MediaCodecList and returns codec names of supported codecs.
      *
@@ -36,4 +35,46 @@
         }
         return supportedCodecs;
     }
+    /**
+     * Returns a decoder that supports the given MediaFormat along with the "features".
+     *
+     * @param format  MediaFormat that the codec should support
+     * @param isSoftware Specifies if this is a software / hardware decoder
+     * @param isEncoder Specifies if the request is for encoders or not.
+     * @param features is the feature that should be supported.
+     * @return name of the codec.
+     */
+    public static String getMediaCodec(MediaFormat format, boolean isSoftware,
+                                  String[] features, boolean isEncoder) {
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+        MediaCodecInfo[] codecInfos = mcl.getCodecInfos();
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (codecInfo.isEncoder() != isEncoder) continue;
+            if (isSoftware != codecInfo.isSoftwareOnly()) continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.equalsIgnoreCase(mime)) {
+                    boolean isOk = true;
+                    MediaCodecInfo.CodecCapabilities codecCapabilities =
+                        codecInfo.getCapabilitiesForType(type);
+                    if (!codecCapabilities.isFormatSupported(format)) {
+                        isOk = false;
+                    }
+                    if (features != null) {
+                        for (String feature : features) {
+                            if (!codecCapabilities.isFeatureSupported(feature)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (isOk) {
+                        return codecInfo.getName();
+                    }
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index c2ed82c..a4ba36a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -16,6 +16,8 @@
 
 package com.android.media.benchmark.library;
 
+import android.view.Surface;
+
 import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaFormat;
@@ -28,13 +30,17 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 
-public class Decoder {
+import com.android.media.benchmark.library.IBufferXfer;
+
+public class Decoder implements IBufferXfer.IReceiveBuffer {
     private static final String TAG = "Decoder";
     private static final boolean DEBUG = false;
     private static final int kQueueDequeueTimeoutUs = 1000;
 
     private final Object mLock = new Object();
     private MediaCodec mCodec;
+    private Surface mSurface = null;
+    private boolean mRender = false;
     private ArrayList<BufferInfo> mInputBufferInfo;
     private Stats mStats;
 
@@ -47,9 +53,29 @@
 
     private ArrayList<ByteBuffer> mInputBuffer;
     private FileOutputStream mOutputStream;
+    private FrameReleaseQueue mFrameReleaseQueue = null;
+    private IBufferXfer.ISendBuffer mIBufferSend = null;
 
+    /* success for decoder */
+    public static final int DECODE_SUCCESS = 0;
+    /* some error happened during decoding */
+    public static final int DECODE_DECODER_ERROR = -1;
+    /* error while creating a decoder */
+    public static final int DECODE_CREATE_ERROR = -2;
     public Decoder() { mStats = new Stats(); }
     public Stats getStats() { return mStats; };
+    @Override
+    public boolean receiveBuffer(IBufferXfer.BufferXferInfo info) {
+        MediaCodec codec = (MediaCodec)info.obj;
+        codec.releaseOutputBuffer(info.idx, mRender);
+        return true;
+    }
+    @Override
+    public boolean connect(IBufferXfer.ISendBuffer receiver) {
+        Log.d(TAG,"Setting interface of the sender");
+        mIBufferSend = receiver;
+        return true;
+    }
     /**
      * Setup of decoder
      *
@@ -59,6 +85,17 @@
         mSignalledError = false;
         mOutputStream = outputStream;
     }
+    public void setupDecoder(Surface surface, boolean render,
+            boolean useFrameReleaseQueue, int frameRate) {
+        mSignalledError = false;
+        mOutputStream = null;
+        mSurface = surface;
+        mRender = render;
+        if (useFrameReleaseQueue) {
+            Log.i(TAG, "Using FrameReleaseQueue with frameRate " + frameRate);
+            mFrameReleaseQueue = new FrameReleaseQueue(mRender, frameRate);
+        }
+    }
 
     private MediaCodec createCodec(String codecName, MediaFormat format) throws IOException {
         String mime = format.getString(MediaFormat.KEY_MIME);
@@ -95,7 +132,8 @@
      * @param asyncMode       Will run on async implementation if true
      * @param format          For creating the decoder if codec name is empty and configuring it
      * @param codecName       Will create the decoder with codecName
-     * @return 0 if decode was successful , -1 for fail, -2 for decoder not created
+     * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+     *         DECODE_CREATE_ERROR for decoder not created
      * @throws IOException if the codec cannot be created.
      */
     public int decode(@NonNull ArrayList<ByteBuffer> inputBuffer,
@@ -112,7 +150,10 @@
         long sTime = mStats.getCurTime();
         mCodec = createCodec(codecName, format);
         if (mCodec == null) {
-            return -2;
+            return DECODE_CREATE_ERROR;
+        }
+        if (mFrameReleaseQueue != null) {
+            mFrameReleaseQueue.setMediaCodec(mCodec);
         }
         if (asyncMode) {
             mCodec.setCallback(new MediaCodec.Callback() {
@@ -158,7 +199,7 @@
         if (DEBUG) {
             Log.d(TAG, "Media Format : " + format.toString());
         }
-        mCodec.configure(format, null, null, isEncoder);
+        mCodec.configure(format, mSurface, null, isEncoder);
         mCodec.start();
         Log.i(TAG, "Codec started ");
         long eTime = mStats.getCurTime();
@@ -168,7 +209,7 @@
             try {
                 synchronized (mLock) { mLock.wait(); }
                 if (mSignalledError) {
-                    return -1;
+                    return DECODE_DECODER_ERROR;
                 }
             } catch (InterruptedException e) {
                 e.printStackTrace();
@@ -201,7 +242,7 @@
                         Log.e(TAG,
                                 "MediaCodec.dequeueOutputBuffer"
                                         + " returned invalid index " + outputBufferId);
-                        return -1;
+                        return DECODE_DECODER_ERROR;
                     }
                 } else {
                     mStats.addOutputTime();
@@ -212,9 +253,13 @@
                 }
             }
         }
+        if (mFrameReleaseQueue != null) {
+            Log.i(TAG, "Ending FrameReleaseQueue");
+            mFrameReleaseQueue.stopFrameRelease();
+        }
         mInputBuffer.clear();
         mInputBufferInfo.clear();
-        return 0;
+        return DECODE_SUCCESS;
     }
 
     /**
@@ -290,7 +335,9 @@
         if (DEBUG) {
             Log.d(TAG,
                     "In OutputBufferAvailable ,"
-                            + " output frame number = " + mNumOutputFrame);
+                            + " output frame number = " + mNumOutputFrame
+                            + " timestamp = " + outputBufferInfo.presentationTimeUs
+                            + " size = " + outputBufferInfo.size);
         }
         if (mOutputStream != null) {
             try {
@@ -303,7 +350,21 @@
                 Log.d(TAG, "Error Dumping File: Exception " + e.toString());
             }
         }
-        mediaCodec.releaseOutputBuffer(outputBufferId, false);
+        if (mFrameReleaseQueue != null) {
+            mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
+                                            outputBufferInfo.presentationTimeUs);
+        } else if (mIBufferSend != null) {
+            IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+            info.buf = mediaCodec.getOutputBuffer(outputBufferId);
+            info.idx = outputBufferId;
+            info.obj = mediaCodec;
+            info.bytesRead = outputBufferInfo.size;
+            info.presentationTimeUs = outputBufferInfo.presentationTimeUs;
+            info.flag = outputBufferInfo.flags;
+            mIBufferSend.sendBuffer(this, info);
+        } else {
+            mediaCodec.releaseOutputBuffer(outputBufferId, mRender);
+        }
         mSawOutputEOS = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
         if (mSawOutputEOS) {
             Log.i(TAG, "Saw output EOS");
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 754cd8e..63d17ee 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -19,6 +19,7 @@
 import android.media.MediaCodec;
 import android.media.MediaCodec.CodecException;
 import android.media.MediaFormat;
+import android.view.Surface;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -28,34 +29,43 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
-public class Encoder {
+public class Encoder implements IBufferXfer.IReceiveBuffer {
     // Change in AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE should also be taken to
     // kDefaultAudioEncodeFrameSize present in BenchmarkCommon.h
     private static final int AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE = 4096;
     private static final String TAG = "Encoder";
     private static final boolean DEBUG = false;
     private static final int kQueueDequeueTimeoutUs = 1000;
-
     private final Object mLock = new Object();
-    private MediaCodec mCodec;
+    private MediaCodec mCodec = null;
     private String mMime;
     private Stats mStats;
 
     private int mOffset;
     private int mFrameSize;
     private int mNumInputFrame;
-    private int mNumFrames;
+    private int mNumFrames = 0;
     private int mFrameRate;
     private int mSampleRate;
     private long mInputBufferSize;
 
+    private int mMinOutputBuffers = 0;
+    private int mNumOutputBuffers = 0;
+    private boolean mUseSurface = false;
+
     private boolean mSawInputEOS;
     private boolean mSawOutputEOS;
     private boolean mSignalledError;
 
-    private FileInputStream mInputStream;
-    private FileOutputStream mOutputStream;
-
+    private FileInputStream mInputStream = null;
+    private FileOutputStream mOutputStream = null;
+    private IBufferXfer.ISendBuffer mIBufferSend = null;
+    /* success for encoder */
+    public static final int ENCODE_SUCCESS = 0;
+    /* some error happened during encoding */
+    public static final int ENCODE_ENCODER_ERROR = -1;
+    /* error while creating an encoder */
+    public static final int ENCODE_CREATE_ERROR = -2;
     public Encoder() {
         mStats = new Stats();
         mNumInputFrame = 0;
@@ -63,6 +73,25 @@
         mSawOutputEOS = false;
         mSignalledError = false;
     }
+    @Override
+    public boolean receiveBuffer(IBufferXfer.BufferXferInfo info) {
+        if (DEBUG) {
+            Log.d(TAG,"Encoder Getting buffers from external: "
+                + " Bytes Read: " + info.bytesRead
+                + " PresentationUs " + info.presentationTimeUs
+                + " flags: " + info.flag);
+        }
+        MediaCodec codec = (MediaCodec)info.obj;
+        codec.queueInputBuffer(info.idx, 0, info.bytesRead,
+            info.presentationTimeUs, info.flag);
+        return true;
+    }
+    @Override
+    public boolean connect(IBufferXfer.ISendBuffer receiver) {
+        mIBufferSend = receiver;
+        return true;
+    }
+    public Stats getStats() { return mStats; };
 
     /**
      * Setup of encoder
@@ -75,6 +104,17 @@
         this.mInputStream = fileInputStream;
         this.mOutputStream = encoderOutputStream;
     }
+    /**
+     * Setup of encoder
+     *
+     * @param useSurface, indicates that application is using surface for input
+     * @param numOutputBuffers indicate the minimum buffers to signal Output
+     * end of stream
+     */
+    public void setupEncoder(boolean useSurface, int numOutputBuffers) {
+        this.mUseSurface = useSurface;
+        this.mMinOutputBuffers = numOutputBuffers;
+    }
 
     private MediaCodec createCodec(String codecName, String mime) throws IOException {
         try {
@@ -100,7 +140,52 @@
             return null;
         }
     }
+    /**
+     * Creates and configures the encoder with the given name, format and mime.
+     * provided a valid list of parameters are passed as inputs. This is needed
+     * to first configure the codec and then may be get surface etc and then
+     * use for encode.
+     *
+     * @param codecName    Will create the encoder with codecName
+     * @param encodeFormat Format of the output data
+     * @param mime         For creating encode format
+     * @return ENCODE_SUCCESS if encode was successful,
+     *         ENCODE_CREATE_ERROR for encoder not created
+     * @throws IOException If the codec cannot be created.
+     */
 
+    public int createAndConfigure(String codecName, MediaFormat encodeFormat,
+                                  String mime) throws IOException {
+        if (mCodec == null) {
+            mMime = mime;
+            mCodec = createCodec(codecName, mime);
+            if (mCodec == null) {
+                return ENCODE_CREATE_ERROR;
+            }
+            /*Configure Codec*/
+            try {
+                mCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            } catch(IllegalArgumentException
+                  | IllegalStateException
+                  | MediaCodec.CryptoException e) {
+                Log.e(TAG, "Failed to configure " + mCodec.getName() + " encoder.");
+                e.printStackTrace();
+                return ENCODE_CREATE_ERROR;
+            }
+        }
+        return ENCODE_SUCCESS;
+    }
+    /**
+     * Requests the surface to use as input to the encoder
+     * @return a valid surface or null if not called after configure.
+     */
+    public Surface getInputSurface() {
+        Surface inputSurface = null;
+        if (mCodec != null) {
+            inputSurface = mCodec.createInputSurface();
+        }
+        return inputSurface;
+    }
     /**
      * Encodes the given raw input file and measures the performance of encode operation,
      * provided a valid list of parameters are passed as inputs.
@@ -110,43 +195,39 @@
      * @param encodeFormat Format of the output data
      * @param frameSize    Size of the frame
      * @param asyncMode    Will run on async implementation if true
-     * @return 0 if encode was successful , -1 for fail, -2 for encoder not created
+     * @return ENCODE_SUCCESS if encode was successful ,ENCODE_ENCODER_ERROR for fail,
+     *         ENCODE_CREATE_ERROR for encoder not created
      * @throws IOException If the codec cannot be created.
      */
     public int encode(String codecName, MediaFormat encodeFormat, String mime, int frameRate,
                       int sampleRate, int frameSize, boolean asyncMode) throws IOException {
-        mInputBufferSize = mInputStream.getChannel().size();
-        mMime = mime;
+        mInputBufferSize = (mInputStream != null) ? mInputStream.getChannel().size() : 0;
         mOffset = 0;
         mFrameRate = frameRate;
         mSampleRate = sampleRate;
         long sTime = mStats.getCurTime();
-        mCodec = createCodec(codecName, mime);
         if (mCodec == null) {
-            return -2;
-        }
-        /*Configure Codec*/
-        try {
-            mCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
-        } catch (IllegalArgumentException | IllegalStateException | MediaCodec.CryptoException e) {
-            Log.e(TAG, "Failed to configure " + mCodec.getName() + " encoder.");
-            e.printStackTrace();
-            return -2;
-        }
-        if (mMime.startsWith("video/")) {
-            mFrameSize = frameSize;
-        } else {
-            int maxInputSize = AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE;
-            MediaFormat format = mCodec.getInputFormat();
-            if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
-                maxInputSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
-            }
-            mFrameSize = frameSize;
-            if (mFrameSize > maxInputSize && maxInputSize > 0) {
-                mFrameSize = maxInputSize;
+            int status = createAndConfigure(codecName, encodeFormat, mime);
+            if(status != ENCODE_SUCCESS) {
+              return status;
             }
         }
-        mNumFrames = (int) ((mInputBufferSize + mFrameSize - 1) / mFrameSize);
+        if (!mUseSurface) {
+            if (mMime.startsWith("video/")) {
+                mFrameSize = frameSize;
+            } else {
+                int maxInputSize = AUDIO_ENCODE_DEFAULT_MAX_INPUT_SIZE;
+                MediaFormat format = mCodec.getInputFormat();
+                if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
+                    maxInputSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
+                }
+                mFrameSize = frameSize;
+                if (mFrameSize > maxInputSize && maxInputSize > 0) {
+                    mFrameSize = maxInputSize;
+                }
+            }
+            mNumFrames = (int) ((mInputBufferSize + mFrameSize - 1) / mFrameSize);
+        }
         if (asyncMode) {
             mCodec.setCallback(new MediaCodec.Callback() {
                 @Override
@@ -196,7 +277,7 @@
             try {
                 synchronized (mLock) { mLock.wait(); }
                 if (mSignalledError) {
-                    return -1;
+                    return ENCODE_ENCODER_ERROR;
                 }
             } catch (InterruptedException e) {
                 e.printStackTrace();
@@ -204,12 +285,12 @@
         } else {
             while (!mSawOutputEOS && !mSignalledError) {
                 /* Queue input data */
-                if (!mSawInputEOS) {
+                if (!mSawInputEOS && !mUseSurface) {
                     int inputBufferId = mCodec.dequeueInputBuffer(kQueueDequeueTimeoutUs);
                     if (inputBufferId < 0 && inputBufferId != MediaCodec.INFO_TRY_AGAIN_LATER) {
                         Log.e(TAG, "MediaCodec.dequeueInputBuffer " + "returned invalid index : " +
                                 inputBufferId);
-                        return -1;
+                        return ENCODE_ENCODER_ERROR;
                     }
                     mStats.addInputTime();
                     onInputAvailable(mCodec, inputBufferId);
@@ -225,7 +306,7 @@
                     } else if (outputBufferId != MediaCodec.INFO_TRY_AGAIN_LATER) {
                         Log.e(TAG, "MediaCodec.dequeueOutputBuffer" + " returned invalid index " +
                                 outputBufferId);
-                        return -1;
+                        return ENCODE_ENCODER_ERROR;
                     }
                 } else {
                     mStats.addOutputTime();
@@ -236,7 +317,7 @@
                 }
             }
         }
-        return 0;
+        return ENCODE_SUCCESS;
     }
 
     private void onOutputAvailable(MediaCodec mediaCodec, int outputBufferId,
@@ -260,13 +341,25 @@
                 return;
             }
         }
+        mNumOutputBuffers++;
+        if (DEBUG) {
+            Log.d(TAG,
+                "In OutputBufferAvailable ,"
+                + " timestamp = " + outputBufferInfo.presentationTimeUs
+                + " size = " + outputBufferInfo.size
+                + " flags = " + outputBufferInfo.flags);
+        }
+
         mStats.addFrameSize(outputBuffer.remaining());
         mediaCodec.releaseOutputBuffer(outputBufferId, false);
         mSawOutputEOS = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+        if (mUseSurface && !mSawOutputEOS) {
+            mSawOutputEOS = (mNumOutputBuffers >= mMinOutputBuffers) ? true : false;
+        }
     }
 
     private void onInputAvailable(MediaCodec mediaCodec, int inputBufferId) throws IOException {
-        if (mSawInputEOS || inputBufferId < 0) {
+        if (mSawInputEOS || inputBufferId < 0 || this.mUseSurface) {
             if (mSawInputEOS) {
                 Log.i(TAG, "Saw input EOS");
             }
@@ -282,6 +375,14 @@
             mSignalledError = true;
             return;
         }
+        if (mIBufferSend != null) {
+            IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+            info.buf = inputBuffer;
+            info.idx = inputBufferId;
+            info.obj = mediaCodec;
+            mIBufferSend.sendBuffer(this, info);
+            return;
+        }
         int bufSize = inputBuffer.capacity();
         int bytesToRead = mFrameSize;
         if (mInputBufferSize - mOffset < mFrameSize) {
@@ -356,9 +457,11 @@
         mOffset = 0;
         mInputBufferSize = 0;
         mNumInputFrame = 0;
+        mMinOutputBuffers = 0;
         mSawInputEOS = false;
         mSawOutputEOS = false;
         mSignalledError = false;
+        mUseSurface = false;
         mStats.reset();
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
new file mode 100644
index 0000000..4b9b505
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+
+import android.media.MediaCodec;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import java.nio.ByteBuffer;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class FrameReleaseQueue {
+    private static final String TAG = "FrameReleaseQueue";
+
+    private MediaCodec mCodec;
+    private LinkedBlockingQueue<FrameInfo> mFrameInfoQueue;
+    private ReleaseThread mReleaseThread;
+    private boolean doFrameRelease = false;
+    private boolean mRender = false;
+    private int mWaitTime = 40; // milliseconds per frame
+    private int firstReleaseTime = -1;
+    private int THRESHOLD_TIME = 5;
+
+    private static class FrameInfo {
+        private int number;
+        private int bufferId;
+        private int displayTime;
+        public FrameInfo(int frameNumber, int frameBufferId, int frameDisplayTime) {
+            this.number = frameNumber;
+            this.bufferId = frameBufferId;
+            this.displayTime = frameDisplayTime;
+        }
+    }
+
+    private class ReleaseThread extends Thread {
+        public void run() {
+            int nextReleaseTime = 0;
+            while (doFrameRelease || mFrameInfoQueue.size() > 0) {
+                FrameInfo curFrameInfo = mFrameInfoQueue.peek();
+                if (curFrameInfo == null) {
+                    nextReleaseTime += mWaitTime;
+                } else {
+                    if (firstReleaseTime == -1) {
+                        firstReleaseTime = getCurSysTime();
+                        nextReleaseTime = firstReleaseTime + mWaitTime;
+                        popAndRelease(curFrameInfo);
+                    } else {
+                        nextReleaseTime += mWaitTime;
+                        int curSysTime = getCurSysTime();
+                        int curMediaTime = curSysTime - firstReleaseTime;
+                        while (curFrameInfo != null && curFrameInfo.displayTime <= curMediaTime) {
+                            if (!((curMediaTime - curFrameInfo.displayTime) < THRESHOLD_TIME)) {
+                                Log.d(TAG, "Dropping expired frame " + curFrameInfo.number);
+                            }
+                            popAndRelease(curFrameInfo);
+                            curFrameInfo = mFrameInfoQueue.peek();
+                        }
+                        if (curFrameInfo != null && curFrameInfo.displayTime > curMediaTime) {
+                            if ((curFrameInfo.displayTime - curMediaTime) < THRESHOLD_TIME) {
+                                popAndRelease(curFrameInfo);
+                            }
+                        }
+                    }
+                }
+                int sleepTime = nextReleaseTime - getCurSysTime();
+                if (sleepTime > 0) {
+                    try {
+                        mReleaseThread.sleep(sleepTime);
+                    } catch (InterruptedException e) {
+                        Log.e(TAG, "Threw InterruptedException on sleep");
+                    }
+                } else {
+                    Log.d(TAG, "Thread sleep time less than 1");
+                }
+            }
+        }
+    }
+
+    public FrameReleaseQueue(boolean render, int frameRate) {
+        this.mFrameInfoQueue = new LinkedBlockingQueue();
+        this.mReleaseThread = new ReleaseThread();
+        this.doFrameRelease = true;
+        this.mRender = render;
+        this.mWaitTime = (int)(1.0f/frameRate * 1000); // wait time in milliseconds per frame
+        Log.i(TAG, "Constructed FrameReleaseQueue with wait time " + this.mWaitTime + " ms");
+    }
+
+    public void setMediaCodec(MediaCodec mediaCodec) {
+        this.mCodec = mediaCodec;
+    }
+
+    public boolean pushFrame(int frameNumber, int frameBufferId, long frameDisplayTime) {
+        int frameDisplayTimeMs = (int)(frameDisplayTime/1000);
+        FrameInfo curFrameInfo = new FrameInfo(frameNumber, frameBufferId, frameDisplayTimeMs);
+        boolean pushSuccess = mFrameInfoQueue.offer(curFrameInfo);
+        if (!pushSuccess) {
+            Log.e(TAG, "Failed to push frame with buffer id " + curFrameInfo.bufferId);
+            return false;
+        }
+        if (!mReleaseThread.isAlive()) {
+            mReleaseThread.start();
+            Log.i(TAG, "Started frame release thread");
+        }
+        return true;
+    }
+
+    private int getCurSysTime() {
+        return (int)(System.nanoTime()/1000000);
+    }
+
+    private void popAndRelease(FrameInfo curFrameInfo) {
+        try {
+            curFrameInfo = mFrameInfoQueue.take();
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Threw InterruptedException on take");
+        }
+        try {
+            mCodec.releaseOutputBuffer(curFrameInfo.bufferId, mRender);
+        } catch (IllegalStateException e) {
+            Log.e(TAG,
+                    "Threw IllegalStateException on releaseOutputBuffer for frame "
+                            + curFrameInfo.number);
+        }
+    }
+
+    public void stopFrameRelease() {
+        doFrameRelease = false;
+        try {
+            mReleaseThread.join();
+            Log.i(TAG, "Joined frame release thread");
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Threw InterruptedException on thread join");
+        }
+    }
+}
+
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
new file mode 100644
index 0000000..a75962c
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+import android.media.MediaCodec;
+import java.nio.ByteBuffer;
+/**
+ * interfaces that can be used to implement
+ * sending of buffers to external and receive using callbacks
+ */
+public class IBufferXfer {
+  static class BufferXferInfo {
+      public ByteBuffer buf;
+      public int idx;
+      public Object obj;
+      int flag;
+      int bytesRead;
+      long presentationTimeUs;
+  }
+
+  public interface IReceiveBuffer {
+      // Implemented by sender to get buffers back
+      boolean receiveBuffer(BufferXferInfo info);
+      // Establishes a connection between the buffer sender and receiver.
+      // Implemented by the entity that sends the buffers to receiver.
+      // the receiverInterface is the interface of the receiver.
+      // The sender uses this interface to send buffers.
+      boolean connect(IBufferXfer.ISendBuffer receiverInterface);
+  }
+  // Implemented by an entity that does not own the buffers and only
+  // wants to manage the buffers. ( Usually the receiver)
+  // The receiver uses returnIface to return the buffers to sender
+  public interface ISendBuffer {
+      boolean sendBuffer(IBufferXfer.IReceiveBuffer returnIface,
+                              BufferXferInfo info);
+  }
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
new file mode 100644
index 0000000..ab55df5
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+
+/**
+ * Class that manages the buffer senders
+*/
+import com.android.media.benchmark.library.IBufferXfer;
+import java.util.ArrayDeque;
+import android.util.Log;
+public class IBufferXferImpl implements IBufferXfer.ISendBuffer {
+
+  private static class BufferInfo {
+      public IBufferXfer.IReceiveBuffer rIface;
+      public IBufferXfer.BufferXferInfo info;
+  }
+  private final String TAG = "IBufferXferImpl";
+  private final ArrayDeque<BufferInfo> mProducerQueue = new ArrayDeque<>();
+  private final ArrayDeque<BufferInfo> mConsumerQueue = new ArrayDeque<>();
+  private IBufferXfer.IReceiveBuffer mProducer = null;
+  private IBufferXfer.IReceiveBuffer mConsumer = null;
+  private final Object mLock = new Object();
+
+  public IBufferXferImpl(IBufferXfer.IReceiveBuffer producer,
+      IBufferXfer.IReceiveBuffer consumer) {
+      mProducer = producer;
+      mConsumer = consumer;
+      // Attach this to be their receiver
+      mProducer.connect(this);
+      mConsumer.connect(this);
+  }
+  @Override
+  public boolean sendBuffer(IBufferXfer.IReceiveBuffer rIface,
+                     IBufferXfer.BufferXferInfo bufferInfo) {
+      if (rIface != mProducer && rIface != mConsumer) {
+         Log.e(TAG, "Interfaces does not match");
+        return false;
+      }
+      boolean status = true;
+      BufferInfo pBuf = null, cBuf = null;
+      synchronized(mLock) {
+          // see which interface this buffer belongs to
+          // producer has a filled buffer and the consumer
+          // buffer needs to be filled.
+          if ( rIface == mProducer ) {
+              if (mConsumerQueue.size() > 0) {
+                  cBuf = mConsumerQueue.remove();
+                  pBuf = new BufferInfo();
+                  pBuf.rIface = rIface;
+                  pBuf.info = bufferInfo;
+              } else {
+                  BufferInfo info = new BufferInfo();
+                  info.rIface = rIface;
+                  info.info = bufferInfo;
+                  mProducerQueue.add(info);
+              }
+          } else if(rIface == mConsumer) {
+              if (mProducerQueue.size() > 0) {
+                  pBuf = mProducerQueue.remove();
+                  cBuf = new BufferInfo();
+                  cBuf.rIface = rIface;
+                  cBuf.info = bufferInfo;
+              } else {
+                  BufferInfo info = new BufferInfo();
+                  info.rIface = rIface;
+                  info.info = bufferInfo;
+                  mConsumerQueue.add(info);
+              }
+          } else {
+              status = false;
+          }
+      }
+
+      if ( pBuf != null && cBuf != null) {
+          int bytesRead = 0;
+          if (cBuf.info.buf != null && pBuf.info.buf != null) {
+              if (cBuf.info.buf.remaining() >= pBuf.info.buf.remaining()) {
+                  bytesRead = pBuf.info.buf.remaining();
+                  cBuf.info.buf.put(pBuf.info.buf);
+              } else {
+                  Log.e(TAG, "Something is wrong with the sizes P:" +
+                      pBuf.info.buf.remaining() +" C:" + cBuf.info.buf.remaining());
+              }
+          }
+          cBuf.info.bytesRead = bytesRead;
+          cBuf.info.presentationTimeUs = pBuf.info.presentationTimeUs;
+          cBuf.info.flag = pBuf.info.flag;
+
+          if (pBuf.rIface != null) {
+              pBuf.rIface.receiveBuffer(pBuf.info);
+          }
+          if (cBuf.rIface != null) {
+              cBuf.rIface.receiveBuffer(cBuf.info);
+          }
+      }
+      return status;
+  }
+  public boolean resetAll() {
+      synchronized(mLock) {
+          while (mProducerQueue.size() > 0) {
+              BufferInfo info = mProducerQueue.remove();
+              info.rIface.receiveBuffer(info.info);
+          }
+          while (mConsumerQueue.size() > 0) {
+              BufferInfo info = mConsumerQueue.remove();
+              info.rIface.receiveBuffer(info.info);
+          }
+          mProducer = null;
+          mConsumer = null;
+      }
+  return true;
+  }
+}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 65c45ff..7506f79 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -494,6 +494,32 @@
     return found;
 }
 
+const std::set<std::string>& getCameraIdsWithZoomLocked(
+        const InFlightRequestMap& inflightMap, const CameraMetadata& metadata,
+        const std::set<std::string>& cameraIdsWithZoom) {
+    camera_metadata_ro_entry overrideEntry =
+            metadata.find(ANDROID_CONTROL_SETTINGS_OVERRIDE);
+    camera_metadata_ro_entry frameNumberEntry =
+            metadata.find(ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER);
+    if (overrideEntry.count != 1
+            || overrideEntry.data.i32[0] != ANDROID_CONTROL_SETTINGS_OVERRIDE_ZOOM
+            || frameNumberEntry.count != 1) {
+        // No valid overriding frame number, skip
+        return cameraIdsWithZoom;
+    }
+
+    uint32_t overridingFrameNumber = frameNumberEntry.data.i32[0];
+    ssize_t idx = inflightMap.indexOfKey(overridingFrameNumber);
+    if (idx < 0) {
+        ALOGE("%s: Failed to find pending request #%d in inflight map",
+                __FUNCTION__, overridingFrameNumber);
+        return cameraIdsWithZoom;
+    }
+
+    const InFlightRequest &r = inflightMap.valueFor(overridingFrameNumber);
+    return r.cameraIdsWithZoom;
+}
+
 void processCaptureResult(CaptureOutputStates& states, const camera_capture_result *result) {
     ATRACE_CALL();
 
@@ -682,10 +708,12 @@
             } else if (request.hasCallback) {
                 CameraMetadata metadata;
                 metadata = result->result;
+                auto cameraIdsWithZoom = getCameraIdsWithZoomLocked(
+                        states.inflightMap, metadata, request.cameraIdsWithZoom);
                 sendCaptureResult(states, metadata, request.resultExtras,
                     collectedPartialResult, frameNumber,
                     hasInputBufferInRequest, request.zslCapture && request.stillCapture,
-                    request.rotateAndCropAuto, request.cameraIdsWithZoom,
+                    request.rotateAndCropAuto, cameraIdsWithZoom,
                     request.physicalMetadatas);
             }
         }
@@ -912,11 +940,13 @@
                     states.listener->notifyShutter(r.resultExtras, msg.timestamp);
                 }
                 // send pending result and buffers
+                const auto& cameraIdsWithZoom = getCameraIdsWithZoomLocked(
+                        inflightMap, r.pendingMetadata, r.cameraIdsWithZoom);
                 sendCaptureResult(states,
                     r.pendingMetadata, r.resultExtras,
                     r.collectedPartialResult, msg.frame_number,
                     r.hasInputBuffer, r.zslCapture && r.stillCapture,
-                    r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
+                    r.rotateAndCropAuto, cameraIdsWithZoom, r.physicalMetadatas);
             }
             returnAndRemovePendingOutputBuffers(
                     states.useHalBufManager, states.listener, r, states.sessionStatsBuilder);
diff --git a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
index e6149da..45c9840 100644
--- a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
@@ -98,5 +98,6 @@
           ANDROID_CONTROL_AUTOFRAMING,
           ANDROID_CONTROL_AUTOFRAMING_STATE,
           ANDROID_CONTROL_SETTINGS_OVERRIDE,
+          ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
         }  },
 };