Merge "mediacodec/mediaextractor: allow multiple Seccomp extensions" am: da4cb8cb6c am: 07d040d389
am: c9c32a8dcc

Change-Id: Ida14853e8bd5aa68e9ac7b21d5aa81338dfb0897
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 8fc410d..2ec89a4 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -87,7 +87,6 @@
     int64_t mStartTimeUs;
     int16_t mMaxAmplitude;
     int64_t mPrevSampleTimeUs;
-    int64_t mFirstSampleTimeUs;
     int64_t mInitialReadTimeUs;
     int64_t mNumFramesReceived;
     int64_t mNumClientOwnedBuffers;
diff --git a/include/media/stagefright/SimpleDecodingSource.h b/include/media/stagefright/SimpleDecodingSource.h
index 534097b..e6aee6a 100644
--- a/include/media/stagefright/SimpleDecodingSource.h
+++ b/include/media/stagefright/SimpleDecodingSource.h
@@ -71,12 +71,13 @@
     // Construct this using a codec, source and looper.
     SimpleDecodingSource(
             const sp<MediaCodec> &codec, const sp<IMediaSource> &source, const sp<ALooper> &looper,
-            bool usingSurface, const sp<AMessage> &format);
+            bool usingSurface, bool isVorbis, const sp<AMessage> &format);
 
     sp<MediaCodec> mCodec;
     sp<IMediaSource> mSource;
     sp<ALooper> mLooper;
     bool mUsingSurface;
+    bool mIsVorbis;
     enum State {
         INIT,
         STARTED,
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index d7b5f65..b79bf2a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -2357,8 +2357,12 @@
 
     case EQ_PARAM_BAND_LEVEL:
         param2 = *pParamTemp;
-        if (param2 >= FIVEBAND_NUMBANDS) {
+        if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32438598");
+                ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_LEVEL band %d", param2);
+            }
             break;
         }
         *(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, param2);
@@ -2368,8 +2372,12 @@
 
     case EQ_PARAM_CENTER_FREQ:
         param2 = *pParamTemp;
-        if (param2 >= FIVEBAND_NUMBANDS) {
+        if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32436341");
+                ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_CENTER_FREQ band %d", param2);
+            }
             break;
         }
         *(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, param2);
@@ -2379,8 +2387,12 @@
 
     case EQ_PARAM_BAND_FREQ_RANGE:
         param2 = *pParamTemp;
