Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f6c9c5bd authored by Wonsik Kim's avatar Wonsik Kim
Browse files

MediaCodec: ensure reply does not get lost

The following operations store mReplyID to wait for a codec event
before replying:

- init, configure, start, flush, stop:
  These operations causes state transitions.
  Defer if mReplyID is set.
  When handling operation complete event, check current state and
  don't reply if the state is not an expected value.

- signalEOS, createInputSurface, setInputSurface:
  These operations do not cause state transitions.
  Defer if mReplyID is set.
  When handling operation complete event, check current state and
  don't reply if the state is not an expected value.

- release:
  Release is special, as we want to release even if we are in the middle
  of handling other operations.
  Reply to current operation to unblock app and start releasing.
  Release complete event is always handled.

Bug: 154678891
Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small
Change-Id: I08286bffb13150813a1d237e49ab1bbf8f544f32
parent 9900ec1c
Loading
Loading
Loading
Loading
+157 −47
Original line number Diff line number Diff line
@@ -1008,6 +1008,12 @@ status_t MediaCodec::PostAndAwaitResponse(
    return err;
}

void MediaCodec::PostReplyWithError(const sp<AMessage> &msg, int32_t err) {
    sp<AReplyToken> replyID;
    CHECK(msg->senderAwaitsResponse(&replyID));
    PostReplyWithError(replyID, err);
}

void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
    int32_t finalErr = err;
    if (mReleasedByResourceManager) {
@@ -1511,7 +1517,6 @@ status_t MediaCodec::reset() {
    mStickyError = OK;

    // reset state not reset by setState(UNINITIALIZED)
    mReplyID = 0;
    mDequeueInputReplyID = 0;
    mDequeueOutputReplyID = 0;
    mDequeueInputTimeoutGeneration = 0;
@@ -2159,7 +2164,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                                if (mState == RELEASING) {
                                    mComponentName.clear();
                                }
                                (new AMessage)->postReply(mReplyID);
                                postPendingRepliesAndDeferredMessages();
                                sendErrorResponse = false;
                            }
                            break;
@@ -2185,7 +2190,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                        case FLUSHED:
                        case STARTED:
                        {
                            sendErrorResponse = false;
                            sendErrorResponse = (mReplyID != nullptr);

                            setStickyError(err);
                            postActivityNotificationIfPossible();
@@ -2215,7 +2220,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

                        default:
                        {
                            sendErrorResponse = false;
                            sendErrorResponse = (mReplyID != nullptr);

                            setStickyError(err);
                            postActivityNotificationIfPossible();
@@ -2242,7 +2247,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                    }

                    if (sendErrorResponse) {
                        PostReplyWithError(mReplyID, err);
                        // TRICKY: replicate PostReplyWithError logic for
                        //         err code override
                        int32_t finalErr = err;
                        if (mReleasedByResourceManager) {
                            // override the err code if MediaCodec has been
                            // released by ResourceManager.
                            finalErr = DEAD_OBJECT;
                        }
                        postPendingRepliesAndDeferredMessages(finalErr);
                    }
                    break;
                }
@@ -2290,7 +2303,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                                MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
                    }

                    (new AMessage)->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages();
                    break;
                }

@@ -2329,7 +2342,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                        mFlags |= kFlagUsesSoftwareRenderer;
                    }
                    setState(CONFIGURED);
                    (new AMessage)->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages();

                    // augment our media metrics info, now that we know more things
                    // such as what the codec extracted from any CSD passed in.
@@ -2374,6 +2387,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

                case kWhatInputSurfaceCreated:
                {
                    if (mState != CONFIGURED) {
                        // state transitioned unexpectedly; we should have replied already.
                        ALOGD("received kWhatInputSurfaceCreated message in state %s",
                                stateString(mState).c_str());
                        break;
                    }
                    // response to initiateCreateInputSurface()
                    status_t err = NO_ERROR;
                    sp<AMessage> response = new AMessage;
@@ -2392,12 +2411,18 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                    } else {
                        response->setInt32("err", err);
                    }
                    response->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages(response);
                    break;
                }

                case kWhatInputSurfaceAccepted:
                {
                    if (mState != CONFIGURED) {
                        // state transitioned unexpectedly; we should have replied already.
                        ALOGD("received kWhatInputSurfaceAccepted message in state %s",
                                stateString(mState).c_str());
                        break;
                    }
                    // response to initiateSetInputSurface()
                    status_t err = NO_ERROR;
                    sp<AMessage> response = new AMessage();
@@ -2408,19 +2433,25 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                    } else {
                        response->setInt32("err", err);
                    }
                    response->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages(response);
                    break;
                }

                case kWhatSignaledInputEOS:
                {
                    if (!isExecuting()) {
                        // state transitioned unexpectedly; we should have replied already.
                        ALOGD("received kWhatSignaledInputEOS message in state %s",
                                stateString(mState).c_str());
                        break;
                    }
                    // response to signalEndOfInputStream()
                    sp<AMessage> response = new AMessage;
                    status_t err;
                    if (msg->findInt32("err", &err)) {
                        response->setInt32("err", err);
                    }
                    response->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages(response);
                    break;
                }

