Merge "Introduce a system APC with its corresponding opt-out"
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
index 7045b6a..402d9aa 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
@@ -94,6 +94,20 @@
 
         // matches limits in codec library
         addParameter(
+            DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+                .withDefault(new C2StreamBitrateModeTuning::output(
+                        0u, C2Config::BITRATE_VARIABLE))
+                .withFields({
+                    C2F(mBitrateMode, value).oneOf({
+                        C2Config::BITRATE_CONST,
+                        C2Config::BITRATE_VARIABLE,
+                        C2Config::BITRATE_IGNORE})
+                })
+                .withSetter(
+                    Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
             DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
                 .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
                 .withFields({C2F(mBitrate, value).inRange(4096, 12000000)})
@@ -102,6 +116,20 @@
 
         // matches levels allowed within codec library
         addParameter(
+                DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY)
+                .withDefault(new C2StreamComplexityTuning::output(0u, 0))
+                .withFields({C2F(mComplexity, value).inRange(0, 10)})
+                .withSetter(Setter<decltype(*mComplexity)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mQuality, C2_PARAMKEY_QUALITY)
+                .withDefault(new C2StreamQualityTuning::output(0u, 80))
+                .withFields({C2F(mQuality, value).inRange(0, 100)})
+                .withSetter(Setter<decltype(*mQuality)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
             DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
                 .withDefault(new C2StreamProfileLevelInfo::output(
                     0u, PROFILE_HEVC_MAIN, LEVEL_HEVC_MAIN_1))
@@ -287,12 +315,21 @@
     std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const {
         return mFrameRate;
     }
+    std::shared_ptr<C2StreamBitrateModeTuning::output> getBitrateMode_l() const {
+        return mBitrateMode;
+    }
     std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const {
         return mBitrate;
     }
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const {
         return mRequestSync;
     }
+    std::shared_ptr<C2StreamComplexityTuning::output> getComplexity_l() const {
+        return mComplexity;
+    }
+    std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const {
+        return mQuality;
+    }
 
    private:
     std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
@@ -304,6 +341,9 @@
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamQualityTuning::output> mQuality;
     std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
     std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
 };
@@ -387,6 +427,19 @@
     work->workletsProcessed = 1u;
 }
 
+static int getQpFromQuality(int quality) {
+    int qp;
+#define MIN_QP 4
+#define MAX_QP 50
+    /* Quality: 100 -> Qp : MIN_QP
+     * Quality: 0 -> Qp : MAX_QP
+     * Qp = ((MIN_QP - MAX_QP) * quality / 100) + MAX_QP;
+     */
+    qp = ((MIN_QP - MAX_QP) * quality / 100) + MAX_QP;
+    qp = std::min(qp, MAX_QP);
+    qp = std::max(qp, MIN_QP);
+    return qp;
+}
 c2_status_t C2SoftHevcEnc::initEncParams() {
     mCodecCtx = nullptr;
     mNumCores = std::min(GetCPUCoreCount(), (size_t) CODEC_MAX_CORES);
@@ -416,9 +469,37 @@
     mIvVideoColorFormat = IV_YUV_420P;
     mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores;
     mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile;
-    mEncParams.s_config_prms.i4_rate_control_mode = 2;
     mEncParams.s_lap_prms.i4_rc_look_ahead_pics = 0;
 
+    switch (mBitrateMode->value) {
+        case C2Config::BITRATE_IGNORE:
+            mEncParams.s_config_prms.i4_rate_control_mode = 3;
+            mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_frame_qp[0] =
+                getQpFromQuality(mQuality->value);
+            break;
+        case C2Config::BITRATE_CONST:
+            mEncParams.s_config_prms.i4_rate_control_mode = 5;
+            break;
+        case C2Config::BITRATE_VARIABLE:
+            [[fallthrough]];
+        default:
+            mEncParams.s_config_prms.i4_rate_control_mode = 2;
+            break;
+        break;
+    }
+
+    if (mComplexity->value == 10) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P0;
+    } else if (mComplexity->value >= 8) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P2;
+    } else if (mComplexity->value >= 7) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P3;
+    } else if (mComplexity->value >= 5) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P4;
+    } else {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P5;
+    }
+
     return C2_OK;
 }
 
@@ -447,11 +528,14 @@
     {
         IntfImpl::Lock lock = mIntf->lock();
         mSize = mIntf->getSize_l();
+        mBitrateMode = mIntf->getBitrateMode_l();
         mBitrate = mIntf->getBitrate_l();
         mFrameRate = mIntf->getFrameRate_l();
         mHevcEncProfile = mIntf->getProfile_l();
         mHevcEncLevel = mIntf->getLevel_l();
         mIDRInterval = mIntf->getSyncFramePeriod_l();
+        mComplexity = mIntf->getComplexity_l();
+        mQuality = mIntf->getQuality_l();
     }
 
     c2_status_t status = initEncParams();
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h
index 9d90b95..8569a3e 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.h
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.h
@@ -77,6 +77,9 @@
     std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamQualityTuning::output> mQuality;
 
 #ifdef FILE_DUMP_ENABLE
     char mInFile[200];
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index 0bb2418..97dde71 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -33,6 +33,7 @@
         "android.hardware.media.bufferpool@2.0",
         "android.hardware.media.c2@1.0",
         "android.hardware.media.omx@1.0",
+        "android.hidl.safe_union@1.0",
         "libbase",
         "libcodec2",
         "libcodec2_vndk",
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index b9f3aa8..817d148 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -22,6 +22,7 @@
 #include <android/hardware/media/bufferpool/2.0/types.h>
 #include <android/hardware/media/c2/1.0/IComponentStore.h>
 #include <android/hardware/media/c2/1.0/types.h>
+#include <android/hidl/safe_union/1.0/types.h>
 #include <gui/IGraphicBufferProducer.h>
 
 #include <C2Component.h>
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 031e637..74320e7 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -120,9 +120,9 @@
     return true;
 }
 
