Merge "Restrict unsigned integer overflow in MPEG4Writer writeEdtsBox" into main
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index 90d5405..3bd236a 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -126,6 +126,7 @@
     contentFrameRate = FRAME_RATE_UNDETERMINED;
     desiredFrameRate = FRAME_RATE_UNDETERMINED;
     actualFrameRate = FRAME_RATE_UNDETERMINED;
+    maxContentDroppedAfterPauseMs = 0;
     freezeEventCount = 0;
     freezeDurationMsHistogram.clear();
     freezeDistanceMsHistogram.clear();
@@ -144,6 +145,7 @@
     getFlag(maxExpectedContentFrameDurationUs, "max_expected_content_frame_duration_us");
     getFlag(frameRateDetectionToleranceUs, "frame_rate_detection_tolerance_us");
     getFlag(liveContentFrameDropToleranceUs, "live_content_frame_drop_tolerance_us");
+    getFlag(pauseAudioLatencyUs, "pause_audio_latency_us");
     getFlag(freezeDurationMsHistogramBuckets, "freeze_duration_ms_histogram_buckets");
     getFlag(freezeDurationMsHistogramToScore, "freeze_duration_ms_histogram_to_score");
     getFlag(freezeDistanceMsHistogramBuckets, "freeze_distance_ms_histogram_buckets");
@@ -181,6 +183,9 @@
     // because of frame drops for live content, or because the user is seeking.
     liveContentFrameDropToleranceUs = 200 * 1000;
 
+    // After a pause is initiated, audio should likely stop playback within 200ms.
+    pauseAudioLatencyUs = 200 * 1000;
+
     // Freeze configuration
     freezeDurationMsHistogramBuckets = {1, 20, 40, 60, 80, 100, 120, 150, 175, 225, 300, 400, 500};
     freezeDurationMsHistogramToScore = {1,  1,  1,  1,  1,   1,   1,   1,   1,   1,   1,   1,   1};
@@ -397,13 +402,13 @@
     mLastRenderTimeUs = -1;
     mLastFreezeEndTimeUs = -1;
     mLastJudderEndTimeUs = -1;
-    mWasPreviousFrameDropped = false;
+    mDroppedContentDurationUs = 0;
     mFreezeEvent.valid = false;
     mJudderEvent.valid = false;
 
-    // Don't worry about tracking frame rendering times from now up until playback catches up to the
-    // discontinuity. While stuttering or freezing could be found in the next few frames, the impact
-    // to the user is is minimal, so better to just keep things simple and don't bother.
+    // Don't worry about tracking frame rendering times from now up until playback catches up to
+    // the discontinuity. While stuttering or freezing could be found in the next few frames, the
+    // impact to the user is is minimal, so better to just keep things simple and don't bother.
     mNextExpectedRenderedFrameQueue = {};
     mTunnelFrameQueuedContentTimeUs = -1;
 
@@ -472,7 +477,7 @@
     updateFrameDurations(mDesiredFrameDurationUs, -1);
     updateFrameDurations(mActualFrameDurationUs, -1);
     updateFrameRate(mMetrics.contentFrameRate, mContentFrameDurationUs, mConfiguration);