@@ -2439,7 +2470,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                                MediaResource::GraphicMemoryResource(getGraphicBufferSize()));
                    }
                    setState(STARTED);
                    (new AMessage)->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages();
                    break;
                }

@@ -2669,7 +2700,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                        break;
                    }
                    setState(INITIALIZED);
                    (new AMessage)->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages();
                    break;
                }

@@ -2693,7 +2724,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                    mReleaseSurface.reset();

                    if (mReplyID != nullptr) {
                        (new AMessage)->postReply(mReplyID);
                        postPendingRepliesAndDeferredMessages();
                    }
                    if (mAsyncReleaseCompleteNotification != nullptr) {
                        flushMediametrics();
@@ -2718,7 +2749,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                        mCodec->signalResume();
                    }

                    (new AMessage)->postReply(mReplyID);
                    postPendingRepliesAndDeferredMessages();
                    break;
                }

@@ -2730,14 +2761,18 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

        case kWhatInit:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            if (mState != UNINITIALIZED) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                PostReplyWithError(msg, INVALID_OPERATION);
                break;
            }

            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            mReplyID = replyID;
            setState(INITIALIZING);

@@ -2799,13 +2834,17 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

        case kWhatConfigure:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            if (mState != INITIALIZED) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                PostReplyWithError(msg, INVALID_OPERATION);
                break;
            }

            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            sp<RefBase> obj;
            CHECK(msg->findObject("surface", &obj));
@@ -2944,14 +2983,18 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
        case kWhatCreateInputSurface:
        case kWhatSetInputSurface:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            // Must be configured, but can't have been started yet.
            if (mState != CONFIGURED) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                PostReplyWithError(msg, INVALID_OPERATION);
                break;
            }

            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            mReplyID = replyID;
            if (msg->what() == kWhatCreateInputSurface) {
@@ -2967,9 +3010,6 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
        }
        case kWhatStart:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            if (mState == FLUSHED) {
                setState(STARTED);
                if (mHavePendingInputBuffers) {
@@ -2977,12 +3017,19 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                    mHavePendingInputBuffers = false;
                }
                mCodec->signalResume();
                PostReplyWithError(replyID, OK);
                PostReplyWithError(msg, OK);
                break;
            } else if (mState != CONFIGURED) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                PostReplyWithError(msg, INVALID_OPERATION);
                break;
            }

            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            mReplyID = replyID;
            setState(STARTING);
