Merge changes Ic93dd0c7,Ibabbb998,I3a26f901,I9708086f into main

* changes:
  Add last video render time to bug reports
  Support video render metrics for OMX
  Fix delay used when polling for rendered buffers
  Add perfetto trigger for video freeze
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index c102c4b..cab522e 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -46,6 +46,7 @@
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/RenderedFrameInfo.h>
 #include <utils/NativeHandle.h>
 
 #include "C2OMXNode.h"
@@ -672,8 +673,7 @@
     }
 
     void onOutputFramesRendered(int64_t mediaTimeUs, nsecs_t renderTimeNs) override {
-        mCodec->mCallback->onOutputFramesRendered(
-                {RenderedFrameInfo(mediaTimeUs, renderTimeNs)});
+        mCodec->mCallback->onOutputFramesRendered({RenderedFrameInfo(mediaTimeUs, renderTimeNs)});
     }
 
     void onOutputBuffersChanged() override {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 75a1ac9..db7db08 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -43,6 +43,7 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/RenderedFrameInfo.h>
 #include <media/stagefright/SurfaceUtils.h>
 #include <media/hardware/HardwareAPI.h>
 #include <media/MediaBufferHolder.h>
@@ -634,7 +635,8 @@
     if (!mBufferChannel) {
         mBufferChannel = std::make_shared<ACodecBufferChannel>(
                 new AMessage(kWhatInputBufferFilled, this),
-                new AMessage(kWhatOutputBufferDrained, this));
+                new AMessage(kWhatOutputBufferDrained, this),
+                new AMessage(kWhatPollForRenderedBuffers, this));
     }
     return mBufferChannel;
 }
@@ -744,6 +746,7 @@
     // if we have not yet started the codec, we can simply set the native window
     if (mBuffers[kPortIndexInput].size() == 0) {
         mNativeWindow = surface;
+        initializeFrameTracking();
         return OK;
     }
 
@@ -852,6 +855,7 @@
 
     mNativeWindow = nativeWindow;
     mNativeWindowUsageBits = usageBits;
+    initializeFrameTracking();
     return OK;
 }
 
@@ -962,7 +966,6 @@
                 BufferInfo info;
                 info.mStatus = BufferInfo::OWNED_BY_US;
                 info.mFenceFd = -1;
-                info.mRenderInfo = NULL;
                 info.mGraphicBuffer = NULL;
                 info.mNewGraphicBuffer = false;
 
@@ -1230,6 +1233,7 @@
 
     *bufferCount = def.nBufferCountActual;
     *bufferSize =  def.nBufferSize;
+    initializeFrameTracking();
     return err;
 }
 
@@ -1268,7 +1272,6 @@
         info.mStatus = BufferInfo::OWNED_BY_US;
         info.mFenceFd = fenceFd;
         info.mIsReadFence = false;
-        info.mRenderInfo = NULL;
         info.mGraphicBuffer = graphicBuffer;
         info.mNewGraphicBuffer = false;
         info.mDequeuedAt = mDequeueCounter;
@@ -1345,7 +1348,6 @@
         BufferInfo info;
         info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
         info.mFenceFd = -1;
-        info.mRenderInfo = NULL;
         info.mGraphicBuffer = NULL;
         info.mNewGraphicBuffer = false;
         info.mDequeuedAt = mDequeueCounter;
@@ -1441,42 +1443,6 @@
     return err;
 }
 
-void ACodec::updateRenderInfoForDequeuedBuffer(
-        ANativeWindowBuffer *buf, int fenceFd, BufferInfo *info) {
-
-    info->mRenderInfo =
-        mRenderTracker.updateInfoForDequeuedBuffer(
-                buf, fenceFd, info - &mBuffers[kPortIndexOutput][0]);
-
-    // check for any fences already signaled
-    notifyOfRenderedFrames(false /* dropIncomplete */, info->mRenderInfo);
-}
-
-void ACodec::onFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) {
-    if (mRenderTracker.onFrameRendered(mediaTimeUs, systemNano) != OK) {
-        mRenderTracker.dumpRenderQueue();
-    }
-}
-
-void ACodec::notifyOfRenderedFrames(bool dropIncomplete, FrameRenderTracker::Info *until) {
-    std::list<FrameRenderTracker::Info> done =
-        mRenderTracker.checkFencesAndGetRenderedFrames(until, dropIncomplete);
-
-    // unlink untracked frames
-    for (std::list<FrameRenderTracker::Info>::const_iterator it = done.cbegin();
-            it != done.cend(); ++it) {
-        ssize_t index = it->getIndex();
-        if (index >= 0 && (size_t)index < mBuffers[kPortIndexOutput].size()) {
-            mBuffers[kPortIndexOutput][index].mRenderInfo = NULL;
-        } else if (index >= 0) {
-            // THIS SHOULD NEVER HAPPEN
-            ALOGE("invalid index %zd in %zu", index, mBuffers[kPortIndexOutput].size());
-        }
-    }
-
-    mCallback->onOutputFramesRendered(done);
-}
-
 void ACodec::onFirstTunnelFrameReady() {
     mCallback->onFirstTunnelFrameReady();
 }
@@ -1531,7 +1497,6 @@
 
                 info->mStatus = BufferInfo::OWNED_BY_US;
                 info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow");
-                updateRenderInfoForDequeuedBuffer(buf, fenceFd, info);
                 return info;
             }
         }
@@ -1576,18 +1541,96 @@
     oldest->mNewGraphicBuffer = true;
     oldest->mStatus = BufferInfo::OWNED_BY_US;
     oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest");
-    mRenderTracker.untrackFrame(oldest->mRenderInfo);
-    oldest->mRenderInfo = NULL;
 
     ALOGV("replaced oldest buffer #%u with age %u, graphicBuffer %p",
             (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
             mDequeueCounter - oldest->mDequeuedAt,
             oldest->mGraphicBuffer->handle);
-
-    updateRenderInfoForDequeuedBuffer(buf, fenceFd, oldest);
     return oldest;
 }
 
