Merge "Fix audio HAL restart sequence to improve uptime"
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
index a9fc7e4..594ee7b 100644
--- a/media/extractors/fuzzers/Android.bp
+++ b/media/extractors/fuzzers/Android.bp
@@ -308,3 +308,25 @@
 
     dictionary: "flac_extractor_fuzzer.dict",
 }
+
+cc_fuzz {
+    name: "midi_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
+
+    srcs: [
+        "midi_extractor_fuzzer.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/av/media/extractors/midi",
+    ],
+
+    static_libs: [
+        "libsonivox",
+        "libmedia_midiiowrapper",
+        "libmidiextractor",
+        "libwatchdog",
+    ],
+
+    dictionary: "midi_extractor_fuzzer.dict",
+}
diff --git a/media/extractors/fuzzers/README.md b/media/extractors/fuzzers/README.md
index 4223b5e..fb1d52f 100644
--- a/media/extractors/fuzzers/README.md
+++ b/media/extractors/fuzzers/README.md
@@ -11,6 +11,7 @@
 + [libmp3extractor](#mp3ExtractorFuzzer)
 + [libaacextractor](#aacExtractorFuzzer)
 + [libflacextractor](#flacExtractor)
++ [libmidiextractor](#midiExtractorFuzzer)
 
 # <a name="ExtractorFuzzerBase"></a> Fuzzer for libextractorfuzzerbase
 All the extractors have a common API - creating a data source, extraction
@@ -321,6 +322,41 @@
   $ adb shell /data/fuzz/arm64/flac_extractor_fuzzer/flac_extractor_fuzzer CORPUS_DIR
 ```
 
+# <a name="midiExtractorFuzzer"></a> Fuzzer for libmidiextractor
+
+## Plugin Design Considerations
+The fuzzer plugin for MIDI extractor uses the `ExtractorFuzzerBase` class and
+implements only the `createExtractor` to create the MIDI extractor class.
+
+##### Maximize code coverage
+Dict file (dictionary file) is created for MIDI to ensure that the required MIDI
+headers are present in every input file that goes to the fuzzer.
+This ensures that larger code gets covered as a range of MIDI headers will be
+present in the input data.
+
+
+## Build
+
+This describes steps to build midi_extractor_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) midi_extractor_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some MIDI files to that folder
+Push this directory to device.
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/midi_extractor_fuzzer/midi_extractor_fuzzer CORPUS_DIR
+```
+
 ## References:
  * http://llvm.org/docs/LibFuzzer.html
  * https://github.com/google/oss-fuzz
diff --git a/media/extractors/fuzzers/midi_extractor_fuzzer.cpp b/media/extractors/fuzzers/midi_extractor_fuzzer.cpp
new file mode 100644
index 0000000..e02a12b
--- /dev/null
+++ b/media/extractors/fuzzers/midi_extractor_fuzzer.cpp
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#include "ExtractorFuzzerBase.h"
+
+#include "MidiExtractor.h"
+
+using namespace android;
+
+class MIDIExtractor : public ExtractorFuzzerBase {
+ public:
+  MIDIExtractor() = default;
+  ~MIDIExtractor() = default;
+
+  bool createExtractor();
+};
+
+bool MIDIExtractor::createExtractor() {
+  mExtractor = new MidiExtractor(mDataSource->wrap());
+  if (!mExtractor) {
+    return false;
+  }
+  mExtractor->name();
+  return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if ((!data) || (size == 0)) {
+    return 0;
+  }
+  MIDIExtractor* extractor = new MIDIExtractor();
+  if (extractor) {
+    extractor->processData(data, size);
+    delete extractor;
+  }
+  return 0;
+}
diff --git a/media/extractors/fuzzers/midi_extractor_fuzzer.dict b/media/extractors/fuzzers/midi_extractor_fuzzer.dict
new file mode 100644
index 0000000..5b6bb8b
--- /dev/null
+++ b/media/extractors/fuzzers/midi_extractor_fuzzer.dict
@@ -0,0 +1,3 @@
+# MIDI Chunks
+kw1="MThd"
+kw2="MTrk"
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 7d16fb4..019a99a 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -3836,43 +3836,44 @@
     switch ((int32_t)mPath[4]) {
         case FOURCC("\251alb"):
         {
-            metadataKey = "album";
+            metadataKey = AMEDIAFORMAT_KEY_ALBUM;
             break;
         }
         case FOURCC("\251ART"):
         {
-            metadataKey = "artist";
+            metadataKey = AMEDIAFORMAT_KEY_ARTIST;
             break;
         }
         case FOURCC("aART"):
         {
-            metadataKey = "albumartist";
+            metadataKey = AMEDIAFORMAT_KEY_ALBUMARTIST;
             break;
         }
         case FOURCC("\251day"):
         {
-            metadataKey = "year";
+            metadataKey = AMEDIAFORMAT_KEY_YEAR;
             break;
         }
         case FOURCC("\251nam"):
         {
-            metadataKey = "title";
+            metadataKey = AMEDIAFORMAT_KEY_TITLE;
             break;
         }
         case FOURCC("\251wrt"):
         {
-            metadataKey = "writer";
+            // various open source taggers agree that the "©wrt" tag is for composer, not writer
+            metadataKey = AMEDIAFORMAT_KEY_COMPOSER;
             break;
         }
         case FOURCC("covr"):
         {
-            metadataKey = "albumart";
+            metadataKey = AMEDIAFORMAT_KEY_ALBUMART;
             break;
         }
         case FOURCC("gnre"):
         case FOURCC("\251gen"):
         {
-            metadataKey = "genre";
+            metadataKey = AMEDIAFORMAT_KEY_GENRE;
             break;
         }
         case FOURCC("cpil"):
@@ -3977,7 +3978,7 @@
         if (!strcmp(metadataKey, "albumart")) {
             AMediaFormat_setBuffer(mFileMetaData, metadataKey,
                     buffer + 8, size - 8);
-        } else if (!strcmp(metadataKey, "genre")) {
+        } else if (!strcmp(metadataKey, AMEDIAFORMAT_KEY_GENRE)) {
             if (flags == 0) {
                 // uint8_t genre code, iTunes genre codes are
                 // the standard id3 codes, except they start
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 4520823..ac7ad9a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -353,6 +353,8 @@
     // Clear any stale timestamps from the previous run.
     drainTimestampsFromService();
 
+    prepareBuffersForStart(); // tell subclasses to get ready
+
     aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
     if (result == AAUDIO_ERROR_INVALID_HANDLE) {
         ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 61591b3..63be978 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -123,7 +123,9 @@
 
     aaudio_result_t stopCallback();
 
-    virtual void advanceClientToMatchServerPosition() = 0;
+    virtual void prepareBuffersForStart() {}
+
+    virtual void advanceClientToMatchServerPosition(int32_t serverMargin = 0) = 0;
 
     virtual void onFlushFromServer() {}
 
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 9fa2e40..d014608 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -41,9 +41,9 @@
 
 AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
 
-void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
+void AudioStreamInternalCapture::advanceClientToMatchServerPosition(int32_t serverMargin) {
     int64_t readCounter = mAudioEndpoint->getDataReadCounter();
-    int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
+    int64_t writeCounter = mAudioEndpoint->getDataWriteCounter() + serverMargin;
 
     // Bump offset so caller does not see the retrograde motion in getFramesRead().
     int64_t offset = readCounter - writeCounter;
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 6436a53..1d65d87 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -46,7 +46,7 @@
     }
 protected:
 
-    void advanceClientToMatchServerPosition() override;
+    void advanceClientToMatchServerPosition(int32_t serverOffset = 0) override;
 
 /**
  * Low level data processing that will not block. It will just read or write as much as it can.
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 1303daf..6337b53 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -86,8 +86,13 @@
     return mServiceInterface.flushStream(mServiceStreamHandle);
 }
 
-void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
-    int64_t readCounter = mAudioEndpoint->getDataReadCounter();
+void AudioStreamInternalPlay::prepareBuffersForStart() {
+    // Prevent stale data from being played.
+    mAudioEndpoint->eraseDataMemory();
+}
+
+void AudioStreamInternalPlay::advanceClientToMatchServerPosition(int32_t serverMargin) {
+    int64_t readCounter = mAudioEndpoint->getDataReadCounter() + serverMargin;
     int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
 
     // Bump offset so caller does not see the retrograde motion in getFramesRead().
@@ -145,7 +150,9 @@
     if (mNeedCatchUp.isRequested()) {
         // Catch an MMAP pointer that is already advancing.
         // This will avoid initial underruns caused by a slow cold start.
-        advanceClientToMatchServerPosition();
+        // We add a one burst margin in case the DSP advances before we can write the data.
+        // This can help prevent the beginning of the stream from being skipped.
+        advanceClientToMatchServerPosition(getFramesPerBurst());
         mNeedCatchUp.acknowledge();
     }
 
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index 2e93157..be95da6 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -65,7 +65,9 @@
 
 protected:
 
-    void advanceClientToMatchServerPosition() override;
+    void prepareBuffersForStart() override;
+
+    void advanceClientToMatchServerPosition(int32_t serverMargin = 0) override;
 
     void onFlushFromServer() override;
 
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 4ad780c..1caee04 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -219,7 +219,7 @@
     ],
 
     header_libs: [
-        "libmedia_headers",
+        "libmedia_datasource_headers",
         "media_ndk_headers",
     ],
 
@@ -236,6 +236,14 @@
         ],
         cfi: true,
     },
+
+    host_supported: true,
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 cc_library_shared {
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 91dbf78..3676d73 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -78,14 +78,14 @@
     }
 }
 
-bool MediaSampleWriter::init(int fd, const OnWritingFinishedCallback& callback) {
-    return init(DefaultMuxer::create(fd), callback);
+bool MediaSampleWriter::init(int fd, const std::weak_ptr<CallbackInterface>& callbacks) {
+    return init(DefaultMuxer::create(fd), callbacks);
 }
 
 bool MediaSampleWriter::init(const std::shared_ptr<MediaSampleWriterMuxerInterface>& muxer,
-                             const OnWritingFinishedCallback& callback) {
-    if (callback == nullptr) {
-        LOG(ERROR) << "Callback cannot be null";
+                             const std::weak_ptr<CallbackInterface>& callbacks) {
+    if (callbacks.lock() == nullptr) {
+        LOG(ERROR) << "Callback object cannot be null";
         return false;
     } else if (muxer == nullptr) {
         LOG(ERROR) << "Muxer cannot be null";
@@ -100,7 +100,7 @@
 
     mState = INITIALIZED;
     mMuxer = muxer;
-    mWritingFinishedCallback = callback;
+    mCallbacks = callbacks;
     return true;
 }
 
@@ -127,7 +127,11 @@
         durationUs = 0;
     }
 
-    mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex), durationUs);
+    const char* mime = nullptr;
+    const bool isVideo = AMediaFormat_getString(trackFormat.get(), AMEDIAFORMAT_KEY_MIME, &mime) &&
+                         (strncmp(mime, "video/", 6) == 0);
+
+    mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex), durationUs, isVideo);
     return true;
 }
 
@@ -144,7 +148,9 @@
 
     mThread = std::thread([this] {
         media_status_t status = writeSamples();
-        mWritingFinishedCallback(status);
+        if (auto callbacks = mCallbacks.lock()) {
+            callbacks->onFinished(this, status);
+        }
     });
     mState = STARTED;
     return true;
@@ -191,6 +197,18 @@
     AMediaCodecBufferInfo bufferInfo;
     uint32_t segmentEndTimeUs = mTrackSegmentLengthUs;
     bool samplesLeft = true;
+    int32_t lastProgressUpdate = 0;
+
+    // Set the "primary" track that will be used to determine progress to the track with longest
+    // duration.
+    int primaryTrackIndex = -1;
+    int64_t longestDurationUs = 0;
+    for (int trackIndex = 0; trackIndex < mTracks.size(); ++trackIndex) {
+        if (mTracks[trackIndex].mDurationUs > longestDurationUs) {
+            primaryTrackIndex = trackIndex;
+            longestDurationUs = mTracks[trackIndex].mDurationUs;
+        }
+    }
 
     while (samplesLeft) {
         samplesLeft = false;
@@ -216,9 +234,10 @@
                     samplesLeft = true;
                 }
 
-                // Record the first sample's timestamp in order to translate duration to EOS time
-                // for tracks that does not start at 0.
+                track.mPrevSampleTimeUs = sample->info.presentationTimeUs;
                 if (!track.mFirstSampleTimeSet) {
+                    // Record the first sample's timestamp in order to translate duration to EOS
+                    // time for tracks that does not start at 0.
                     track.mFirstSampleTimeUs = sample->info.presentationTimeUs;
                     track.mFirstSampleTimeSet = true;
                 }
@@ -238,6 +257,22 @@
             } while (sample->info.presentationTimeUs < segmentEndTimeUs && !track.mReachedEos);
         }
 
+        // TODO(lnilsson): Add option to toggle progress reporting on/off.
+        if (primaryTrackIndex >= 0) {
+            const TrackRecord& track = mTracks[primaryTrackIndex];
+
+            const int64_t elapsed = track.mPrevSampleTimeUs - track.mFirstSampleTimeUs;
+            int32_t progress = (elapsed * 100) / track.mDurationUs;
+            progress = std::clamp(progress, 0, 100);
+
+            if (progress > lastProgressUpdate) {
+                if (auto callbacks = mCallbacks.lock()) {
+                    callbacks->onProgressUpdate(this, progress);
+                }
+                lastProgressUpdate = progress;
+            }
+        }
+
         segmentEndTimeUs += mTrackSegmentLengthUs;
     }
 
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index bde1cf6..49bfdfe 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -151,11 +151,16 @@
     sendCallback(status);
 }
 
-void MediaTranscoder::onSampleWriterFinished(media_status_t status) {
+void MediaTranscoder::onFinished(const MediaSampleWriter* writer __unused, media_status_t status) {
     LOG((status != AMEDIA_OK) ? ERROR : DEBUG) << "Sample writer finished with status " << status;
     sendCallback(status);
 }
 
+void MediaTranscoder::onProgressUpdate(const MediaSampleWriter* writer __unused, int32_t progress) {
+    // Dispatch progress updated to the client.
+    mCallbacks->onProgressUpdate(this, progress);
+}
+
 MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks)
       : mCallbacks(callbacks) {}
 
@@ -288,8 +293,7 @@
     }
 
     mSampleWriter = std::make_unique<MediaSampleWriter>();
-    const bool initOk = mSampleWriter->init(
-            fd, std::bind(&MediaTranscoder::onSampleWriterFinished, this, std::placeholders::_1));
+    const bool initOk = mSampleWriter->init(fd, shared_from_this());
 
     if (!initOk) {
         LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
index d971f3e..92ddc2f 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
@@ -71,18 +71,27 @@
     /** The default segment length. */
     static constexpr uint32_t kDefaultTrackSegmentLengthUs = 1 * 1000 * 1000;  // 1 sec.
 
-    /** Client callback for when the writer is finished. */
-    using OnWritingFinishedCallback = std::function<void(media_status_t)>;
+    /** Callback interface. */
+    class CallbackInterface {
+    public:
+        /**
+         * Sample writer finished. The finished callback is only called after the sample writer has
+         * been successfully started.
+         */
+        virtual void onFinished(const MediaSampleWriter* writer, media_status_t status) = 0;
+
+        /** Sample writer progress update in percent. */
+        virtual void onProgressUpdate(const MediaSampleWriter* writer, int32_t progress) = 0;
+
+        virtual ~CallbackInterface() = default;
+    };
 
     /**
      * Constructor with custom segment length.
      * @param trackSegmentLengthUs The segment length to use for this MediaSampleWriter.
      */
     MediaSampleWriter(uint32_t trackSegmentLengthUs)
-          : mTrackSegmentLengthUs(trackSegmentLengthUs),
-            mWritingFinishedCallback(nullptr),
-            mMuxer(nullptr),
-            mState(UNINITIALIZED){};
+          : mTrackSegmentLengthUs(trackSegmentLengthUs), mMuxer(nullptr), mState(UNINITIALIZED){};
 
     /** Constructor using the default segment length. */
     MediaSampleWriter() : MediaSampleWriter(kDefaultTrackSegmentLengthUs){};
@@ -95,21 +104,19 @@
      * to be initialized before tracks are added and can only be initialized once.
      * @param fd An open file descriptor to write to. The caller is responsible for closing this
      *        file descriptor and it is safe to do so once this method returns.
-     * @param callback Client callback that gets called when the sample writer has finished, after
-     *        it was successfully started.
+     * @param callbacks Client callback object that gets called by the sample writer.
      * @return True if the writer was successfully initialized.
      */
-    bool init(int fd, const OnWritingFinishedCallback& callback /* nonnull */);
+    bool init(int fd, const std::weak_ptr<CallbackInterface>& callbacks /* nonnull */);
 
     /**
      * Initializes the sample writer with a custom muxer interface implementation.
      * @param muxer The custom muxer interface implementation.
-     * @param callback Client callback that gets called when the sample writer has finished, after
-     *        it was successfully started.
+     * @param @param callbacks Client callback object that gets called by the sample writer.
      * @return True if the writer was successfully initialized.
      */
     bool init(const std::shared_ptr<MediaSampleWriterMuxerInterface>& muxer /* nonnull */,
-              const OnWritingFinishedCallback& callback /* nonnull */);
+              const std::weak_ptr<CallbackInterface>& callbacks /* nonnull */);
 
     /**
      * Adds a new track to the sample writer. Tracks must be added after the sample writer has been
@@ -145,24 +152,28 @@
 
     struct TrackRecord {
         TrackRecord(const std::shared_ptr<MediaSampleQueue>& sampleQueue, size_t trackIndex,
-                    int64_t durationUs)
+                    int64_t durationUs, bool isVideo)
               : mSampleQueue(sampleQueue),
                 mTrackIndex(trackIndex),
                 mDurationUs(durationUs),
                 mFirstSampleTimeUs(0),
+                mPrevSampleTimeUs(0),
                 mFirstSampleTimeSet(false),
-                mReachedEos(false) {}
+                mReachedEos(false),
+                mIsVideo(isVideo) {}
 
         std::shared_ptr<MediaSampleQueue> mSampleQueue;
         const size_t mTrackIndex;
         int64_t mDurationUs;
         int64_t mFirstSampleTimeUs;
+        int64_t mPrevSampleTimeUs;
         bool mFirstSampleTimeSet;
         bool mReachedEos;
+        bool mIsVideo;
     };
 
     const uint32_t mTrackSegmentLengthUs;
-    OnWritingFinishedCallback mWritingFinishedCallback;
+    std::weak_ptr<CallbackInterface> mCallbacks;
     std::shared_ptr<MediaSampleWriterMuxerInterface> mMuxer;
     std::vector<TrackRecord> mTracks;
     std::thread mThread;
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
index 7a36c8c..33bd9d4 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
@@ -17,6 +17,9 @@
 #ifndef ANDROID_MEDIA_TRANSCODER_H
 #define ANDROID_MEDIA_TRANSCODER_H
 
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <media/MediaSampleWriter.h>
 #include <media/MediaTrackTranscoderCallback.h>
 #include <media/NdkMediaError.h>
 #include <media/NdkMediaFormat.h>
@@ -30,11 +33,11 @@
 namespace android {
 
 class MediaSampleReader;
-class MediaSampleWriter;
 class Parcel;
 
 class MediaTranscoder : public std::enable_shared_from_this<MediaTranscoder>,
-                        public MediaTrackTranscoderCallback {
+                        public MediaTrackTranscoderCallback,
+                        public MediaSampleWriter::CallbackInterface {
 public:
     /** Callbacks from transcoder to client. */
     class CallbackInterface {
@@ -126,6 +129,12 @@
     virtual void onTrackError(const MediaTrackTranscoder* transcoder,
                               media_status_t status) override;
     // ~MediaTrackTranscoderCallback
+
+    // MediaSampleWriter::CallbackInterface
+    virtual void onFinished(const MediaSampleWriter* writer, media_status_t status) override;
+    virtual void onProgressUpdate(const MediaSampleWriter* writer, int32_t progress) override;
+    // ~MediaSampleWriter::CallbackInterface
+
     void onSampleWriterFinished(media_status_t status);
     void sendCallback(media_status_t status);
 
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
index e3cb192..c82ec28 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -32,28 +32,6 @@
 
 namespace android {
 
-/** Minimal one-shot semaphore */
-class SimpleSemaphore {
-public:
-    void signal() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mSignaled = true;
-        mCondition.notify_all();
-    }
-
-    void wait() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        while (!mSignaled) {
-            mCondition.wait(lock);
-        }
-    }
-
-private:
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-    bool mSignaled = false;
-};
-
 /** Muxer interface to enable MediaSampleWriter testing. */
 class TestMuxer : public MediaSampleWriterMuxerInterface {
 public:
@@ -151,11 +129,22 @@
         for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
             AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
             ASSERT_NE(trackFormat, nullptr);
+
+            const char* mime = nullptr;
+            AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+            if (strncmp(mime, "video/", 6) == 0) {
+                mVideoTrackIndex = trackIndex;
+            } else if (strncmp(mime, "audio/", 6) == 0) {
+                mAudioTrackIndex = trackIndex;
+            }
+
             mTrackFormats.push_back(
                     std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete));
 
             AMediaExtractor_selectTrack(mExtractor, trackIndex);
         }
+        EXPECT_GE(mVideoTrackIndex, 0);
+        EXPECT_GE(mAudioTrackIndex, 0);
     }
 
     void reset() const {
@@ -167,6 +156,60 @@
     AMediaExtractor* mExtractor = nullptr;
     size_t mTrackCount = 0;
     std::vector<std::shared_ptr<AMediaFormat>> mTrackFormats;
+    int mVideoTrackIndex = -1;
+    int mAudioTrackIndex = -1;
+};
+
+class TestCallbacks : public MediaSampleWriter::CallbackInterface {
+public:
+    TestCallbacks(bool expectSuccess = true) : mExpectSuccess(expectSuccess) {}
+
+    bool hasFinished() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        return mFinished;
+    }
+
+    // MediaSampleWriter::CallbackInterface
+    virtual void onFinished(const MediaSampleWriter* writer __unused,
+                            media_status_t status) override {
+        std::unique_lock<std::mutex> lock(mMutex);
+        EXPECT_FALSE(mFinished);
+        if (mExpectSuccess) {
+            EXPECT_EQ(status, AMEDIA_OK);
+        } else {
+            EXPECT_NE(status, AMEDIA_OK);
+        }
+        mFinished = true;
+        mCondition.notify_all();
+    }
+
+    virtual void onProgressUpdate(const MediaSampleWriter* writer __unused,
+                                  int32_t progress) override {
+        EXPECT_GT(progress, mLastProgress);
+        EXPECT_GE(progress, 0);
+        EXPECT_LE(progress, 100);
+
+        mLastProgress = progress;
+        mProgressUpdateCount++;
+    }
+    // ~MediaSampleWriter::CallbackInterface
+
+    void waitForWritingFinished() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mFinished) {
+            mCondition.wait(lock);
+        }
+    }
+
+    uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mFinished = false;
+    bool mExpectSuccess;
+    int32_t mLastProgress = -1;
+    uint32_t mProgressUpdateCount = 0;
 };
 
 class MediaSampleWriterTests : public ::testing::Test {
@@ -222,7 +265,7 @@
 protected:
     std::shared_ptr<TestMuxer> mTestMuxer;
     std::shared_ptr<MediaSampleQueue> mSampleQueue;
-    const MediaSampleWriter::OnWritingFinishedCallback mEmptyCallback = [](media_status_t) {};
+    std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
 };
 
 TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
@@ -239,14 +282,14 @@
 
 TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
     MediaSampleWriter writer{};
-    EXPECT_TRUE(writer.init(mTestMuxer, mEmptyCallback));
+    EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
     EXPECT_FALSE(writer.start());
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
 }
 
 TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
     MediaSampleWriter writer{};
-    EXPECT_TRUE(writer.init(mTestMuxer, mEmptyCallback));
+    EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
 
     EXPECT_FALSE(writer.addTrack(mSampleQueue, nullptr));
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
@@ -259,31 +302,25 @@
 TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
     MediaSampleWriter writer{};
 
-    bool callbackFired = false;
-    MediaSampleWriter::OnWritingFinishedCallback stoppedCallback =
-            [&callbackFired](media_status_t status) {
-                EXPECT_NE(status, AMEDIA_OK);
-                EXPECT_FALSE(callbackFired);
-                callbackFired = true;
-            };
-
-    EXPECT_TRUE(writer.init(mTestMuxer, stoppedCallback));
+    std::shared_ptr<TestCallbacks> callbacks =
+            std::make_shared<TestCallbacks>(false /* expectSuccess */);
+    EXPECT_TRUE(writer.init(mTestMuxer, callbacks));
 
     const TestMediaSource& mediaSource = getMediaSource();
     EXPECT_TRUE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
 
-    EXPECT_TRUE(writer.start());
+    ASSERT_TRUE(writer.start());
     EXPECT_FALSE(writer.start());
 
     EXPECT_TRUE(writer.stop());
-    EXPECT_TRUE(callbackFired);
+    EXPECT_TRUE(callbacks->hasFinished());
     EXPECT_FALSE(writer.stop());
 }
 
 TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
     MediaSampleWriter writer{};