@@ -2991,15 +3038,42 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
            break;
        }

        case kWhatStop:
        case kWhatStop: {
            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            [[fallthrough]];
        }
        case kWhatRelease:
        {
            State targetState =
                (msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED;

            if ((mState == RELEASING && targetState == UNINITIALIZED)
                    || (mState == STOPPING && targetState == INITIALIZED)) {
                mDeferredMessages.push_back(msg);
                break;
            }

            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            sp<AMessage> asyncNotify;
            (void)msg->findMessage("async", &asyncNotify);
            // post asyncNotify if going out of scope.
            struct AsyncNotifyPost {
                AsyncNotifyPost(const sp<AMessage> &asyncNotify) : mAsyncNotify(asyncNotify) {}
                ~AsyncNotifyPost() {
                    if (mAsyncNotify) {
                        mAsyncNotify->post();
                    }
                }
                void clear() { mAsyncNotify.clear(); }
            private:
                sp<AMessage> mAsyncNotify;
            } asyncNotifyPost{asyncNotify};

            // already stopped/released
            if (mState == UNINITIALIZED && mReleasedByResourceManager) {
                sp<AMessage> response = new AMessage;
@@ -3063,12 +3137,14 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
            // after this, and we'll no longer be able to reply.
            if (mState == FLUSHING || mState == STOPPING
                    || mState == CONFIGURING || mState == STARTING) {
                (new AMessage)->postReply(mReplyID);
                // mReply is always set if in these states.
                postPendingRepliesAndDeferredMessages();
            }

            if (mFlags & kFlagSawMediaServerDie) {
                // It's dead, Jim. Don't expect initiateShutdown to yield
                // any useful results now...
                // Any pending reply would have been handled at kWhatError.
                setState(UNINITIALIZED);
                if (targetState == UNINITIALIZED) {
                    mComponentName.clear();
@@ -3082,12 +3158,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
            // reply now with an error to unblock the client, client can
            // release after the failure (instead of ANR).
            if (msg->what() == kWhatStop && (mFlags & kFlagStickyError)) {
                // Any pending reply would have been handled at kWhatError.
                PostReplyWithError(replyID, getStickyError());
                break;
            }

            sp<AMessage> asyncNotify;
            if (msg->findMessage("async", &asyncNotify) && asyncNotify != nullptr) {
            if (asyncNotify != nullptr) {
                if (mSurface != NULL) {
                    if (!mReleaseSurface) {
                        mReleaseSurface.reset(new ReleaseSurface);
@@ -3107,6 +3183,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                }
            }

            if (mReplyID) {
                // State transition replies are handled above, so this reply
                // would not be related to state transition. As we are
                // shutting down the component, just fail the operation.
                postPendingRepliesAndDeferredMessages(UNKNOWN_ERROR);
            }
            mReplyID = replyID;
            setState(msg->what() == kWhatStop ? STOPPING : RELEASING);

@@ -3121,8 +3203,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

            if (asyncNotify != nullptr) {
                mResourceManagerProxy->markClientForPendingRemoval();
                (new AMessage)->postReply(mReplyID);
                mReplyID = 0;
                postPendingRepliesAndDeferredMessages();
                asyncNotifyPost.clear();
                mAsyncReleaseCompleteNotification = asyncNotify;
            }

@@ -3293,17 +3375,21 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

        case kWhatSignalEndOfInputStream:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            if (!isExecuting() || !mHaveInputSurface) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                PostReplyWithError(msg, INVALID_OPERATION);
                break;
            } else if (mFlags & kFlagStickyError) {
                PostReplyWithError(replyID, getStickyError());
                PostReplyWithError(msg, getStickyError());
                break;
            }

            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            mReplyID = replyID;
            mCodec->signalEndOfInputStream();
            break;
@@ -3345,16 +3431,20 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {

        case kWhatFlush:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            if (!isExecuting()) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                PostReplyWithError(msg, INVALID_OPERATION);
                break;
            } else if (mFlags & kFlagStickyError) {
                PostReplyWithError(replyID, getStickyError());
                PostReplyWithError(msg, getStickyError());
                break;
            }

            if (mReplyID) {
                mDeferredMessages.push_back(msg);
                break;
            }
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            mReplyID = replyID;
            // TODO: skip flushing if already FLUSHED
@@ -4188,6 +4278,26 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData(
    return OK;
}

void MediaCodec::postPendingRepliesAndDeferredMessages(status_t err /* = OK */) {
    sp<AMessage> response{new AMessage};
    if (err != OK) {
        response->setInt32("err", err);
    }
    postPendingRepliesAndDeferredMessages(response);
}

void MediaCodec::postPendingRepliesAndDeferredMessages(const sp<AMessage> &response) {
    CHECK(mReplyID);
    response->postReply(mReplyID);
    mReplyID.clear();
    ALOGV_IF(!mDeferredMessages.empty(),
            "posting %zu deferred messages", mDeferredMessages.size());
    for (sp<AMessage> msg : mDeferredMessages) {
        msg->post();
    }
    mDeferredMessages.clear();
}

std::string MediaCodec::stateString(State state) {
    const char *rval = NULL;
    char rawbuffer[16]; // room for "%d"
+5 −0
Original line number Diff line number Diff line
@@ -366,6 +366,7 @@ private:
    AString mOwnerName;
    sp<MediaCodecInfo> mCodecInfo;
    sp<AReplyToken> mReplyID;
    std::vector<sp<AMessage>> mDeferredMessages;
    uint32_t mFlags;
    status_t mStickyError;
    sp<Surface> mSurface;
@@ -435,6 +436,7 @@ private:
    static status_t PostAndAwaitResponse(
            const sp<AMessage> &msg, sp<AMessage> *response);

    void PostReplyWithError(const sp<AMessage> &msg, int32_t err);
    void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);

    status_t init(const AString &name);
@@ -484,6 +486,9 @@ private:
    bool hasPendingBuffer(int portIndex);
    bool hasPendingBuffer();

    void postPendingRepliesAndDeferredMessages(status_t err = OK);
    void postPendingRepliesAndDeferredMessages(const sp<AMessage> &response);

    /* called to get the last codec error when the sticky flag is set.
     * if no such codec error is found, returns UNKNOWN_ERROR.
     */