-// C2FieldSupportedValues::range's type -> FieldSupportedValues::Range
+// C2FieldSupportedValues::range's type -> ValueRange
 bool objcpy(
-        FieldSupportedValues::Range* d,
+        ValueRange* d,
         const decltype(C2FieldSupportedValues::range)& s) {
     d->min = static_cast<PrimitiveValue>(s.min.u64);
     d->max = static_cast<PrimitiveValue>(s.max.u64);
@@ -135,45 +135,45 @@
 // C2FieldSupportedValues -> FieldSupportedValues
 bool objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) {
     switch (s.type) {
-    case C2FieldSupportedValues::EMPTY:
-        d->type = FieldSupportedValues::Type::EMPTY;
-        d->values.resize(0);
-        break;
-    case C2FieldSupportedValues::RANGE:
-        d->type = FieldSupportedValues::Type::RANGE;
-        if (!objcpy(&d->range, s.range)) {
-            LOG(ERROR) << "Invalid C2FieldSupportedValues::range.";
-            return false;
+    case C2FieldSupportedValues::EMPTY: {
+            d->empty(::android::hidl::safe_union::V1_0::Monostate{});
+            break;
         }
-        d->values.resize(0);
-        break;
-    default:
-        switch (s.type) {
-        case C2FieldSupportedValues::VALUES:
-            d->type = FieldSupportedValues::Type::VALUES;
-            break;
-        case C2FieldSupportedValues::FLAGS:
-            d->type = FieldSupportedValues::Type::FLAGS;
-            break;
-        default:
-            LOG(DEBUG) << "Unrecognized C2FieldSupportedValues::type_t "
-                       << "with underlying value " << underlying_value(s.type)
-                       << ".";
-            d->type = static_cast<FieldSupportedValues::Type>(s.type);
-            if (!objcpy(&d->range, s.range)) {
+    case C2FieldSupportedValues::RANGE: {
+            ValueRange range{};
+            if (!objcpy(&range, s.range)) {
                 LOG(ERROR) << "Invalid C2FieldSupportedValues::range.";
+                d->range(range);
                 return false;
             }
+            d->range(range);
+            break;
         }
-        copyVector<uint64_t>(&d->values, s.values);
+    case C2FieldSupportedValues::VALUES: {
+            hidl_vec<PrimitiveValue> values;
+            copyVector<uint64_t>(&values, s.values);
+            d->values(values);
+            break;
+        }
+    case C2FieldSupportedValues::FLAGS: {
+            hidl_vec<PrimitiveValue> flags;
+            copyVector<uint64_t>(&flags, s.values);
+            d->flags(flags);
+            break;
+        }
+    default:
+        LOG(DEBUG) << "Unrecognized C2FieldSupportedValues::type_t "
+                   << "with underlying value " << underlying_value(s.type)
+                   << ".";
+        return false;
     }
     return true;
 }
 
-// FieldSupportedValues::Range -> C2FieldSupportedValues::range's type
+// ValueRange -> C2FieldSupportedValues::range's type
 bool objcpy(
         decltype(C2FieldSupportedValues::range)* d,
-        const FieldSupportedValues::Range& s) {
+        const ValueRange& s) {
     d->min.u64 = static_cast<uint64_t>(s.min);
     d->max.u64 = static_cast<uint64_t>(s.max);
     d->step.u64 = static_cast<uint64_t>(s.step);
@@ -184,37 +184,33 @@
 
 // FieldSupportedValues -> C2FieldSupportedValues
 bool objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) {
-    switch (s.type) {
-    case FieldSupportedValues::Type::EMPTY:
-        d->type = C2FieldSupportedValues::EMPTY;
-        break;
-    case FieldSupportedValues::Type::RANGE:
-        d->type = C2FieldSupportedValues::RANGE;
-        if (!objcpy(&d->range, s.range)) {
-            LOG(ERROR) << "Invalid FieldSupportedValues::range.";
-            return false;
+    switch (s.getDiscriminator()) {
+    case FieldSupportedValues::hidl_discriminator::empty: {
+            d->type = C2FieldSupportedValues::EMPTY;
+            break;
         }
-        d->values.resize(0);
-        break;
-    default:
-        switch (s.type) {
-        case FieldSupportedValues::Type::VALUES:
-            d->type = C2FieldSupportedValues::VALUES;
-            break;
-        case FieldSupportedValues::Type::FLAGS:
-            d->type = C2FieldSupportedValues::FLAGS;
-            break;
-        default:
-            LOG(DEBUG) << "Unrecognized FieldSupportedValues::Type "
-                       << "with underlying value " << underlying_value(s.type)
-                       << ".";
-            d->type = static_cast<C2FieldSupportedValues::type_t>(s.type);
-            if (!objcpy(&d->range, s.range)) {
+    case FieldSupportedValues::hidl_discriminator::range: {
+            d->type = C2FieldSupportedValues::RANGE;
+            if (!objcpy(&d->range, s.range())) {
                 LOG(ERROR) << "Invalid FieldSupportedValues::range.";
                 return false;
             }
+            d->values.resize(0);
+            break;
         }
-        copyVector<uint64_t>(&d->values, s.values);
+    case FieldSupportedValues::hidl_discriminator::values: {
+            d->type = C2FieldSupportedValues::VALUES;
+            copyVector<uint64_t>(&d->values, s.values());
+            break;
+        }
+    case FieldSupportedValues::hidl_discriminator::flags: {
+            d->type = C2FieldSupportedValues::FLAGS;
+            copyVector<uint64_t>(&d->values, s.flags());
+            break;
+        }
+    default:
+        LOG(WARNING) << "Unrecognized FieldSupportedValues::getDiscriminator()";
+        return false;
     }
     return true;
 }
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index 1f36270..d73b731 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -127,4 +127,12 @@
             queueCondition.notify_all();
         }
     }
-}
\ No newline at end of file
+}
+
+// Return current time in micro seconds
+int64_t getNowUs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
+}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index fca2902..c577dac 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -201,4 +201,6 @@
     std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
     uint32_t& framesReceived);
 
+int64_t getNowUs();
+
 #endif  // MEDIA_C2_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
index ec803d7..74548b5 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
@@ -26,6 +26,32 @@
 #include <VtsHalHidlTargetTestBase.h>
 #include "media_c2_hidl_test_common.h"
 
+/* Time_Out for start(), stop(), reset(), release(), flush(), queue() are
+ * defined in hardware/interfaces/media/c2/1.0/IComponent.hal. Adding 50ms
+ * extra in case of timeout is 500ms, 1ms extra in case timeout is 1ms/5ms. All
+ * timeout is calculated in us.
+ */
+#define START_TIME_OUT                  550000
+#define STOP_TIME_OUT                   550000
+#define RESET_TIME_OUT                  550000
+#define RELEASE_TIME_OUT                550000
+#define FLUSH_TIME_OUT                  6000
+#define QUEUE_TIME_OUT                  2000
+
+// Time_Out for config(), query(), querySupportedParams() are defined in
+// hardware/interfaces/media/c2/1.0/IConfigurable.hal.
+#define CONFIG_TIME_OUT                 6000
+#define QUERY_TIME_OUT                  6000
+#define QUERYSUPPORTEDPARAMS_TIME_OUT   2000
+
+#define CHECK_TIMEOUT(timeConsumed, TIME_OUT, FuncName)          \
+    if (timeConsumed > TIME_OUT) {                               \
+        ALOGW(                                                   \
+            "TIMED_OUT %s  timeConsumed=%" PRId64 " us is "      \
+            "greater than threshold %d us",                      \
+            FuncName, timeConsumed, TIME_OUT);                   \
+    }
+
 static ComponentTestEnvironment* gEnv = nullptr;
 
 namespace {
@@ -244,6 +270,93 @@
     std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false),
     std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false)));
 
