Merge "Reclaim allocations on AACExtractor destructor" into rvc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index 140a2e8..3098001 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -14,6 +14,7 @@
 
 apex_defaults {
     name: "com.android.media-defaults",
+    updatable: true,
     java_libs: ["updatable-media"],
     multilib: {
         first: {
@@ -65,6 +66,7 @@
 
 apex_defaults {
     name: "com.android.media.swcodec-defaults",
+    updatable: true,
     binaries: [
         "mediaswcodec",
     ],
diff --git a/media/codec2/OWNERS b/media/codec2/OWNERS
new file mode 100644
index 0000000..46a9fca
--- /dev/null
+++ b/media/codec2/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+wonsik@google.com
+lajos@google.com
+pawin@google.com
+taklee@google.com
diff --git a/media/codec2/core/OWNERS b/media/codec2/core/OWNERS
new file mode 100644
index 0000000..31ecca5
--- /dev/null
+++ b/media/codec2/core/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+lajos@google.com
diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp
index 13d60c8..25fc4cd 100644
--- a/media/extractors/mp4/AC4Parser.cpp
+++ b/media/extractors/mp4/AC4Parser.cpp
@@ -600,6 +600,10 @@
         if (ac4_dsi_version == 1) {
             uint64_t end = (mDSISize - mBitReader.numBitsLeft()) / 8;
             uint64_t presentation_bytes = end - start;
+            if (pres_bytes < presentation_bytes) {
+                ALOGE("pres_bytes is smaller than presentation_bytes.");
+                return false;
+            }
             uint64_t skip_bytes = pres_bytes - presentation_bytes;
             ALOGV("skipping = %" PRIu64 " bytes", skip_bytes);
             CHECK_BITS_LEFT(skip_bytes * 8);
diff --git a/media/libmedia/aidl/android/media/IResourceManagerService.aidl b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
index 3dd0859..1b2d522 100644
--- a/media/libmedia/aidl/android/media/IResourceManagerService.aidl
+++ b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
@@ -94,4 +94,12 @@
      *        remove existing override on originalPid if newPid is -1.
      */
     void overridePid(int originalPid, int newPid);
+
+    /**
+     * Mark a client for pending removal
+     *
+     * @param pid pid from which the client's resources will be removed.
+     * @param clientId clientId within the pid that will be removed.
+     */
+    void markClientForPendingRemoval(int pid, long clientId);
 }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index c10e6e0..cf7f423 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -2009,6 +2009,9 @@
             (*meta)->setInt32(kKeyRotation, mRotationDegrees);
         }
     }
+    if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) {
+        (*meta)->setInt32(kKeyEmptyTrackMalFormed, true);
+    }
 }
 
 status_t StagefrightRecorder::pause() {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index dbbdd1b..cb6798e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1131,8 +1131,8 @@
         err = writerErr;
     }
 
-    // Do not write out movie header on error.
-    if (err != OK) {
+    // Do not write out movie header on error except malformed track.
+    if (err != OK && err != ERROR_MALFORMED) {
         release();
         return err;
     }
@@ -2687,7 +2687,7 @@
 status_t MPEG4Writer::Track::stop(bool stopSource) {
     ALOGD("%s track stopping. %s source", getTrackType(), stopSource ? "Stop" : "Not Stop");
     if (!mStarted) {
-        ALOGE("Stop() called but track is not started");
+        ALOGE("Stop() called but track is not started or stopped");
         return ERROR_END_OF_STREAM;
     }
 
@@ -2711,6 +2711,7 @@
     err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
     WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err,
                 stopSource ? "Stop" : "Not Stop");
+    mStarted = false;
     return err;
 }
 
@@ -3108,6 +3109,7 @@
     int32_t nActualFrames = 0;        // frames containing non-CSD data (non-0 length)
     int32_t nZeroLengthFrames = 0;
     int64_t lastTimestampUs = 0;      // Previous sample time stamp
+    int64_t previousSampleTimestampWithoutFudgeUs = 0; // Timestamp received/without fudge for STTS
     int64_t lastDurationUs = 0;       // Between the previous two samples
     int64_t currDurationTicks = 0;    // Timescale based ticks
     int64_t lastDurationTicks = 0;    // Timescale based ticks
@@ -3120,6 +3122,8 @@
     int64_t lastCttsOffsetTimeTicks = -1;  // Timescale based ticks
     int32_t cttsSampleCount = 0;           // Sample count in the current ctts table entry
     uint32_t lastSamplesPerChunk = 0;