-    mWasPreviousFrameDropped = false;
+    mDroppedContentDurationUs = 0;
 }
 
 void VideoRenderQualityTracker::processMetricsForDroppedFrame(int64_t contentTimeUs,
@@ -483,7 +488,9 @@
     updateFrameDurations(mActualFrameDurationUs, -1);
     updateFrameRate(mMetrics.contentFrameRate, mContentFrameDurationUs, mConfiguration);
     updateFrameRate(mMetrics.desiredFrameRate, mDesiredFrameDurationUs, mConfiguration);
-    mWasPreviousFrameDropped = true;
+    if (mContentFrameDurationUs[0] != -1) {
+        mDroppedContentDurationUs += mContentFrameDurationUs[0];
+    }
 }
 
 void VideoRenderQualityTracker::processMetricsForRenderedFrame(int64_t contentTimeUs,
@@ -491,6 +498,8 @@
                                                                int64_t actualRenderTimeUs,
                                                                FreezeEvent *freezeEventOut,
                                                                JudderEvent *judderEventOut) {
+    const Configuration& c = mConfiguration;
+
     // Capture the timestamp at which the first frame was rendered
     if (mMetrics.firstRenderTimeUs == 0) {
         mMetrics.firstRenderTimeUs = actualRenderTimeUs;
@@ -513,11 +522,36 @@
     updateFrameRate(mMetrics.desiredFrameRate, mDesiredFrameDurationUs, mConfiguration);
     updateFrameRate(mMetrics.actualFrameRate, mActualFrameDurationUs, mConfiguration);
 
-    // If the previous frame was dropped, there was a freeze if we've already rendered a frame
-    if (mWasPreviousFrameDropped && mLastRenderTimeUs != -1) {
-        processFreeze(actualRenderTimeUs, mLastRenderTimeUs, mLastFreezeEndTimeUs, mFreezeEvent,
-                      mMetrics, mConfiguration);
-        mLastFreezeEndTimeUs = actualRenderTimeUs;
+    // A freeze occurs if frames were dropped NOT after a discontinuity
+    if (mDroppedContentDurationUs != 0 && mLastRenderTimeUs != -1) {
+        // When pausing, audio playback may continue for a brief period of time after video
+        // pauses while the audio buffers drain. When resuming, a small number of video frames
+        // might be dropped to catch up to the audio position. This is acceptable behacvior and
+        // should not count as a freeze.
+        bool isLikelyCatchingUpAfterPause = false;
+        // A pause can be detected if a freeze occurs for a longer period of time than the
+        // content duration of the dropped frames. This strategy works because, for freeze
+        // events (no video pause), the content duration of the dropped frames will closely track
+        // the wall clock time (freeze duration). When pausing, however, the wall clock time
+        // (freeze duration) will be longer than the content duration of the dropped frames
+        // required to catch up to the audio position.
+        const int64_t wallClockDurationUs = actualRenderTimeUs - mLastRenderTimeUs;
+        // 200ms is chosen because it is larger than what a hiccup in the display pipeline could
+        // likely be, but shorter than the duration for which a user could pause for.
+        static const int32_t MAX_PIPELINE_HICCUP_DURATION_US = 200 * 1000;
+        if (wallClockDurationUs > mDroppedContentDurationUs + MAX_PIPELINE_HICCUP_DURATION_US) {
+            // Capture the amount of content that is dropped after pause, so we can push apps to be
+            // better about this behavior.
+            if (mDroppedContentDurationUs / 1000 > mMetrics.maxContentDroppedAfterPauseMs) {
+                mMetrics.maxContentDroppedAfterPauseMs = int32_t(mDroppedContentDurationUs / 1000);
+            }
+            isLikelyCatchingUpAfterPause = mDroppedContentDurationUs <= c.pauseAudioLatencyUs;
+        }
+        if (!isLikelyCatchingUpAfterPause) {
+            processFreeze(actualRenderTimeUs, mLastRenderTimeUs, mLastFreezeEndTimeUs, mFreezeEvent,
+                        mMetrics, mConfiguration);
+            mLastFreezeEndTimeUs = actualRenderTimeUs;
+        }
     }
     maybeCaptureFreezeEvent(actualRenderTimeUs, mLastFreezeEndTimeUs, mFreezeEvent, mMetrics,
                             mConfiguration, freezeEventOut);
@@ -536,7 +570,7 @@
     maybeCaptureJudderEvent(actualRenderTimeUs, mLastJudderEndTimeUs, mJudderEvent, mMetrics,
                             mConfiguration, judderEventOut);
 
-    mWasPreviousFrameDropped = false;
+    mDroppedContentDurationUs = 0;
 }
 
 void VideoRenderQualityTracker::processFreeze(int64_t actualRenderTimeUs, int64_t lastRenderTimeUs,
diff --git a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
index cf53f27..d58dfad 100644
--- a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
+++ b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
@@ -63,6 +63,11 @@
     // post-render.
     float actualFrameRate;
 
+    // The amount of content duration skipped by the app after a pause when video was trying to
+    // resume. This sometimes happen when catching up to the audio position which continued playing
+    // after video pauses.
+    int32_t maxContentDroppedAfterPauseMs;
+
     // A histogram of the durations of freezes due to dropped/skipped frames.
     MediaHistogram<int32_t> freezeDurationMsHistogram;
     // The computed overall freeze score using the above histogram and score conversion table. The
@@ -155,6 +160,11 @@
         // seeking forward.
         int32_t liveContentFrameDropToleranceUs;
 
+        // The amount of time it takes for audio to stop playback after a pause is initiated. Used
+        // for providing some allowance of dropped video frames to catch back up to the audio
+        // position when resuming playback.
+        int32_t pauseAudioLatencyUs;
+
         // Freeze configuration
         //
         // The values used to distribute freeze durations across a histogram.
@@ -441,8 +451,8 @@
     // The render duration of the playback.
     int64_t mRenderDurationMs;
 
-    // True if the previous frame was dropped.
-    bool mWasPreviousFrameDropped;
+    // The duration of the content that was dropped.
+    int64_t mDroppedContentDurationUs;
 
     // The freeze event that's currently being tracked.
     FreezeEvent mFreezeEvent;
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index 78140dd..9b6315c 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -140,6 +140,7 @@
     EXPECT_EQ(c.maxExpectedContentFrameDurationUs, d.maxExpectedContentFrameDurationUs);
     EXPECT_EQ(c.frameRateDetectionToleranceUs, d.frameRateDetectionToleranceUs);
     EXPECT_EQ(c.liveContentFrameDropToleranceUs, d.liveContentFrameDropToleranceUs);
+    EXPECT_EQ(c.pauseAudioLatencyUs, d.pauseAudioLatencyUs);
     EXPECT_EQ(c.freezeDurationMsHistogramBuckets, d.freezeDurationMsHistogramBuckets);
     EXPECT_EQ(c.freezeDurationMsHistogramToScore, d.freezeDurationMsHistogramToScore);
     EXPECT_EQ(c.freezeDistanceMsHistogramBuckets, d.freezeDistanceMsHistogramBuckets);
@@ -171,6 +172,7 @@
     EXPECT_EQ(c.maxExpectedContentFrameDurationUs, d.maxExpectedContentFrameDurationUs);
     EXPECT_EQ(c.frameRateDetectionToleranceUs, d.frameRateDetectionToleranceUs);
     EXPECT_EQ(c.liveContentFrameDropToleranceUs, d.liveContentFrameDropToleranceUs);
+    EXPECT_EQ(c.pauseAudioLatencyUs, d.pauseAudioLatencyUs);
     EXPECT_EQ(c.freezeDurationMsHistogramBuckets, d.freezeDurationMsHistogramBuckets);
     EXPECT_EQ(c.freezeDurationMsHistogramToScore, d.freezeDurationMsHistogramToScore);
     EXPECT_EQ(c.freezeDistanceMsHistogramBuckets, d.freezeDistanceMsHistogramBuckets);
@@ -202,6 +204,7 @@
     EXPECT_EQ(c.maxExpectedContentFrameDurationUs, d.maxExpectedContentFrameDurationUs);
     EXPECT_EQ(c.frameRateDetectionToleranceUs, d.frameRateDetectionToleranceUs);
     EXPECT_EQ(c.liveContentFrameDropToleranceUs, d.liveContentFrameDropToleranceUs);
+    EXPECT_EQ(c.pauseAudioLatencyUs, d.pauseAudioLatencyUs);
     EXPECT_EQ(c.freezeDurationMsHistogramBuckets, d.freezeDurationMsHistogramBuckets);
     EXPECT_EQ(c.freezeDurationMsHistogramToScore, d.freezeDurationMsHistogramToScore);
     EXPECT_EQ(c.freezeDistanceMsHistogramBuckets, d.freezeDistanceMsHistogramBuckets);
@@ -233,6 +236,8 @@
                 return "10b0";
             } else if (flag == "render_metrics_live_content_frame_drop_tolerance_us") {
                 return "c100";
+            } else if (flag == "render_metrics_pause_audio_latency_us") {
+                return "1ab0";
             } else if (flag == "render_metrics_freeze_duration_ms_histogram_buckets") {
                 return "1,5300,3b400,123";
             } else if (flag == "render_metrics_freeze_duration_ms_histogram_to_score") {
@@ -276,6 +281,7 @@
     EXPECT_EQ(c.maxExpectedContentFrameDurationUs, d.maxExpectedContentFrameDurationUs);
     EXPECT_EQ(c.frameRateDetectionToleranceUs, d.frameRateDetectionToleranceUs);
     EXPECT_EQ(c.liveContentFrameDropToleranceUs, d.liveContentFrameDropToleranceUs);
+    EXPECT_EQ(c.pauseAudioLatencyUs, d.pauseAudioLatencyUs);
     EXPECT_EQ(c.freezeDurationMsHistogramBuckets, d.freezeDurationMsHistogramBuckets);
     EXPECT_EQ(c.freezeDurationMsHistogramToScore, d.freezeDurationMsHistogramToScore);
     EXPECT_EQ(c.freezeDistanceMsHistogramBuckets, d.freezeDistanceMsHistogramBuckets);
@@ -307,6 +313,8 @@
                 return "3000";
             } else if (flag == "render_metrics_live_content_frame_drop_tolerance_us") {
                 return "4000";
+            } else if (flag == "render_metrics_pause_audio_latency_us") {
+                return "300000";
             } else if (flag == "render_metrics_freeze_duration_ms_histogram_buckets") {
                 return "100,200,300,400";
             } else if (flag == "render_metrics_freeze_duration_ms_histogram_to_score") {
@@ -359,6 +367,8 @@
     EXPECT_NE(c.frameRateDetectionToleranceUs, d.frameRateDetectionToleranceUs);
     EXPECT_EQ(c.liveContentFrameDropToleranceUs, 4000);
     EXPECT_NE(c.liveContentFrameDropToleranceUs, d.liveContentFrameDropToleranceUs);
+    EXPECT_EQ(c.pauseAudioLatencyUs, 300000);
+    EXPECT_NE(c.pauseAudioLatencyUs, d.pauseAudioLatencyUs);
     {
         std::vector<int32_t> expected({100,200,300,400});
         EXPECT_EQ(c.freezeDurationMsHistogramBuckets, expected);
@@ -1181,4 +1191,53 @@
 
     EXPECT_EQ(h.getTraceTriggeredCount(), 0);
 }
+
+TEST_F(VideoRenderQualityTrackerTest, doesNotCountCatchUpAfterPauseAsFreeze) {
+    Configuration c;
+    c.enabled = true;
+    c.pauseAudioLatencyUs = 200 * 1000; // allows for up to 10 frames to be dropped to catch up
+                                        // to the audio position
+    Helper h(20, c);
+    // A few frames followed by a long pause
+    h.render({20, 20, 1000});
+    h.drop(10); // simulate catching up to audio
+    h.render({20, 20, 1000});
+    h.drop(11); // simulate catching up to audio but then also dropping frames
+    h.render({20});
+
+    // Only 1 freeze is counted because the first freeze (200ms) because it's equal to or below the
+    // pause latency allowance, and the algorithm assumes a legitimate case of the video trying to
+    // catch up to the audio position, which continued to play for a short period of time (less than
+    // 200ms) after the pause was initiated
+    EXPECT_EQ(h.getMetrics().freezeDurationMsHistogram.getCount(), 1);
+}
+
+TEST_F(VideoRenderQualityTrackerTest, capturesMaximumContentDroppedAfterPause) {
+    Configuration c;
+    c.enabled = true;
+    c.pauseAudioLatencyUs = 200 * 1000; // allows for up to 10 frames to be dropped to catch up
+                                        // to the audio position
+    Helper h(20, c);
+
+    // Freezes are below the pause latency are captured
+    h.render({20, 20, 1000});
+    h.drop(6);
+    h.render({20, 20, 1000});
+    h.drop(8);
+    h.render({20, 20, 1000});
+    h.drop(7);
+    h.render({20});
+    EXPECT_EQ(h.getMetrics().maxContentDroppedAfterPauseMs, 8 * 20);
+
+    // Freezes are above the pause latency are also captured
+    h.render({20, 20, 1000});
+    h.drop(10);
+    h.render({20, 20, 1000});
+    h.drop(12);
+    h.render({20, 20, 1000});
+    h.drop(11);
+    h.render({20});
+    EXPECT_EQ(h.getMetrics().maxContentDroppedAfterPauseMs, 12 * 20);
+}
+
 } // android
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
index 921ad7d..efc58b4 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
@@ -60,15 +60,21 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
             "android-camera-fwk-eng@google.com",
         ],
-        componentid: 155276,
+        componentid: 41727,
         libfuzzer_options: [
             //based on b/187360866
             "timeout=770",
         ],
-
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libcameraservice",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/services/camera/virtualcamera/OWNERS b/services/camera/virtualcamera/OWNERS
new file mode 100644
index 0000000..db34336
--- /dev/null
+++ b/services/camera/virtualcamera/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1171888
+include platform/frameworks/base:/services/companion/java/com/android/server/companion/virtual/OWNERS
+caen@google.com
+jsebechlebsky@google.com