-        if (param2 >= FIVEBAND_NUMBANDS) {
+        if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32247948");
+                ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d", param2);
+            }
             break;
         }
         EqualizerGetBandFreqRange(pContext, param2, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 21fddb1..b7d27d6 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -59,6 +59,8 @@
 
 #define DISCARD_MEASUREMENTS_TIME_MS 2000 // discard measurements older than this number of ms
 
+#define MAX_LATENCY_MS 3000 // 3 seconds of latency for audio pipeline
+
 // maximum number of buffers for which we keep track of the measurements
 #define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 // note: buffer index is stored in uint8_t
 
@@ -521,18 +523,29 @@
             break;
         }
         switch (*(uint32_t *)p->data) {
-        case VISUALIZER_PARAM_CAPTURE_SIZE:
-            pContext->mCaptureSize = *((uint32_t *)p->data + 1);
-            ALOGV("set mCaptureSize = %" PRIu32, pContext->mCaptureSize);
-            break;
+        case VISUALIZER_PARAM_CAPTURE_SIZE: {
+            const uint32_t captureSize = *((uint32_t *)p->data + 1);
+            if (captureSize > VISUALIZER_CAPTURE_SIZE_MAX) {
+                android_errorWriteLog(0x534e4554, "31781965");
+                *(int32_t *)pReplyData = -EINVAL;
+                ALOGW("set mCaptureSize = %u > %u", captureSize, VISUALIZER_CAPTURE_SIZE_MAX);
+            } else {
+                pContext->mCaptureSize = captureSize;
+                ALOGV("set mCaptureSize = %u", captureSize);
+            }
+            } break;
         case VISUALIZER_PARAM_SCALING_MODE:
             pContext->mScalingMode = *((uint32_t *)p->data + 1);
             ALOGV("set mScalingMode = %" PRIu32, pContext->mScalingMode);
             break;
-        case VISUALIZER_PARAM_LATENCY:
-            pContext->mLatency = *((uint32_t *)p->data + 1);
-            ALOGV("set mLatency = %" PRIu32, pContext->mLatency);
-            break;
+        case VISUALIZER_PARAM_LATENCY: {
+            uint32_t latency = *((uint32_t *)p->data + 1);
+            if (latency > MAX_LATENCY_MS) {
+                latency = MAX_LATENCY_MS; // clamp latency b/31781965
+            }
+            pContext->mLatency = latency;
+            ALOGV("set mLatency = %u", latency);
+            } break;
         case VISUALIZER_PARAM_MEASUREMENT_MODE:
             pContext->mMeasurementMode = *((uint32_t *)p->data + 1);
             ALOGV("set mMeasurementMode = %" PRIu32, pContext->mMeasurementMode);
@@ -571,10 +584,18 @@
                 if (latencyMs < 0) {
                     latencyMs = 0;
                 }
-                const uint32_t deltaSmpl =
-                    pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
-                int32_t capturePoint = pContext->mCaptureIdx - captureSize - deltaSmpl;
+                uint32_t deltaSmpl = captureSize
+                        + pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
 
+                // large sample rate, latency, or capture size, could cause overflow.
+                // do not offset more than the size of buffer.
+                if (deltaSmpl > CAPTURE_BUF_SIZE) {
+                    android_errorWriteLog(0x534e4554, "31781965");
+                    deltaSmpl = CAPTURE_BUF_SIZE;
+                }
+
+                int32_t capturePoint = pContext->mCaptureIdx - deltaSmpl;
+                // a negative capturePoint means we wrap the buffer.
                 if (capturePoint < 0) {
                     uint32_t size = -capturePoint;
                     if (size > captureSize) {
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 790c6da..efdee77 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -60,7 +60,6 @@
       mStartTimeUs(0),
       mMaxAmplitude(0),
       mPrevSampleTimeUs(0),
-      mFirstSampleTimeUs(-1ll),
       mInitialReadTimeUs(0),
       mNumFramesReceived(0),
       mNumClientOwnedBuffers(0) {
@@ -277,12 +276,8 @@
     }
 
     if (mSampleRate != mOutSampleRate) {
-        if (mFirstSampleTimeUs < 0) {
-            mFirstSampleTimeUs = timeUs;
-        }
-        timeUs = mFirstSampleTimeUs + (timeUs - mFirstSampleTimeUs)
-                * (int64_t)mSampleRate / (int64_t)mOutSampleRate;
-        buffer->meta_data()->setInt64(kKeyTime, timeUs);
+            timeUs *= (int64_t)mSampleRate / (int64_t)mOutSampleRate;
+            buffer->meta_data()->setInt64(kKeyTime, timeUs);
     }
 
     *out = buffer;
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index de21c5e..2503a32 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -18,6 +18,7 @@
 
 #include <media/ICrypto.h>
 #include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
@@ -74,7 +75,10 @@
                 err = codec->getOutputFormat(&format);
             }
             if (err == OK) {
-                return new SimpleDecodingSource(codec, source, looper, surface != NULL, format);
+                return new SimpleDecodingSource(codec, source, looper,
+                        surface != NULL,
+                        strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
+                        format);
             }
 
             ALOGD("Failed to configure codec '%s'", componentName.c_str());
@@ -90,11 +94,12 @@
 
 SimpleDecodingSource::SimpleDecodingSource(
         const sp<MediaCodec> &codec, const sp<IMediaSource> &source, const sp<ALooper> &looper,
-        bool usingSurface, const sp<AMessage> &format)
+        bool usingSurface, bool isVorbis, const sp<AMessage> &format)
     : mCodec(codec),
       mSource(source),
       mLooper(looper),
       mUsingSurface(usingSurface),
+      mIsVorbis(isVorbis),
       mProtectedState(format) {
     mCodec->getName(&mComponentName);
 }
@@ -280,16 +285,25 @@
             if (in_buf != NULL) {
                 int64_t timestampUs = 0;
                 CHECK(in_buf->meta_data()->findInt64(kKeyTime, &timestampUs));
-                if (in_buf->range_length() > in_buffer->capacity()) {
+                if (in_buf->range_length() + (mIsVorbis ? 4 : 0) > in_buffer->capacity()) {
                     ALOGW("'%s' received %zu input bytes for buffer of size %zu",
                             mComponentName.c_str(),
-                            in_buf->range_length(), in_buffer->capacity());
+                            in_buf->range_length() + (mIsVorbis ? 4 : 0), in_buffer->capacity());
                 }
+                size_t cpLen = min(in_buf->range_length(), in_buffer->capacity());
                 memcpy(in_buffer->base(), (uint8_t *)in_buf->data() + in_buf->range_offset(),
-                       min(in_buf->range_length(), in_buffer->capacity()));
+                        cpLen );
+
+                if (mIsVorbis) {
+                    int32_t numPageSamples;
+                    if (!in_buf->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
+                        numPageSamples = -1;
+                    }
+                    memcpy(in_buffer->base() + cpLen, &numPageSamples, sizeof(numPageSamples));
+                }
 
                 res = mCodec->queueInputBuffer(
-                        in_ix, 0 /* offset */, in_buf->range_length(),
+                        in_ix, 0 /* offset */, in_buf->range_length() + (mIsVorbis ? 4 : 0),
                         timestampUs, 0 /* flags */);
                 if (res != OK) {
                     ALOGI("[%s] failed to queue input buffer #%zu", mComponentName.c_str(), in_ix);
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index aaf6b3d..a0eb630 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -839,20 +839,21 @@
     }
 }
 