+void ACodec::initializeFrameTracking() {
+    mTrackedFrames.clear();
+
+    int isWindowToDisplay = 0;
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+            &isWindowToDisplay);
+    mIsWindowToDisplay = isWindowToDisplay == 1;
+    // No frame tracking is needed if we're not sending frames to the display
+    if (!mIsWindowToDisplay) {
+        // Return early so we don't call into SurfaceFlinger (requiring permissions)
+        return;
+    }
+
+    int hasPresentFenceTimes = 0;
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT,
+            &hasPresentFenceTimes);
+    mHasPresentFenceTimes = hasPresentFenceTimes == 1;
+    if (!mHasPresentFenceTimes) {
+        ALOGI("Using latch times for frame rendered signals - present fences not supported");
+    }
+
+    status_t err = native_window_enable_frame_timestamps(mNativeWindow.get(), true);
+    if (err) {
+        ALOGE("Failed to enable frame timestamps (%d)", err);
+    }
+}
+
+void ACodec::trackReleasedFrame(int64_t frameId, int64_t mediaTimeUs, int64_t desiredRenderTimeNs) {
+    // If the render time is earlier than now, then we're suggesting it should be rendered ASAP,
+    // so track the frame as if the desired render time is now.
+    int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
+    if (desiredRenderTimeNs < nowNs) {
+        desiredRenderTimeNs = nowNs;
+    }
+    // We've just queued a frame to the surface, so keep track of it and later check to see if it is
+    // actually rendered.
+    TrackedFrame frame;
+    frame.id = frameId;
+    frame.mediaTimeUs = mediaTimeUs;
+    frame.desiredRenderTimeNs = desiredRenderTimeNs;
+    mTrackedFrames.push_back(frame);
+}
+
+void ACodec::pollForRenderedFrames() {
+    std::list<RenderedFrameInfo> renderedFrameInfos;
+    // Scan all frames and check to see if the frames that SHOULD have been rendered by now, have,
+    // in fact, been rendered.
+    int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
+    while (!mTrackedFrames.empty()) {
+        TrackedFrame & frame = mTrackedFrames.front();
+        // Frames that should have been rendered at least 100ms in the past are checked
+        if (frame.desiredRenderTimeNs > nowNs - 100*1000*1000LL) {
+            break;
+        }
+
+        status_t err;
+        nsecs_t latchOrPresentTimeNs = NATIVE_WINDOW_TIMESTAMP_INVALID;
+        err = native_window_get_frame_timestamps(mNativeWindow.get(), frame.id,
+                /* outRequestedPresentTime */ nullptr, /* outAcquireTime */ nullptr,
+                mHasPresentFenceTimes ? nullptr : &latchOrPresentTimeNs, // latch time
+                /* outFirstRefreshStartTime */ nullptr, /* outLastRefreshStartTime */ nullptr,
+                /* outGpuCompositionDoneTime */ nullptr,
+                mHasPresentFenceTimes ? &latchOrPresentTimeNs : nullptr, // display present time,
+                /* outDequeueReadyTime */ nullptr, /* outReleaseTime */ nullptr);
+        if (err) {
+            ALOGE("Failed to get frame timestamps for %lld: %d", (long long) frame.id, err);
+        }
+        // If we don't have a render time by now, then consider the frame as dropped
+        if (latchOrPresentTimeNs != NATIVE_WINDOW_TIMESTAMP_PENDING &&
+            latchOrPresentTimeNs != NATIVE_WINDOW_TIMESTAMP_INVALID) {
+            renderedFrameInfos.push_back(RenderedFrameInfo(frame.mediaTimeUs,
+                                                           latchOrPresentTimeNs));
+        }
+
+        mTrackedFrames.pop_front();
+    }
+
+    if (!renderedFrameInfos.empty()) {
+        mCallback->onOutputFramesRendered(renderedFrameInfos);
+    }
+}
+
 status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
     if (portIndex == kPortIndexInput) {
         mBufferChannel->setInputBufferArray({});
@@ -1663,11 +1706,6 @@
         ::close(info->mFenceFd);
     }
 
-    if (portIndex == kPortIndexOutput) {
-        mRenderTracker.untrackFrame(info->mRenderInfo, i);
-        info->mRenderInfo = NULL;
-    }
-
     // remove buffer even if mOMXNode->freeBuffer fails
     mBuffers[portIndex].erase(mBuffers[portIndex].begin() + i);
     return err;
@@ -6032,22 +6070,10 @@
     sp<RefBase> obj;
     CHECK(msg->findObject("messages", &obj));
     sp<MessageList> msgList = static_cast<MessageList *>(obj.get());
-
-    bool receivedRenderedEvents = false;
     for (std::list<sp<AMessage>>::const_iterator it = msgList->getList().cbegin();
           it != msgList->getList().cend(); ++it) {
         (*it)->setWhat(ACodec::kWhatOMXMessageItem);
         mCodec->handleMessage(*it);
-        int32_t type;
-        CHECK((*it)->findInt32("type", &type));
-        if (type == omx_message::FRAME_RENDERED) {
-            receivedRenderedEvents = true;
-        }
-    }
-
-    if (receivedRenderedEvents) {
-        // NOTE: all buffers are rendered in this case
-        mCodec->notifyOfRenderedFrames();
     }
     return true;
 }
@@ -6609,15 +6635,6 @@
     info->mDequeuedAt = ++mCodec->mDequeueCounter;
     info->mStatus = BufferInfo::OWNED_BY_US;
 
-    if (info->mRenderInfo != NULL) {
-        // The fence for an emptied buffer must have signaled, but there still could be queued
-        // or out-of-order dequeued buffers in the render queue prior to this buffer. Drop these,
-        // as we will soon requeue this buffer to the surface. While in theory we could still keep
-        // track of buffers that are requeued to the surface, it is better to add support to the
-        // buffer-queue to notify us of released buffers and their fences (in the future).
-        mCodec->notifyOfRenderedFrames(true /* dropIncomplete */);
-    }
-
     // byte buffers cannot take fences, so wait for any fence now
     if (mCodec->mNativeWindow == NULL) {
         (void)mCodec->waitForFence(fenceFd, "onOMXFillBufferDone");
@@ -6824,14 +6841,6 @@
             mCodec->mLastHdr10PlusBuffer = hdr10PlusInfo;
         }
 
-        // save buffers sent to the surface so we can get render time when they return
-        int64_t mediaTimeUs = -1;
-        buffer->meta()->findInt64("timeUs", &mediaTimeUs);
-        if (mediaTimeUs >= 0) {
-            mCodec->mRenderTracker.onFrameQueued(
-                    mediaTimeUs, info->mGraphicBuffer, new Fence(::dup(info->mFenceFd)));
-        }
-
         int64_t timestampNs = 0;
         if (!msg->findInt64("timestampNs", &timestampNs)) {
             // use media timestamp if client did not request a specific render timestamp
@@ -6845,11 +6854,25 @@
         err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs);
         ALOGW_IF(err != NO_ERROR, "failed to set buffer timestamp: %d", err);
 
