Merge "add tuner delay hint to TunerFilter"
diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING
index 4984b8f..a7c2cfe 100644
--- a/media/extractors/TEST_MAPPING
+++ b/media/extractors/TEST_MAPPING
@@ -1,6 +1,9 @@
 {
   "presubmit": [
 
+        {
+            "name": "CtsMediaTranscodingTestCases"
+        }
     // TODO(b/153661591) enable test once the bug is fixed
     // This tests the extractor path
     // {
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 9617556..d9fddf3 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -25,6 +25,7 @@
 
 #include <android/media/IAudioPolicyService.h>
 #include <android-base/macros.h>
+#include <android-base/stringprintf.h>
 #include <audio_utils/clock.h>
 #include <audio_utils/primitives.h>
 #include <binder/IPCThreadState.h>
@@ -44,6 +45,7 @@
 static const int kMaxLoopCountNotifications = 32;
 
 using ::android::aidl_utils::statusTFromBinderStatus;
+using ::android::base::StringPrintf;
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -539,6 +541,7 @@
     uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
     pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid));
     sp<IAudioTrackCallback> _callback = callback.promote();
+    std::string errorMessage;
     // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
     ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
           "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
@@ -563,32 +566,34 @@
     case TRANSFER_CALLBACK:
     case TRANSFER_SYNC_NOTIF_CALLBACK:
         if (_callback == nullptr || sharedBuffer != 0) {
-            ALOGE("%s(): Transfer type %s but callback == nullptr || sharedBuffer != 0",
+            errorMessage = StringPrintf(
+                    "%s: Transfer type %s but callback == nullptr || sharedBuffer != 0",
                     convertTransferToText(transferType), __func__);
             status = BAD_VALUE;
-            goto exit;
+            goto error;
         }
         break;
     case TRANSFER_OBTAIN:
     case TRANSFER_SYNC:
         if (sharedBuffer != 0) {
-            ALOGE("%s(): Transfer type TRANSFER_OBTAIN but sharedBuffer != 0", __func__);
+            errorMessage = StringPrintf(
+                    "%s: Transfer type TRANSFER_OBTAIN but sharedBuffer != 0", __func__);
             status = BAD_VALUE;
-            goto exit;
+            goto error;
         }
         break;
     case TRANSFER_SHARED:
         if (sharedBuffer == 0) {
-            ALOGE("%s(): Transfer type TRANSFER_SHARED but sharedBuffer == 0", __func__);
+            errorMessage = StringPrintf(
+                    "%s: Transfer type TRANSFER_SHARED but sharedBuffer == 0", __func__);
             status = BAD_VALUE;
-            goto exit;
+            goto error;
         }
         break;
     default:
-        ALOGE("%s(): Invalid transfer type %d",
-                __func__, transferType);
+        errorMessage = StringPrintf("%s: Invalid transfer type %d", __func__, transferType);
         status = BAD_VALUE;
-        goto exit;
+        goto error;
     }
     mSharedBuffer = sharedBuffer;
     mTransfer = transferType;
@@ -602,9 +607,9 @@
 
     // invariant that mAudioTrack != 0 is true only after set() returns successfully
     if (mAudioTrack != 0) {
-        ALOGE("%s(): Track already in use", __func__);
+        errorMessage = StringPrintf("%s: Track already in use", __func__);
         status = INVALID_OPERATION;
-        goto exit;
+        goto error;
     }
 
     // handle default values first.