+    int64_t lastSampleDurationUs = -1;      // Duration calculated from EOS buffer and its timestamp
+    int64_t lastSampleDurationTicks = -1;   // Timescale based ticks
 
     if (mIsAudio) {
         prctl(PR_SET_NAME, (unsigned long)"AudioTrackWriterThread", 0, 0, 0);
@@ -3139,14 +3143,28 @@
     MediaBufferBase *buffer;
     const char *trackName = getTrackType();
     while (!mDone && (err = mSource->read(&buffer)) == OK) {
+        int32_t isEOS = false;
         if (buffer->range_length() == 0) {
-            buffer->release();
-            buffer = NULL;
-            ++nZeroLengthFrames;
-            continue;
+            if (buffer->meta_data().findInt32(kKeyIsEndOfStream, &isEOS) && isEOS) {
+                int64_t eosSampleTimestampUs = -1;
+                CHECK(buffer->meta_data().findInt64(kKeyTime, &eosSampleTimestampUs));
+                ALOGV("eosSampleTimestampUs:%" PRId64, eosSampleTimestampUs);
+                lastSampleDurationUs = eosSampleTimestampUs - previousSampleTimestampWithoutFudgeUs
+                                       - previousPausedDurationUs;
+                CHECK(lastSampleDurationUs >= 0);
+                lastSampleDurationTicks = (lastSampleDurationUs * mTimeScale + 500000LL)/1000000LL;
+                buffer->release();
+                buffer = nullptr;
+                mSource->stop();
+                break;
+            } else {
+                buffer->release();
+                buffer = nullptr;
+                ++nZeroLengthFrames;
+                continue;
+            }
         }
 
-
         // If the codec specific data has not been received yet, delay pause.
         // After the codec specific data is received, discard what we received
         // when the track is to be paused.
@@ -3470,6 +3488,8 @@
                 break;
             }
 
+            previousSampleTimestampWithoutFudgeUs = timestampUs;
+
             // if the duration is different for this sample, see if it is close enough to the previous
             // duration that we can fudge it and use the same value, to avoid filling the stts table
             // with lots of near-identical entries.
@@ -3567,47 +3587,61 @@
         }
     }
     if (isTrackMalFormed()) {
+        mIsMalformed = true;
         dumpTimeStamps();
         err = ERROR_MALFORMED;
     }
 
     mOwner->trackProgressStatus(mTrackId, -1, err);
 
-    if (mIsHeic) {
-        if (!mChunkSamples.empty()) {
-            bufferChunk(0);
-            ++nChunks;
-        }
-    } else {
-        // Last chunk
-        if (!hasMultipleTracks) {
-            addOneStscTableEntry(1, mStszTableEntries->count());
-        } else if (!mChunkSamples.empty()) {
-            addOneStscTableEntry(++nChunks, mChunkSamples.size());
-            bufferChunk(timestampUs);
-        }
-
-        // We don't really know how long the last frame lasts, since
-        // there is no frame time after it, just repeat the previous
-        // frame's duration.
-        if (mStszTableEntries->count() == 1) {
-            lastDurationUs = 0;  // A single sample's duration
-            lastDurationTicks = 0;
+    // Add final entries only for non-empty tracks.
+    if (mStszTableEntries->count() > 0) {
+        if (mIsHeic) {
+            if (!mChunkSamples.empty()) {
+                bufferChunk(0);
+                ++nChunks;
+            }
         } else {
-            ++sampleCount;  // Count for the last sample
-        }
+            // Last chunk
+            if (!hasMultipleTracks) {
+                addOneStscTableEntry(1, mStszTableEntries->count());
+            } else if (!mChunkSamples.empty()) {
+                addOneStscTableEntry(++nChunks, mChunkSamples.size());
+                bufferChunk(timestampUs);
+            }
 
-        addOneSttsTableEntry(sampleCount, lastDurationTicks);
+            // We don't really know how long the last frame lasts, since
+            // there is no frame time after it, just repeat the previous
+            // frame's duration.
+            if (mStszTableEntries->count() == 1) {
+                if (lastSampleDurationUs >= 0) {
+                    addOneSttsTableEntry(sampleCount, lastSampleDurationTicks);
+                } else {
+                    lastDurationUs = 0;  // A single sample's duration
+                    lastDurationTicks = 0;
+                    addOneSttsTableEntry(sampleCount, lastDurationTicks);
+                }
+            } else if (lastSampleDurationUs >= 0) {
+                addOneSttsTableEntry(sampleCount, lastDurationTicks);
+                addOneSttsTableEntry(1, lastSampleDurationTicks);
+            } else {
+                ++sampleCount;  // Count for the last sample
+                addOneSttsTableEntry(sampleCount, lastDurationTicks);
+            }
 
-        // The last ctts box entry may not have been written yet, and this
-        // is to make sure that we write out the last ctts box entry.
-        if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
-            if (cttsSampleCount > 0) {
-                addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+            // The last ctts box entry may not have been written yet, and this
+            // is to make sure that we write out the last ctts box entry.
+            if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
+                if (cttsSampleCount > 0) {
+                    addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+                }
+            }
+            if (lastSampleDurationUs >= 0) {
+                mTrackDurationUs += lastSampleDurationUs;
+            } else {
+                mTrackDurationUs += lastDurationUs;
             }
         }
-
-        mTrackDurationUs += lastDurationUs;
     }
     mReachedEOS = true;
 
@@ -3630,14 +3664,24 @@
         return true;
     }
 