-    EXPECT_TRUE(writer.init(mTestMuxer, mEmptyCallback));
+    EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
 
     const TestMediaSource& mediaSource = getMediaSource();
     EXPECT_TRUE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
@@ -295,22 +332,48 @@
 
 TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
     MediaSampleWriter writer{};
-    EXPECT_FALSE(writer.init(mTestMuxer, nullptr));
+
+    std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
+    EXPECT_FALSE(writer.init(mTestMuxer, unassignedWp));
+
+    std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
+    EXPECT_FALSE(writer.init(mTestMuxer, unassignedSp));
 
     const TestMediaSource& mediaSource = getMediaSource();
     EXPECT_FALSE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
     ASSERT_FALSE(writer.start());
 }
 
+TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
+    static constexpr uint32_t kSegmentLengthUs = 1;
+    const TestMediaSource& mediaSource = getMediaSource();
+
+    MediaSampleWriter writer{kSegmentLengthUs};
+    EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
+
+    std::shared_ptr<AMediaFormat> videoFormat =
+            std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+    AMediaFormat_copy(videoFormat.get(),
+                      mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
+
+    AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
+    EXPECT_TRUE(writer.addTrack(mSampleQueue, videoFormat));
+    ASSERT_TRUE(writer.start());
+
+    for (int64_t pts = 0; pts < 100; ++pts) {
+        mSampleQueue->enqueue(newSampleWithPts(pts));
+    }
+    mSampleQueue->enqueue(newSampleEos());
+    mTestCallbacks->waitForWritingFinished();
+
+    EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
+}
+
 TEST_F(MediaSampleWriterTests, TestInterleaving) {
     static constexpr uint32_t kSegmentLength = MediaSampleWriter::kDefaultTrackSegmentLengthUs;
-    SimpleSemaphore semaphore;
 
     MediaSampleWriter writer{kSegmentLength};
-    EXPECT_TRUE(writer.init(mTestMuxer, [&semaphore](media_status_t status) {
-        EXPECT_EQ(status, AMEDIA_OK);
-        semaphore.signal();
-    }));
+    EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
 
     // Use two tracks for this test.
     static constexpr int kNumTracks = 2;
@@ -356,7 +419,7 @@
     ASSERT_TRUE(writer.start());
 
     // Wait for writer to complete.
-    semaphore.wait();
+    mTestCallbacks->waitForWritingFinished();
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
 
     // Verify sample order.
@@ -386,16 +449,14 @@
 
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
     EXPECT_TRUE(writer.stop());
+    EXPECT_TRUE(mTestCallbacks->hasFinished());
 }
 
 TEST_F(MediaSampleWriterTests, TestAbortInputQueue) {
-    SimpleSemaphore semaphore;
-
     MediaSampleWriter writer{};
-    EXPECT_TRUE(writer.init(mTestMuxer, [&semaphore](media_status_t status) {
-        EXPECT_NE(status, AMEDIA_OK);
-        semaphore.signal();
-    }));
+    std::shared_ptr<TestCallbacks> callbacks =
+            std::make_shared<TestCallbacks>(false /* expectSuccess */);
+    EXPECT_TRUE(writer.init(mTestMuxer, callbacks));
 
     // Use two tracks for this test.
     static constexpr int kNumTracks = 2;
@@ -417,7 +478,8 @@
     for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
         sampleQueues[trackIdx]->abort();
     }