+        uint64_t frameId;
+        err = native_window_get_next_frame_id(mCodec->mNativeWindow.get(), &frameId);
+
         info->checkReadFence("onOutputBufferDrained before queueBuffer");
         err = mCodec->mNativeWindow->queueBuffer(
                     mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);
-        // TODO(b/266211548): Poll the native window for rendered buffers, since when queueing
-        // buffers, the frame event history delta is retrieved.
+
+        int64_t mediaTimeUs = -1;
+        buffer->meta()->findInt64("timeUs", &mediaTimeUs);
+        if (mCodec->mIsWindowToDisplay) {
+            mCodec->trackReleasedFrame(frameId, mediaTimeUs, timestampNs);
+            mCodec->pollForRenderedFrames();
+        } else {
+            // When the surface is an intermediate surface, onFrameRendered is triggered immediately
+            // when the frame is queued to the non-display surface
+            mCodec->mCallback->onOutputFramesRendered({RenderedFrameInfo(mediaTimeUs,
+                                                                         timestampNs)});
+        }
+
         info->mFenceFd = -1;
         if (err == OK) {
             info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
@@ -7076,7 +7099,6 @@
     ++mCodec->mNodeGeneration;
 
     mCodec->mComponentName = componentName;
-    mCodec->mRenderTracker.setComponentName(componentName);
     mCodec->mFlags = 0;
 
     if (componentName.endsWith(".secure")) {
@@ -7713,7 +7735,6 @@
 
 void ACodec::ExecutingState::stateEntered() {
     ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
-    mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
     mCodec->processDeferredMessages();
 }
 
@@ -7824,7 +7845,15 @@
                     mCodec->signalSubmitOutputMetadataBufferIfEOS_workaround();
                 }
             }
-            return true;
+            handled = true;
+            break;
+        }
+
+        case kWhatPollForRenderedBuffers:
+        {
+            mCodec->pollForRenderedFrames();
+            handled = true;
+            break;
         }
 
         default:
@@ -8520,7 +8549,7 @@
 }
 
 bool ACodec::ExecutingState::onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) {
-    mCodec->onFrameRendered(mediaTimeUs, systemNano);
+    mCodec->mCallback->onOutputFramesRendered({RenderedFrameInfo(mediaTimeUs, systemNano)});
     return true;
 }
 
@@ -8694,7 +8723,7 @@
 
 bool ACodec::OutputPortSettingsChangedState::onOMXFrameRendered(
         int64_t mediaTimeUs, nsecs_t systemNano) {
-    mCodec->onFrameRendered(mediaTimeUs, systemNano);
+    mCodec->mCallback->onOutputFramesRendered({RenderedFrameInfo(mediaTimeUs, systemNano)});
     return true;
 }
 
@@ -8725,10 +8754,6 @@
                             OMX_CommandPortEnable, kPortIndexOutput);
                 }
 
-                // Clear the RenderQueue in which queued GraphicBuffers hold the
-                // actual buffer references in order to free them early.
-                mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
-
                 if (err == OK) {
                     err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
                     ALOGE_IF(err != OK, "Failed to allocate output port buffers after port "
@@ -9112,8 +9137,6 @@
         // the native window for rendering. Let's get those back as well.
         mCodec->waitUntilAllPossibleNativeWindowBuffersAreReturnedToUs();
 
-        mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
-
         mCodec->mCallback->onFlushCompleted();
 
         mCodec->mPortEOS[kPortIndexInput] =
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 8f2bed2..ad42813 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/ACodec.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/MediaCodecBuffer.h>
 #include <system/window.h>
@@ -87,9 +88,11 @@
 }
 
 ACodecBufferChannel::ACodecBufferChannel(
-        const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained)
+        const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained,
+        const sp<AMessage> &pollForRenderedBuffers)
     : mInputBufferFilled(inputBufferFilled),
       mOutputBufferDrained(outputBufferDrained),
+      mPollForRenderedBuffers(pollForRenderedBuffers),
       mHeapSeqNum(-1) {
 }
 
@@ -488,7 +491,7 @@
 }
 
 void ACodecBufferChannel::pollForRenderedBuffers() {
-    // TODO(b/266211548): Poll the native window for rendered buffers.
+    mPollForRenderedBuffers->post();
 }
 
 status_t ACodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 617a2cf..829ee6b 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -79,6 +79,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/RenderedFrameInfo.h>
 #include <media/stagefright/SurfaceUtils.h>
 #include <nativeloader/dlext_namespaces.h>
 #include <private/android_filesystem_config.h>
@@ -210,6 +211,7 @@
 // Render metrics
 static const char *kCodecPlaybackDurationSec = "android.media.mediacodec.playback-duration-sec";
 static const char *kCodecFirstRenderTimeUs = "android.media.mediacodec.first-render-time-us";
+static const char *kCodecLastRenderTimeUs = "android.media.mediacodec.last-render-time-us";
 static const char *kCodecFramesReleased = "android.media.mediacodec.frames-released";
 static const char *kCodecFramesRendered = "android.media.mediacodec.frames-rendered";
 static const char *kCodecFramesDropped = "android.media.mediacodec.frames-dropped";
@@ -911,7 +913,7 @@
             const sp<AMessage> &outputFormat) override;
     virtual void onInputSurfaceDeclined(status_t err) override;
     virtual void onSignaledInputEOS(status_t err) override;
-    virtual void onOutputFramesRendered(const std::list<FrameRenderTracker::Info> &done) override;
+    virtual void onOutputFramesRendered(const std::list<RenderedFrameInfo> &done) override;
     virtual void onOutputBuffersChanged() override;
     virtual void onFirstTunnelFrameReady() override;
     virtual void onMetricsUpdated(const sp<AMessage> &updatedMetrics) override;
@@ -1021,7 +1023,7 @@
     notify->post();
 }
 