-    if (!mIsHeic && mStszTableEntries->count() == 0) {  // no samples written
-        ALOGE("The number of recorded samples is 0");
-        return true;
-    }
-
-    if (mIsVideo && mStssTableEntries->count() == 0) {  // no sync frames for video
-        ALOGE("There are no sync frames for video track");
-        return true;
+    int32_t emptyTrackMalformed = false;
+    if (mOwner->mStartMeta &&
+        mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) &&
+        emptyTrackMalformed) {
+        if (!mIsHeic && mStszTableEntries->count() == 0) {  // no samples written
+            ALOGE("The number of recorded samples is 0");
+            return true;
+        }
+        if (mIsVideo && mStssTableEntries->count() == 0) {  // no sync frames for video
+            ALOGE("There are no sync frames for video track");
+            return true;
+        }
+    } else {
+        // No sync frames for video.
+        if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) {
+            ALOGE("There are no sync frames for video track");
+            return true;
+        }
     }
 
     if (OK != checkCodecSpecificData()) {         // no codec specific data
@@ -3886,25 +3930,28 @@
 
 void MPEG4Writer::Track::writeStblBox() {
     mOwner->beginBox("stbl");
-    mOwner->beginBox("stsd");
-    mOwner->writeInt32(0);               // version=0, flags=0
-    mOwner->writeInt32(1);               // entry count
-    if (mIsAudio) {
-        writeAudioFourCCBox();
-    } else if (mIsVideo) {
-        writeVideoFourCCBox();
-    } else {
-        writeMetadataFourCCBox();
+    // Add subboxes only for non-empty tracks.
+    if (mStszTableEntries->count() > 0) {
+        mOwner->beginBox("stsd");
+        mOwner->writeInt32(0);               // version=0, flags=0
+        mOwner->writeInt32(1);               // entry count
+        if (mIsAudio) {
+            writeAudioFourCCBox();
+        } else if (mIsVideo) {
+            writeVideoFourCCBox();
+        } else {
+            writeMetadataFourCCBox();
+        }
+        mOwner->endBox();  // stsd
+        writeSttsBox();
+        if (mIsVideo) {
+            writeCttsBox();
+            writeStssBox();
+        }
+        writeStszBox();
+        writeStscBox();
+        writeCo64Box();
     }
-    mOwner->endBox();  // stsd
-    writeSttsBox();
-    if (mIsVideo) {
-        writeCttsBox();
-        writeStssBox();
-    }
-    writeStszBox();
-    writeStscBox();
-    writeCo64Box();
     mOwner->endBox();  // stbl
 }
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b597583..3e39c6d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -199,6 +199,7 @@
     void addResource(const MediaResourceParcel &resource);
     void removeResource(const MediaResourceParcel &resource);
     void removeClient();
+    void markClientForPendingRemoval();
     bool reclaimResource(const std::vector<MediaResourceParcel> &resources);
 
 private:
@@ -280,6 +281,14 @@
     mService->removeClient(mPid, getId(mClient));
 }
 
+void MediaCodec::ResourceManagerServiceProxy::markClientForPendingRemoval() {
+    Mutex::Autolock _l(mLock);
+    if (mService == nullptr) {
+        return;
+    }
+    mService->markClientForPendingRemoval(mPid, getId(mClient));
+}
+
 bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
         const std::vector<MediaResourceParcel> &resources) {
     Mutex::Autolock _l(mLock);
@@ -1432,7 +1441,13 @@
 
 status_t MediaCodec::release() {
     sp<AMessage> msg = new AMessage(kWhatRelease, this);
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
 
+status_t MediaCodec::releaseAsync() {
+    sp<AMessage> msg = new AMessage(kWhatRelease, this);
+    msg->setInt32("async", 1);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
@@ -2600,7 +2615,9 @@
 
                     mResourceManagerProxy->removeClient();
 
-                    (new AMessage)->postReply(mReplyID);
+                    if (mReplyID != nullptr) {
+                        (new AMessage)->postReply(mReplyID);
+                    }
                     break;
                 }
 
@@ -2999,6 +3016,14 @@
                 pushBlankBuffersToNativeWindow(mSurface.get());
             }
 
+            int32_t async = 0;
+            if (msg->findInt32("async", &async) && async) {
+                mResourceManagerProxy->markClientForPendingRemoval();
+                handleSetSurface(NULL);
+                (new AMessage)->postReply(mReplyID);
+                mReplyID = 0;
+            }
+
             break;
         }
 
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 3808d7e..1cb45ac 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -157,7 +157,6 @@
 
 status_t MediaMuxer::stop() {
     Mutex::Autolock autoLock(mMuxerLock);
-
     if (mState == STARTED || mState == ERROR) {
         mState = STOPPED;
         for (size_t i = 0; i < mTrackList.size(); i++) {
@@ -165,7 +164,10 @@
                 return INVALID_OPERATION;
             }
         }
+        // Unlock this mutex to allow notify to be called during stop process.
+        mMuxerLock.unlock();
         status_t err = mWriter->stop();
+        mMuxerLock.lock();
         if (err != OK || mError != OK) {
             ALOGE("stop err: %d, mError:%d", err, mError);
         }
@@ -217,6 +219,11 @@
         sampleMetaData.setInt32(kKeyIsMuxerData, 1);
     }
 
