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

Commit b83eb465 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Camera: Detect session parameter updates and re-configure camera"

parents a31ef4ce ac3ce6c3
Loading
Loading
Loading
Loading
+255 −13
Original line number Diff line number Diff line
@@ -194,8 +194,14 @@ status_t Camera3Device::initializeCommonLocked() {

    mTagMonitor.initialize(mVendorTagId);

    Vector<int32_t> sessionParamKeys;
    camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
            ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
    if (sessionKeysEntry.count > 0) {
        sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
    }
    /** Start up request queue thread */
    mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
    mRequestThread = new RequestThread(this, mStatusTracker, mInterface, sessionParamKeys);
    res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
    if (res != OK) {
        SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -1104,7 +1110,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
    if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
        // This point should only be reached via API1 (API2 must explicitly call configureStreams)
        // so unilaterally select normal operating mode.
        res = configureStreamsLocked(CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE, mSessionParams);
        res = filterParamsAndConfigureLocked(request, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
        // Stream configuration failed. Client might try other configuraitons.
        if (res != OK) {
            CLOGE("Can't set up streams: %s (%d)", strerror(-res), res);
@@ -1508,11 +1514,20 @@ status_t Camera3Device::configureStreams(const CameraMetadata& sessionParams, in
    Mutex::Autolock il(mInterfaceLock);
    Mutex::Autolock l(mLock);

    return filterParamsAndConfigureLocked(sessionParams, operatingMode);
}

status_t Camera3Device::filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
        int operatingMode) {
    //Filter out any incoming session parameters
    const CameraMetadata params(sessionParams);
    CameraMetadata filteredParams;
    camera_metadata_entry_t availableSessionKeys = mDeviceInfo.find(
            ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
    CameraMetadata filteredParams(availableSessionKeys.count);
    camera_metadata_t *meta = const_cast<camera_metadata_t *>(
            filteredParams.getAndLock());
    set_camera_metadata_vendor_id(meta, mVendorTagId);
    filteredParams.unlock(meta);
    if (availableSessionKeys.count > 0) {
        for (size_t i = 0; i < availableSessionKeys.count; i++) {
            camera_metadata_ro_entry entry = params.find(
@@ -2203,10 +2218,47 @@ void Camera3Device::cancelStreamsConfigurationLocked() {
    // properly clean things up
    internalUpdateStatusLocked(STATUS_UNCONFIGURED);
    mNeedConfig = true;

    res = mPreparerThread->resume();
    if (res != OK) {
        ALOGE("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
    }
}

bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
    ATRACE_CALL();
    bool ret = false;

    Mutex::Autolock il(mInterfaceLock);
    nsecs_t maxExpectedDuration = getExpectedInFlightDuration();

    Mutex::Autolock l(mLock);
    auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
    if (rc == NO_ERROR) {
        mNeedConfig = true;
        rc = configureStreamsLocked(mOperatingMode, sessionParams, /*notifyRequestThread*/ false);
        if (rc == NO_ERROR) {
            ret = true;
            mPauseStateNotify = false;
            //Moving to active state while holding 'mLock' is important.
            //There could be pending calls to 'create-/deleteStream' which
            //will trigger another stream configuration while the already
            //present streams end up with outstanding buffers that will
            //not get drained.
            internalUpdateStatusLocked(STATUS_ACTIVE);
        } else {
            setErrorStateLocked("%s: Failed to re-configure camera: %d",
                    __FUNCTION__, rc);
        }
    } else {
        ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
    }

    return ret;
}

status_t Camera3Device::configureStreamsLocked(int operatingMode,
        const CameraMetadata& sessionParams) {
        const CameraMetadata& sessionParams, bool notifyRequestThread) {
    ATRACE_CALL();
    status_t res;

@@ -2247,6 +2299,8 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
    // Start configuring the streams
    ALOGV("%s: Camera %s: Starting stream configuration", __FUNCTION__, mId.string());

    mPreparerThread->pause();

    camera3_stream_configuration config;
    config.operation_mode = mOperatingMode;
    config.num_streams = (mInputStream != NULL) + mOutputStreams.size();
@@ -2338,7 +2392,9 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,

    // Request thread needs to know to avoid using repeat-last-settings protocol
    // across configure_streams() calls
    mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration);
    if (notifyRequestThread) {
        mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration, sessionParams);
    }

    char value[PROPERTY_VALUE_MAX];
    property_get("camera.fifo.disable", value, "0");
@@ -2376,6 +2432,12 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
    // tear down the deleted streams after configure streams.
    mDeletedStreams.clear();

    auto rc = mPreparerThread->resume();
    if (rc != OK) {
        SET_ERR_L("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
        return rc;
    }

    return OK;
}

@@ -3747,7 +3809,7 @@ void Camera3Device::HalInterface::onBufferFreed(

Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
        sp<StatusTracker> statusTracker,
        sp<HalInterface> interface) :
        sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys) :
        Thread(/*canCallJava*/false),
        mParent(parent),
        mStatusTracker(statusTracker),
@@ -3764,7 +3826,9 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
        mRepeatingLastFrameNumber(
            hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
        mPrepareVideoStream(false),
        mRequestLatency(kRequestLatencyBinSize) {
        mRequestLatency(kRequestLatencyBinSize),
        mSessionParamKeys(sessionParamKeys),
        mLatestSessionParams(sessionParamKeys.size()) {
    mStatusId = statusTracker->addComponent();
}

@@ -3777,10 +3841,12 @@ void Camera3Device::RequestThread::setNotificationListener(
    mListener = listener;
}

void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed) {
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed,
        const CameraMetadata& sessionParams) {
    ATRACE_CALL();
    Mutex::Autolock l(mRequestLock);
    mReconfigured = true;
    mLatestSessionParams = sessionParams;
    // Prepare video stream for high speed recording.
    mPrepareVideoStream = isConstrainedHighSpeed;
}
@@ -4191,6 +4257,52 @@ nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_
    return maxExpectedDuration;
}

bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
    ATRACE_CALL();
    bool updatesDetected = false;

    for (auto tag : mSessionParamKeys) {
        camera_metadata_ro_entry entry = settings.find(tag);
        camera_metadata_entry lastEntry = mLatestSessionParams.find(tag);

        if (entry.count > 0) {
            bool isDifferent = false;
            if (lastEntry.count > 0) {
                // Have a last value, compare to see if changed
                if (lastEntry.type == entry.type &&
                        lastEntry.count == entry.count) {
                    // Same type and count, compare values
                    size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
                    size_t entryBytes = bytesPerValue * lastEntry.count;
                    int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
                    if (cmp != 0) {
                        isDifferent = true;
                    }
                } else {
                    // Count or type has changed
                    isDifferent = true;
                }
            } else {
                // No last entry, so always consider to be different
                isDifferent = true;
            }

            if (isDifferent) {
                ALOGV("%s: Session parameter tag id %d changed", __FUNCTION__, tag);
                mLatestSessionParams.update(entry);
                updatesDetected = true;
            }
        } else if (lastEntry.count > 0) {
            // Value has been removed
            ALOGV("%s: Session parameter tag id %d removed", __FUNCTION__, tag);
            mLatestSessionParams.erase(tag);
            updatesDetected = true;
        }
    }

    return updatesDetected;
}

bool Camera3Device::RequestThread::threadLoop() {
    ATRACE_CALL();
    status_t res;
@@ -4217,6 +4329,49 @@ bool Camera3Device::RequestThread::threadLoop() {
        latestRequestId = NAME_NOT_FOUND;
    }

    // 'mNextRequests' will at this point contain either a set of HFR batched requests
    //  or a single request from streaming or burst. In either case the first element
    //  should contain the latest camera settings that we need to check for any session
    //  parameter updates.
    if (updateSessionParameters(mNextRequests[0].captureRequest->mSettings)) {
        res = OK;

        //Input stream buffers are already acquired at this point so an input stream
        //will not be able to move to idle state unless we force it.
        if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
            res = mNextRequests[0].captureRequest->mInputStream->forceToIdle();
            if (res != OK) {
                ALOGE("%s: Failed to force idle input stream: %d", __FUNCTION__, res);
                cleanUpFailedRequests(/*sendRequestError*/ false);
                return false;
            }
        }

        if (res == OK) {
            sp<StatusTracker> statusTracker = mStatusTracker.promote();
            if (statusTracker != 0) {
                statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);

                sp<Camera3Device> parent = mParent.promote();
                if (parent != nullptr) {
                    mReconfigured |= parent->reconfigureCamera(mLatestSessionParams);
                }

                statusTracker->markComponentActive(mStatusId);
                setPaused(false);
            }

            if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
                mNextRequests[0].captureRequest->mInputStream->restoreConfiguredState();
                if (res != OK) {
                    ALOGE("%s: Failed to restore configured input stream: %d", __FUNCTION__, res);
                    cleanUpFailedRequests(/*sendRequestError*/ false);
                    return false;
                }
            }
        }
    }

    // Prepare a batch of HAL requests and output buffers.
    res = prepareHalRequests();
    if (res == TIMED_OUT) {
@@ -4980,7 +5135,7 @@ status_t Camera3Device::RequestThread::addDummyTriggerIds(

Camera3Device::PreparerThread::PreparerThread() :
        Thread(/*canCallJava*/false), mListener(nullptr),
        mActive(false), mCancelNow(false) {
        mActive(false), mCancelNow(false), mCurrentMaxCount(0), mCurrentPrepareComplete(false) {
}

Camera3Device::PreparerThread::~PreparerThread() {
@@ -5031,18 +5186,101 @@ status_t Camera3Device::PreparerThread::prepare(int maxCount, sp<Camera3StreamIn
    }

    // queue up the work
    mPendingStreams.push_back(stream);
    mPendingStreams.emplace(maxCount, stream);
    ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId());

    return OK;
}

void Camera3Device::PreparerThread::pause() {
    ATRACE_CALL();

    Mutex::Autolock l(mLock);

    std::unordered_map<int, sp<camera3::Camera3StreamInterface> > pendingStreams;
    pendingStreams.insert(mPendingStreams.begin(), mPendingStreams.end());
    sp<camera3::Camera3StreamInterface> currentStream = mCurrentStream;
    int currentMaxCount = mCurrentMaxCount;
    mPendingStreams.clear();
    mCancelNow = true;
    while (mActive) {
        auto res = mThreadActiveSignal.waitRelative(mLock, kActiveTimeout);
        if (res == TIMED_OUT) {
            ALOGE("%s: Timed out waiting on prepare thread!", __FUNCTION__);
            return;
        } else if (res != OK) {
            ALOGE("%s: Encountered an error: %d waiting on prepare thread!", __FUNCTION__, res);
            return;
        }
    }

    //Check whether the prepare thread was able to complete the current
    //stream. In case work is still pending emplace it along with the rest
    //of the streams in the pending list.
    if (currentStream != nullptr) {
        if (!mCurrentPrepareComplete) {
            pendingStreams.emplace(currentMaxCount, currentStream);
        }
    }

    mPendingStreams.insert(pendingStreams.begin(), pendingStreams.end());
    for (const auto& it : mPendingStreams) {
        it.second->cancelPrepare();
    }
}

status_t Camera3Device::PreparerThread::resume() {
    ATRACE_CALL();
    status_t res;

    Mutex::Autolock l(mLock);
    sp<NotificationListener> listener = mListener.promote();

    if (mActive) {
        ALOGE("%s: Trying to resume an already active prepare thread!", __FUNCTION__);
        return NO_INIT;
    }

    auto it = mPendingStreams.begin();
    for (; it != mPendingStreams.end();) {
        res = it->second->startPrepare(it->first);
        if (res == OK) {
            if (listener != NULL) {
                listener->notifyPrepared(it->second->getId());
            }
            it = mPendingStreams.erase(it);
        } else if (res != NOT_ENOUGH_DATA) {
            ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__,
                    res, strerror(-res));
            it = mPendingStreams.erase(it);
        } else {
            it++;
        }
    }

    if (mPendingStreams.empty()) {
        return OK;
    }

    res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
    if (res != OK) {
        ALOGE("%s: Unable to start preparer stream: %d (%s)",
                __FUNCTION__, res, strerror(-res));
        return res;
    }
    mCancelNow = false;
    mActive = true;
    ALOGV("%s: Preparer stream started", __FUNCTION__);

    return OK;
}

status_t Camera3Device::PreparerThread::clear() {
    ATRACE_CALL();
    Mutex::Autolock l(mLock);

    for (const auto& stream : mPendingStreams) {
        stream->cancelPrepare();
    for (const auto& it : mPendingStreams) {
        it.second->cancelPrepare();
    }
    mPendingStreams.clear();
    mCancelNow = true;
@@ -5067,12 +5305,15 @@ bool Camera3Device::PreparerThread::threadLoop() {
                // threadLoop _must not_ re-acquire mLock after it sets mActive to false; would
                // cause deadlock with prepare()'s requestExitAndWait triggered by !mActive.
                mActive = false;
                mThreadActiveSignal.signal();
                return false;
            }

            // Get next stream to prepare
            auto it = mPendingStreams.begin();
            mCurrentStream = *it;
            mCurrentStream = it->second;
            mCurrentMaxCount = it->first;
            mCurrentPrepareComplete = false;
            mPendingStreams.erase(it);
            ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId());
            ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId());
@@ -5107,6 +5348,7 @@ bool Camera3Device::PreparerThread::threadLoop() {

    ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
    mCurrentStream.clear();
    mCurrentPrepareComplete = true;

    return true;
}
+39 −4
Original line number Diff line number Diff line
@@ -551,12 +551,24 @@ class Camera3Device :
    sp<CaptureRequest> createCaptureRequest(const CameraMetadata &request,
                                            const SurfaceMap &surfaceMap);

    /**
     * Internally re-configure camera device using new session parameters.
     * This will get triggered by the request thread.
     */
    bool reconfigureCamera(const CameraMetadata& sessionParams);

    /**
     * Filter stream session parameters and configure camera HAL.
     */
    status_t filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
            int operatingMode);

    /**
     * Take the currently-defined set of streams and configure the HAL to use
     * them. This is a long-running operation (may be several hundered ms).
     */
    status_t           configureStreamsLocked(int operatingMode,
            const CameraMetadata& sessionParams);
            const CameraMetadata& sessionParams, bool notifyRequestThread = true);

    /**
     * Cancel stream configuration that did not finish successfully.
@@ -655,7 +667,7 @@ class Camera3Device :

        RequestThread(wp<Camera3Device> parent,
                sp<camera3::StatusTracker> statusTracker,
                sp<HalInterface> interface);
                sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys);
        ~RequestThread();

        void     setNotificationListener(wp<NotificationListener> listener);
@@ -663,7 +675,8 @@ class Camera3Device :
        /**
         * Call after stream (re)-configuration is completed.
         */
        void     configurationComplete(bool isConstrainedHighSpeed);
        void     configurationComplete(bool isConstrainedHighSpeed,
                const CameraMetadata& sessionParams);

        /**
         * Set or clear the list of repeating requests. Does not block
@@ -812,6 +825,12 @@ class Camera3Device :
        // Calculate the expected maximum duration for a request
        nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);

        // Check and update latest session parameters based on the current request settings.
        bool updateSessionParameters(const CameraMetadata& settings);

        // Re-configure camera using the latest session parameters.
        bool reconfigureCamera();

        wp<Camera3Device>  mParent;
        wp<camera3::StatusTracker>  mStatusTracker;
        sp<HalInterface>   mInterface;
@@ -869,6 +888,9 @@ class Camera3Device :

        static const int32_t kRequestLatencyBinSize = 40; // in ms
        CameraLatencyHistogram mRequestLatency;

        Vector<int32_t>    mSessionParamKeys;
        CameraMetadata     mLatestSessionParams;
    };
    sp<RequestThread> mRequestThread;

@@ -1006,21 +1028,34 @@ class Camera3Device :
         */
        status_t clear();

        /**
         * Pause all preparation activities
         */
        void pause();

        /**
         * Resume preparation activities
         */
        status_t resume();

      private:
        Mutex mLock;
        Condition mThreadActiveSignal;

        virtual bool threadLoop();

        // Guarded by mLock

        wp<NotificationListener> mListener;
        List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
        std::unordered_map<int, sp<camera3::Camera3StreamInterface> > mPendingStreams;
        bool mActive;
        bool mCancelNow;

        // Only accessed by threadLoop and the destructor

        sp<camera3::Camera3StreamInterface> mCurrentStream;
        int mCurrentMaxCount;
        bool mCurrentPrepareComplete;
    };
    sp<PreparerThread> mPreparerThread;

+82 −1
Original line number Diff line number Diff line
@@ -140,6 +140,75 @@ android_dataspace Camera3Stream::getOriginalDataSpace() const {
    return mOriginalDataSpace;
}

status_t Camera3Stream::forceToIdle() {
    ATRACE_CALL();
    Mutex::Autolock l(mLock);
    status_t res;

    switch (mState) {
        case STATE_ERROR:
        case STATE_CONSTRUCTED:
        case STATE_IN_CONFIG:
        case STATE_PREPARING:
        case STATE_IN_RECONFIG:
            ALOGE("%s: Invalid state: %d", __FUNCTION__, mState);
            res = NO_INIT;
            break;
        case STATE_CONFIGURED:
            if (hasOutstandingBuffersLocked()) {
                sp<StatusTracker> statusTracker = mStatusTracker.promote();
                if (statusTracker != 0) {
                    statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
                }
            }

            mState = STATE_IN_IDLE;
            res = OK;

            break;
        default:
            ALOGE("%s: Unknown state %d", __FUNCTION__, mState);
            res = NO_INIT;
    }

    return res;
}

status_t Camera3Stream::restoreConfiguredState() {
    ATRACE_CALL();
    Mutex::Autolock l(mLock);
    status_t res;

    switch (mState) {
        case STATE_ERROR:
        case STATE_CONSTRUCTED:
        case STATE_IN_CONFIG:
        case STATE_PREPARING:
        case STATE_IN_RECONFIG:
        case STATE_CONFIGURED:
            ALOGE("%s: Invalid state: %d", __FUNCTION__, mState);
            res = NO_INIT;
            break;
        case STATE_IN_IDLE:
            if (hasOutstandingBuffersLocked()) {
                sp<StatusTracker> statusTracker = mStatusTracker.promote();
                if (statusTracker != 0) {
                    statusTracker->markComponentActive(mStatusId);
                }
            }

            mState = STATE_CONFIGURED;
            res = OK;

            break;
        default:
            ALOGE("%s: Unknown state %d", __FUNCTION__, mState);
            res = NO_INIT;
    }

    return res;
}

camera3_stream* Camera3Stream::startConfiguration() {
    ATRACE_CALL();
    Mutex::Autolock l(mLock);
@@ -150,6 +219,7 @@ camera3_stream* Camera3Stream::startConfiguration() {
            ALOGE("%s: In error state", __FUNCTION__);
            return NULL;
        case STATE_CONSTRUCTED:
        case STATE_IN_IDLE:
            // OK
            break;
        case STATE_IN_CONFIG:
@@ -179,6 +249,11 @@ camera3_stream* Camera3Stream::startConfiguration() {
        return NULL;
    }

    if (mState == STATE_IN_IDLE) {
        // Skip configuration.
        return this;
    }

    // Stop tracking if currently doing so
    if (mStatusId != StatusTracker::NO_STATUS_ID) {
        sp<StatusTracker> statusTracker = mStatusTracker.promote();
@@ -219,6 +294,9 @@ status_t Camera3Stream::finishConfiguration() {
            ALOGE("%s: Cannot finish configuration that hasn't been started",
                    __FUNCTION__);
            return INVALID_OPERATION;
        case STATE_IN_IDLE:
            //Skip configuration in this state
            return OK;
        default:
            ALOGE("%s: Unknown state", __FUNCTION__);
            return INVALID_OPERATION;
@@ -267,6 +345,7 @@ status_t Camera3Stream::cancelConfiguration() {
            return INVALID_OPERATION;
        case STATE_IN_CONFIG:
        case STATE_IN_RECONFIG:
        case STATE_IN_IDLE:
            // OK
            break;
        case STATE_CONSTRUCTED:
@@ -282,7 +361,9 @@ status_t Camera3Stream::cancelConfiguration() {
    mUsage = mOldUsage;
    camera3_stream::max_buffers = mOldMaxBuffers;

    mState = (mState == STATE_IN_RECONFIG) ? STATE_CONFIGURED : STATE_CONSTRUCTED;
    mState = ((mState == STATE_IN_RECONFIG) || (mState == STATE_IN_IDLE)) ? STATE_CONFIGURED :
            STATE_CONSTRUCTED;

    return OK;
}

+29 −2
Original line number Diff line number Diff line
@@ -68,6 +68,12 @@ namespace camera3 {
 *    duration.  In this state, only prepareNextBuffer() and cancelPrepare()
 *    may be called.
 *
 *  STATE_IN_IDLE: This is a temporary state only intended to be used for input
 *    streams and only for the case where we need to re-configure the camera device
 *    while the input stream has an outstanding buffer. All other streams should not
 *    be able to switch to this state. For them this is invalid and should be handled
 *    as an unknown state.
 *
 * Transition table:
 *
 *    <none>               => STATE_CONSTRUCTED:
@@ -98,6 +104,11 @@ namespace camera3 {
 *        all stream buffers, or cancelPrepare is called.
 *    STATE_CONFIGURED     => STATE_ABANDONED:
 *        When the buffer queue of the stream is abandoned.
 *    STATE_CONFIGURED     => STATE_IN_IDLE:
 *        Only for an input stream which has an outstanding buffer.
 *    STATE_IN_IDLE     => STATE_CONFIGURED:
 *        After the internal re-configuration, the input should revert back to
 *        the configured state.
 *
 * Status Tracking:
 *    Each stream is tracked by StatusTracker as a separate component,
@@ -108,7 +119,9 @@ namespace camera3 {
 *
 *    - ACTIVE: One or more buffers have been handed out (with #getBuffer).
 *    - IDLE: All buffers have been returned (with #returnBuffer), and their
 *          respective release_fence(s) have been signaled.
 *          respective release_fence(s) have been signaled. The only exception to this
 *          rule is an input stream that moves to "STATE_IN_IDLE" during internal
 *          re-configuration.
 *
 *    A typical use case is output streams. When the HAL has any buffers
 *    dequeued, the stream is marked ACTIVE. When the HAL returns all buffers
@@ -386,6 +399,19 @@ class Camera3Stream :
     */
    bool             isAbandoned() const;

    /**
     * Switch a configured stream with possibly outstanding buffers in idle
     * state. Configuration for such streams will be skipped assuming there
     * are no changes to the stream parameters.
     */
    status_t         forceToIdle();

    /**
     * Restore a forced idle stream to configured state, marking it active
     * in case it contains outstanding buffers.
     */
    status_t         restoreConfiguredState();

  protected:
    const int mId;
    /**
@@ -414,7 +440,8 @@ class Camera3Stream :
        STATE_IN_RECONFIG,
        STATE_CONFIGURED,
        STATE_PREPARING,
        STATE_ABANDONED
        STATE_ABANDONED,
        STATE_IN_IDLE
    } mState;

    mutable Mutex mLock;