@@ -613,9 +618,9 @@
     }
     if (pAttributes == NULL) {
         if (uint32_t(streamType) >= AUDIO_STREAM_PUBLIC_CNT) {
-            ALOGE("%s(): Invalid stream type %d", __func__, streamType);
+            errorMessage = StringPrintf("%s: Invalid stream type %d", __func__, streamType);
             status = BAD_VALUE;
-            goto exit;
+            goto error;
         }
         mOriginalStreamType = streamType;
 
@@ -639,16 +644,16 @@
 
     // validate parameters
     if (!audio_is_valid_format(format)) {
-        ALOGE("%s(): Invalid format %#x", __func__, format);
+        errorMessage = StringPrintf("%s: Invalid format %#x", __func__, format);
         status = BAD_VALUE;
-        goto exit;
+        goto error;
     }
     mFormat = format;
 
     if (!audio_is_output_channel(channelMask)) {
-        ALOGE("%s(): Invalid channel mask %#x",  __func__, channelMask);
+        errorMessage = StringPrintf("%s: Invalid channel mask %#x",  __func__, channelMask);
         status = BAD_VALUE;
-        goto exit;
+        goto error;
     }
     mChannelMask = channelMask;
     channelCount = audio_channel_count_from_out_mask(channelMask);
@@ -687,8 +692,10 @@
 
     // sampling rate must be specified for direct outputs
     if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+        errorMessage = StringPrintf(
+                "%s: sample rate must be specified for direct outputs", __func__);
         status = BAD_VALUE;
-        goto exit;
+        goto error;
     }
     mSampleRate = sampleRate;
     mOriginalSampleRate = sampleRate;
@@ -718,16 +725,17 @@
         mNotificationsPerBufferReq = 0;
     } else {
         if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
-            ALOGE("%s(): notificationFrames=%d not permitted for non-fast track",
+            errorMessage = StringPrintf(
+                    "%s: notificationFrames=%d not permitted for non-fast track",
                     __func__, notificationFrames);
             status = BAD_VALUE;
-            goto exit;
+            goto error;
         }
         if (frameCount > 0) {
             ALOGE("%s(): notificationFrames=%d not permitted with non-zero frameCount=%zu",
                     __func__, notificationFrames, frameCount);
             status = BAD_VALUE;
-            goto exit;
+            goto error;
         }
         mNotificationFramesReq = 0;
         const uint32_t minNotificationsPerBuffer = 1;
@@ -772,6 +780,7 @@
             mAudioTrackThread->requestExitAndWait();
             mAudioTrackThread.clear();
         }
+        // We do not goto error to prevent double-logging errors.
         goto exit;
     }
 
@@ -805,6 +814,12 @@
     mFramesWrittenAtRestore = -1; // -1 is a unique initializer.
     mVolumeHandler = new media::VolumeHandler();
 
+error:
+    if (status != NO_ERROR) {
+        ALOGE_IF(!errorMessage.empty(), "%s", errorMessage.c_str());
+        reportError(status, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE, errorMessage.c_str());
+    }
+    // fall through
 exit:
     mStatus = status;
     return status;
@@ -1832,12 +1847,13 @@
 {
     status_t status;
     bool callbackAdded = false;
+    std::string errorMessage;
 
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
     if (audioFlinger == 0) {
-        ALOGE("%s(%d): Could not get audioflinger",
+        errorMessage = StringPrintf("%s(%d): Could not get audioflinger",
                 __func__, mPortId);
-        status = NO_INIT;
+        status = DEAD_OBJECT;
         goto exit;
     }
 
@@ -1914,10 +1930,11 @@
     }
 
     if (status != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
-        ALOGE("%s(%d): AudioFlinger could not create track, status: %d output %d",
+        errorMessage = StringPrintf(
+                "%s(%d): AudioFlinger could not create track, status: %d output %d",
                 __func__, mPortId, status, output.outputId);
         if (status == NO_ERROR) {
-            status = NO_INIT;
+            status = INVALID_OPERATION; // device not ready
         }
         goto exit;
     }
@@ -1948,8 +1965,8 @@
     output.audioTrack->getCblk(&sfr);
     sp<IMemory> iMem = VALUE_OR_FATAL(aidl2legacy_NullableSharedFileRegion_IMemory(sfr));
     if (iMem == 0) {
-        ALOGE("%s(%d): Could not get control block", __func__, mPortId);
-        status = NO_INIT;
+        errorMessage = StringPrintf("%s(%d): Could not get control block", __func__, mPortId);
+        status = FAILED_TRANSACTION;
         goto exit;
     }
     // TODO: Using unsecurePointer() has some associated security pitfalls
@@ -1958,8 +1975,9 @@
     //       issue (e.g. by copying).
     void *iMemPointer = iMem->unsecurePointer();
     if (iMemPointer == NULL) {
-        ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);
-        status = NO_INIT;
+        errorMessage = StringPrintf(
+                "%s(%d): Could not get control block pointer", __func__, mPortId);
+        status = FAILED_TRANSACTION;
         goto exit;
     }
     // invariant that mAudioTrack != 0 is true only after set() returns successfully
@@ -2017,8 +2035,10 @@
         //       issue (e.g. by copying).
         buffers = mSharedBuffer->unsecurePointer();
         if (buffers == NULL) {
-            ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);
-            status = NO_INIT;
+            errorMessage = StringPrintf(
+                    "%s(%d): Could not get buffer pointer", __func__, mPortId);
+            ALOGE("%s", errorMessage.c_str());
+            status = FAILED_TRANSACTION;
             goto exit;
         }
     }