+    if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+        sampleMetaData.setInt32(kKeyIsEndOfStream, 1);
+        ALOGV("BUFFER_FLAG_EOS");
+    }
+
     sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
     // This pushBuffer will wait until the mediaBuffer is consumed.
     return currentTrack->pushBuffer(mediaBuffer);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 022c48e..0e983b2 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -139,6 +139,8 @@
     // object.
     status_t release();
 
+    status_t releaseAsync();
+
     status_t flush();
 
     status_t queueInputBuffer(
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index b9cd18a..3b701f6 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -70,6 +70,7 @@
     kKeyIsSyncFrame       = 'sync',  // int32_t (bool)
     kKeyIsCodecConfig     = 'conf',  // int32_t (bool)
     kKeyIsMuxerData       = 'muxd',  // int32_t (bool)
+    kKeyIsEndOfStream     = 'feos',  // int32_t (bool)
     kKeyTime              = 'time',  // int64_t (usecs)
     kKeyDecodingTime      = 'decT',  // int64_t (decoding timestamp in usecs)
     kKeyNTPTime           = 'ntpT',  // uint64_t (ntp-timestamp)
@@ -237,6 +238,9 @@
     kKeyOpaqueCSD2       = 'csd2',
 
     kKeyHapticChannelCount = 'hapC',
+
+    // Treat empty track as malformed for MediaRecorder.
+    kKeyEmptyTrackMalFormed = 'nemt', // bool (int32_t)
 };
 
 enum {
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 1aa8a20..574bd7a 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -112,7 +112,9 @@
     sp<ABuffer> profileLevelID = NULL;
     if (GetAttribute(params, "profile-level-id", &val)) {
         profileLevelID = decodeHex(val);
-        CHECK_EQ(profileLevelID->size(), 3u);
+        if (profileLevelID != NULL && profileLevelID->size() != 3u) {
+            profileLevelID = NULL;
+        }
     }
 
     Vector<sp<ABuffer> > paramSets;
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index 409f141..ff063e3 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -243,6 +243,10 @@
     sp<MetaData> trackMeta = new MetaData;
     convertMessageToMetaData(format, trackMeta);
     mCurrentTrack = new MediaAdapter(trackMeta);
+    if (mCurrentTrack == nullptr) {
+        ALOGE("MediaAdapter returned nullptr");
+        return -1;
+    }
     status_t result = mWriter->addSource(mCurrentTrack);
     return result;
 }