-void CodecCallback::onOutputFramesRendered(const std::list<FrameRenderTracker::Info> &done) {
+void CodecCallback::onOutputFramesRendered(const std::list<RenderedFrameInfo> &done) {
     sp<AMessage> notify(mNotify->dup());
     notify->setInt32("what", kWhatOutputFramesRendered);
     if (MediaCodec::CreateFramesRenderedMessage(done, notify)) {
@@ -1344,6 +1346,7 @@
         const VideoRenderQualityMetrics &m = mVideoRenderQualityTracker.getMetrics();
         if (m.frameReleasedCount > 0) {
             mediametrics_setInt64(mMetricsHandle, kCodecFirstRenderTimeUs, m.firstRenderTimeUs);
+            mediametrics_setInt64(mMetricsHandle, kCodecLastRenderTimeUs, m.lastRenderTimeUs);
             mediametrics_setInt64(mMetricsHandle, kCodecFramesReleased, m.frameReleasedCount);
             mediametrics_setInt64(mMetricsHandle, kCodecFramesRendered, m.frameRenderedCount);
             mediametrics_setInt64(mMetricsHandle, kCodecFramesSkipped, m.frameSkippedCount);
@@ -6186,12 +6189,10 @@
     return onQueueInputBuffer(msg);
 }
 
-//static
-size_t MediaCodec::CreateFramesRenderedMessage(
-        const std::list<FrameRenderTracker::Info> &done, sp<AMessage> &msg) {
+template<typename T>
+static size_t CreateFramesRenderedMessageInternal(const std::list<T> &done, sp<AMessage> &msg) {
     size_t index = 0;
-    for (std::list<FrameRenderTracker::Info>::const_iterator it = done.cbegin();
-            it != done.cend(); ++it) {
+    for (typename std::list<T>::const_iterator it = done.cbegin(); it != done.cend(); ++it) {
         if (it->getRenderTimeNs() < 0) {
             continue; // dropped frame from tracking
         }
@@ -6202,6 +6203,18 @@
     return index;
 }
 
+//static
+size_t MediaCodec::CreateFramesRenderedMessage(
+        const std::list<RenderedFrameInfo> &done, sp<AMessage> &msg) {
+    return CreateFramesRenderedMessageInternal(done, msg);
+}
+
+//static
+size_t MediaCodec::CreateFramesRenderedMessage(
+        const std::list<FrameRenderTracker::Info> &done, sp<AMessage> &msg) {
+    return CreateFramesRenderedMessageInternal(done, msg);
+}
+
 status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
     size_t index;
     CHECK(msg->findSize("index", &index));
@@ -6293,7 +6306,9 @@
             // presentation timestamp is used instead, which almost certainly occurs in the past,
             // since it's almost always a zero-based offset from the start of the stream. In these
             // scenarios, we expect the frame to be rendered with no delay.
-            int64_t delayUs = noRenderTime ? 0 : renderTimeNs / 1000 - ALooper::GetNowUs();
+            int64_t nowUs = ALooper::GetNowUs();
+            int64_t renderTimeUs = renderTimeNs / 1000;
+            int64_t delayUs = renderTimeUs < nowUs ? 0 : renderTimeUs - nowUs;
             delayUs += 100 * 1000; /* 100ms in microseconds */
             status_t err =
                     mMsgPollForRenderedBuffers->postUnique(/* token= */ mMsgPollForRenderedBuffers,
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index fbd8577..90d5405 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -15,7 +15,11 @@
  */
 
 #define LOG_TAG "VideoRenderQualityTracker"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
+
 #include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/Mutex.h>
 
 #include <media/stagefright/VideoRenderQualityTracker.h>
 
@@ -25,8 +29,10 @@
 #include <stdio.h>
 #include <sys/time.h>
 
+#include <android-base/macros.h>
 #include <android-base/parsebool.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 
 namespace android {
 
@@ -38,6 +44,7 @@
 
 typedef VideoRenderQualityTracker::Configuration::GetServerConfigurableFlagFn
         GetServerConfigurableFlagFn;
+typedef VideoRenderQualityTracker::TraceTriggerFn TraceTriggerFn;
 
 static void getServerConfigurableFlag(GetServerConfigurableFlagFn getServerConfigurableFlagFn,
                                       char const *flagNameSuffix, bool *value) {
@@ -149,6 +156,10 @@
     getFlag(judderEventMax, "judder_event_max");
     getFlag(judderEventDetailsMax, "judder_event_details_max");
     getFlag(judderEventDistanceToleranceMs, "judder_event_distance_tolerance_ms");
+    getFlag(traceTriggerEnabled, "trace_trigger_enabled");
+    getFlag(traceTriggerThrottleMs, "trace_trigger_throttle_ms");
+    getFlag(traceMinFreezeDurationMs, "trace_minimum_freeze_duration_ms");
+    getFlag(traceMaxFreezeDurationMs, "trace_maximum_freeze_duration_ms");
 #undef getFlag
     return c;
 }
@@ -186,15 +197,25 @@
     judderEventMax = 0; // enabled only when debugging
     judderEventDetailsMax = 20;
     judderEventDistanceToleranceMs = 5000; // lump judder occurrences together when 5s or less
+
+    // Perfetto trigger configuration.
+    traceTriggerEnabled = android::base::GetProperty(
+        "ro.build.type", "user") != "user"; // Enabled for non-user builds for debugging.
+    traceTriggerThrottleMs = 5 * 60 * 1000; // 5 mins.
+    traceMinFreezeDurationMs = 400;
+    traceMaxFreezeDurationMs = 1500;
 }
 
-VideoRenderQualityTracker::VideoRenderQualityTracker() : mConfiguration(Configuration()) {
+VideoRenderQualityTracker::VideoRenderQualityTracker()
+    : mConfiguration(Configuration()), mTraceTriggerFn(triggerTrace) {
     configureHistograms(mMetrics, mConfiguration);
     clear();
 }
 
-VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration) :
-        mConfiguration(configuration) {
+VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration,
+                                                     const TraceTriggerFn traceTriggerFn)
+    : mConfiguration(configuration),
+      mTraceTriggerFn(traceTriggerFn == nullptr ? triggerTrace : traceTriggerFn) {
     configureHistograms(mMetrics, mConfiguration);
     clear();
 }
@@ -231,6 +252,11 @@
 
     resetIfDiscontinuity(contentTimeUs, -1);
 
+    if (mTraceFrameSkippedToken == -1) {
+       mTraceFrameSkippedToken = contentTimeUs;
+       ATRACE_ASYNC_BEGIN("Video frame(s) skipped", mTraceFrameSkippedToken);
+    }
+
     // Frames skipped at the end of playback shouldn't be counted as skipped frames, since the
     // app could be terminating the playback. The pending count will be added to the metrics if and
     // when the next frame is rendered.
@@ -261,11 +287,25 @@
         return;
     }
 
+    if (mTraceFrameSkippedToken != -1) {
+        ATRACE_ASYNC_END("Video frame(s) skipped", mTraceFrameSkippedToken);
+        mTraceFrameSkippedToken = -1;
+    }
+
     int64_t actualRenderTimeUs = actualRenderTimeNs / 1000;
 
     if (mLastRenderTimeUs != -1) {
-        mRenderDurationMs += (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
+        int64_t frameRenderDurationMs = (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
+        mRenderDurationMs += frameRenderDurationMs;
+        if (mConfiguration.traceTriggerEnabled
+            // Threshold for visible video freeze.
+            && frameRenderDurationMs >= mConfiguration.traceMinFreezeDurationMs
+            // Threshold for removing long render durations which could be video pause.
+            && frameRenderDurationMs < mConfiguration.traceMaxFreezeDurationMs) {
+            triggerTraceWithThrottle(mTraceTriggerFn, mConfiguration, actualRenderTimeUs);
+        }
     }
+
     // Now that a frame has been rendered, the previously skipped frames can be processed as skipped
     // frames since the app is not skipping them to terminate playback.
     for (int64_t contentTimeUs : mPendingSkippedFrameContentTimeUsList) {
@@ -455,6 +495,8 @@
     if (mMetrics.firstRenderTimeUs == 0) {
         mMetrics.firstRenderTimeUs = actualRenderTimeUs;
     }
+    // Capture the timestamp at which the last frame was rendered
+    mMetrics.lastRenderTimeUs = actualRenderTimeUs;
 
     mMetrics.frameRenderedCount++;
 
@@ -736,4 +778,42 @@
     return false;
 }
 
+void VideoRenderQualityTracker::triggerTraceWithThrottle(const TraceTriggerFn traceTriggerFn,
+                                                         const Configuration &c,
+                                                         const int64_t triggerTimeUs) {
+    static int64_t lastTriggerUs = -1;
+    static Mutex updateLastTriggerLock;
+
+    Mutex::Autolock autoLock(updateLastTriggerLock);
+    if (lastTriggerUs != -1) {
+        int32_t sinceLastTriggerMs = int32_t((triggerTimeUs - lastTriggerUs) / 1000);
+        // Throttle the trace trigger calls to reduce continuous PID fork calls in a short time
+        // to impact device performance, and reduce spamming trace reports.
+        if (sinceLastTriggerMs < c.traceTriggerThrottleMs) {
+            ALOGI("Not triggering trace - not enough time since last trigger");
+            return;
+        }
+    }
+    lastTriggerUs = triggerTimeUs;
+    (*traceTriggerFn)();
+}
+
+void VideoRenderQualityTracker::triggerTrace() {
+    // Trigger perfetto to stop always-on-tracing (AOT) to collect trace into a file for video
+    // freeze event, the collected trace categories are configured by AOT.
+    const char* args[] = {"/system/bin/trigger_perfetto", "com.android.codec-video-freeze", NULL};
+    pid_t pid = fork();
+    if (pid < 0) {
+        ALOGI("Failed to fork for triggering trace");
+        return;
+    }
+    if (pid == 0) {
+        // child process.
+        execvp(args[0], const_cast<char**>(args));
+        ALOGW("Failed to trigger trace %s", args[1]);
+        _exit(1);
+    }
+    ALOGI("Triggered trace %s", args[1]);
+}
+
 } // namespace android
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index f92a945..946d533 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -29,6 +29,7 @@
 #include <media/IOMX.h>
 
 namespace android {
+ struct ACodec;
 namespace hardware {
 class HidlMemory;
 };
@@ -63,7 +64,8 @@
     };
 
     ACodecBufferChannel(
-            const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained);
+            const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained,
+            const sp<AMessage> &pollForRenderedBuffers);
     virtual ~ACodecBufferChannel();
 
     // BufferChannelBase interface
@@ -138,6 +140,7 @@
 
     const sp<AMessage> mInputBufferFilled;
     const sp<AMessage> mOutputBufferDrained;
+    const sp<AMessage> mPollForRenderedBuffers;
 
     sp<MemoryDealer> mDealer;
     sp<IMemory> mDecryptDestination;
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 232cf6b..2a5c168 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -19,7 +19,7 @@
 
 #include <set>
 #include <stdint.h>
-#include <list>
+#include <deque>
 #include <vector>
 #include <android/native_window.h>
 #include <media/hardware/MetadataBufferType.h>
@@ -27,9 +27,9 @@
 #include <media/IOMX.h>
 #include <media/stagefright/AHierarchicalStateMachine.h>
 #include <media/stagefright/CodecBase.h>
-#include <media/stagefright/FrameRenderTracker.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/SkipCutBuffer.h>
+#include <ui/GraphicBuffer.h>
 #include <utils/NativeHandle.h>
 #include <OMX_Audio.h>
 #include <hardware/gralloc.h>
@@ -156,6 +156,7 @@
         kWhatForceStateTransition    = 'fstt',
         kWhatCheckIfStuck            = 'Cstk',
         kWhatSubmitExtraOutputMetadataBuffer = 'sbxo',
+        kWhatPollForRenderedBuffers  = 'pfrb',
     };
 
     enum {
@@ -177,6 +178,13 @@
                             | static_cast<uint64_t>(BufferUsage::VIDEO_DECODER),
     };
 
+    struct TrackedFrame {
+        int64_t id;
+        int64_t mediaTimeUs;
+        int64_t desiredRenderTimeNs;
+        nsecs_t renderTimeNs;
+    };
+
     struct BufferInfo {
         enum Status {
             OWNED_BY_US,
@@ -204,7 +212,6 @@
         sp<GraphicBuffer> mGraphicBuffer;
         bool mNewGraphicBuffer;
         int mFenceFd;
-        FrameRenderTracker::Info *mRenderInfo;
 
         // The following field and 4 methods are used for debugging only
         bool mIsReadFence;
@@ -253,6 +260,8 @@
 
     bool mUsingNativeWindow;
     sp<ANativeWindow> mNativeWindow;
+    bool mIsWindowToDisplay;
+    bool mHasPresentFenceTimes;
     int mNativeWindowUsageBits;
     android_native_rect_t mLastNativeWindowCrop;
     int32_t mLastNativeWindowDataSpace;
@@ -267,7 +276,7 @@
     // format updates. This will equal to mOutputFormat until the first actual frame is received.
     sp<AMessage> mBaseOutputFormat;
 
-    FrameRenderTracker mRenderTracker; // render information for buffers rendered by ACodec
+    std::deque<TrackedFrame> mTrackedFrames; // render information for buffers sent to a window
     std::vector<BufferInfo> mBuffers[2];
     bool mPortEOS[2];
     status_t mInputEOSResult;
@@ -349,6 +358,10 @@
     status_t freeOutputBuffersNotOwnedByComponent();
     BufferInfo *dequeueBufferFromNativeWindow();
 
+    void initializeFrameTracking();
+    void trackReleasedFrame(int64_t frameId, int64_t mediaTimeUs, int64_t desiredRenderTimeNs);
+    void pollForRenderedFrames();
+
     inline bool storingMetadataInDecodedBuffers() {
         return (mPortMode[kPortIndexOutput] == IOMX::kPortModeDynamicANWBuffer) && !mIsEncoder;
     }
@@ -571,21 +584,6 @@
     void processDeferredMessages();
 
     void onFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano);
-    // called when we have dequeued a buffer |buf| from the native window to track render info.
-    // |fenceFd| is the dequeue fence, and |info| points to the buffer info where this buffer is
-    // stored.
-    void updateRenderInfoForDequeuedBuffer(
-            ANativeWindowBuffer *buf, int fenceFd, BufferInfo *info);
-
-    // Checks to see if any frames have rendered up until |until|, and to notify client
-    // (MediaCodec) of rendered frames up-until the frame pointed to by |until| or the first
-    // unrendered frame. These frames are removed from the render queue.
-    // If |dropIncomplete| is true, unrendered frames up-until |until| will be dropped from the
-    // queue, allowing all rendered framed up till then to be notified of.
-    // (This will effectively clear the render queue up-until (and including) |until|.)
-    // If |until| is NULL, or is not in the rendered queue, this method will check all frames.
-    void notifyOfRenderedFrames(
-            bool dropIncomplete = false, FrameRenderTracker::Info *until = NULL);
 
     void onFirstTunnelFrameReady();
 
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 9b5b93c..0927653 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -41,7 +41,7 @@
 struct BufferProducerWrapper;
 class MediaCodecBuffer;
 struct PersistentSurface;
-struct RenderedFrameInfo;
+class RenderedFrameInfo;
 class Surface;
 struct ICrypto;
 class IMemory;
diff --git a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
index c14755a..cab7ecc 100644
--- a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
+++ b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
@@ -32,61 +32,59 @@
 
 namespace android {
 
-// Tracks the render information about a frame. Frames go through several states while
-// the render information is tracked:
-//
-// 1. queued frame: mMediaTime and mGraphicBuffer are set for the frame. mFence is the
-// queue fence (read fence). mIndex is negative, and mRenderTimeNs is invalid.
-// Key characteristics: mFence is not NULL and mIndex is negative.
-//
-// 2. dequeued frame: mFence is updated with the dequeue fence (write fence). mIndex is set.
-// Key characteristics: mFence is not NULL and mIndex is non-negative. mRenderTime is still
-// invalid.
-//
-// 3. rendered frame or frame: mFence is cleared, mRenderTimeNs is set.
-// Key characteristics: mFence is NULL.
-//
-struct RenderedFrameInfo {
-    // set by client during onFrameQueued or onFrameRendered
-    int64_t getMediaTimeUs() const  { return mMediaTimeUs; }
-
-    // -1 if frame is not yet rendered
-    nsecs_t getRenderTimeNs() const { return mRenderTimeNs; }
-
-    // set by client during updateRenderInfoForDequeuedBuffer; -1 otherwise
-    ssize_t getIndex() const        { return mIndex; }
-
-    // creates information for a queued frame
-    RenderedFrameInfo(int64_t mediaTimeUs, const sp<GraphicBuffer> &graphicBuffer,
-            const sp<Fence> &fence)
-        : mMediaTimeUs(mediaTimeUs),
-          mRenderTimeNs(-1),
-          mIndex(-1),
-          mGraphicBuffer(graphicBuffer),
-          mFence(fence) {
-    }
-
-    // creates information for a frame rendered on a tunneled surface
-    RenderedFrameInfo(int64_t mediaTimeUs, nsecs_t renderTimeNs)
-        : mMediaTimeUs(mediaTimeUs),
-          mRenderTimeNs(renderTimeNs),
-          mIndex(-1),
-          mGraphicBuffer(NULL),
-          mFence(NULL) {
-    }
-
-private:
-    int64_t mMediaTimeUs;
-    nsecs_t mRenderTimeNs;
-    ssize_t mIndex;         // to be used by client
-    sp<GraphicBuffer> mGraphicBuffer;
-    sp<Fence> mFence;
-
-    friend struct FrameRenderTracker;
-};
-
 struct FrameRenderTracker {
-    typedef RenderedFrameInfo Info;
+    // Tracks the render information about a frame. Frames go through several states while
+    // the render information is tracked:
+    //
+    // 1. queued frame: mMediaTime and mGraphicBuffer are set for the frame. mFence is the
+    // queue fence (read fence). mIndex is negative, and mRenderTimeNs is invalid.
+    // Key characteristics: mFence is not NULL and mIndex is negative.
+    //
+    // 2. dequeued frame: mFence is updated with the dequeue fence (write fence). mIndex is set.
+    // Key characteristics: mFence is not NULL and mIndex is non-negative. mRenderTime is still
+    // invalid.
+    //
+    // 3. rendered frame or frame: mFence is cleared, mRenderTimeNs is set.
+    // Key characteristics: mFence is NULL.
+    //
+    struct Info {
+        // set by client during onFrameQueued or onFrameRendered
+        int64_t getMediaTimeUs() const  { return mMediaTimeUs; }
+
+        // -1 if frame is not yet rendered
+        nsecs_t getRenderTimeNs() const { return mRenderTimeNs; }
+
+        // set by client during updateRenderInfoForDequeuedBuffer; -1 otherwise
+        ssize_t getIndex() const        { return mIndex; }
+
+        // creates information for a queued frame
+        Info(int64_t mediaTimeUs, const sp<GraphicBuffer> &graphicBuffer,
+                const sp<Fence> &fence)
+          : mMediaTimeUs(mediaTimeUs),
+            mRenderTimeNs(-1),
+            mIndex(-1),
+            mGraphicBuffer(graphicBuffer),
+            mFence(fence) {
+        }
+
+        // creates information for a frame rendered on a tunneled surface
+        Info(int64_t mediaTimeUs, nsecs_t renderTimeNs)
+            : mMediaTimeUs(mediaTimeUs),
+            mRenderTimeNs(renderTimeNs),
+            mIndex(-1),
+            mGraphicBuffer(NULL),
+            mFence(NULL) {
+        }
+
+    private:
+        int64_t mMediaTimeUs;
+        nsecs_t mRenderTimeNs;
+        ssize_t mIndex;         // to be used by client
+        sp<GraphicBuffer> mGraphicBuffer;
+        sp<Fence> mFence;
+
+        friend struct FrameRenderTracker;
+    };
 
     FrameRenderTracker();
 
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 009680a..f99a78b 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -64,6 +64,7 @@
 class MediaCodecBuffer;
 class IMemory;
 struct PersistentSurface;
+class RenderedFrameInfo;
 class SoftwareRenderer;
 class Surface;
 namespace hardware {
@@ -281,6 +282,8 @@
     // by adding rendered frame information to a base notification message. Returns the number
     // of frames that were rendered.
     static size_t CreateFramesRenderedMessage(
+            const std::list<RenderedFrameInfo> &done, sp<AMessage> &msg);
+    static size_t CreateFramesRenderedMessage(
             const std::list<FrameRenderTracker::Info> &done, sp<AMessage> &msg);
 
     static status_t CanFetchLinearBlock(
diff --git a/media/libstagefright/include/media/stagefright/RenderedFrameInfo.h b/media/libstagefright/include/media/stagefright/RenderedFrameInfo.h
new file mode 100644
index 0000000..4b8a58d
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/RenderedFrameInfo.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef RENDERED_FRAME_INFO_H
+#define RENDERED_FRAME_INFO_H
+
+namespace android {
+
+class RenderedFrameInfo {
+public:
+    RenderedFrameInfo(int64_t mediaTimeUs, int64_t renderTimeNs)
+        : mMediaTimeUs(mediaTimeUs), mRenderTimeNs(renderTimeNs) {}
+
+    int64_t getMediaTimeUs() const  { return mMediaTimeUs; }
+    nsecs_t getRenderTimeNs() const { return mRenderTimeNs;}
+
+private:
+    int64_t mMediaTimeUs;
+    nsecs_t mRenderTimeNs;
+};
+
+} // android
+
+#endif // RENDERED_FRAME_INFO_H
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
index 82ba81c..cf53f27 100644
--- a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
+++ b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
@@ -38,6 +38,9 @@
     // The render time of the first video frame.
     int64_t firstRenderTimeUs;
 
+    // The render time of the last video frame.
+    int64_t lastRenderTimeUs;
+
     // The number of frames released to be rendered.
     int64_t frameReleasedCount;
 
@@ -197,6 +200,21 @@
         // The maximum distance in time between two judder occurrences such that both will be
         // lumped into the same judder event.
         int32_t judderEventDistanceToleranceMs;
+        //
+        // Whether or not Perfetto trace trigger is enabled.
+        bool traceTriggerEnabled;
+        //
+        // The throttle time for Perfetto trace trigger to avoid triggering multiple traces for
+        // the same event in a short time.
+        int32_t traceTriggerThrottleMs;
+        //
+        // The minimum frame render duration to recognize video freeze event to collect trace.
+        int32_t traceMinFreezeDurationMs;
+        //
+        // The maximum frame render duration to recognize video freeze event. A frame render
+        // duration that is larger than the max duration would not trigger trace collection for
+        // video freeze because it's highly possible a video pause.
+        int32_t traceMaxFreezeDurationMs;
     };
 
     struct FreezeEvent {
@@ -253,8 +271,11 @@
         Details details;
     };
 
+    typedef void (*TraceTriggerFn)();
+
     VideoRenderQualityTracker();
-    VideoRenderQualityTracker(const Configuration &configuration);
+    VideoRenderQualityTracker(const Configuration &configuration,
+                              const TraceTriggerFn traceTriggerFn = nullptr);
 
     // Called when a tunnel mode frame has been queued.
     void onTunnelFrameQueued(int64_t contentTimeUs);
@@ -373,6 +394,14 @@
                                         JudderEvent &e, const VideoRenderQualityMetrics & m,
                                         const Configuration &c, JudderEvent *judderEventOut);
 
+    // Trigger trace collection for video freeze.
+    static void triggerTrace();
+
+    // Trigger collection of a Perfetto Always-On-Tracing (AOT) trace file for video freeze,
+    // triggerTimeUs is used as a throttle to avoid triggering multiple traces in a short time.
+    static void triggerTraceWithThrottle(TraceTriggerFn traceTriggerFn,
+                                         const Configuration &c, const int64_t triggerTimeUs);
+
     // Check to see if a discontinuity has occurred by examining the content time and the
     // app-desired render time. If so, reset some internal state.
     bool resetIfDiscontinuity(int64_t contentTimeUs, int64_t desiredRenderTimeUs);
@@ -391,6 +420,9 @@
     // Configurable elements of the metrics algorithms.
     const Configuration mConfiguration;
 
+    // The function for triggering trace collection for video freeze.
+    const TraceTriggerFn mTraceTriggerFn;
+
     // Metrics are updated every time a frame event occurs - skipped, dropped, rendered.
     VideoRenderQualityMetrics mMetrics;
 
@@ -442,6 +474,9 @@
     // Frame durations derived from timestamps captured by the display subsystem, indicating the
     // wall clock atime at which the frame is actually rendered.
     FrameDurationUs mActualFrameDurationUs;
+
+    // Token of async atrace for video frame dropped/skipped by the app.
+    int64_t mTraceFrameSkippedToken= -1;
 };
 
 }  // namespace android
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index ee76a67..78140dd 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -36,10 +36,11 @@
 class Helper {
 public:
     Helper(double contentFrameDurationMs, const Configuration &configuration) :
-            mVideoRenderQualityTracker(configuration) {
+            mVideoRenderQualityTracker(configuration, testTraceTrigger) {
         mContentFrameDurationUs = int64_t(contentFrameDurationMs * 1000);
         mMediaTimeUs = 0;
         mClockTimeNs = 0;
+        sTraceTriggeredCount = 0;
     }
 
     void changeContentFrameDuration(double contentFrameDurationMs) {
@@ -100,6 +101,10 @@
         return e;
     }
 
+    int getTraceTriggeredCount() {
+        return sTraceTriggeredCount;
+    }
+
 private:
     VideoRenderQualityTracker mVideoRenderQualityTracker;
     int64_t mContentFrameDurationUs;
@@ -107,8 +112,16 @@
     int64_t mClockTimeNs;
     VideoRenderQualityTracker::FreezeEvent mFreezeEvent;
     VideoRenderQualityTracker::JudderEvent mJudderEvent;
+
+    static int sTraceTriggeredCount;
+
+    static void testTraceTrigger() {
+        sTraceTriggeredCount++;
+    };
 };
 
+int Helper::sTraceTriggeredCount = 0;
+
 class VideoRenderQualityTrackerTest : public ::testing::Test {
 public:
     VideoRenderQualityTrackerTest() {}
@@ -139,6 +152,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withEmpty) {
@@ -166,6 +183,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withInvalid) {
@@ -193,6 +214,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withAlmostValid) {
@@ -232,6 +257,14 @@
                 return "10*10";
             } else if (flag == "render_metrics_judder_event_distance_tolerance_ms") {
                 return "140-a";
+            } else if (flag == "render_metrics_trace_trigger_enabled") {
+                return "fals";
+            } else if (flag == "render_metrics_trace_trigger_throttle_ms") {
+                return "12345678901234";
+            } else if (flag == "render_metrics_trace_minimum_freeze_duration_ms") {
+                return "10b0";
+            } else if (flag == "render_metrics_trace_maximum_freeze_duration_ms") {
+                return "100a";
             }
             return "";
         }
@@ -255,6 +288,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withValid) {
@@ -294,6 +331,14 @@
                 return "10000";
             } else if (flag == "render_metrics_judder_event_distance_tolerance_ms") {
                 return "11000";
+            } else if (flag == "render_metrics_trace_trigger_enabled") {
+                return "true";
+            } else if (flag == "render_metrics_trace_trigger_throttle_ms") {
+                return "50000";
+            } else if (flag == "render_metrics_trace_minimum_freeze_duration_ms") {
+                return "1000";
+            } else if (flag == "render_metrics_trace_maximum_freeze_duration_ms") {
+                return "5000";
             }
             return "";
         }
@@ -353,6 +398,11 @@
     EXPECT_NE(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, 11000);
     EXPECT_NE(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+
+    EXPECT_EQ(c.traceTriggerEnabled, true);
+    EXPECT_EQ(c.traceTriggerThrottleMs, 50000);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, 1000);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, 5000);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, countsReleasedFrames) {
@@ -1060,4 +1110,75 @@
     EXPECT_EQ(h.getMetrics().judderScore, 10 + 300 + 2000);
 }
 
+TEST_F(VideoRenderQualityTrackerTest,
+       freezesForTraceDuration_withThrottle_throttlesTraceTrigger) {
+    Configuration c;
+    c.enabled = true;
+    c.traceTriggerEnabled = true; // The trigger is enabled, so traces should be triggered.
+    // The value of traceTriggerThrottleMs must be larger than traceMinFreezeDurationMs. Otherwise,
+    // the throttle does work.
+    c.traceTriggerThrottleMs = 200;
+    c.traceMinFreezeDurationMs = 40;
+    int32_t freeze = c.traceMinFreezeDurationMs;
+
+    Helper h(20, c);
+    // Freeze triggers separated by 80ms which is less than the threshold.
+    h.render({
+        freeze, // Freeze duration does not check trace trigger.
+        20,     // Trace triggered.
+        20,     // Throttle time:  20/200ms
+        20,     // Throttle time:  40/200ms
+        freeze, // Throttle time:  80/200ms
+        20,     // Throttle time: 100/200ms (Trace not triggered)
+    });
+    EXPECT_EQ(h.getTraceTriggeredCount(), 1);
+    // Next freeze trigger is separated by 200ms which breaks the throttle threshold.
+    h.render({
+        20,     // Throttle time: 120/200ms
+        20,     // Throttle time: 140/200ms
+        20,     // Throttle time: 160/200ms
+        freeze, // Throttle time: 200/200ms
+        20,     // Trace triggered.
+    });
+    EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+    // Next freeze trigger is separated by 80ms which is less than the threshold.
+    h.render({
+        20,     // Throttle time:  20/200ms
+        20,     // Throttle time:  40/200ms
+        freeze, // Throttle time:  80/200ms
+        20,     // Throttle time: 100/200ms (Trace not triggered)
+    });
+    EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+}
+
+TEST_F(VideoRenderQualityTrackerTest, freezeForTraceDuration_triggersTrace) {
+    Configuration c;
+    c.enabled = true;
+    c.traceTriggerEnabled = true; // The trigger is enabled, so traces should be triggered.
+    c.traceTriggerThrottleMs = 0; // Disable throttle in the test case.
+    int32_t freeze1 = c.traceMinFreezeDurationMs;
+    int32_t freeze2 = c.traceMaxFreezeDurationMs - 1;
+    int32_t couldBeAPause = c.traceMaxFreezeDurationMs + 1;
+
+    Helper h(20, c);
+    h.render({freeze1, 20, freeze2, 20, couldBeAPause, 20});
+
+    EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+}
+
+TEST_F(VideoRenderQualityTrackerTest,
+       freezeForTraceDuration_withTraceDisabled_doesNotTriggerTrace) {
+    Configuration c;
+    c.enabled = true;
+    c.traceTriggerEnabled = false; // The trigger is disabled, so no traces should be triggered.
+    c.traceTriggerThrottleMs = 0; // Disable throttle in the test case.
+    int32_t freeze1 = c.traceMinFreezeDurationMs;
+    int32_t freeze2 = c.traceMaxFreezeDurationMs - 1;
+    int32_t couldBeAPause = c.traceMaxFreezeDurationMs + 1;
+
+    Helper h(20, c);
+    h.render({freeze1, 20, freeze2, 20, couldBeAPause, 20});
+
+    EXPECT_EQ(h.getTraceTriggeredCount(), 0);
+}
 } // android