+// Test API's Timeout
+TEST_F(Codec2ComponentHidlTest, Timeout) {
+    ALOGV("Timeout Test");
+    c2_status_t err = C2_OK;
+
+    int64_t startTime = getNowUs();
+    err = mComponent->start();
+    int64_t timeConsumed = getNowUs() - startTime;
+    CHECK_TIMEOUT(timeConsumed, START_TIME_OUT, "start()");
+    ALOGV("mComponent->start() timeConsumed=%" PRId64 " us", timeConsumed);
+    ASSERT_EQ(err, C2_OK);
+
+    startTime = getNowUs();
+    err = mComponent->reset();
+    timeConsumed = getNowUs() - startTime;
+    CHECK_TIMEOUT(timeConsumed, RESET_TIME_OUT, "reset()");
+    ALOGV("mComponent->reset() timeConsumed=%" PRId64 " us", timeConsumed);
+    ASSERT_EQ(err, C2_OK);
+
+    err = mComponent->start();
+    ASSERT_EQ(err, C2_OK);
+
+    // Query supported params by the component
+    std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+    startTime = getNowUs();
+    err = mComponent->querySupportedParams(&params);
+    timeConsumed = getNowUs() - startTime;
+    CHECK_TIMEOUT(timeConsumed, QUERYSUPPORTEDPARAMS_TIME_OUT,
+                  "querySupportedParams()");
+    ALOGV("mComponent->querySupportedParams() timeConsumed=%" PRId64 " us",
+          timeConsumed);
+    ASSERT_EQ(err, C2_OK);
+
+    std::vector<std::unique_ptr<C2Param>> queried;
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    // Query and config all the supported params
+    for (std::shared_ptr<C2ParamDescriptor> p : params) {
+        startTime = getNowUs();
+        err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
+        timeConsumed = getNowUs() - startTime;
+        CHECK_TIMEOUT(timeConsumed, QUERY_TIME_OUT, "query()");
+        EXPECT_NE(queried.size(), 0u);
+        EXPECT_EQ(err, C2_OK);
+        ALOGV("mComponent->query() for %s timeConsumed=%" PRId64 " us",
+              p->name().c_str(), timeConsumed);
+
+        startTime = getNowUs();
+        err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
+        timeConsumed = getNowUs() - startTime;
+        CHECK_TIMEOUT(timeConsumed, CONFIG_TIME_OUT, "config()");
+        ASSERT_EQ(err, C2_OK);
+        ASSERT_EQ(failures.size(), 0u);
+        ALOGV("mComponent->config() for %s timeConsumed=%" PRId64 " us",
+              p->name().c_str(), timeConsumed);
+    }
+
+    std::list<std::unique_ptr<C2Work>> workList;
+    startTime = getNowUs();
+    err = mComponent->queue(&workList);
+    timeConsumed = getNowUs() - startTime;
+    ALOGV("mComponent->queue() timeConsumed=%" PRId64 " us", timeConsumed);
+    CHECK_TIMEOUT(timeConsumed, QUEUE_TIME_OUT, "queue()");
+    ASSERT_EQ(err, C2_OK);
+
+    startTime = getNowUs();
+    err = mComponent->flush(C2Component::FLUSH_COMPONENT, &workList);
+    timeConsumed = getNowUs() - startTime;
+    ALOGV("mComponent->flush() timeConsumed=%" PRId64 " us", timeConsumed);
+    CHECK_TIMEOUT(timeConsumed, FLUSH_TIME_OUT, "flush()");
+    ASSERT_EQ(err, C2_OK);
+
+    startTime = getNowUs();
+    err = mComponent->stop();
+    timeConsumed = getNowUs() - startTime;
+    ALOGV("mComponent->stop() timeConsumed=%" PRId64 " us", timeConsumed);
+    CHECK_TIMEOUT(timeConsumed, STOP_TIME_OUT, "stop()");
+    ASSERT_EQ(err, C2_OK);
+
+    startTime = getNowUs();
+    err = mComponent->release();
+    timeConsumed = getNowUs() - startTime;
+    ALOGV("mComponent->release() timeConsumed=%" PRId64 " us", timeConsumed);
+    CHECK_TIMEOUT(timeConsumed, RELEASE_TIME_OUT, "release()");
+    ASSERT_EQ(err, C2_OK);
+
+}
+
 }  // anonymous namespace
 
 // TODO: Add test for Invalid work,
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index 03d859a..962df0f 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -272,19 +272,14 @@
     work->input.buffers.clear();
     if (block) {
         std::shared_ptr<C2Buffer> c2Buffer(
-                // TODO: fence
                 new Buffer2D(block->share(
-                        C2Rect(block->width(), block->height()), ::C2Fence())),
-                [buffer, source = getSource()](C2Buffer *ptr) {
-                    delete ptr;
-                    // TODO: fence
-                    (void)source->onInputBufferEmptied(buffer, -1);
-                });
+                        C2Rect(block->width(), block->height()), ::C2Fence())));
         work->input.buffers.push_back(c2Buffer);
     }
     work->worklets.clear();
     work->worklets.emplace_back(new C2Worklet);
     std::list<std::unique_ptr<C2Work>> items;
+    uint64_t index = work->input.ordinal.frameIndex.peeku();
     items.push_back(std::move(work));
 
     c2_status_t err = comp->queue(&items);
@@ -292,6 +287,7 @@
         return UNKNOWN_ERROR;
     }
 
+    (void)mBufferIdsInUse.emplace(index, buffer);
     return OK;
 }
 
@@ -326,4 +322,18 @@
     mHeight = height;
 }
 