@@ -301,7 +305,7 @@
     getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
     ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
 
-    getInputBufferInfo(inputFile, inputInfo);
+    ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
     status = addWriterSource(isAudio, param);
     ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
 
@@ -338,7 +342,7 @@
     getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
     ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
 
-    getInputBufferInfo(inputFile, inputInfo);
+    ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
     status = addWriterSource(isAudio, param);
     ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
 
@@ -397,7 +401,7 @@
     getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
     ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
 
-    getInputBufferInfo(inputFile, inputInfo);
+    ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
     status = addWriterSource(isAudio, param);
     ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
 
diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp
index 631a2ab..59ce8db 100644
--- a/media/libstagefright/webm/WebmFrameThread.cpp
+++ b/media/libstagefright/webm/WebmFrameThread.cpp
@@ -321,6 +321,7 @@
 status_t WebmFrameMediaSourceThread::pause() {
     if (mStarted) {
         mPaused = true;
+        mResumed = false;
     }
     return OK;
 }
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index e2e1429..4bf103c 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -89,6 +89,31 @@
         "android.hidl.memory@1.0",
     ],
 
+    runtime_libs: [
+        "libstagefright_soft_aacdec",
+        "libstagefright_soft_aacenc",
+        "libstagefright_soft_amrdec",
+        "libstagefright_soft_amrnbenc",
+        "libstagefright_soft_amrwbenc",
+        "libstagefright_soft_avcdec",
+        "libstagefright_soft_avcenc",
+        "libstagefright_soft_flacdec",
+        "libstagefright_soft_flacenc",
+        "libstagefright_soft_g711dec",
+        "libstagefright_soft_gsmdec",
+        "libstagefright_soft_hevcdec",
+        "libstagefright_soft_mp3dec",
+        "libstagefright_soft_mpeg2dec",
+        "libstagefright_soft_mpeg4dec",
+        "libstagefright_soft_mpeg4enc",
+        "libstagefright_soft_opusdec",
+        "libstagefright_soft_rawdec",
+        "libstagefright_soft_vorbisdec",
+        "libstagefright_soft_vpxdec",
+        "libstagefright_soft_vpxenc",
+        "libstagefright_softomx_plugin",
+    ],
+
     // OMX interfaces force this to stay in 32-bit mode;
     compile_multilib: "32",
 
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index be5af00..ff45c87 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -114,6 +114,7 @@
         info.uid = uid;
         info.clientId = clientId;
         info.client = client;
+        info.pendingRemoval = false;
 
         index = infos.add(clientId, info);
     }
@@ -648,6 +649,36 @@
     return Status::ok();
 }
 
