/*
 * Copyright (C) 2019 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.
 */

// #define LOG_NDEBUG 0
#define LOG_TAG "GCH_HdrplusCaptureSession"
#define ATRACE_TAG ATRACE_TAG_CAMERA
#include "hdrplus_capture_session.h"

#include <cutils/properties.h>
#include <inttypes.h>
#include <log/log.h>
#include <utils/Trace.h>

#include <set>

#include "hal_utils.h"
#include "hdrplus_process_block.h"
#include "hdrplus_request_processor.h"
#include "hdrplus_result_processor.h"
#include "realtime_process_block.h"
#include "realtime_zsl_request_processor.h"
#include "realtime_zsl_result_processor.h"
#include "vendor_tag_defs.h"

namespace android {
namespace google_camera_hal {
bool HdrplusCaptureSession::IsStreamConfigurationSupported(
    CameraDeviceSessionHwl* device_session_hwl,
    const StreamConfiguration& stream_config) {
  ATRACE_CALL();
  if (device_session_hwl == nullptr) {
    ALOGE("%s: device_session_hwl is nullptr", __FUNCTION__);
    return false;
  }

  uint32_t num_physical_cameras =
      device_session_hwl->GetPhysicalCameraIds().size();
  if (num_physical_cameras > 1) {
    ALOGD("%s: HdrplusCaptureSession doesn't support %u physical cameras",
          __FUNCTION__, num_physical_cameras);
    return false;
  }

  std::unique_ptr<HalCameraMetadata> characteristics;
  status_t res = device_session_hwl->GetCameraCharacteristics(&characteristics);
  if (res != OK) {
    ALOGE("%s: GetCameraCharacteristics failed.", __FUNCTION__);
    return BAD_VALUE;
  }

  if (hal_utils::IsStreamHdrplusCompatible(stream_config,
                                           characteristics.get()) == false) {
    return false;
  }

  if (!hal_utils::IsBayerCamera(characteristics.get())) {
    ALOGD("%s: camera %d is not a bayer camera", __FUNCTION__,
          device_session_hwl->GetCameraId());
    return false;
  }

  ALOGI("%s: HDR+ is enabled", __FUNCTION__);
  ALOGD("%s: HdrplusCaptureSession supports the stream config", __FUNCTION__);
  return true;
}

std::unique_ptr<HdrplusCaptureSession> HdrplusCaptureSession::Create(
    CameraDeviceSessionHwl* device_session_hwl,
    const StreamConfiguration& stream_config,
    ProcessCaptureResultFunc process_capture_result,
    ProcessBatchCaptureResultFunc /*process_batch_capture_result*/,
    NotifyFunc notify, HwlSessionCallback /*session_callback*/,
    std::vector<HalStream>* hal_configured_streams,
    CameraBufferAllocatorHwl* /*camera_allocator_hwl*/) {
  ATRACE_CALL();
  auto session =
      std::unique_ptr<HdrplusCaptureSession>(new HdrplusCaptureSession());
  if (session == nullptr) {
    ALOGE("%s: Creating HdrplusCaptureSession failed.", __FUNCTION__);
    return nullptr;
  }

  status_t res = session->Initialize(device_session_hwl, stream_config,
                                     process_capture_result, notify,
                                     hal_configured_streams);
  if (res != OK) {
    ALOGE("%s: Initializing HdrplusCaptureSession failed: %s (%d).",
          __FUNCTION__, strerror(-res), res);
    return nullptr;
  }

  return session;
}

HdrplusCaptureSession::~HdrplusCaptureSession() {
  ATRACE_CALL();
  if (device_session_hwl_ != nullptr) {
    device_session_hwl_->DestroyPipelines();
  }
}

status_t HdrplusCaptureSession::ConfigureStreams(
    const StreamConfiguration& stream_config,
    RequestProcessor* request_processor, ProcessBlock* process_block,
    int32_t* raw_stream_id) {
  ATRACE_CALL();
  if (request_processor == nullptr || process_block == nullptr ||
      raw_stream_id == nullptr) {
    ALOGE(
        "%s: request_processor (%p) or process_block (%p) is nullptr or "
        "raw_stream_id (%p) is nullptr",
        __FUNCTION__, request_processor, process_block, raw_stream_id);
    return BAD_VALUE;
  }

  StreamConfiguration process_block_stream_config;
  // Configure streams for request processor
  status_t res = request_processor->ConfigureStreams(
      internal_stream_manager_.get(), stream_config,
      &process_block_stream_config);
  if (res != OK) {
    ALOGE("%s: Configuring stream for request processor failed.", __FUNCTION__);
    return res;
  }

  // Check all streams are configured.
  if (stream_config.streams.size() > process_block_stream_config.streams.size()) {
    ALOGE("%s: stream_config has %zu streams but only configured %zu streams",
          __FUNCTION__, stream_config.streams.size(),
          process_block_stream_config.streams.size());
    return UNKNOWN_ERROR;
  }

  for (auto& stream : stream_config.streams) {
    bool found = false;
    for (auto& configured_stream : process_block_stream_config.streams) {
      if (stream.id == configured_stream.id) {
        found = true;
        break;
      }
    }

    if (!found) {
      ALOGE("%s: Cannot find stream %u in configured streams.", __FUNCTION__,
            stream.id);
      return UNKNOWN_ERROR;
    }
  }

  for (auto& configured_stream : process_block_stream_config.streams) {
    if (configured_stream.format == kHdrplusRawFormat) {
      *raw_stream_id = configured_stream.id;
      break;
    }
  }

  if (*raw_stream_id == -1) {
    ALOGE("%s: Configuring stream fail due to wrong raw_stream_id",
          __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  // Configure streams for process block.
  res = process_block->ConfigureStreams(process_block_stream_config,
                                        stream_config);
  if (res != OK) {
    ALOGE("%s: Configuring stream for process block failed.", __FUNCTION__);
    return res;
  }

  return OK;
}

status_t HdrplusCaptureSession::ConfigureHdrplusStreams(
    const StreamConfiguration& stream_config,
    RequestProcessor* hdrplus_request_processor,
    ProcessBlock* hdrplus_process_block) {
  ATRACE_CALL();
  if (hdrplus_process_block == nullptr || hdrplus_request_processor == nullptr) {
    ALOGE("%s: hdrplus_process_block or hdrplus_request_processor is nullptr",
          __FUNCTION__);
    return BAD_VALUE;
  }

  StreamConfiguration process_block_stream_config;
  // Configure streams for request processor
  status_t res = hdrplus_request_processor->ConfigureStreams(
      internal_stream_manager_.get(), stream_config,
      &process_block_stream_config);
  if (res != OK) {
    ALOGE("%s: Configuring stream for request processor failed.", __FUNCTION__);
    return res;
  }

  // Check all streams are configured.
  if (stream_config.streams.size() > process_block_stream_config.streams.size()) {
    ALOGE("%s: stream_config has %zu streams but only configured %zu streams",
          __FUNCTION__, stream_config.streams.size(),
          process_block_stream_config.streams.size());
    return UNKNOWN_ERROR;
  }

  for (auto& stream : stream_config.streams) {
    bool found = false;
    for (auto& configured_stream : process_block_stream_config.streams) {
      if (stream.id == configured_stream.id) {
        found = true;
        break;
      }
    }

    if (!found) {
      ALOGE("%s: Cannot find stream %u in configured streams.", __FUNCTION__,
            stream.id);
      return UNKNOWN_ERROR;
    }
  }

  // Configure streams for HDR+ process block.
  res = hdrplus_process_block->ConfigureStreams(process_block_stream_config,
                                                stream_config);
  if (res != OK) {
    ALOGE("%s: Configuring hdrplus stream for process block failed.",
          __FUNCTION__);
    return res;
  }

  return OK;
}

status_t HdrplusCaptureSession::BuildPipelines(
    ProcessBlock* process_block, ProcessBlock* hdrplus_process_block,
    std::vector<HalStream>* hal_configured_streams) {
  ATRACE_CALL();
  if (process_block == nullptr || hal_configured_streams == nullptr) {
    ALOGE("%s: process_block (%p) or hal_configured_streams (%p) is nullptr",
          __FUNCTION__, process_block, hal_configured_streams);
    return BAD_VALUE;
  }

  status_t res = device_session_hwl_->BuildPipelines();
  if (res != OK) {
    ALOGE("%s: Building pipelines failed: %s(%d)", __FUNCTION__, strerror(-res),
          res);
    return res;
  }

  res = process_block->GetConfiguredHalStreams(hal_configured_streams);
  if (res != OK) {
    ALOGE("%s: Getting HAL streams failed: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  std::vector<HalStream> hdrplus_hal_configured_streams;
  res = hdrplus_process_block->GetConfiguredHalStreams(
      &hdrplus_hal_configured_streams);
  if (res != OK) {
    ALOGE("%s: Getting HDR+ HAL streams failed: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  // Combine realtime and HDR+ hal stream.
  // Only usage of internal raw stream is different, so combine usage directly
  uint64_t consumer_usage = 0;
  for (uint32_t i = 0; i < hdrplus_hal_configured_streams.size(); i++) {
    if (hdrplus_hal_configured_streams[i].override_format == kHdrplusRawFormat) {
      consumer_usage = hdrplus_hal_configured_streams[i].consumer_usage;
      break;
    }
  }

  for (uint32_t i = 0; i < hal_configured_streams->size(); i++) {
    if (hal_configured_streams->at(i).override_format == kHdrplusRawFormat) {
      hal_configured_streams->at(i).consumer_usage = consumer_usage;
      if (hal_configured_streams->at(i).max_buffers < kRawMinBufferCount) {
        hal_configured_streams->at(i).max_buffers = kRawMinBufferCount;
      }
      // Allocate internal raw stream buffers
      uint32_t additional_num_buffers =
          (hal_configured_streams->at(i).max_buffers >= kRawBufferCount)
              ? 0
              : (kRawBufferCount - hal_configured_streams->at(i).max_buffers);
      res = internal_stream_manager_->AllocateBuffers(
          hal_configured_streams->at(i), additional_num_buffers);
      if (res != OK) {
        ALOGE("%s: AllocateBuffers failed.", __FUNCTION__);
        return UNKNOWN_ERROR;
      }
      break;
    }
  }

  return OK;
}

status_t HdrplusCaptureSession::ConnectProcessChain(
    RequestProcessor* request_processor,
    std::unique_ptr<ProcessBlock> process_block,
    std::unique_ptr<ResultProcessor> result_processor) {
  ATRACE_CALL();
  if (request_processor == nullptr) {
    ALOGE("%s: request_processor is nullptr", __FUNCTION__);
    return BAD_VALUE;
  }

  status_t res = process_block->SetResultProcessor(std::move(result_processor));
  if (res != OK) {
    ALOGE("%s: Setting result process in process block failed.", __FUNCTION__);
    return res;
  }

  res = request_processor->SetProcessBlock(std::move(process_block));
  if (res != OK) {
    ALOGE(
        "%s: Setting process block for HdrplusRequestProcessor failed: %s(%d)",
        __FUNCTION__, strerror(-res), res);
    return res;
  }

  return OK;
}

status_t HdrplusCaptureSession::SetupRealtimeProcessChain(
    const StreamConfiguration& stream_config,
    ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
    std::unique_ptr<ProcessBlock>* realtime_process_block,
    std::unique_ptr<ResultProcessor>* realtime_result_processor,
    int32_t* raw_stream_id) {
  ATRACE_CALL();
  if (realtime_process_block == nullptr ||
      realtime_result_processor == nullptr || raw_stream_id == nullptr) {
    ALOGE(
        "%s: realtime_process_block(%p) or realtime_result_processor(%p) or "
        "raw_stream_id(%p) is nullptr",
        __FUNCTION__, realtime_process_block, realtime_result_processor,
        raw_stream_id);
    return BAD_VALUE;
  }
  // Create realtime process block.
  auto process_block = RealtimeProcessBlock::Create(device_session_hwl_);
  if (process_block == nullptr) {
    ALOGE("%s: Creating RealtimeProcessBlock failed.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  // Create realtime request processor.
  request_processor_ = RealtimeZslRequestProcessor::Create(
      device_session_hwl_, HAL_PIXEL_FORMAT_RAW10);
  if (request_processor_ == nullptr) {
    ALOGE("%s: Creating RealtimeZslsRequestProcessor failed.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  status_t res = ConfigureStreams(stream_config, request_processor_.get(),
                                  process_block.get(), raw_stream_id);
  if (res != OK) {
    ALOGE("%s: Configuring stream failed: %s(%d)", __FUNCTION__, strerror(-res),
          res);
    return res;
  }

  // Create realtime result processor.
  auto result_processor = RealtimeZslResultProcessor::Create(
      internal_stream_manager_.get(), *raw_stream_id, HAL_PIXEL_FORMAT_RAW10);
  if (result_processor == nullptr) {
    ALOGE("%s: Creating RealtimeZslResultProcessor failed.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }
  result_processor->SetResultCallback(process_capture_result, notify,
                                      /*process_batch_capture_result=*/nullptr);

  *realtime_process_block = std::move(process_block);
  *realtime_result_processor = std::move(result_processor);

  return OK;
}

status_t HdrplusCaptureSession::SetupHdrplusProcessChain(
    const StreamConfiguration& stream_config,
    ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
    std::unique_ptr<ProcessBlock>* hdrplus_process_block,
    std::unique_ptr<ResultProcessor>* hdrplus_result_processor,
    int32_t raw_stream_id) {
  ATRACE_CALL();
  if (hdrplus_process_block == nullptr || hdrplus_result_processor == nullptr) {
    ALOGE(
        "%s: hdrplus_process_block(%p) or hdrplus_result_processor(%p) is "
        "nullptr",
        __FUNCTION__, hdrplus_process_block, hdrplus_result_processor);
    return BAD_VALUE;
  }

  // Create hdrplus process block.
  auto process_block = HdrplusProcessBlock::Create(
      device_session_hwl_, device_session_hwl_->GetCameraId());
  if (process_block == nullptr) {
    ALOGE("%s: Creating HdrplusProcessBlock failed.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  // Create hdrplus request processor.
  hdrplus_request_processor_ = HdrplusRequestProcessor::Create(
      device_session_hwl_, raw_stream_id, device_session_hwl_->GetCameraId());
  if (hdrplus_request_processor_ == nullptr) {
    ALOGE("%s: Creating HdrplusRequestProcessor failed.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  // Create hdrplus result processor.
  auto result_processor = HdrplusResultProcessor::Create(
      internal_stream_manager_.get(), raw_stream_id);
  if (result_processor == nullptr) {
    ALOGE("%s: Creating HdrplusResultProcessor failed.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }
  result_processor->SetResultCallback(process_capture_result, notify,
                                      /*process_batch_capture_result=*/nullptr);

  status_t res = ConfigureHdrplusStreams(
      stream_config, hdrplus_request_processor_.get(), process_block.get());
  if (res != OK) {
    ALOGE("%s: Configuring hdrplus stream failed: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  *hdrplus_process_block = std::move(process_block);
  *hdrplus_result_processor = std::move(result_processor);

  return OK;
}

status_t HdrplusCaptureSession::Initialize(
    CameraDeviceSessionHwl* device_session_hwl,
    const StreamConfiguration& stream_config,
    ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
    std::vector<HalStream>* hal_configured_streams) {
  ATRACE_CALL();
  if (!IsStreamConfigurationSupported(device_session_hwl, stream_config)) {
    ALOGE("%s: stream configuration is not supported.", __FUNCTION__);
    return BAD_VALUE;
  }

  std::unique_ptr<HalCameraMetadata> characteristics;
  status_t res = device_session_hwl->GetCameraCharacteristics(&characteristics);
  if (res != OK) {
    ALOGE("%s: GetCameraCharacteristics failed.", __FUNCTION__);
    return BAD_VALUE;
  }

  camera_metadata_ro_entry entry;
  res = characteristics->Get(VendorTagIds::kHdrUsageMode, &entry);
  if (res == OK) {
    hdr_mode_ = static_cast<HdrMode>(entry.data.u8[0]);
  }

  for (auto stream : stream_config.streams) {
    if (utils::IsPreviewStream(stream)) {
      hal_preview_stream_id_ = stream.id;
      break;
    }
  }
  device_session_hwl_ = device_session_hwl;
  internal_stream_manager_ = InternalStreamManager::Create();
  if (internal_stream_manager_ == nullptr) {
    ALOGE("%s: Cannot create internal stream manager.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  // Create result dispatcher
  result_dispatcher_ =
      ResultDispatcher::Create(kPartialResult, process_capture_result,
                               /*process_batch_capture_result=*/nullptr, notify,
                               stream_config, "HdrplusDispatcher");
  if (result_dispatcher_ == nullptr) {
    ALOGE("%s: Cannot create result dispatcher.", __FUNCTION__);
    return UNKNOWN_ERROR;
  }

  device_session_notify_ = notify;
  process_capture_result_ =
      ProcessCaptureResultFunc([this](std::unique_ptr<CaptureResult> result) {
        ProcessCaptureResult(std::move(result));
      });
  notify_ = NotifyFunc(
      [this](const NotifyMessage& message) { NotifyHalMessage(message); });

  // Setup realtime process chain
  int32_t raw_stream_id = -1;
  std::unique_ptr<ProcessBlock> realtime_process_block;
  std::unique_ptr<ResultProcessor> realtime_result_processor;

  res = SetupRealtimeProcessChain(stream_config, process_capture_result_,
                                  notify_, &realtime_process_block,
                                  &realtime_result_processor, &raw_stream_id);
  if (res != OK) {
    ALOGE("%s: SetupRealtimeProcessChain fail: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  // Setup hdrplus process chain
  std::unique_ptr<ProcessBlock> hdrplus_process_block;
  std::unique_ptr<ResultProcessor> hdrplus_result_processor;

  res = SetupHdrplusProcessChain(stream_config, process_capture_result_,
                                 notify_, &hdrplus_process_block,
                                 &hdrplus_result_processor, raw_stream_id);
  if (res != OK) {
    ALOGE("%s: SetupHdrplusProcessChain fail: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  // Realtime and HDR+ streams are configured
  // Start to build pipleline
  res = BuildPipelines(realtime_process_block.get(),
                       hdrplus_process_block.get(), hal_configured_streams);
  if (res != OK) {
    ALOGE("%s: Building pipelines failed: %s(%d)", __FUNCTION__, strerror(-res),
          res);
    return res;
  }

  res = PurgeHalConfiguredStream(stream_config, hal_configured_streams);
  if (res != OK) {
    ALOGE("%s: Removing internal streams from configured stream failed: %s(%d)",
          __FUNCTION__, strerror(-res), res);
    return res;
  }

  // Connect realtime process chain
  res = ConnectProcessChain(request_processor_.get(),
                            std::move(realtime_process_block),
                            std::move(realtime_result_processor));
  if (res != OK) {
    ALOGE("%s: Connecting process chain failed: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  // Connect HDR+ process chain
  res = ConnectProcessChain(hdrplus_request_processor_.get(),
                            std::move(hdrplus_process_block),
                            std::move(hdrplus_result_processor));
  if (res != OK) {
    ALOGE("%s: Connecting HDR+ process chain failed: %s(%d)", __FUNCTION__,
          strerror(-res), res);
    return res;
  }

  return OK;
}

status_t HdrplusCaptureSession::ProcessRequest(const CaptureRequest& request) {
  ATRACE_CALL();
  bool is_hdrplus_request =
      hal_utils::IsRequestHdrplusCompatible(request, hal_preview_stream_id_);

  status_t res = result_dispatcher_->AddPendingRequest(request);
  if (res != OK) {
    ALOGE("%s: frame(%d) fail to AddPendingRequest", __FUNCTION__,
          request.frame_number);
    return BAD_VALUE;
  }

  if (is_hdrplus_request) {
    ALOGI("%s: hdrplus snapshot (%d), output stream size:%zu", __FUNCTION__,
          request.frame_number, request.output_buffers.size());
    res = hdrplus_request_processor_->ProcessRequest(request);
    if (res != OK) {
      ALOGI("%s: hdrplus snapshot frame(%d) request to realtime process",
            __FUNCTION__, request.frame_number);
      res = request_processor_->ProcessRequest(request);
    }
  } else {
    res = request_processor_->ProcessRequest(request);
  }

  if (res != OK) {
    ALOGE("%s: ProcessRequest (%d) fail and remove pending request",
          __FUNCTION__, request.frame_number);
    result_dispatcher_->RemovePendingRequest(request.frame_number);
  }
  return res;
}

status_t HdrplusCaptureSession::Flush() {
  ATRACE_CALL();
  return request_processor_->Flush();
}

void HdrplusCaptureSession::ProcessCaptureResult(
    std::unique_ptr<CaptureResult> result) {
  ATRACE_CALL();
  std::lock_guard<std::mutex> lock(callback_lock_);
  if (result == nullptr) {
    return;
  }

  if (result->result_metadata && hdr_mode_ != HdrMode::kHdrplusMode) {
    device_session_hwl_->FilterResultMetadata(result->result_metadata.get());
  }

  status_t res = result_dispatcher_->AddResult(std::move(result));
  if (res != OK) {
    ALOGE("%s: fail to AddResult", __FUNCTION__);
    return;
  }
}

status_t HdrplusCaptureSession::PurgeHalConfiguredStream(
    const StreamConfiguration& stream_config,
    std::vector<HalStream>* hal_configured_streams) {
  if (hal_configured_streams == nullptr) {
    ALOGE("%s: HAL configured stream list is null.", __FUNCTION__);
    return BAD_VALUE;
  }

  std::set<int32_t> framework_stream_id_set;
  for (auto& stream : stream_config.streams) {
    framework_stream_id_set.insert(stream.id);
  }

  std::vector<HalStream> configured_streams;
  for (auto& hal_stream : *hal_configured_streams) {
    if (framework_stream_id_set.find(hal_stream.id) !=
        framework_stream_id_set.end()) {
      configured_streams.push_back(hal_stream);
    }
  }
  *hal_configured_streams = configured_streams;
  return OK;
}

void HdrplusCaptureSession::NotifyHalMessage(const NotifyMessage& message) {
  ATRACE_CALL();
  std::lock_guard<std::mutex> lock(callback_lock_);
  if (device_session_notify_ == nullptr) {
    ALOGE("%s: device_session_notify_ is nullptr. Dropping a message.",
          __FUNCTION__);
    return;
  }

  if (message.type == MessageType::kShutter) {
    status_t res = result_dispatcher_->AddShutter(
        message.message.shutter.frame_number,
        message.message.shutter.timestamp_ns,
        message.message.shutter.readout_timestamp_ns);
    if (res != OK) {
      ALOGE("%s: AddShutter for frame %u failed: %s (%d).", __FUNCTION__,
            message.message.shutter.frame_number, strerror(-res), res);
      return;
    }
  } else if (message.type == MessageType::kError) {
    status_t res = result_dispatcher_->AddError(message.message.error);
    if (res != OK) {
      ALOGE("%s: AddError for frame %u failed: %s (%d).", __FUNCTION__,
            message.message.error.frame_number, strerror(-res), res);
      return;
    }
  } else {
    ALOGW("%s: Unsupported message type: %u", __FUNCTION__, message.type);
    device_session_notify_(message);
  }
}
}  // namespace google_camera_hal
}  // namespace android