+void C2OMXNode::onInputBufferDone(c2_cntr64_t index) {
+    if (!mBufferSource) {
+        ALOGD("Buffer source not set (index=%llu)", index.peekull());
+        return;
+    }
+    auto it = mBufferIdsInUse.find(index.peeku());
+    if (it == mBufferIdsInUse.end()) {
+        ALOGV("Untracked input index %llu (maybe already removed)", index.peekull());
+        return;
+    }
+    (void)mBufferSource->onInputBufferEmptied(it->second, -1);
+    (void)mBufferIdsInUse.erase(it);
+}
+
 }  // namespace android
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index b5a815e..b7bd696 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -75,9 +75,23 @@
             OMX_INDEXTYPE *index) override;
     status_t dispatchMessage(const omx_message &msg) override;
 
+    /**
+     * Returns underlying IOMXBufferSource object.
+     */
     sp<IOMXBufferSource> getSource();
+
+    /**
+     * Configure the frame size.
+     */
     void setFrameSize(uint32_t width, uint32_t height);
 
+    /**
+     * Clean up work item reference.
+     *
+     * \param index input work index
+     */
+    void onInputBufferDone(c2_cntr64_t index);
+
 private:
     std::weak_ptr<Codec2Client::Component> mComp;
     sp<IOMXBufferSource> mBufferSource;
@@ -96,6 +110,8 @@
     bool mFirstInputFrame; // true for first input
     c2_cntr64_t mPrevInputTimestamp; // input timestamp for previous frame
     c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame
+
+    std::map<uint64_t, buffer_id> mBufferIdsInUse;
 };
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 5f60378..8474ce8 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -370,6 +370,10 @@
         return err;
     }
 
+    void onInputBufferDone(c2_cntr64_t index) override {
+        mNode->onInputBufferDone(index);
+    }
+
 private:
     sp<BGraphicBufferSource> mSource;
     sp<C2OMXNode> mNode;
@@ -742,10 +746,21 @@
                 return BAD_VALUE;
             }
             if ((config->mDomain & Config::IS_ENCODER) && (config->mDomain & Config::IS_VIDEO)) {
-                if (!msg->findInt32(KEY_BIT_RATE, &i32)
-                        && !msg->findFloat(KEY_BIT_RATE, &flt)) {
-                    ALOGD("bitrate is missing, which is required for video encoders.");
-                    return BAD_VALUE;
+                C2Config::bitrate_mode_t mode = C2Config::BITRATE_VARIABLE;
+                if (msg->findInt32(KEY_BITRATE_MODE, &i32)) {
+                    mode = (C2Config::bitrate_mode_t) i32;
+                }
+                if (mode == BITRATE_MODE_CQ) {
+                    if (!msg->findInt32(KEY_QUALITY, &i32)) {
+                        ALOGD("quality is missing, which is required for video encoders in CQ.");
+                        return BAD_VALUE;
+                    }
+                } else {
+                    if (!msg->findInt32(KEY_BIT_RATE, &i32)
+                            && !msg->findFloat(KEY_BIT_RATE, &flt)) {
+                        ALOGD("bitrate is missing, which is required for video encoders.");
+                        return BAD_VALUE;
+                    }
                 }
                 if (!msg->findInt32(KEY_I_FRAME_INTERVAL, &i32)
                         && !msg->findFloat(KEY_I_FRAME_INTERVAL, &flt)) {
@@ -1572,6 +1587,13 @@
 
 void CCodec::onInputBufferDone(uint64_t frameIndex, size_t arrayIndex) {
     mChannel->onInputBufferDone(frameIndex, arrayIndex);
+    if (arrayIndex == 0) {
+        // We always put no more than one buffer per work, if we use an input surface.
+        Mutexed<Config>::Locked config(mConfig);
+        if (config->mInputSurface) {
+            config->mInputSurface->onInputBufferDone(frameIndex);
+        }
+    }
 }
 
 void CCodec::onMessageReceived(const sp<AMessage> &msg) {
@@ -1704,6 +1726,9 @@
                     ++stream;
                 }
             }
+            if (config->mInputSurface) {
+                config->mInputSurface->onInputBufferDone(work->input.ordinal.frameIndex);
+            }
             mChannel->onWorkDone(
                     std::move(work), changed ? config->mOutputFormat : nullptr,
                     initData.hasChanged() ? initData.update().get() : nullptr);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index a6fa333..55a525e 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -2483,6 +2483,7 @@
             bool post = true;
             if (!configs->empty()) {
                 sp<ABuffer> config = configs->front();
+                configs->pop_front();
                 if (buffer->capacity() >= config->size()) {
                     memcpy(buffer->base(), config->data(), config->size());
                     buffer->setRange(0, config->size());
diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h
index d9c4eec..8341fd5 100644
--- a/media/codec2/sfplugin/InputSurfaceWrapper.h
+++ b/media/codec2/sfplugin/InputSurfaceWrapper.h
@@ -98,6 +98,13 @@
         mDataSpace = dataSpace;
     }
 
+    /**
+     * Clean up C2Work related references if necessary. No-op by default.
+     *
+     * \param index index of input work.
+     */
+    virtual void onInputBufferDone(c2_cntr64_t /* index */) {}
+
 protected:
     android_dataspace mDataSpace;
 };
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 20cc643..d6d24c1 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -1336,6 +1336,13 @@
     mReader = NULL;
 
     delete mDataSource;
+
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        TrackInfo *info = &mTracks.editItemAt(i);
+        if (info->mMeta) {
+            AMediaFormat_delete(info->mMeta);
+        }
+    }
 }
 
 size_t MatroskaExtractor::countTracks() {
@@ -1808,6 +1815,8 @@
 void MatroskaExtractor::addTracks() {
     const mkvparser::Tracks *tracks = mSegment->GetTracks();
 
+    AMediaFormat *meta = nullptr;
+
     for (size_t index = 0; index < tracks->GetTracksCount(); ++index) {
         const mkvparser::Track *track = tracks->GetTrackByIndex(index);
 
@@ -1832,7 +1841,11 @@
 
         enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
 
-        AMediaFormat *meta = AMediaFormat_new();
+        if (meta) {
+            AMediaFormat_clear(meta);
+        } else {
+            meta = AMediaFormat_new();
+        }
 
         status_t err = OK;
         int32_t nalSize = -1;
@@ -2067,21 +2080,26 @@
         long long durationNs = mSegment->GetDuration();
         AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_DURATION, (durationNs + 500) / 1000);
 
+        const char *mimetype = "";
+        if (!AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mimetype)) {
+            // do not add this track to the track list
+            ALOGW("ignoring track with unknown mime");
+            continue;
+        }
+
         mTracks.push();
         size_t n = mTracks.size() - 1;
         TrackInfo *trackInfo = &mTracks.editItemAt(n);
         initTrackInfo(track, meta, trackInfo);
         trackInfo->mNalLengthSize = nalSize;
 
-        const char *mimetype = "";
-        AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mimetype);
-
         if ((!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) ||
             (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_AVC) && isSetCsdFrom1stFrame)) {
             // Attempt to recover from AVC track without codec private data
             err = synthesizeAVCC(trackInfo, n);
             if (err != OK) {
                 mTracks.pop();
+                continue;
             }
         } else if ((!strcmp("V_MPEG2", codecID) && codecPrivateSize == 0) ||
             (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG2) && isSetCsdFrom1stFrame)) {
@@ -2089,6 +2107,7 @@
             err = synthesizeMPEG2(trackInfo, n);
             if (err != OK) {
                 mTracks.pop();
+                continue;
             }
         } else if ((!strcmp("V_MPEG4/ISO/ASP", codecID) && codecPrivateSize == 0) ||
             (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG4) && isSetCsdFrom1stFrame) ||