+Status ResourceManagerService::markClientForPendingRemoval(int32_t pid, int64_t clientId) {
+    String8 log = String8::format(
+            "markClientForPendingRemoval(pid %d, clientId %lld)",
+            pid, (long long) clientId);
+    mServiceLog->add(log);
+
+    Mutex::Autolock lock(mLock);
+    if (!mProcessInfo->isValidPid(pid)) {
+        ALOGE("Rejected markClientForPendingRemoval call with invalid pid.");
+        return Status::fromServiceSpecificError(BAD_VALUE);
+    }
+    ssize_t index = mMap.indexOfKey(pid);
+    if (index < 0) {
+        ALOGV("markClientForPendingRemoval: didn't find pid %d for clientId %lld",
+              pid, (long long)clientId);
+        return Status::ok();
+    }
+    ResourceInfos &infos = mMap.editValueAt(index);
+
+    index = infos.indexOfKey(clientId);
+    if (index < 0) {
+        ALOGV("markClientForPendingRemoval: didn't find clientId %lld", (long long) clientId);
+        return Status::ok();
+    }
+
+    ResourceInfo &info = infos.editValueAt(index);
+    info.pendingRemoval = true;
+    return Status::ok();
+}
+
 bool ResourceManagerService::getPriority_l(int pid, int* priority) {
     int newPid = pid;
 
@@ -693,6 +724,12 @@
     int lowestPriorityPid;
     int lowestPriority;
     int callingPriority;
+
+    // Before looking into other processes, check if we have clients marked for
+    // pending removal in the same process.
+    if (getBiggestClient_l(callingPid, type, client, true /* pendingRemovalOnly */)) {
+        return true;
+    }
     if (!getPriority_l(callingPid, &callingPriority)) {
         ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d",
                 callingPid);
@@ -761,7 +798,8 @@
 }
 
 bool ResourceManagerService::getBiggestClient_l(
-        int pid, MediaResource::Type type, std::shared_ptr<IResourceManagerClient> *client) {
+        int pid, MediaResource::Type type, std::shared_ptr<IResourceManagerClient> *client,
+        bool pendingRemovalOnly) {
     ssize_t index = mMap.indexOfKey(pid);
     if (index < 0) {
         ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid);
@@ -773,6 +811,9 @@
     const ResourceInfos &infos = mMap.valueAt(index);
     for (size_t i = 0; i < infos.size(); ++i) {
         const ResourceList &resources = infos[i].resources;
+        if (pendingRemovalOnly && !infos[i].pendingRemoval) {
+            continue;
+        }
         for (auto it = resources.begin(); it != resources.end(); it++) {
             const MediaResourceParcel &resource = it->second;
             if (resource.type == type) {
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index f500c62..ee982b7 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -18,6 +18,8 @@
 #ifndef ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
 #define ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
 
+#include <map>
+
 #include <aidl/android/media/BnResourceManagerService.h>
 #include <arpa/inet.h>
 #include <media/MediaResource.h>
@@ -50,6 +52,7 @@
     std::shared_ptr<IResourceManagerClient> client;
     sp<DeathNotifier> deathNotifier;
     ResourceList resources;
+    bool pendingRemoval{false};
 };
 
 // TODO: convert these to std::map
@@ -122,6 +125,8 @@
             int originalPid,
             int newPid) override;
 
+    Status markClientForPendingRemoval(int32_t pid, int64_t clientId) override;
+
     Status removeResource(int pid, int64_t clientId, bool checkValid);
 
 private:
@@ -146,7 +151,8 @@
     // Gets the client who owns biggest piece of specified resource type from pid.
     // Returns false if failed. The client will remain unchanged if failed.
     bool getBiggestClient_l(int pid, MediaResource::Type type,
-            std::shared_ptr<IResourceManagerClient> *client);
+            std::shared_ptr<IResourceManagerClient> *client,
+            bool pendingRemovalOnly = false);
 
     bool isCallingPriorityHigher_l(int callingPid, int pid);
 
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 5d839fa..702935d 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -472,6 +472,56 @@
         }
     }
 
+    void testMarkClientForPendingRemoval() {
+        bool result;
+
+        {
+            addResource();
+            mService->mSupportsSecureWithNonSecureCodec = true;
+
+            std::vector<MediaResourceParcel> resources;
+            resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+
+            // Remove low priority clients
+            mService->removeClient(kTestPid1, getId(mTestClient1));
+
+            // no lower priority client
+            CHECK_STATUS_FALSE(mService->reclaimResource(kTestPid2, resources, &result));
+            verifyClients(false /* c1 */, false /* c2 */, false /* c3 */);
+
+            mService->markClientForPendingRemoval(kTestPid2, getId(mTestClient2));
+
+            // client marked for pending removal from the same process got reclaimed
+            CHECK_STATUS_TRUE(mService->reclaimResource(kTestPid2, resources, &result));
+            verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
+
+            // clean up client 3 which still left
+            mService->removeClient(kTestPid2, getId(mTestClient3));
+        }
+
+        {
+            addResource();
+            mService->mSupportsSecureWithNonSecureCodec = true;
+
+            std::vector<MediaResourceParcel> resources;
+            resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+
+            mService->markClientForPendingRemoval(kTestPid2, getId(mTestClient2));
+
+            // client marked for pending removal from the same process got reclaimed
+            // first, even though there are lower priority process
+            CHECK_STATUS_TRUE(mService->reclaimResource(kTestPid2, resources, &result));
+            verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
+
+            // lower priority client got reclaimed
+            CHECK_STATUS_TRUE(mService->reclaimResource(kTestPid2, resources, &result));
+            verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
+
+            // clean up client 3 which still left
+            mService->removeClient(kTestPid2, getId(mTestClient3));
+        }
+    }
+
     void testRemoveClient() {
         addResource();
 
@@ -900,4 +950,8 @@
     testOverridePid();
 }
 
+TEST_F(ResourceManagerServiceTest, markClientForPendingRemoval) {
+    testMarkClientForPendingRemoval();
+}
+
 } // namespace android