FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device,
                               sp<Camera2Client> client) :
    FrameProcessorBase(device),
    mClient(client),
    mLastFrameNumberOfFaces(0),
    mLast3AFrameNumber(-1) {

    sp<CameraDeviceBase> d = device.promote();
    mSynthesize3ANotify = !(d->willNotify3A());

    {
        SharedParameters::Lock l(client->getParameters());

        if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
            mUsePartialResult = (mNumPartialResults > 1);
        } else {
            mUsePartialResult = l.mParameters.quirks.partialResults;
        }

        // Initialize starting 3A state
        m3aState.afTriggerId = l.mParameters.afTriggerCounter;
        m3aState.aeTriggerId = l.mParameters.precaptureTriggerCounter;
        // Check if lens is fixed-focus
        if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) {
            m3aState.afMode = ANDROID_CONTROL_AF_MODE_OFF;
        }
    }
}
status_t FrameProcessor::process3aState(const CaptureResult &frame,
        const sp<Camera2Client> &client) {

    ATRACE_CALL();
    const CameraMetadata &metadata = frame.mMetadata;
    camera_metadata_ro_entry_t entry;
    int cameraId = client->getCameraId();

    entry = metadata.find(ANDROID_REQUEST_FRAME_COUNT);
    int32_t frameNumber = entry.data.i32[0];

    // Don't send 3A notifications for the same frame number twice
    if (frameNumber <= mLast3AFrameNumber) {
        ALOGV("%s: Already sent 3A for frame number %d, skipping",
                __FUNCTION__, frameNumber);
        return OK;
    }

    mLast3AFrameNumber = frameNumber;

    // Get 3A states from result metadata
    bool gotAllStates = true;

    AlgState new3aState;

    // TODO: Also use AE mode, AE trigger ID

    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AF_MODE,
            &new3aState.afMode, frameNumber, cameraId);

    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AWB_MODE,
            &new3aState.awbMode, frameNumber, cameraId);

    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AE_STATE,
            &new3aState.aeState, frameNumber, cameraId);

    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AF_STATE,
            &new3aState.afState, frameNumber, cameraId);

    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AWB_STATE,
            &new3aState.awbState, frameNumber, cameraId);

    if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
        new3aState.afTriggerId = frame.mResultExtras.afTriggerId;
        new3aState.aeTriggerId = frame.mResultExtras.precaptureTriggerId;
    } else {
        gotAllStates &= get3aResult<int32_t>(metadata, ANDROID_CONTROL_AF_TRIGGER_ID,
                 &new3aState.afTriggerId, frameNumber, cameraId);

        gotAllStates &= get3aResult<int32_t>(metadata, ANDROID_CONTROL_AE_PRECAPTURE_ID,
                 &new3aState.aeTriggerId, frameNumber, cameraId);
    }

    if (!gotAllStates) return BAD_VALUE;

    if (new3aState.aeState != m3aState.aeState) {
        ALOGV("%s: Camera %d: AE state %d->%d",
                __FUNCTION__, cameraId,
                m3aState.aeState, new3aState.aeState);
        client->notifyAutoExposure(new3aState.aeState, new3aState.aeTriggerId);
    }

    if (new3aState.afState != m3aState.afState ||
        new3aState.afMode != m3aState.afMode ||
        new3aState.afTriggerId != m3aState.afTriggerId) {
        ALOGV("%s: Camera %d: AF state %d->%d. AF mode %d->%d. Trigger %d->%d",
                __FUNCTION__, cameraId,
                m3aState.afState, new3aState.afState,
                m3aState.afMode, new3aState.afMode,
                m3aState.afTriggerId, new3aState.afTriggerId);
        client->notifyAutoFocus(new3aState.afState, new3aState.afTriggerId);
    }
    if (new3aState.awbState != m3aState.awbState ||
        new3aState.awbMode != m3aState.awbMode) {
        ALOGV("%s: Camera %d: AWB state %d->%d. AWB mode %d->%d",
                __FUNCTION__, cameraId,
                m3aState.awbState, new3aState.awbState,
                m3aState.awbMode, new3aState.awbMode);
        client->notifyAutoWhitebalance(new3aState.awbState,
                new3aState.aeTriggerId);
    }

    m3aState = new3aState;

    return OK;
}