-static size_t StringSize(const uint8_t *start, uint8_t encoding) {
+// return includes terminator;  if unterminated, returns > limit
+static size_t StringSize(const uint8_t *start, size_t limit, uint8_t encoding) {
+
     if (encoding == 0x00 || encoding == 0x03) {
         // ISO 8859-1 or UTF-8
-        return strlen((const char *)start) + 1;
+        return strnlen((const char *)start, limit) + 1;
     }
 
     // UCS-2
     size_t n = 0;
-    while (start[n] != '\0' || start[n + 1] != '\0') {
+    while ((n+1 < limit) && (start[n] != '\0' || start[n + 1] != '\0')) {
         n += 2;
     }
-
-    // Add size of null termination.
-    return n + 2;
+    n += 2;
+    return n;
 }
 
 const void *
@@ -873,11 +874,19 @@
 
         if (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) {
             uint8_t encoding = data[0];
-            mime->setTo((const char *)&data[1]);
-            size_t mimeLen = strlen((const char *)&data[1]) + 1;
+            size_t consumed = 1;
+
+            // *always* in an 8-bit encoding
+            size_t mimeLen = StringSize(&data[consumed], size - consumed, 0x00);
+            if (mimeLen > size - consumed) {
+                ALOGW("bogus album art size: mime");
+                return NULL;
+            }
+            mime->setTo((const char *)&data[consumed]);
+            consumed += mimeLen;
 
 #if 0
-            uint8_t picType = data[1 + mimeLen];
+            uint8_t picType = data[consumed];
             if (picType != 0x03) {
                 // Front Cover Art
                 it.next();
@@ -885,20 +894,30 @@
             }
 #endif
 
-            size_t descLen = StringSize(&data[2 + mimeLen], encoding);
-
-            if (size < 2 ||
-                    size - 2 < mimeLen ||
-                    size - 2 - mimeLen < descLen) {
-                ALOGW("bogus album art sizes");
+            consumed++;
+            if (consumed >= size) {
+                ALOGW("bogus album art size: pic type");
                 return NULL;
             }
-            *length = size - 2 - mimeLen - descLen;
 
-            return &data[2 + mimeLen + descLen];
+            size_t descLen = StringSize(&data[consumed], size - consumed, encoding);
+            consumed += descLen;
+
+            if (consumed >= size) {
+                ALOGW("bogus album art size: description");
+                return NULL;
+            }
+
+            *length = size - consumed;
+
+            return &data[consumed];
         } else {
             uint8_t encoding = data[0];
 
+            if (size <= 5) {
+                return NULL;
+            }
+
             if (!memcmp(&data[1], "PNG", 3)) {
                 mime->setTo("image/png");
             } else if (!memcmp(&data[1], "JPG", 3)) {
@@ -918,7 +937,10 @@
             }
 #endif
 
-            size_t descLen = StringSize(&data[5], encoding);
+            size_t descLen = StringSize(&data[5], size - 5, encoding);
+            if (descLen > size - 5) {
+                return NULL;
+            }
 
             *length = size - 5 - descLen;
 
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 1d819b5..267f24d 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -792,7 +792,8 @@
         if (mPrevCaptureUs < 0ll) {
             // first capture
             mPrevCaptureUs = timeUs;
-            mPrevFrameUs = timeUs;
+            // adjust the first sample timestamp.
+            mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
         } else {
             // snap to nearest capture point
             int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 8c10310..355a2dd 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -805,13 +805,6 @@
         }
         memset(data, 0, allottedSize);
 
-        // if we are not connecting the buffers, the sizes must match
-        if (allottedSize != params->size()) {
-            CLOG_ERROR(useBuffer, BAD_VALUE, SIMPLE_BUFFER(portIndex, (size_t)allottedSize, data));
-            delete[] data;
-            return BAD_VALUE;
-        }
-
         buffer_meta = new BufferMeta(
                 params, portIndex, false /* copyToOmx */, false /* copyFromOmx */, data);
     } else {
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index be71f43..166e6f1 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -221,8 +221,7 @@
 
 
 static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
-    iter = mObj->mIds.begin();
-    while (iter != mObj->mIds.end()) {
+    for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) {
         if (iter->array() == id.ptr && iter->size() == id.length) {
             return true;
         }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 20d2896..3675998 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -602,6 +602,13 @@
         android_errorWriteLog(0x534e4554, "29251553");
         return -EINVAL;
     }
+    if (cmdCode == EFFECT_CMD_GET_PARAM &&
+            (sizeof(effect_param_t) > cmdSize ||
+                    ((effect_param_t *)pCmdData)->psize > cmdSize
+                                                          - sizeof(effect_param_t))) {
+        android_errorWriteLog(0x534e4554, "32438594");
+        return -EINVAL;
+    }
     if ((cmdCode == EFFECT_CMD_SET_PARAM
             || cmdCode == EFFECT_CMD_SET_PARAM_DEFERRED) &&  // DEFERRED not generally used
         (sizeof(effect_param_t) > cmdSize
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 9d5f33c..9a7839b 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -238,6 +238,10 @@
     {
         String8 supportedPreviewFpsRange;
         for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+            if (!isFpsSupported(availablePreviewSizes,
+                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, availableFpsRanges.data.i32[i+1])) {
+                continue;
+            }
             if (i != 0) supportedPreviewFpsRange += ",";
             supportedPreviewFpsRange += String8::format("(%d,%d)",
                     availableFpsRanges.data.i32[i] * kFpsToApiScale,
@@ -255,7 +259,10 @@
             // from the [min, max] fps range use the max value
             int fps = fpsFromRange(availableFpsRanges.data.i32[i],
                                    availableFpsRanges.data.i32[i+1]);
-
+            if (!isFpsSupported(availablePreviewSizes,
+                    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, fps)) {
+                continue;
+            }
             // de-dupe frame rates
             if (sortedPreviewFrameRates.indexOf(fps) == NAME_NOT_FOUND) {
                 sortedPreviewFrameRates.add(fps);
@@ -951,21 +958,40 @@
         return NO_INIT;
     }
 
+    // Get supported preview fps ranges.
+    Vector<Size> supportedPreviewSizes;
+    Vector<FpsRange> supportedPreviewFpsRanges;
+    const Size PREVIEW_SIZE_BOUND = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+    status_t res = getFilteredSizes(PREVIEW_SIZE_BOUND, &supportedPreviewSizes);
+    if (res != OK) return res;
+    for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+        if (!isFpsSupported(supportedPreviewSizes,
+                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, availableFpsRanges.data.i32[i+1])) {
+            continue;
+        }
+        FpsRange fpsRange = {availableFpsRanges.data.i32[i], availableFpsRanges.data.i32[i+1]};
+        supportedPreviewFpsRanges.add(fpsRange);
+    }
+    if (supportedPreviewFpsRanges.size() == 0) {
+        ALOGE("Supported preview fps range is empty");
+        return NO_INIT;
+    }
+
     int32_t bestStillCaptureFpsRange[2] = {
-        availableFpsRanges.data.i32[0], availableFpsRanges.data.i32[1]
+        supportedPreviewFpsRanges[0].low, supportedPreviewFpsRanges[0].high
     };
     int32_t curRange =
             bestStillCaptureFpsRange[1] - bestStillCaptureFpsRange[0];
-    for (size_t i = 2; i < availableFpsRanges.count; i += 2) {
+    for (size_t i = 1; i < supportedPreviewFpsRanges.size(); i ++) {
         int32_t nextRange =
-                availableFpsRanges.data.i32[i + 1] -
-                availableFpsRanges.data.i32[i];
+                supportedPreviewFpsRanges[i].high -
+                supportedPreviewFpsRanges[i].low;
         if ( (nextRange > curRange) ||       // Maximize size of FPS range first
                 (nextRange == curRange &&    // Then minimize low-end FPS
-                 bestStillCaptureFpsRange[0] > availableFpsRanges.data.i32[i])) {
+                 bestStillCaptureFpsRange[0] > supportedPreviewFpsRanges[i].low)) {
 
-            bestStillCaptureFpsRange[0] = availableFpsRanges.data.i32[i];
-            bestStillCaptureFpsRange[1] = availableFpsRanges.data.i32[i + 1];
+            bestStillCaptureFpsRange[0] = supportedPreviewFpsRanges[i].low;
+            bestStillCaptureFpsRange[1] = supportedPreviewFpsRanges[i].high;
             curRange = nextRange;
         }
     }
@@ -2836,22 +2862,7 @@
 
 int64_t Parameters::getJpegStreamMinFrameDurationNs(Parameters::Size size) {
     if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        const int STREAM_DURATION_SIZE = 4;
-        const int STREAM_FORMAT_OFFSET = 0;
-        const int STREAM_WIDTH_OFFSET = 1;
-        const int STREAM_HEIGHT_OFFSET = 2;
-        const int STREAM_DURATION_OFFSET = 3;
-        camera_metadata_ro_entry_t availableStreamMinDurations =
-                    staticInfo(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
-        for (size_t i = 0; i < availableStreamMinDurations.count; i+= STREAM_DURATION_SIZE) {
-            int64_t format = availableStreamMinDurations.data.i64[i + STREAM_FORMAT_OFFSET];
-            int64_t width = availableStreamMinDurations.data.i64[i + STREAM_WIDTH_OFFSET];
-            int64_t height = availableStreamMinDurations.data.i64[i + STREAM_HEIGHT_OFFSET];
-            int64_t duration = availableStreamMinDurations.data.i64[i + STREAM_DURATION_OFFSET];
-            if (format == HAL_PIXEL_FORMAT_BLOB && width == size.width && height == size.height) {
-                return duration;
-            }
-        }
+        return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
     } else {
         Vector<Size> availableJpegSizes = getAvailableJpegSizes();
         size_t streamIdx = availableJpegSizes.size();
@@ -2875,6 +2886,57 @@
     return -1;
 }
 
+int64_t Parameters::getMinFrameDurationNs(Parameters::Size size, int fmt) {
+    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+        ALOGE("Min frame duration for HAL 3.1 or lower is not supported");
+        return -1;
+    }
+
+    const int STREAM_DURATION_SIZE = 4;
+    const int STREAM_FORMAT_OFFSET = 0;
+    const int STREAM_WIDTH_OFFSET = 1;
+    const int STREAM_HEIGHT_OFFSET = 2;
+    const int STREAM_DURATION_OFFSET = 3;
+    camera_metadata_ro_entry_t availableStreamMinDurations =
+                staticInfo(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+    for (size_t i = 0; i < availableStreamMinDurations.count; i+= STREAM_DURATION_SIZE) {
+        int64_t format = availableStreamMinDurations.data.i64[i + STREAM_FORMAT_OFFSET];
+        int64_t width = availableStreamMinDurations.data.i64[i + STREAM_WIDTH_OFFSET];
+        int64_t height = availableStreamMinDurations.data.i64[i + STREAM_HEIGHT_OFFSET];
+        int64_t duration = availableStreamMinDurations.data.i64[i + STREAM_DURATION_OFFSET];
+        if (format == fmt && width == size.width && height == size.height) {
+            return duration;
+        }
+    }
+
+    return -1;
+}
+
+bool Parameters::isFpsSupported(const Vector<Size> &sizes, int format, int32_t fps) {
+    // Skip the check for older HAL version, as the min duration is not supported.
+    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+        return true;
+    }
+
+    // Get min frame duration for each size and check if the given fps range can be supported.
+    const int32_t FPS_MARGIN = 1;
+    for (size_t i = 0 ; i < sizes.size(); i++) {
+        int64_t minFrameDuration = getMinFrameDurationNs(sizes[i], format);
+        if (minFrameDuration <= 0) {
+            ALOGE("Min frame duration (%" PRId64") for size (%dx%d) and format 0x%x is wrong!",
+                minFrameDuration, sizes[i].width, sizes[i].height, format);
+            return false;
+        }
+        int32_t maxSupportedFps = 1e9 / minFrameDuration;
+        // Add some margin here for the case where the hal supports 29.xxxfps.
+        maxSupportedFps += FPS_MARGIN;
+        if (fps > maxSupportedFps) {
+            return false;
+        }
+    }
+    return true;
+}
+
 SortedVector<int32_t> Parameters::getAvailableOutputFormats() {
     SortedVector<int32_t> outputFormats; // Non-duplicated output formats
     if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 798aab5..c8ecbba 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -115,6 +115,11 @@
         int32_t height;
     };
 
+    struct FpsRange {
+        int32_t low;
+        int32_t high;
+    };
+
     int32_t exposureCompensation;
     bool autoExposureLock;
     bool autoWhiteBalanceLock;
@@ -390,6 +395,15 @@
     // return -1 if input jpeg size cannot be found in supported size list
     int64_t getJpegStreamMinFrameDurationNs(Parameters::Size size);
 
+    // Helper function to get minimum frame duration for a size/format combination
+    // return -1 if input size/format combination cannot be found.
+    int64_t getMinFrameDurationNs(Parameters::Size size, int format);
+
+    // Helper function to check if a given fps is supported by all the sizes with
+    // the same format.
+    // return true if the device doesn't support min frame duration metadata tag.
+    bool isFpsSupported(const Vector<Size> &size, int format, int32_t fps);
+
     // Helper function to get non-duplicated available output formats
     SortedVector<int32_t> getAvailableOutputFormats();
     // Helper function to get available output jpeg sizes
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 73a4124..bfa04f6 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2222,6 +2222,14 @@
     }
 }
 
+void Camera3Device::removeInFlightMapEntryLocked(int idx) {
+    mInFlightMap.removeItemsAt(idx, 1);
+
+    // Indicate idle inFlightMap to the status tracker
+    if (mInFlightMap.size() == 0) {
+        mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE);
+    }
+}
 
 void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) {
 
@@ -2257,13 +2265,7 @@
         returnOutputBuffers(request.pendingOutputBuffers.array(),
             request.pendingOutputBuffers.size(), 0);
 
-        mInFlightMap.removeItemsAt(idx, 1);
-
-        // Indicate idle inFlightMap to the status tracker
-        if (mInFlightMap.size() == 0) {
-            mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE);
-        }
-
+        removeInFlightMapEntryLocked(idx);
         ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
      }
 
@@ -3438,6 +3440,20 @@
                         captureRequest->mResultExtras);
             }
         }
+
+        // Remove yet-to-be submitted inflight request from inflightMap
+        {
+          sp<Camera3Device> parent = mParent.promote();
+          if (parent != NULL) {
+              Mutex::Autolock l(parent->mInFlightLock);
+              ssize_t idx = parent->mInFlightMap.indexOfKey(captureRequest->mResultExtras.frameNumber);
+              if (idx >= 0) {
+                  ALOGV("%s: Remove inflight request from queue: frameNumber %" PRId64,
+                        __FUNCTION__, captureRequest->mResultExtras.frameNumber);
+                  parent->removeInFlightMapEntryLocked(idx);
+              }
+          }
+        }
     }
 
     Mutex::Autolock l(mRequestLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 33429a6..87c43f3 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -849,6 +849,9 @@
 
     /**** Scope for mInFlightLock ****/
 
+    // Remove the in-flight map entry of the given index from mInFlightMap.
+    // It must only be called with mInFlightLock held.
+    void removeInFlightMapEntryLocked(int idx);
     // Remove the in-flight request of the given index from mInFlightMap
     // if it's no longer needed. It must only be called with mInFlightLock held.
     void removeInFlightRequestIfReadyLocked(int idx);