@@ -2116,17 +2136,44 @@
     }
 
 exit:
-    if (status != NO_ERROR && callbackAdded) {
-        // note: mOutput is always valid is callbackAdded is true
-        AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
+    if (status != NO_ERROR) {
+        if (callbackAdded) {
+            // note: mOutput is always valid is callbackAdded is true
+            AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
+        }
+        ALOGE_IF(!errorMessage.empty(), "%s", errorMessage.c_str());
+        reportError(status, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE, errorMessage.c_str());
     }
-
     mStatus = status;
 
     // sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlinger
     return status;
 }
 
+void AudioTrack::reportError(status_t status, const char *event, const char *message) const
+{
+    if (status == NO_ERROR) return;
+    // We report error on the native side because some callers do not come
+    // from Java.
+    mediametrics::LogItem(std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) + "error")
+        .set(AMEDIAMETRICS_PROP_EVENT, event)
+        .set(AMEDIAMETRICS_PROP_ERROR, mediametrics::statusToErrorString(status))
+        .set(AMEDIAMETRICS_PROP_ERRORMESSAGE, message)
+        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
+        .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
+        .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(mAttributes.content_type).c_str())
+        .set(AMEDIAMETRICS_PROP_USAGE, toString(mAttributes.usage).c_str())
+        .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
+        .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str())
+        .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
+        .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mReqFrameCount) // requested frame count
+        // the following are NOT immutable
+        .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+        .set(AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)mPlaybackRate.mSpeed)
+        .set(AMEDIAMETRICS_PROP_PLAYBACK_PITCH, (double)mPlaybackRate.mPitch)
+        .record();
+}
+
 status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
 {
     if (audioBuffer == NULL) {
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index dd4d2da..ee262f3 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -40,7 +40,7 @@
 
 // ----------------------------------------------------------------------------
 
-class AudioEffect : public RefBase
+class AudioEffect : public virtual RefBase
 {
 public:
 
diff --git a/media/libaudioclient/include/media/AudioIoDescriptor.h b/media/libaudioclient/include/media/AudioIoDescriptor.h
index ef729ed..405ec7d 100644
--- a/media/libaudioclient/include/media/AudioIoDescriptor.h
+++ b/media/libaudioclient/include/media/AudioIoDescriptor.h
@@ -39,7 +39,7 @@
 
 // audio input/output descriptor used to cache output configurations in client process to avoid
 // frequent calls through IAudioFlinger
-class AudioIoDescriptor : public RefBase {
+class AudioIoDescriptor : public virtual RefBase {
 public:
     AudioIoDescriptor() = default;
     // For AUDIO_{INPUT|OUTPUT}_CLOSED events.
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 7f5c29a..368b20a 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -538,7 +538,7 @@
 
 
     // A listener for capture state changes.
-    class CaptureStateListener : public RefBase {
+    class CaptureStateListener : public virtual RefBase {
     public:
         // Called whenever capture state changes.
         virtual void onStateChanged(bool active) = 0;
@@ -563,7 +563,7 @@
 
     // ----------------------------------------------------------------------------
 
-    class AudioVolumeGroupCallback : public RefBase
+    class AudioVolumeGroupCallback : public virtual RefBase
     {
     public:
 
@@ -578,7 +578,7 @@
     static status_t addAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback);
     static status_t removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback);
 
-    class AudioPortCallback : public RefBase
+    class AudioPortCallback : public virtual RefBase
     {
     public:
 
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 1bc2c32..56d341e 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1489,6 +1489,9 @@
     std::string mMetricsId;  // GUARDED_BY(mLock), could change in createTrack_l().
     std::string mCallerName; // for example "aaudio"
 
+    // report error to mediametrics.
+    void reportError(status_t status, const char *event, const char *message) const;
+
 private:
     class AudioTrackCallback : public media::BnAudioTrackCallback {
     public:
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 485648d..b4ee4dc 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -65,7 +65,7 @@
 
 // ----------------------------------------------------------------------------
 
-class IAudioFlinger : public RefBase {
+class IAudioFlinger : public virtual RefBase {
 public:
     static constexpr char DEFAULT_SERVICE_NAME[] = "media.audio_flinger";
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 92140d5..86e0566 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4101,7 +4101,7 @@
                     }
                     if (outputDesc != nullptr) {
                         audio_port_config srcMixPortConfig = {};
-                        outputDesc->toAudioPortConfig(&srcMixPortConfig, &patch->sources[0]);
+                        outputDesc->toAudioPortConfig(&srcMixPortConfig, nullptr);
                         // for volume control, we may need a valid stream
                         srcMixPortConfig.ext.mix.usecase.stream = sourceDesc != nullptr ?
                                     sourceDesc->stream() : AUDIO_STREAM_PATCH;
@@ -5958,14 +5958,20 @@
                     client->getSecondaryOutputs().begin(),
                     client->getSecondaryOutputs().end(),
                     secondaryDescs.begin(), secondaryDescs.end())) {
-                std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
-                std::vector<audio_io_handle_t> secondaryOutputIds;
-                for (const auto& secondaryDesc : secondaryDescs) {
-                    secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
-                    weakSecondaryDescs.push_back(secondaryDesc);
+                if (!audio_is_linear_pcm(client->config().format)) {
+                    // If the format is not PCM, the tracks should be invalidated to get correct
+                    // behavior when the secondary output is changed.
+                    streamsToInvalidate.insert(client->stream());
+                } else {
+                    std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
+                    std::vector<audio_io_handle_t> secondaryOutputIds;
+                    for (const auto &secondaryDesc: secondaryDescs) {
+                        secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
+                        weakSecondaryDescs.push_back(secondaryDesc);
+                    }
+                    trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
+                    client->setSecondaryOutputs(std::move(weakSecondaryDescs));
                 }
-                trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
-                client->setSecondaryOutputs(std::move(weakSecondaryDescs));
             }
         }
     }
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index 0b5ad79..8a67b0a 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -19,12 +19,14 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libbase \
+    libbinder \
     libcutils \
     libcameraservice \
     libhidlbase \
     liblog \
     libcamera_client \
     libcamera_metadata \
+    libgui \
     libui \
     libutils \
     libjpeg \
diff --git a/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp b/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp
new file mode 100644
index 0000000..6586b74
--- /dev/null
+++ b/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "PreviewSchedulerTest"
+
+#include <chrono>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/Surface.h>
+
+#include "../device3/Camera3OutputStream.h"
+#include "../device3/PreviewFrameScheduler.h"
+
+using namespace android;
+using namespace android::camera3;
+
+// Consumer buffer available listener
+class SimpleListener : public BufferItemConsumer::FrameAvailableListener {
+public:
+    SimpleListener(size_t frameCount): mFrameCount(frameCount) {}
+
+    void waitForFrames() {
+        Mutex::Autolock lock(mMutex);
+        while (mFrameCount > 0) {
+            mCondition.wait(mMutex);
+        }
+    }
+
+    void onFrameAvailable(const BufferItem& /*item*/) override {
+        Mutex::Autolock lock(mMutex);
+        if (mFrameCount > 0) {
+            mFrameCount--;
+            mCondition.signal();
+        }
+    }
+
+    void reset(size_t frameCount) {
+        Mutex::Autolock lock(mMutex);
+        mFrameCount = frameCount;
+    }
+private:
+    size_t mFrameCount;
+    Mutex mMutex;
+    Condition mCondition;
+};
+
+// Test the PreviewFrameScheduler functionatliy of re-timing buffers
+TEST(PreviewSchedulerTest, BasicPreviewSchedulerTest) {
+    const int ID = 0;
+    const int FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+    const uint32_t WIDTH = 640;
+    const uint32_t HEIGHT = 480;
+    const int32_t TRANSFORM = 0;
+    const nsecs_t T_OFFSET = 0;
+    const android_dataspace DATASPACE = HAL_DATASPACE_UNKNOWN;
+    const camera_stream_rotation_t ROTATION = CAMERA_STREAM_ROTATION_0;
+    const String8 PHY_ID;
+    const std::unordered_set<int32_t> PIX_MODES;
+    const int BUFFER_COUNT = 4;
+    const int TOTAL_BUFFER_COUNT = BUFFER_COUNT * 2;
+
+    // Create buffer queue
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    ASSERT_NE(producer, nullptr);
+    ASSERT_NE(consumer, nullptr);
+    ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(WIDTH, HEIGHT));
+
+    // Set up consumer
+    sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumer,
+            GRALLOC_USAGE_HW_COMPOSER, BUFFER_COUNT);
+    ASSERT_NE(bufferConsumer, nullptr);
+    sp<SimpleListener> consumerListener = new SimpleListener(BUFFER_COUNT);
+    bufferConsumer->setFrameAvailableListener(consumerListener);
+
+    // Set up producer
+    sp<Surface> surface = new Surface(producer);
+    sp<StubProducerListener> listener = new StubProducerListener();
+    ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+    sp<ANativeWindow> anw(surface);
+    ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), TOTAL_BUFFER_COUNT));
+
+    // Create Camera3OutputStream and PreviewFrameScheduler
+    sp<Camera3OutputStream> stream = new Camera3OutputStream(ID, surface, WIDTH, HEIGHT,
+            FORMAT, DATASPACE, ROTATION, T_OFFSET, PHY_ID, PIX_MODES);
+    ASSERT_NE(stream, nullptr);
+    std::unique_ptr<PreviewFrameScheduler> scheduler =
+            std::make_unique<PreviewFrameScheduler>(*stream, surface);
+    ASSERT_NE(scheduler, nullptr);
+
+    // The pair of nsecs_t: camera timestamp delta (negative means in the past) and frame interval
+    const std::pair<nsecs_t, nsecs_t> inputTimestamps[][BUFFER_COUNT] = {
+        // 30fps, no interval
+        {{-100000000LL, 0}, {-66666667LL, 0},
+          {-33333333LL, 0}, {0, 0}},
+        // 30fps, 33ms interval
+        {{-100000000LL, 33333333LL}, {-66666667LL, 33333333LL},
+          {-33333333LL, 33333333LL}, {0, 0}},
+        // 30fps, variable interval
+        {{-100000000LL, 16666667LL}, {-66666667LL, 33333333LL},
+          {-33333333LL, 50000000LL}, {0, 0}},
+        // 60fps, 16.7ms interval
+        {{-50000000LL, 16666667LL}, {-33333333LL, 16666667LL},
+          {-16666667LL, 16666667LL}, {0, 0}},
+        // 60fps, variable interval
+        {{-50000000LL, 8666667LL}, {-33333333LL, 19666667LL},
+          {-16666667LL, 20666667LL}, {0, 0}},
+    };
+
+    const nsecs_t USE_AS_IS = -1; // Use the producer set timestamp
+    const nsecs_t USE_OVERRIDE = -2; // Use the scheduler overridden timestamp
+    const nsecs_t expectedTimestamps[][BUFFER_COUNT] = {
+        // 30fps, no interval: first 2 frames as is, and last 2 frames are
+        // overridden.
+        {USE_AS_IS, USE_AS_IS, USE_OVERRIDE, USE_OVERRIDE},
+        // 30fps, 33ms interval: all frames are overridden
+        {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+        // 30fps, variable interval: all frames are overridden
+        {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+        // 60fps, 16.7ms interval: all frames are overridden
+        {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+        // 60fps, variable interval: all frames are overridden
+        {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+    };
+
+    // Go through different use cases, and check the buffer timestamp
+    size_t iterations = sizeof(inputTimestamps)/sizeof(inputTimestamps[0]);
+    for (size_t i = 0; i < iterations; i++) {
+        // Space out different test sets to reset the frame scheduler
+        nsecs_t timeBase = systemTime() - s2ns(1) * (iterations - i);
+        nsecs_t lastQueueTime = 0;
+        nsecs_t duration = 0;
+        for (size_t j = 0; j < BUFFER_COUNT; j++) {
+            ANativeWindowBuffer* buffer = nullptr;
+            int fenceFd;
+            ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buffer, &fenceFd));
+
+            // Sleep to space out queuePreviewBuffer
+            nsecs_t currentTime = systemTime();
+            if (duration > 0 && duration > currentTime - lastQueueTime) {
+                std::this_thread::sleep_for(
+                        std::chrono::nanoseconds(duration + lastQueueTime - currentTime));
+            }
+            nsecs_t timestamp = timeBase + inputTimestamps[i][j].first;
+            ASSERT_EQ(NO_ERROR,
+                    scheduler->queuePreviewBuffer(timestamp, TRANSFORM, buffer, fenceFd));
+
+            lastQueueTime = systemTime();
+            duration = inputTimestamps[i][j].second;
+        }
+
+        // Collect output timestamps, making sure they are either set by
+        // producer, or set by the scheduler.
+        consumerListener->waitForFrames();
+        nsecs_t outputTimestamps[BUFFER_COUNT];
+        for (size_t j = 0; j < BUFFER_COUNT; j++) {
+            BufferItem bufferItem;
+            ASSERT_EQ(NO_ERROR, bufferConsumer->acquireBuffer(&bufferItem, 0/*presentWhen*/));
+
+            outputTimestamps[j] = bufferItem.mTimestamp;
+            ALOGV("%s: [%zu][%zu]: input: %" PRId64 ", output: %" PRId64, __FUNCTION__,
+                  i, j, timeBase + inputTimestamps[i][j].first, bufferItem.mTimestamp);
+            if (expectedTimestamps[i][j] == USE_OVERRIDE) {
+                ASSERT_GT(bufferItem.mTimestamp, inputTimestamps[i][j].first);
+            } else if (expectedTimestamps[i][j] == USE_AS_IS) {
+                ASSERT_EQ(bufferItem.mTimestamp, timeBase + inputTimestamps[i][j].first);
+            }
+
+            ASSERT_EQ(NO_ERROR, bufferConsumer->releaseBuffer(bufferItem));
+        }
+
+        // Check the output timestamp intervals are aligned with input intervals
+        const nsecs_t SHIFT_THRESHOLD = ms2ns(2);
+        for (size_t j = 0; j < BUFFER_COUNT - 1; j ++) {
+            if (expectedTimestamps[i][j] == USE_OVERRIDE &&
+                    expectedTimestamps[i][j+1] == USE_OVERRIDE) {
+                nsecs_t interval_shift = outputTimestamps[j+1] - outputTimestamps[j] -
+                        (inputTimestamps[i][j+1].first - inputTimestamps[i][j].first);
+                ASSERT_LE(std::abs(interval_shift), SHIFT_THRESHOLD);
+            }
+        }
+
+        consumerListener->reset(BUFFER_COUNT);
+    }
+
+    // Disconnect the surface
+    ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
diff --git a/services/mediaextractor/TEST_MAPPING b/services/mediaextractor/TEST_MAPPING
new file mode 100644
index 0000000..7a66eeb
--- /dev/null
+++ b/services/mediaextractor/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsMediaTranscodingTestCases"
+        }
+    ]
+}
diff --git a/services/tuner/TunerFilter.cpp b/services/tuner/TunerFilter.cpp
index 7e38a1e..fb5bfa3 100644
--- a/services/tuner/TunerFilter.cpp
+++ b/services/tuner/TunerFilter.cpp
@@ -346,7 +346,7 @@
     return res;
 }
 
-::ndk::ScopedAStatus TunerFilter::createSharedFilter(string* _aidl_return) {
+::ndk::ScopedAStatus TunerFilter::acquireSharedFilterToken(string* _aidl_return) {
     Mutex::Autolock _l(mLock);
     if (mFilter == nullptr) {
         ALOGE("IFilter is not initialized");
@@ -369,7 +369,7 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus TunerFilter::releaseSharedFilter(const string& /* in_filterToken */) {
+::ndk::ScopedAStatus TunerFilter::freeSharedFilterToken(const string& /* in_filterToken */) {
     Mutex::Autolock _l(mLock);
     if (mFilter == nullptr) {
         ALOGE("IFilter is not initialized");
diff --git a/services/tuner/TunerFilter.h b/services/tuner/TunerFilter.h
index 1ab712c..529c191 100644
--- a/services/tuner/TunerFilter.h
+++ b/services/tuner/TunerFilter.h
@@ -93,8 +93,8 @@
     ::ndk::ScopedAStatus stop() override;
     ::ndk::ScopedAStatus flush() override;
     ::ndk::ScopedAStatus close() override;
-    ::ndk::ScopedAStatus createSharedFilter(string* _aidl_return) override;
-    ::ndk::ScopedAStatus releaseSharedFilter(const string& in_filterToken) override;
+    ::ndk::ScopedAStatus acquireSharedFilterToken(string* _aidl_return) override;
+    ::ndk::ScopedAStatus freeSharedFilterToken(const string& in_filterToken) override;
     ::ndk::ScopedAStatus getFilterType(DemuxFilterType* _aidl_return) override;
     ::ndk::ScopedAStatus setDelayHint(const FilterDelayHint& in_hint) override;
 
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
index 454634f..dc40f03 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
@@ -102,19 +102,19 @@
     void close();
 
     /**
-     * Create a new SharedFilter instance.
+     * Acquire a new SharedFilter token.
      *
      * @return a token of the newly created SharedFilter instance.
      */
-    String createSharedFilter();
+    String acquireSharedFilterToken();
 
     /**
-     * Release a SharedFilter instance.
+     * Free a SharedFilter token.
      *
-     * @param filterToken the SharedFilter will be released.
+     * @param filterToken the SharedFilter token will be released.
      * @return a token of the newly created SharedFilter instance.
      */
-    void releaseSharedFilter(in String filterToken);
+    void freeSharedFilterToken(in String filterToken);
 
     /**
      * Get filter type.
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index 879cd84..7b76093 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -19,6 +19,7 @@
 #include "TunerHidlFilter.h"
 
 #include <aidl/android/hardware/tv/tuner/Constant.h>
+#include <aidl/android/hardware/tv/tuner/DemuxScIndex.h>
 #include <aidl/android/hardware/tv/tuner/Result.h>
 #include <aidlcommonsupport/NativeHandle.h>
 #include <binder/IPCThreadState.h>
@@ -55,6 +56,7 @@
 using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettingsFilterSettings;
 using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterType;
 using ::aidl::android::hardware::tv::tuner::DemuxPid;
+using ::aidl::android::hardware::tv::tuner::DemuxScIndex;
 using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettings;
 using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettingsFilterSettings;
 using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettings;
@@ -521,7 +523,7 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus TunerHidlFilter::createSharedFilter(string* _aidl_return) {
+::ndk::ScopedAStatus TunerHidlFilter::acquireSharedFilterToken(string* _aidl_return) {
     Mutex::Autolock _l(mLock);
     if (mFilter == nullptr) {
         ALOGE("IFilter is not initialized");
@@ -545,7 +547,7 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus TunerHidlFilter::releaseSharedFilter(const string& /* in_filterToken */) {
+::ndk::ScopedAStatus TunerHidlFilter::freeSharedFilterToken(const string& /* in_filterToken */) {
     Mutex::Autolock _l(mLock);
     if (mFilter == nullptr) {
         ALOGE("IFilter is not initialized");
@@ -872,16 +874,26 @@
         const DemuxFilterRecordSettings& settings) {
     HidlDemuxFilterRecordSettings record{
             .tsIndexMask = static_cast<uint32_t>(settings.tsIndexMask),
-            .scIndexType = static_cast<HidlDemuxRecordScIndexType>(settings.scIndexType),
     };
 
     switch (settings.scIndexMask.getTag()) {
     case DemuxFilterScIndexMask::scIndex: {
+        record.scIndexType = static_cast<HidlDemuxRecordScIndexType>(settings.scIndexType);
         record.scIndexMask.sc(
                 static_cast<uint32_t>(settings.scIndexMask.get<DemuxFilterScIndexMask::scIndex>()));
         break;
     }
+    case DemuxFilterScIndexMask::scAvc: {
+        record.scIndexType = HidlDemuxRecordScIndexType::SC;
+        uint32_t index =
+                static_cast<uint32_t>(settings.scIndexMask.get<DemuxFilterScIndexMask::scAvc>());
+        // HIDL HAL starting from 1 << 4; AIDL starting from 1 << 0.
+        index = index << 4;
+        record.scIndexMask.sc(index);
+        break;
+    }
     case DemuxFilterScIndexMask::scHevc: {
+        record.scIndexType = static_cast<HidlDemuxRecordScIndexType>(settings.scIndexType);
         record.scIndexMask.scHevc(
                 static_cast<uint32_t>(settings.scIndexMask.get<DemuxFilterScIndexMask::scHevc>()));
         break;
@@ -1100,8 +1112,13 @@
         DemuxFilterScIndexMask scIndexMask;
         if (tsRecordEvent.scIndexMask.getDiscriminator() ==
             HidlDemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) {
-            scIndexMask.set<DemuxFilterScIndexMask::scIndex>(
-                    static_cast<int32_t>(tsRecordEvent.scIndexMask.sc()));
+            int32_t hidlScIndex = static_cast<int32_t>(tsRecordEvent.scIndexMask.sc());
+            if (hidlScIndex <= static_cast<int32_t>(DemuxScIndex::SEQUENCE)) {
+                scIndexMask.set<DemuxFilterScIndexMask::scIndex>(hidlScIndex);
+            } else {
+                // HIDL HAL starting from 1 << 4; AIDL starting from 1 << 0.
+                scIndexMask.set<DemuxFilterScIndexMask::scAvc>(hidlScIndex >> 4);
+            }
         } else if (tsRecordEvent.scIndexMask.getDiscriminator() ==
                    HidlDemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) {
             scIndexMask.set<DemuxFilterScIndexMask::scHevc>(
diff --git a/services/tuner/hidl/TunerHidlFilter.h b/services/tuner/hidl/TunerHidlFilter.h
index 86b89e5..b8fad22 100644
--- a/services/tuner/hidl/TunerHidlFilter.h
+++ b/services/tuner/hidl/TunerHidlFilter.h
@@ -183,8 +183,8 @@
     ::ndk::ScopedAStatus stop() override;
     ::ndk::ScopedAStatus flush() override;
     ::ndk::ScopedAStatus close() override;
-    ::ndk::ScopedAStatus createSharedFilter(string* _aidl_return) override;
-    ::ndk::ScopedAStatus releaseSharedFilter(const string& in_filterToken) override;
+    ::ndk::ScopedAStatus acquireSharedFilterToken(string* _aidl_return) override;
+    ::ndk::ScopedAStatus freeSharedFilterToken(const string& in_filterToken) override;
     ::ndk::ScopedAStatus getFilterType(DemuxFilterType* _aidl_return) override;
     ::ndk::ScopedAStatus setDelayHint(const FilterDelayHint& in_hint) override;