-    semaphore.wait();
+
+    callbacks->waitForWritingFinished();
 
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
@@ -465,12 +527,8 @@
     ASSERT_GT(destinationFd, 0);
 
     // Initialize writer.
-    SimpleSemaphore semaphore;
     MediaSampleWriter writer{};
-    EXPECT_TRUE(writer.init(destinationFd, [&semaphore](media_status_t status) {
-        EXPECT_EQ(status, AMEDIA_OK);
-        semaphore.signal();
-    }));
+    EXPECT_TRUE(writer.init(destinationFd, mTestCallbacks));
     close(destinationFd);
 
     // Add tracks.
@@ -497,7 +555,7 @@
     }
 
     // Wait for writer.
-    semaphore.wait();
+    mTestCallbacks->waitForWritingFinished();
     EXPECT_TRUE(writer.stop());
 
     // Compare output file with source.
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
index 80083f7..b5d32ed 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -307,6 +307,20 @@
 
             if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
                 mSawInputEos = true;
+                if (mIsFirst && !inHeader->nFilledLen) {
+                     ALOGV("empty first EOS");
+                     outHeader->nFilledLen = 0;
+                     outHeader->nTimeStamp = inHeader->nTimeStamp;
+                     outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+                     mSignalledOutputEos = true;
+                     outInfo->mOwnedByUs = false;
+                     outQueue.erase(outQueue.begin());
+                     notifyFillBufferDone(outHeader);
+                     inInfo->mOwnedByUs = false;
+                     inQueue.erase(inQueue.begin());
+                     notifyEmptyBufferDone(inHeader);
+                     return;
+                }
             }
 
             mConfig->pInputBuffer =
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 1b71a2b..5d697f7 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -47,6 +47,14 @@
     ],
 
     min_sdk_version: "29",
+
+    host_supported: true,
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 9371353..d0e0cc7 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -35,6 +35,8 @@
 cc_library_headers {
     name: "media_ndk_headers",
     vendor_available: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     apex_available: [
         "//apex_available:platform",
         "com.android.media",