@@ -2099,9 +2118,14 @@
             err = synthesizeMPEG4(trackInfo, n);
             if (err != OK) {
                 mTracks.pop();
+                continue;
             }
         }
-
+        // the TrackInfo owns the metadata now
+        meta = nullptr;
+    }
+    if (meta) {
+        AMediaFormat_delete(meta);
     }
 }
 
diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h
index d53d9e3..99fad17 100644
--- a/media/extractors/mkv/MatroskaExtractor.h
+++ b/media/extractors/mkv/MatroskaExtractor.h
@@ -61,10 +61,8 @@
         TrackInfo() {
             mMeta = NULL;
         }
+
         ~TrackInfo() {
-            if (mMeta) {
-                AMediaFormat_delete(mMeta);
-            }
         }
         unsigned long mTrackNum;
         bool mEncrypted;
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 22819cb..5ff1c59 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -3675,8 +3675,10 @@
 
     void *tmpData;
     size_t tmpDataSize;
+    const char *s;
     if (size >= 8 && metadataKey &&
-            !AMediaFormat_getBuffer(mFileMetaData, metadataKey, &tmpData, &tmpDataSize)) {
+            !AMediaFormat_getBuffer(mFileMetaData, metadataKey, &tmpData, &tmpDataSize) &&
+            !AMediaFormat_getString(mFileMetaData, metadataKey, &s)) {
         if (!strcmp(metadataKey, "albumart")) {
             AMediaFormat_setBuffer(mFileMetaData, metadataKey,
                     buffer + 8, size - 8);
@@ -3918,10 +3920,9 @@
         };
         static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
 
-        void *tmpData;
-        size_t tmpDataSize;
         for (size_t i = 0; i < kNumMapEntries; ++i) {
-            if (!AMediaFormat_getBuffer(mFileMetaData, kMap[i].key, &tmpData, &tmpDataSize)) {
+            const char *ss;
+            if (!AMediaFormat_getString(mFileMetaData, kMap[i].key, &ss)) {
                 ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
                 if (it->done()) {
                     delete it;
@@ -5318,7 +5319,9 @@
 }
 
 int32_t MPEG4Source::parseHEVCLayerId(const uint8_t *data, size_t size) {
-    CHECK(data != nullptr && size >= (mNALLengthSize + 2));
+    if (data == nullptr || size < mNALLengthSize + 2) {
+        return -1;
+    }
 
     // HEVC NAL-header (16-bit)
     //  1   6      6     3
diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
index fd21545..5079634 100755
--- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
+++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
@@ -60,6 +60,7 @@
 
 # run multichannel effects at different configs, saving only the stereo channel
 # pair.
+error_count=0
 for flags in "${flags_arr[@]}"
 do
     for fs in ${fs_arr[*]}
@@ -69,6 +70,13 @@
             adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \
                 -o:$testdir/sinesweep_$((chMask))_$((fs)).raw -chMask:$chMask -fs:$fs $flags
 
+            shell_ret=$?
+            if [ $shell_ret -ne 0 ]; then
+                echo "error: $shell_ret"
+                ((++error_count))
+            fi
+
+
             # two channel files should be identical to higher channel
             # computation (first 2 channels).
             # Do not compare cases where -bE is in flags (due to mono computation)
@@ -82,8 +90,17 @@
                     $testdir/sinesweep_$((chMask))_$((fs)).raw -thr:90.308998
             fi
 
+            # both cmp and snr return EXIT_FAILURE on mismatch.
+            shell_ret=$?
+            if [ $shell_ret -ne 0 ]; then
+                echo "error: $shell_ret"
+                ((++error_count))
+            fi
+
         done
     done
 done
 
 adb shell rm -r $testdir
+echo "$error_count errors"
+exit $error_count
diff --git a/media/libeffects/lvm/tests/snr.cpp b/media/libeffects/lvm/tests/snr.cpp
index 88110c0..885994c 100644
--- a/media/libeffects/lvm/tests/snr.cpp
+++ b/media/libeffects/lvm/tests/snr.cpp
@@ -84,6 +84,7 @@
     printf("\nError: missing input/reference files\n");
     return -1;
   }
+  int ret = EXIT_SUCCESS;
   auto sn = pcm_format == 0
       ? getSignalNoise<short>(finp, fref)
       : getSignalNoise<float>(finp, fref);
@@ -92,6 +93,7 @@
     // compare the measured snr value with threshold
     if (snr < thr) {
       printf("%.6f less than threshold %.6f\n", snr, thr);
+      ret = EXIT_FAILURE;
     } else {
       printf("%.6f\n", snr);
     }
@@ -99,5 +101,5 @@
   fclose(finp);
   fclose(fref);
 
-  return 0;
+  return ret;
 }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 93dd1c3..63681fa 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -164,9 +164,12 @@
     mAnalyticsItem->setInt32(kRecorderVideoIframeInterval, mIFramesIntervalSec);
     // TBD mAudioSourceNode = 0;
     // TBD mUse64BitFileOffset = false;
-    mAnalyticsItem->setInt32(kRecorderMovieTimescale, mMovieTimeScale);
-    mAnalyticsItem->setInt32(kRecorderAudioTimescale, mAudioTimeScale);
-    mAnalyticsItem->setInt32(kRecorderVideoTimescale, mVideoTimeScale);
+    if (mMovieTimeScale != -1)
+        mAnalyticsItem->setInt32(kRecorderMovieTimescale, mMovieTimeScale);
+    if (mAudioTimeScale != -1)
+        mAnalyticsItem->setInt32(kRecorderAudioTimescale, mAudioTimeScale);
+    if (mVideoTimeScale != -1)
+        mAnalyticsItem->setInt32(kRecorderVideoTimescale, mVideoTimeScale);
     // TBD mCameraId        = 0;
     // TBD mStartTimeOffsetMs = -1;
     mAnalyticsItem->setInt32(kRecorderVideoProfile, mVideoEncoderProfile);
@@ -2210,7 +2213,7 @@
 }
 
 status_t StagefrightRecorder::getMetrics(Parcel *reply) {
-    ALOGD("StagefrightRecorder::getMetrics");
+    ALOGV("StagefrightRecorder::getMetrics");
 
     if (reply == NULL) {
         ALOGE("Null pointer argument");
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index b0ce688..29c3a35 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "RemoteMediaExtractor"
 #include <utils/Log.h>
 
+#include <binder/IPCThreadState.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/MediaAnalyticsItem.h>
 #include <media/MediaSource.h>
@@ -51,6 +52,11 @@
     if (MEDIA_LOG) {
         mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor);
 
+        // we're in the extractor service, we want to attribute to the app
+        // that invoked us.
+        int uid = IPCThreadState::self()->getCallingUid();
+        mAnalyticsItem->setUid(uid);
+
         // track the container format (mpeg, aac, wvm, etc)
         size_t ntracks = extractor->countTracks();
         mAnalyticsItem->setCString(kExtractorFormat, extractor->name());
diff --git a/media/libstagefright/codecs/amrnb/common/src/lsp.cpp b/media/libstagefright/codecs/amrnb/common/src/lsp.cpp
index 0e3f772..81d9cde 100644
--- a/media/libstagefright/codecs/amrnb/common/src/lsp.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/lsp.cpp
@@ -173,7 +173,7 @@
     *st = NULL;
 
     /* allocate memory */
-    if ((s = (lspState *) malloc(sizeof(lspState))) == NULL)
+    if ((s = (lspState *) calloc(sizeof(lspState), 1)) == NULL)
     {
         /* fprintf(stderr, "lsp_init: can not malloc state structure\n"); */
         return -1;
@@ -182,11 +182,13 @@
     /* Initialize quantization state */
     if (0 != Q_plsf_init(&s->qSt))
     {
+        lsp_exit(&s);
         return -1;
     }
 
     if (0 != lsp_reset(s))
     {
+        lsp_exit(&s);
         return -1;
     }
 
diff --git a/media/libstagefright/codecs/amrnb/dec/src/sp_dec.cpp b/media/libstagefright/codecs/amrnb/dec/src/sp_dec.cpp
index 2989b74..49cafff 100644
--- a/media/libstagefright/codecs/amrnb/dec/src/sp_dec.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/src/sp_dec.cpp
@@ -268,13 +268,7 @@
     if (Decoder_amr_init(&s->decoder_amrState)
             || Post_Process_reset(&s->postHP_state))
     {
-        Speech_Decode_FrameState *tmp = s;
-        /*
-         *  dereferencing type-punned pointer avoid
-         *  breaking strict-aliasing rules
-         */
-        void** tempVoid = (void**) tmp;
-        GSMDecodeFrameExit(tempVoid);
+        free(s);
         return (-1);
     }
 
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index e20174f..f785bfa 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -115,6 +115,9 @@
             <Limit name="block-count" range="1-4096" /> <!-- max 512x512 -->
             <Limit name="blocks-per-second" range="1-122880" />
             <Limit name="bitrate" range="1-10000000" />
+            <Limit name="complexity" range="0-10"  default="0" />
+            <Limit name="quality" range="0-100"  default="80" />
+            <Feature name="bitrate-modes" value="VBR,CBR,CQ" />
         </MediaCodec>
         <MediaCodec name="c2.android.mpeg4.encoder" type="video/mp4v-es">
             <Alias name="OMX.google.mpeg4.encoder" />
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index d153598..c62c2cd 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -2125,7 +2125,10 @@
     size_t offset = 0;
     while (offset < buffer->size()) {
         const uint8_t *adtsHeader = buffer->data() + offset;
-        CHECK_LT(offset + 5, buffer->size());
+        if (buffer->size() <= offset+5) {
+            ALOGV("buffer does not contain a complete header");
+            return ERROR_MALFORMED;
+        }
         // non-const pointer for decryption if needed
         uint8_t *adtsFrame = buffer->data() + offset;
 
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
index e32f676..7d446ab 100644
--- a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
@@ -149,6 +149,11 @@
     }
 
     // ADTS header is included in the size
+    if (size < adtsHdrSize) {
+        ALOGV("processAAC: size (%zu) < adtsHdrSize (%zu)", size, adtsHdrSize);
+        android_errorWriteLog(0x534e4554, "128433933");
+        return;
+    }
     size_t offset = adtsHdrSize;
     size_t remainingBytes = size - adtsHdrSize;
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b8f88cf..43260c2 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3297,7 +3297,8 @@
         // output threads.
         // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
         // because of code checking output when entering the function.
-        // Note: io is never 0 when creating an effect on an input
+        // Note: io is never AUDIO_IO_HANDLE_NONE when creating an effect on an input by APM.
+        // An AudioEffect created from the Java API will have io as AUDIO_IO_HANDLE_NONE.
         if (io == AUDIO_IO_HANDLE_NONE) {
             // look for the thread where the specified audio session is present
             io = findIoHandleBySessionId_l(sessionId, mPlaybackThreads);
@@ -3307,6 +3308,25 @@
             if (io == AUDIO_IO_HANDLE_NONE) {
                 io = findIoHandleBySessionId_l(sessionId, mMmapThreads);
             }
+
+            // If you wish to create a Record preprocessing AudioEffect in Java,
+            // you MUST create an AudioRecord first and keep it alive so it is picked up above.
+            // Otherwise it will fail when created on a Playback thread by legacy
+            // handling below.  Ditto with Mmap, the associated Mmap track must be created
+            // before creating the AudioEffect or the io handle must be specified.
+            //
+            // Detect if the effect is created after an AudioRecord is destroyed.
+            if (getOrphanEffectChain_l(sessionId).get() != nullptr) {
+                ALOGE("%s: effect %s with no specified io handle is denied because the AudioRecord"
+                        " for session %d no longer exists",
+                         __func__, desc.name, sessionId);
+                lStatus = PERMISSION_DENIED;
+                goto Exit;
+            }
+
+            // Legacy handling of creating an effect on an expired or made-up
+            // session id.  We think that it is a Playback effect.
+            //
             // If no output thread contains the requested session ID, default to
             // first output. The effect chain will be moved to the correct output
             // thread when a track with the same session ID is created
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e94fb49..984d9fe 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7245,7 +7245,7 @@
                 } else {
                     // FIXME could do a partial drop of framesOut
                     if (activeTrack->mFramesToDrop > 0) {
-                        activeTrack->mFramesToDrop -= framesOut;
+                        activeTrack->mFramesToDrop -= (ssize_t)framesOut;
                         if (activeTrack->mFramesToDrop <= 0) {
                             activeTrack->clearSyncStartEvent();
                         }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 635de6f..5b4e2eb 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -334,6 +334,13 @@
 void AudioInputDescriptor::updateClientRecordingConfiguration(
     int event, const sp<RecordClientDescriptor>& client)
 {
+    // do not send callback if starting and no device is selected yet to avoid
+    // double callbacks from startInput() before and after the device is selected
+    if (event ==  RECORD_CONFIG_EVENT_START
+            && mPatchHandle == AUDIO_PATCH_HANDLE_NONE) {
+        return;
+    }
+
     const audio_config_base_t sessionConfig = client->config();
     const record_client_info_t recordClientInfo{client->uid(), client->session(),
                                                 client->source(), client->portId(),
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e8e9fa6..762a4b1 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2025,7 +2025,7 @@
         mSoundTriggerSessions.indexOfKey(session) > 0;
     *portId = AudioPort::getNextUniqueId();
 
-    clientDesc = new RecordClientDescriptor(*portId, uid, session, *attr, *config,
+    clientDesc = new RecordClientDescriptor(*portId, uid, session, attributes, *config,
                                             requestedDeviceId, attributes.source, flags,
                                             isSoundTrigger);
     inputDesc = mInputs.valueFor(*input);
@@ -4828,6 +4828,7 @@
         ALOGW("closeOutput() unknown output %d", output);
         return;
     }
+    const bool closingOutputWasActive = closingOutput->isActive();
     mPolicyMixes.closeOutput(closingOutput);
 
     // look for duplicated outputs connected to the output being removed.
@@ -4867,6 +4868,9 @@
         mpClientInterface->onAudioPatchListUpdate();
     }
 
+    if (closingOutputWasActive) {
+        closingOutput->stop();
+    }
     closingOutput->close();
 
     removeOutput(output);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 1ec3262..0a7b841 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -376,15 +376,17 @@
         return PERMISSION_DENIED;
     }
 
+    bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
     if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
         attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
         attr->source == AUDIO_SOURCE_VOICE_CALL ||
         attr->source == AUDIO_SOURCE_ECHO_REFERENCE) &&
-        !captureAudioOutputAllowed(pid, uid)) {
+        !canCaptureOutput) {
         return PERMISSION_DENIED;
     }
 
-    if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
+    bool canCaptureHotword = captureHotwordAllowed(pid, uid);
+    if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
         return BAD_VALUE;
     }
 
@@ -415,7 +417,7 @@
             case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
                 // FIXME: use the same permission as for remote submix for now.
             case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
-                if (!captureAudioOutputAllowed(pid, uid)) {
+                if (!canCaptureOutput) {
                     ALOGE("getInputForAttr() permission denied: capture not allowed");
                     status = PERMISSION_DENIED;
                 }
@@ -442,7 +444,8 @@
         }
 
         sp<AudioRecordClient> client = new AudioRecordClient(*attr, *input, uid, pid, session,
-                                                             *selectedDeviceId, opPackageName);
+                                                             *selectedDeviceId, opPackageName,
+                                                             canCaptureOutput, canCaptureHotword);
         mAudioRecordClients.add(*portId, client);
     }
 
@@ -1132,9 +1135,10 @@
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
+    // startAudioSource should be created as the calling uid
+    const uid_t callingUid = IPCThreadState::self()->getCallingUid();
     AutoCallerClear acc;
-    return mAudioPolicyManager->startAudioSource(source, attributes, portId,
-                                                 IPCThreadState::self()->getCallingUid());
+    return mAudioPolicyManager->startAudioSource(source, attributes, portId, callingUid);
 }
 
 status_t AudioPolicyService::stopAudioSource(audio_port_handle_t portId)
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 8cbf3af..e858e8d 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -414,32 +414,35 @@
 {
 //    Go over all active clients and allow capture (does not force silence) in the
 //    following cases:
-//    The client is the assistant
+//    Another client in the same UID has already been allowed to capture
+//    OR The client is the assistant
 //        AND an accessibility service is on TOP
 //               AND the source is VOICE_RECOGNITION or HOTWORD
 //        OR uses VOICE_RECOGNITION AND is on TOP OR latest started
 //               OR uses HOTWORD
-//            AND there is no privacy sensitive active capture
+//            AND there is no active privacy sensitive capture or call
+//                OR client has CAPTURE_AUDIO_OUTPUT privileged permission
 //    OR The client is an accessibility service
 //        AND is on TOP OR latest started
 //        AND the source is VOICE_RECOGNITION or HOTWORD
-//    OR the source is one of: AUDIO_SOURCE_VOICE_DOWNLINK, AUDIO_SOURCE_VOICE_UPLINK,
-//       AUDIO_SOURCE_VOICE_CALL
+//    OR the client source is virtual (remote submix, call audio TX or RX...)
 //    OR Any other client
 //        AND The assistant is not on TOP
-//        AND is on TOP OR latest started
-//        AND there is no privacy sensitive active capture
+//        AND there is no active privacy sensitive capture or call
+//                OR client has CAPTURE_AUDIO_OUTPUT privileged permission
 //TODO: mamanage pre processing effects according to use case priority
 
     sp<AudioRecordClient> topActive;
     sp<AudioRecordClient> latestActive;
     sp<AudioRecordClient> latestSensitiveActive;
+
     nsecs_t topStartNs = 0;
     nsecs_t latestStartNs = 0;
     nsecs_t latestSensitiveStartNs = 0;
     bool isA11yOnTop = mUidPolicy->isA11yOnTop();
     bool isAssistantOnTop = false;
     bool isSensitiveActive = false;
+    bool isInCall = mPhoneState == AUDIO_MODE_IN_CALL;
 
     // if Sensor Privacy is enabled then all recordings should be silenced.
     if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -449,15 +452,18 @@
 
     for (size_t i =0; i < mAudioRecordClients.size(); i++) {
         sp<AudioRecordClient> current = mAudioRecordClients[i];
-        if (!current->active) continue;
-        if (isPrivacySensitiveSource(current->attributes.source)) {
-            if (current->startTimeNs > latestSensitiveStartNs) {
-                latestSensitiveActive = current;
-                latestSensitiveStartNs = current->startTimeNs;
-            }
-            isSensitiveActive = true;
+        if (!current->active) {
+            continue;
         }
-        if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) {
+
+        app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(current->uid));
+        // clients which app is in IDLE state are not eligible for top active or
+        // latest active
+        if (appState == APP_STATE_IDLE) {
+            continue;
+        }
+
+        if (appState == APP_STATE_TOP) {
             if (current->startTimeNs > topStartNs) {
                 topActive = current;
                 topStartNs = current->startTimeNs;
@@ -470,72 +476,105 @@
             latestActive = current;
             latestStartNs = current->startTimeNs;
         }
+        if (isPrivacySensitiveSource(current->attributes.source)) {
+            if (current->startTimeNs > latestSensitiveStartNs) {
+                latestSensitiveActive = current;
+                latestSensitiveStartNs = current->startTimeNs;
+            }
+            isSensitiveActive = true;
+        }
     }
 
-    if (topActive == nullptr && latestActive == nullptr) {
-        return;
+    // if no active client with UI on Top, consider latest active as top
+    if (topActive == nullptr) {
+        topActive = latestActive;
     }
 
-    if (topActive != nullptr) {
-        latestActive = nullptr;
-    }
+    std::vector<uid_t> enabledUids;
 
     for (size_t i =0; i < mAudioRecordClients.size(); i++) {
         sp<AudioRecordClient> current = mAudioRecordClients[i];
-        if (!current->active) continue;
+        if (!current->active) {
+            continue;
+        }
+
+        // keep capture allowed if another client with the same UID has already
+        // been allowed to capture
+        if (std::find(enabledUids.begin(), enabledUids.end(), current->uid)
+                != enabledUids.end()) {
+            continue;
+        }
 
         audio_source_t source = current->attributes.source;
-        bool isOnTop = current == topActive;
-        bool isLatest = current == latestActive;
-        bool isLatestSensitive = current == latestSensitiveActive;
-        bool forceIdle = true;
+        bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid;
+        bool isLatestSensitive = latestSensitiveActive == nullptr ?
+                                 false : current->uid == latestSensitiveActive->uid;
+
+        // By default allow capture if:
+        //     The assistant is not on TOP
+        //     AND there is no active privacy sensitive capture or call
+        //             OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+        bool allowCapture = !isAssistantOnTop
+                && !(isSensitiveActive && !(isLatestSensitive || current->canCaptureOutput))
+                && !(isInCall && !current->canCaptureOutput);
 
         if (isVirtualSource(source)) {
-            forceIdle = false;
+            // Allow capture for virtual (remote submix, call audio TX or RX...) sources
+            allowCapture = true;
         } else if (mUidPolicy->isAssistantUid(current->uid)) {
+            // For assistant allow capture if:
+            //     An accessibility service is on TOP
+            //            AND the source is VOICE_RECOGNITION or HOTWORD
+            //     OR is on TOP OR latest started AND uses VOICE_RECOGNITION
+            //            OR uses HOTWORD
+            //         AND there is no active privacy sensitive capture or call
+            //             OR client has CAPTURE_AUDIO_OUTPUT privileged permission
             if (isA11yOnTop) {
                 if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) {
-                    forceIdle = false;
+                    allowCapture = true;
                 }
             } else {
-                if ((((isOnTop || isLatest) && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
-                     source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) {
-                    forceIdle = false;
+                if (((isTopOrLatestActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
+                        source == AUDIO_SOURCE_HOTWORD) &&
+                        (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) {
+                    allowCapture = true;
                 }
             }
         } else if (mUidPolicy->isA11yUid(current->uid)) {
-            if ((isOnTop || isLatest) &&
-                (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) {
-                forceIdle = false;
-            }
-        } else {
-            if (!isAssistantOnTop && (isOnTop || isLatest) &&
-                (!isSensitiveActive || isLatestSensitive)) {
-                forceIdle = false;
+            // For accessibility service allow capture if:
+            //     Is on TOP OR latest started
+            //     AND the source is VOICE_RECOGNITION or HOTWORD
+            if (isTopOrLatestActive &&
+                    (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) {
+                allowCapture = true;
             }
         }
         setAppState_l(current->uid,
-                      forceIdle ? APP_STATE_IDLE :
-                                  apmStatFromAmState(mUidPolicy->getUidState(current->uid)));
+                      allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(current->uid)) :
+                                APP_STATE_IDLE);
+        if (allowCapture) {
+            enabledUids.push_back(current->uid);
+        }
     }
 }
 
 void AudioPolicyService::silenceAllRecordings_l() {
     for (size_t i = 0; i < mAudioRecordClients.size(); i++) {
         sp<AudioRecordClient> current = mAudioRecordClients[i];
-        setAppState_l(current->uid, APP_STATE_IDLE);
+        if (!isVirtualSource(current->attributes.source)) {
+            setAppState_l(current->uid, APP_STATE_IDLE);
+        }
     }
 }
 
 /* static */
 app_state_t AudioPolicyService::apmStatFromAmState(int amState) {
-    switch (amState) {
-    case ActivityManager::PROCESS_STATE_UNKNOWN:
+
+    if (amState == ActivityManager::PROCESS_STATE_UNKNOWN) {
         return APP_STATE_IDLE;
-    case ActivityManager::PROCESS_STATE_TOP:
-        return APP_STATE_TOP;
-    default:
-        break;
+    } else if (amState <= ActivityManager::PROCESS_STATE_TOP) {
+      // include persistent services
+      return APP_STATE_TOP;
     }
     return APP_STATE_FOREGROUND;
 }
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index a2e75cd..160f70f 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -753,13 +753,17 @@
                 AudioRecordClient(const audio_attributes_t attributes,
                           const audio_io_handle_t io, uid_t uid, pid_t pid,
                           const audio_session_t session, const audio_port_handle_t deviceId,
-                          const String16& opPackageName) :
+                          const String16& opPackageName,
+                          bool canCaptureOutput, bool canCaptureHotword) :
                     AudioClient(attributes, io, uid, pid, session, deviceId),
-                    opPackageName(opPackageName), startTimeNs(0) {}
+                    opPackageName(opPackageName), startTimeNs(0),
+                    canCaptureOutput(canCaptureOutput), canCaptureHotword(canCaptureHotword) {}
                 ~AudioRecordClient() override = default;
 
         const String16 opPackageName;        // client package name
         nsecs_t startTimeNs;
+        const bool canCaptureOutput;
+        const bool canCaptureHotword;
     };
 
     // --- AudioPlaybackClient ---