/*
 * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * Copyright (C) 2017 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.
 */

#include <vector>
#include <string>

#include "QtiComposerClient.h"

namespace vendor {
namespace qti {
namespace hardware {
namespace display {
namespace composer {
namespace V3_0 {
namespace implementation {

ComposerHandleImporter mHandleImporter;

BufferCacheEntry::BufferCacheEntry() : mHandle(nullptr) {}

BufferCacheEntry::BufferCacheEntry(BufferCacheEntry&& other) {
  mHandle = other.mHandle;
  other.mHandle = nullptr;
}

BufferCacheEntry& BufferCacheEntry::operator=(buffer_handle_t handle) {
  clear();
  mHandle = handle;
  return *this;
}

BufferCacheEntry::~BufferCacheEntry() {
  clear();
}

void BufferCacheEntry::clear() {
  if (mHandle) {
    mHandleImporter.freeBuffer(mHandle);
  }
}

QtiComposerClient::QtiComposerClient() : mWriter(kWriterInitialSize), mReader(*this) {
  hwc_session_ = HWCSession::GetInstance();
  mHandleImporter.initialize();
}

QtiComposerClient::~QtiComposerClient() {
  // We want to call hwc2_close here (and move hwc2_open to the
  // constructor), with the assumption that hwc2_close would
  //
  //  - clean up all resources owned by the client
  //  - make sure all displays are blank (since there is no layer)
  //
  // But since SF used to crash at this point, different hwcomposer2
  // implementations behave differently on hwc2_close.  Our only portable
  // choice really is to abort().  But that is not an option anymore
  // because we might also have VTS or VR as clients that can come and go.
  //
  // Below we manually clean all resources (layers and virtual
  // displays), and perform a presentDisplay afterwards.
  ALOGW("destroying composer client");

  enableCallback(false);

  // no need to grab the mutex as any in-flight hwbinder call would have
  // kept the client alive
  for (const auto& dpy : mDisplayData) {
    ALOGW("destroying client resources for display %" PRIu64, dpy.first);

    for (const auto& ly : dpy.second.Layers) {
      hwc_session_->DestroyLayer(dpy.first, ly.first);
    }

    if (dpy.second.IsVirtual) {
      destroyVirtualDisplay(dpy.first);
    } else {
      ALOGW("performing a final presentDisplay");

      std::vector<Layer> changedLayers;
      std::vector<IComposerClient::Composition> compositionTypes;
      uint32_t displayRequestMask = 0;
      std::vector<Layer> requestedLayers;
      std::vector<uint32_t> requestMasks;
      mReader.validateDisplay(dpy.first, changedLayers, compositionTypes, displayRequestMask,
                              requestedLayers, requestMasks);

      hwc_session_->AcceptDisplayChanges(dpy.first);

      shared_ptr<Fence> presentFence = nullptr;
      std::vector<Layer> releasedLayers;
      std::vector<shared_ptr<Fence>> releaseFences;
      mReader.presentDisplay(dpy.first, &presentFence, releasedLayers, releaseFences);
    }
  }

  mDisplayData.clear();

  mHandleImporter.cleanup();

  ALOGW("removed composer client");
}

void QtiComposerClient::onHotplug(hwc2_callback_data_t callbackData, hwc2_display_t display,
                                    int32_t connected) {
  auto client = reinterpret_cast<QtiComposerClient*>(callbackData);
  auto connect = static_cast<composer_V2_4::IComposerCallback::Connection>(connected);
  if (connect == composer_V2_4::IComposerCallback::Connection::CONNECTED) {
    std::lock_guard<std::mutex> lock_d(client->mDisplayDataMutex);
    client->mDisplayData.emplace(display, DisplayData(false));
  }

  auto ret = client->callback_->onHotplug(display, connect);
  ALOGW_IF(!ret.isOk(), "failed to send onHotplug: %s. SF likely unavailable.",
           ret.description().c_str());

  if (connect == composer_V2_4::IComposerCallback::Connection::DISCONNECTED) {
    // Trigger refresh to make sure disconnect event received/updated properly by SurfaceFlinger.
    client->hwc_session_->Refresh(HWC_DISPLAY_PRIMARY);
    // Wait for sufficient time to ensure sufficient resources are available to process connection.
    uint32_t vsync_period;
    client->hwc_session_->GetVsyncPeriod(HWC_DISPLAY_PRIMARY, &vsync_period);
    usleep(vsync_period * 2 / 1000);

    // Wait for the input command message queue to process before destroying the local display data.
    std::lock_guard<std::mutex> lock(client->mCommandMutex);
    std::lock_guard<std::mutex> lock_d(client->mDisplayDataMutex);
    client->mDisplayData.erase(display);
  }
}

void QtiComposerClient::onRefresh(hwc2_callback_data_t callbackData, hwc2_display_t display) {
  auto client = reinterpret_cast<QtiComposerClient*>(callbackData);
  auto ret = client->callback_->onRefresh(display);
  ALOGW_IF(!ret.isOk(), "failed to send onRefresh: %s. SF likely unavailable.",
           ret.description().c_str());
}

void QtiComposerClient::onVsync(hwc2_callback_data_t callbackData, hwc2_display_t display,
                                int64_t timestamp) {
  auto client = reinterpret_cast<QtiComposerClient*>(callbackData);
  auto ret = client->callback_->onVsync(display, timestamp);
  ALOGW_IF(!ret.isOk(), "failed to send onVsync: %s. SF likely unavailable.",
           ret.description().c_str());
}

void QtiComposerClient::onVsync_2_4(hwc2_callback_data_t callbackData, hwc2_display_t display,
                                    int64_t timestamp, VsyncPeriodNanos vsyncPeriodNanos) {
  auto client = reinterpret_cast<QtiComposerClient*>(callbackData);
  auto ret = client->callback24_->onVsync_2_4(display, timestamp, vsyncPeriodNanos);
  ALOGW_IF(!ret.isOk(), "failed to send onVsync_2_4: %s. SF likely unavailable.",
           ret.description().c_str());
}

void QtiComposerClient::onVsyncPeriodTimingChanged(hwc2_callback_data_t callbackData,
      hwc2_display_t display, hwc_vsync_period_change_timeline_t *updatedTimeline) {
   VsyncPeriodChangeTimeline timeline =
                                   {updatedTimeline->newVsyncAppliedTimeNanos,
                                    static_cast<bool>(updatedTimeline->refreshRequired),
                                    updatedTimeline->refreshTimeNanos};

  auto client = reinterpret_cast<QtiComposerClient*>(callbackData);
  auto ret = client->callback24_->onVsyncPeriodTimingChanged(display, timeline);
  ALOGW_IF(!ret.isOk(), "failed to send onVsyncPeriodTimingChanged: %s. SF likely unavailable.",
          ret.description().c_str());
}

void QtiComposerClient::onSeamlessPossible(hwc2_callback_data_t callbackData,
                                           hwc2_display_t display) {
  auto client = reinterpret_cast<QtiComposerClient*>(callbackData);
  auto ret = client->callback24_->onSeamlessPossible(display);
  ALOGW_IF(!ret.isOk(), "failed to send onSeamlessPossible: %s. SF likely unavailable.",
           ret.description().c_str());
}

// convert fenceFd to or from hidl_handle
// Handle would still own original fence. Hence create a Fence object on duped fd.
Error QtiComposerClient::getFence(const hidl_handle& fenceHandle, shared_ptr<Fence>* outFence,
                                  const string& name) {
  auto handle = fenceHandle.getNativeHandle();
  if (handle && handle->numFds > 1) {
    ALOGE("invalid fence handle with %d fds", handle->numFds);
    return Error::BAD_PARAMETER;
  }

  int fenceFd = (handle && handle->numFds == 1) ? handle->data[0] : -1;
  *outFence = Fence::Create(dup(fenceFd), name);

  return Error::NONE;
}

// Handle would own fence hereafter. Hence provide a dupped fd.
hidl_handle QtiComposerClient::getFenceHandle(const shared_ptr<Fence>& fence,
                                              char* handleStorage) {
  native_handle_t* handle = nullptr;
  if (fence) {
    handle = native_handle_init(handleStorage, 1, 0);
    if (handle) {
      handle->data[0] = Fence::Dup(fence);
    }
  }

  return hidl_handle(handle);
}

Error QtiComposerClient::getDisplayReadbackBuffer(Display display,
                                                  const native_handle_t* rawHandle,
                                                  const native_handle_t** outHandle) {
  // TODO(user): revisit for caching and freeBuffer in success case.
  if (!mHandleImporter.importBuffer(rawHandle)) {
    ALOGE("%s: importBuffer failed: ", __FUNCTION__);
    return Error::NO_RESOURCES;
  }

  std::lock_guard<std::mutex> lock(mDisplayDataMutex);
  auto iter = mDisplayData.find(display);
  if (iter == mDisplayData.end()) {
    mHandleImporter.freeBuffer(rawHandle);
    return Error::BAD_DISPLAY;
  }

  *outHandle = rawHandle;
  return Error::NONE;
}

void QtiComposerClient::getCapabilities() {
  uint32_t count = 0;
  hwc_session_->GetCapabilities(&count, nullptr);

  std::vector<int32_t> composer_caps(count);
  hwc_session_->GetCapabilities(&count, composer_caps.data());
  composer_caps.resize(count);

  mCapabilities.reserve(count);
  for (auto cap : composer_caps) {
    mCapabilities.insert(static_cast<hwc2_capability_t>(cap));
  }
}

void QtiComposerClient::enableCallback(bool enable) {
  if (enable) {
    hwc_session_->RegisterCallback(HWC2_CALLBACK_HOTPLUG, this,
                                   reinterpret_cast<hwc2_function_pointer_t>(onHotplug));
    hwc_session_->RegisterCallback(HWC2_CALLBACK_REFRESH, this,
                                   reinterpret_cast<hwc2_function_pointer_t>(onRefresh));
    if (!mUseCallback24_) {
      hwc_session_->RegisterCallback(HWC2_CALLBACK_VSYNC, this,
                                     reinterpret_cast<hwc2_function_pointer_t>(onVsync));
    } else {
      hwc_session_->RegisterCallback(HWC2_CALLBACK_VSYNC_2_4, this,
                                     reinterpret_cast<hwc2_function_pointer_t>(onVsync_2_4));
      hwc_session_->RegisterCallback(HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED, this,
                             reinterpret_cast<hwc2_function_pointer_t>(onVsyncPeriodTimingChanged));
      hwc_session_->RegisterCallback(HWC2_CALLBACK_SEAMLESS_POSSIBLE, this,
                                     reinterpret_cast<hwc2_function_pointer_t>(onSeamlessPossible));
    }
  } else {
    hwc_session_->RegisterCallback(HWC2_CALLBACK_HOTPLUG, this, nullptr);
    hwc_session_->RegisterCallback(HWC2_CALLBACK_REFRESH, this, nullptr);
    if (!mUseCallback24_) {
      hwc_session_->RegisterCallback(HWC2_CALLBACK_VSYNC, this, nullptr);
    } else {
      hwc_session_->RegisterCallback(HWC2_CALLBACK_VSYNC_2_4, this, nullptr);
      hwc_session_->RegisterCallback(HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED, this, nullptr);
      hwc_session_->RegisterCallback(HWC2_CALLBACK_SEAMLESS_POSSIBLE, this, nullptr);
    }
  }
}

// Methods from ::android::hardware::graphics::composer::V2_1::IComposerClient follow.
Return<void> QtiComposerClient::registerCallback(
                                            const sp<composer_V2_1::IComposerCallback>& callback) {
  callback_ = callback;
  enableCallback(callback != nullptr);
  return Void();
}

Return<uint32_t> QtiComposerClient::getMaxVirtualDisplayCount() {
  return hwc_session_->GetMaxVirtualDisplayCount();
}

Return<void> QtiComposerClient::createVirtualDisplay(uint32_t width, uint32_t height,
                                                     common_V1_0::PixelFormat formatHint,
                                                     uint32_t outputBufferSlotCount,
                                                     createVirtualDisplay_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t createVirtualDisplay_2_2
  int32_t format = static_cast<int32_t>(formatHint);
  uint64_t display;
  auto error = hwc_session_->CreateVirtualDisplay(width, height, &format, &display);

  if (static_cast<Error>(error) == Error::NONE) {
    std::lock_guard<std::mutex> lock(mDisplayDataMutex);

    auto dpy = mDisplayData.emplace(static_cast<Display>(display), DisplayData(true)).first;
    dpy->second.OutputBuffers.resize(outputBufferSlotCount);
  }

  _hidl_cb(static_cast<Error>(error), display, static_cast<common_V1_0::PixelFormat>(format));
  return Void();
}

Return<composer_V2_1::Error> QtiComposerClient::destroyVirtualDisplay(uint64_t display) {
  auto error = hwc_session_->DestroyVirtualDisplay(display);
  if (static_cast<Error>(error) == Error::NONE) {
    std::lock_guard<std::mutex> lock(mDisplayDataMutex);

    mDisplayData.erase(display);
  }

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::createLayer(uint64_t display, uint32_t bufferSlotCount,
                                            createLayer_cb _hidl_cb) {
  composer_V2_1::Layer layer = 0;
  auto error = hwc_session_->CreateLayer(display, &layer);
  Error err = static_cast<Error>(error);
  if (err == Error::NONE) {
    std::lock_guard<std::mutex> lock(mDisplayDataMutex);
    auto dpy = mDisplayData.find(display);
    // The display entry may have already been removed by onHotplug.
    if (dpy != mDisplayData.end()) {
      auto ly = dpy->second.Layers.emplace(layer, LayerBuffers()).first;
      ly->second.Buffers.resize(bufferSlotCount);
    } else {
      err = Error::BAD_DISPLAY;
      // Note: We do not destroy the layer on this error as the hotplug
      // disconnect invalidates the display id. The implementation should
      // ensure all layers for the display are destroyed.
    }
  }

  _hidl_cb(err, layer);
  return Void();
}

Return<Error> QtiComposerClient::destroyLayer(uint64_t display, uint64_t layer) {
  auto error = hwc_session_->DestroyLayer(display, layer);
  Error err = static_cast<Error>(error);
  if (err == Error::NONE) {
    std::lock_guard<std::mutex> lock(mDisplayDataMutex);

    auto dpy = mDisplayData.find(display);
    // The display entry may have already been removed by onHotplug.
    if (dpy != mDisplayData.end()) {
      dpy->second.Layers.erase(layer);
    }
  }

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::getActiveConfig(uint64_t display, getActiveConfig_cb _hidl_cb) {
  uint32_t config = 0;
  auto error = hwc_session_->GetActiveConfig(display, &config);

  _hidl_cb(static_cast<Error>(error), config);

  return Void();
}

Return<Error> QtiComposerClient::getClientTargetSupport(uint64_t display, uint32_t width,
                                                        uint32_t height,
                                                        common_V1_0::PixelFormat format,
                                                        common_V1_0::Dataspace dataspace) {
  auto error = hwc_session_->GetClientTargetSupport(display, width, height,
                                                    static_cast<int32_t>(format),
                                                    static_cast<int32_t>(dataspace));

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::getColorModes(uint64_t display, getColorModes_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t getColorModes_2_3
  hidl_vec<common_V1_0::ColorMode> modes;
  uint32_t count = 0;

  auto error = hwc_session_->GetColorModes(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), modes);
    return Void();
  }

  modes.resize(count);
  error = hwc_session_->GetColorModes(display, &count,
              reinterpret_cast<std::underlying_type<common_V1_0::ColorMode>::type*>(modes.data()));

  _hidl_cb(static_cast<Error>(error), modes);
  return Void();
}

Return<void> QtiComposerClient::getDisplayAttribute(uint64_t display, uint32_t config,
                                               composer_V2_1::IComposerClient::Attribute attribute,
                                               getDisplayAttribute_cb _hidl_cb) {
  int32_t value = 0;
  auto error = hwc_session_->GetDisplayAttribute(
      display, config, static_cast<composer_V2_4::IComposerClient::Attribute>(attribute), &value);

  _hidl_cb(static_cast<Error>(error), value);
  return Void();
}

Return<void> QtiComposerClient::getDisplayConfigs(uint64_t display,
                                                  getDisplayConfigs_cb _hidl_cb) {
  hidl_vec<uint32_t> configs;
  uint32_t count = 0;

  auto error = hwc_session_->GetDisplayConfigs(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), configs);
    return Void();
  }

  configs.resize(count);
  error = hwc_session_->GetDisplayConfigs(display, &count, configs.data());

  _hidl_cb(static_cast<Error>(error), configs);
  return Void();
}

Return<void> QtiComposerClient::getDisplayName(uint64_t display, getDisplayName_cb _hidl_cb) {
  uint32_t count = 0;
  hidl_string name_reply;
  std::vector<char> name;

  auto error = hwc_session_->GetDisplayName(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), name_reply);
    return Void();
  }

  name.resize(count + 1);
  error = hwc_session_->GetDisplayName(display, &count, name.data());
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), name_reply);
    return Void();
  }

  name.resize(count + 1);
  name[count] = '\0';
  name_reply.setToExternal(name.data(), count);

  _hidl_cb(static_cast<Error>(error), name_reply);
  return Void();
}

Return<void> QtiComposerClient::getDisplayType(uint64_t display, getDisplayType_cb _hidl_cb) {
  int32_t hwc_type;
  auto error = hwc_session_->GetDisplayType(display, &hwc_type);

  _hidl_cb(static_cast<Error>(error), static_cast<IComposerClient::DisplayType>(hwc_type));
  return Void();
}

Return<void> QtiComposerClient::getDozeSupport(uint64_t display, getDozeSupport_cb _hidl_cb) {
  int32_t hwc_support = 0;
  auto error = hwc_session_->GetDozeSupport(display, &hwc_support);

  _hidl_cb(static_cast<Error>(error), hwc_support);
  return Void();
}

Return<void> QtiComposerClient::getHdrCapabilities(uint64_t display,
                                                   getHdrCapabilities_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t getHdrCapabilities_2_3
  uint32_t count = 0;
  hidl_vec<common_V1_0::Hdr> types;
  float max_lumi = 0.0f;
  float max_avg_lumi = 0.0f;
  float min_lumi = 0.0f;

  auto error = hwc_session_->GetHdrCapabilities(display, &count, nullptr, &max_lumi,
                                                &max_avg_lumi, &min_lumi);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), types, max_lumi, max_avg_lumi, min_lumi);
    return Void();
  }

  types.resize(count);
  error = hwc_session_->GetHdrCapabilities(display, &count,
           reinterpret_cast<std::underlying_type<common_V1_2::Hdr>::type*>(types.data()),
           &max_lumi, &max_avg_lumi, &min_lumi);

  _hidl_cb(static_cast<Error>(error), types, max_lumi, max_avg_lumi, min_lumi);
  return Void();
}

Return<Error> QtiComposerClient::setClientTargetSlotCount(uint64_t display,
                                                          uint32_t clientTargetSlotCount) {
  std::lock_guard<std::mutex> lock(mDisplayDataMutex);

  auto dpy = mDisplayData.find(display);
  if (dpy == mDisplayData.end()) {
    return Error::BAD_DISPLAY;
  }
  dpy->second.ClientTargets.resize(clientTargetSlotCount);

  return Error::NONE;
}

Return<Error> QtiComposerClient::setActiveConfig(uint64_t display, uint32_t config) {
  auto error = hwc_session_->SetActiveConfig(display, config);

  return static_cast<Error>(error);
}

Return<Error> QtiComposerClient::setColorMode(uint64_t display, common_V1_0::ColorMode mode) {
  auto error = hwc_session_->SetColorMode(display, static_cast<int32_t>(mode));

  return static_cast<Error>(error);
}

Return<Error> QtiComposerClient::setPowerMode(uint64_t display,
                                              composer_V2_1::IComposerClient::PowerMode mode) {
  // TODO(user): Implement combinedly w.r.t setPowerMode_2_2
  auto error = hwc_session_->SetPowerMode(display, static_cast<int32_t>(mode));

  return static_cast<Error>(error);
}

Return<Error> QtiComposerClient::setVsyncEnabled(uint64_t display,
                                                 composer_V2_1::IComposerClient::Vsync enabled) {
  auto error = hwc_session_->SetVsyncEnabled(display, static_cast<int32_t>(enabled));

  return static_cast<Error>(error);
}

Return<Error> QtiComposerClient::setInputCommandQueue(
                                                    const MQDescriptorSync<uint32_t>& descriptor) {
  std::lock_guard<std::mutex> lock(mCommandMutex);
  return mReader.setMQDescriptor(descriptor) ? Error::NONE : Error::NO_RESOURCES;
}

Return<void> QtiComposerClient::getOutputCommandQueue(getOutputCommandQueue_cb _hidl_cb) {
  // no locking as we require this function to be called inside
  // executeCommands_cb

  auto outDescriptor = mWriter.getMQDescriptor();
  if (outDescriptor) {
    _hidl_cb(Error::NONE, *outDescriptor);
  } else {
    _hidl_cb(Error::NO_RESOURCES, MQDescriptorSync<uint32_t>());
  }

  return Void();
}

Return<void> QtiComposerClient::executeCommands(uint32_t inLength,
                                                const hidl_vec<hidl_handle>& inHandles,
                                                executeCommands_cb _hidl_cb) {
  std::lock_guard<std::mutex> lock(mCommandMutex);

  bool outChanged = false;
  uint32_t outLength = 0;
  hidl_vec<hidl_handle> outHandles;

  if (!mReader.readQueue(inLength, inHandles)) {
    _hidl_cb(Error::BAD_PARAMETER, outChanged, outLength, outHandles);
    return Void();
  }

  Error err = mReader.parse();
  if (err == Error::NONE &&
      !mWriter.writeQueue(outChanged, outLength, outHandles)) {
    err = Error::NO_RESOURCES;
  }

  _hidl_cb(Error::NONE, outChanged, outLength, outHandles);

  mReader.reset();
  mWriter.reset();

  return Void();
}


// Methods from ::android::hardware::graphics::composer::V2_2::IComposerClient follow.
Return<void> QtiComposerClient::getPerFrameMetadataKeys(uint64_t display,
                                                        getPerFrameMetadataKeys_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t getPerFrameMetadataKeys_2_3
  std::vector<PerFrameMetadataKey_V2> keys;
  uint32_t count = 0;

  auto error = hwc_session_->GetPerFrameMetadataKeys(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), keys);
    return Void();
  }

  keys.resize(count);
  error = hwc_session_->GetPerFrameMetadataKeys(display, &count,
               reinterpret_cast<std::underlying_type<PerFrameMetadataKey_V2>::type*>(keys.data()));

  _hidl_cb(static_cast<Error>(error), keys);
  return Void();
}

Return<void> QtiComposerClient::getReadbackBufferAttributes(uint64_t display,
                                                         getReadbackBufferAttributes_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t getReadbackBufferAttributes_2_3
  int32_t format = 0;
  int32_t dataspace = 0;

  auto error = hwc_session_->GetReadbackBufferAttributes(display, &format, &dataspace);

  if (error != HWC2_ERROR_NONE) {
    format = 0;
    dataspace = 0;
  }

  _hidl_cb(static_cast<Error>(error), static_cast<common_V1_1::PixelFormat>(format),
           static_cast<common_V1_1::Dataspace>(dataspace));
  return Void();
}

Return<void> QtiComposerClient::getReadbackBufferFence(uint64_t display,
                                                       getReadbackBufferFence_cb _hidl_cb) {
  shared_ptr<Fence> fence = nullptr;
  auto error = hwc_session_->GetReadbackBufferFence(display, &fence);
  if (static_cast<Error>(error) != Error::NONE) {
    _hidl_cb(static_cast<Error>(error), nullptr);
    return Void();
  }

  NATIVE_HANDLE_DECLARE_STORAGE(fenceStorage, 1, 0);

  _hidl_cb(static_cast<Error>(error), getFenceHandle(fence, fenceStorage));
  return Void();
}

Return<Error> QtiComposerClient::setReadbackBuffer(uint64_t display, const hidl_handle& buffer,
                                                   const hidl_handle& releaseFence) {
  shared_ptr<Fence> fence = nullptr;
  Error error = getFence(releaseFence, &fence, "read_back");
  if (error != Error::NONE) {
    return error;
  }

  const native_handle_t* readbackBuffer;
  error = getDisplayReadbackBuffer(display, buffer.getNativeHandle(), &readbackBuffer);
  if (error != Error::NONE) {
    return error;
  }

  auto err = hwc_session_->SetReadbackBuffer(display, readbackBuffer, fence);
  return static_cast<Error>(err);
}

Return<void> QtiComposerClient::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
                                                         common_V1_1::PixelFormat formatHint,
                                                         uint32_t outputBufferSlotCount,
                                                         createVirtualDisplay_2_2_cb _hidl_cb) {
  int32_t format = static_cast<int32_t>(formatHint);
  uint64_t display;
  auto error = hwc_session_->CreateVirtualDisplay(width, height, &format, &display);

  if (static_cast<Error>(error) == Error::NONE) {
    std::lock_guard<std::mutex> lock(mDisplayDataMutex);

    auto dpy = mDisplayData.emplace(static_cast<Display>(display), DisplayData(true)).first;
    dpy->second.OutputBuffers.resize(outputBufferSlotCount);
  }

  _hidl_cb(static_cast<Error>(error), display, static_cast<common_V1_1::PixelFormat>(format));
  return Void();
}

Return<Error> QtiComposerClient::getClientTargetSupport_2_2(uint64_t display, uint32_t width,
                                                            uint32_t height,
                                                            common_V1_1::PixelFormat format,
                                                            common_V1_1::Dataspace dataspace) {
  auto error = hwc_session_->GetClientTargetSupport(display, width, height,
                                                    static_cast<int32_t>(format),
                                                    static_cast<int32_t>(dataspace));

  return static_cast<Error>(error);
}

Return<Error> QtiComposerClient::setPowerMode_2_2(uint64_t display,
                                                  composer_V2_2::IComposerClient::PowerMode mode) {
  if (mode == IComposerClient::PowerMode::ON_SUSPEND) {
    return Error::UNSUPPORTED;
  }
  auto error = hwc_session_->SetPowerMode(display, static_cast<int32_t>(mode));

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::getColorModes_2_2(uint64_t display,
                                                  getColorModes_2_2_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t getColorModes_2_3
  hidl_vec<common_V1_1::ColorMode> modes;
  uint32_t count = 0;

  auto error = hwc_session_->GetColorModes(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), modes);
    return Void();
  }

  modes.resize(count);
  error = hwc_session_->GetColorModes(display, &count,
              reinterpret_cast<std::underlying_type<common_V1_1::ColorMode>::type*>(modes.data()));

  _hidl_cb(static_cast<Error>(error), modes);
  return Void();
}

Return<void> QtiComposerClient::getRenderIntents(uint64_t display, common_V1_1::ColorMode mode,
                                                 getRenderIntents_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t getRenderIntents_2_3
  uint32_t count = 0;
  std::vector<RenderIntent> intents;

  auto error = hwc_session_->GetRenderIntents(display, int32_t(mode), &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), intents);
    return Void();
  }

  intents.resize(count);
  error = hwc_session_->GetRenderIntents(display, int32_t(mode), &count,
  reinterpret_cast<std::underlying_type<RenderIntent>::type*>(intents.data()));

  _hidl_cb(static_cast<Error>(error), intents);
  return Void();
}

Return<Error> QtiComposerClient::setColorMode_2_2(uint64_t display, common_V1_1::ColorMode mode,
                                                  common_V1_1::RenderIntent intent) {
  auto error = hwc_session_->SetColorModeWithRenderIntent(display, static_cast<int32_t>(mode),
                                                          static_cast<int32_t>(intent));

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::getDataspaceSaturationMatrix(common_V1_1::Dataspace dataspace,
                                                        getDataspaceSaturationMatrix_cb _hidl_cb) {
  if (dataspace != common_V1_1::Dataspace::SRGB_LINEAR) {
    _hidl_cb(Error::BAD_PARAMETER, std::array<float, 16>{0.0f}.data());
    return Void();
  }

  std::array<float, 16> matrix;
  int32_t error = HWC2_ERROR_UNSUPPORTED;
  error = hwc_session_->GetDataspaceSaturationMatrix(static_cast<int32_t>(dataspace),
                                                     matrix.data());
  if (error != HWC2_ERROR_NONE) {
    matrix = {
      1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
      0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    };
  }
  _hidl_cb(Error::NONE, matrix.data());
  return Void();
}

Return<void> QtiComposerClient::executeCommands_2_2(uint32_t inLength,
                                                    const hidl_vec<hidl_handle>& inHandles,
                                                    executeCommands_2_2_cb _hidl_cb) {
  std::lock_guard<std::mutex> lock(mCommandMutex);

  bool outChanged = false;
  uint32_t outLength = 0;
  hidl_vec<hidl_handle> outHandles;

  if (!mReader.readQueue(inLength, inHandles)) {
    _hidl_cb(Error::BAD_PARAMETER, outChanged, outLength, outHandles);
    return Void();
  }

  Error err = mReader.parse();
  if (err == Error::NONE &&
      !mWriter.writeQueue(outChanged, outLength, outHandles)) {
      err = Error::NO_RESOURCES;
  }

  _hidl_cb(Error::NONE, outChanged, outLength, outHandles);

  mReader.reset();
  mWriter.reset();

  return Void();
}


// Methods from ::android::hardware::graphics::composer::V2_3::IComposerClient follow.
Return<void> QtiComposerClient::getDisplayIdentificationData(uint64_t display,
                                                        getDisplayIdentificationData_cb _hidl_cb) {
  uint8_t port = 0;
  uint32_t size = 0;
  std::vector<uint8_t> data(size);

  auto error = hwc_session_->GetDisplayIdentificationData(display, &port, &size, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), port, data);
    return Void();
  }

  data.resize(size);
  error = hwc_session_->GetDisplayIdentificationData(display, &port, &size, data.data());

  _hidl_cb(static_cast<Error>(error), port, data);
  return Void();
}

Return<void> QtiComposerClient::getReadbackBufferAttributes_2_3(uint64_t display,
                                                     getReadbackBufferAttributes_2_3_cb _hidl_cb) {
  int32_t format = 0;
  int32_t dataspace = 0;

  auto error = hwc_session_->GetReadbackBufferAttributes(display, &format, &dataspace);

  if (error != HWC2_ERROR_NONE) {
    format = 0;
    dataspace = 0;
  }

  _hidl_cb(static_cast<Error>(error), static_cast<common_V1_2::PixelFormat>(format),
           static_cast<common_V1_2::Dataspace>(dataspace));
  return Void();
}

Return<Error> QtiComposerClient::getClientTargetSupport_2_3(uint64_t display, uint32_t width,
                                                            uint32_t height,
                                                            common_V1_2::PixelFormat format,
                                                            common_V1_2::Dataspace dataspace) {
  auto error = hwc_session_->GetClientTargetSupport(display, width, height,
                                                    static_cast<int32_t>(format),
                                                    static_cast<int32_t>(dataspace));

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::getDisplayedContentSamplingAttributes(uint64_t display,
                                               getDisplayedContentSamplingAttributes_cb _hidl_cb) {
  // getDisplayedContentSamplingAttributes is not supported
  int constexpr invalid = -1;
  auto error = Error::UNSUPPORTED;
  common_V1_2::PixelFormat format = static_cast<common_V1_2::PixelFormat>(invalid);
  common_V1_2::Dataspace dataspace = static_cast<common_V1_2::Dataspace>(invalid);
  hidl_bitfield<IComposerClient::FormatColorComponent> componentMask =
    static_cast<hidl_bitfield<IComposerClient::FormatColorComponent>>(invalid);

  _hidl_cb(error, format, dataspace, componentMask);
  return Void();
}

Return<Error> QtiComposerClient::setDisplayedContentSamplingEnabled(uint64_t display,
                                   composer_V2_3::IComposerClient::DisplayedContentSampling enable,
                                   hidl_bitfield<FormatColorComponent> componentMask,
                                   uint64_t maxFrames) {
  // setDisplayedContentSamplingEnabled is not supported
  return Error::UNSUPPORTED;
}

Return<void> QtiComposerClient::getDisplayedContentSample(uint64_t display, uint64_t maxFrames,
                                                          uint64_t timestamp,
                                                          getDisplayedContentSample_cb _hidl_cb) {
  // getDisplayedContentSample is not supported
  auto error = Error::UNSUPPORTED;
  uint64_t frameCount = 0;
  hidl_vec<uint64_t> sampleComponent0 = 0;
  hidl_vec<uint64_t> sampleComponent1 = 0;
  hidl_vec<uint64_t> sampleComponent2 = 0;
  hidl_vec<uint64_t> sampleComponent3 = 0;

  _hidl_cb(error, frameCount, sampleComponent0, sampleComponent1, sampleComponent2,
           sampleComponent3);
  return Void();
}

Return<void> QtiComposerClient::executeCommands_2_3(uint32_t inLength,
                                                    const hidl_vec<hidl_handle>& inHandles,
                                                    executeCommands_2_3_cb _hidl_cb) {
  // TODO(user): Implement combinedly w.r.t executeCommands_2_2
  std::lock_guard<std::mutex> lock(mCommandMutex);

  bool outChanged = false;
  uint32_t outLength = 0;
  hidl_vec<hidl_handle> outHandles;

  if (!mReader.readQueue(inLength, inHandles)) {
    _hidl_cb(Error::BAD_PARAMETER, outChanged, outLength, outHandles);
    return Void();
  }

  Error err = mReader.parse();
  if (err == Error::NONE &&
      !mWriter.writeQueue(outChanged, outLength, outHandles)) {
      err = Error::NO_RESOURCES;
  }

  _hidl_cb(Error::NONE, outChanged, outLength, outHandles);

  mReader.reset();
  mWriter.reset();

  return Void();
}

Return<void> QtiComposerClient::getRenderIntents_2_3(uint64_t display, common_V1_2::ColorMode mode,
                                                     getRenderIntents_2_3_cb _hidl_cb) {
  uint32_t count = 0;
  std::vector<RenderIntent> intents;

  auto error = hwc_session_->GetRenderIntents(display, int32_t(mode), &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), intents);
    return Void();
  }

  intents.resize(count);
  error = hwc_session_->GetRenderIntents(display, int32_t(mode), &count,
  reinterpret_cast<std::underlying_type<RenderIntent>::type*>(intents.data()));

  _hidl_cb(static_cast<Error>(error), intents);
  return Void();
}

Return<void> QtiComposerClient::getColorModes_2_3(uint64_t display,
                                                  getColorModes_2_3_cb _hidl_cb) {
  hidl_vec<common_V1_2::ColorMode> modes;
  uint32_t count = 0;

  auto error = hwc_session_->GetColorModes(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), modes);
    return Void();
  }

  modes.resize(count);
  error = hwc_session_->GetColorModes(display, &count,
              reinterpret_cast<std::underlying_type<common_V1_2::ColorMode>::type*>(modes.data()));

  _hidl_cb(static_cast<Error>(error), modes);
  return Void();
}

Return<Error> QtiComposerClient::setColorMode_2_3(uint64_t display, common_V1_2::ColorMode mode,
                                                  common_V1_1::RenderIntent intent) {
  auto error = hwc_session_->SetColorModeWithRenderIntent(display, static_cast<int32_t>(mode),
                                                          static_cast<int32_t>(intent));

  return static_cast<Error>(error);
}

Return<void> QtiComposerClient::getDisplayCapabilities(uint64_t display,
                                                       getDisplayCapabilities_cb _hidl_cb) {
  hidl_vec<composer_V2_3::IComposerClient::DisplayCapability> capabilities;
  uint32_t count = 0;
  auto error = hwc_session_->GetDisplayCapabilities(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), capabilities);
    return Void();
  }

  capabilities.resize(count);
  error = hwc_session_->GetDisplayCapabilities(
      display, &count,
      reinterpret_cast<std::underlying_type<composer_V2_3::IComposerClient::DisplayCapability>::type
                           *>(capabilities.data()));

  _hidl_cb(static_cast<Error>(error), capabilities);
  return Void();
}

Return<void> QtiComposerClient::getPerFrameMetadataKeys_2_3(uint64_t display,
                                                         getPerFrameMetadataKeys_2_3_cb _hidl_cb) {
  std::vector<PerFrameMetadataKey> keys;
  uint32_t count = 0;

  auto error = hwc_session_->GetPerFrameMetadataKeys(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), keys);
    return Void();
  }

  keys.resize(count);
  error = hwc_session_->GetPerFrameMetadataKeys(display, &count,
                  reinterpret_cast<std::underlying_type<PerFrameMetadataKey>::type*>(keys.data()));

  _hidl_cb(static_cast<Error>(error), keys);
  return Void();
}

Return<void> QtiComposerClient::getHdrCapabilities_2_3(uint64_t display,
                                                       getHdrCapabilities_2_3_cb _hidl_cb) {
  uint32_t count = 0;
  hidl_vec<common_V1_2::Hdr> types;
  float max_lumi = 0.0f;
  float max_avg_lumi = 0.0f;
  float min_lumi = 0.0f;

  auto error = hwc_session_->GetHdrCapabilities(display, &count, nullptr, &max_lumi,
                                                &max_avg_lumi, &min_lumi);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<Error>(error), types, max_lumi, max_avg_lumi, min_lumi);
    return Void();
  }

  types.resize(count);
  error = hwc_session_->GetHdrCapabilities(display, &count,
           reinterpret_cast<std::underlying_type<common_V1_2::Hdr>::type*>(types.data()),
           &max_lumi, &max_avg_lumi, &min_lumi);

  _hidl_cb(static_cast<Error>(error), types, max_lumi, max_avg_lumi, min_lumi);
  return Void();
}

Return<void> QtiComposerClient::getDisplayBrightnessSupport(uint64_t display,
                                                         getDisplayBrightnessSupport_cb _hidl_cb) {
  bool support = false;
  auto error = hwc_session_->GetDisplayBrightnessSupport(display, &support);

  _hidl_cb(static_cast<Error>(error), support);
  return Void();
}

Return<Error> QtiComposerClient::setDisplayBrightness(uint64_t display, float brightness) {
  if (std::isnan(brightness) || brightness > 1.0f || (brightness < 0.0f && brightness != -1.0f)) {
    return Error::BAD_PARAMETER;
  }

  auto error = hwc_session_->SetDisplayBrightness(display, brightness);
  return static_cast<Error>(error);
}

// Methods from ::android::hardware::graphics::composer::V2_4::IComposerClient follow.
Return<void> QtiComposerClient::registerCallback_2_4(
    const sp<composer_V2_4::IComposerCallback> &callback) {
  callback_ = sp<composer_V2_1::IComposerCallback>(callback.get());
  callback24_ = callback;
  mUseCallback24_ = true;
  enableCallback(callback != nullptr);
  return Void();
}

Return<void> QtiComposerClient::getDisplayCapabilities_2_4(uint64_t display,
                                                           getDisplayCapabilities_2_4_cb _hidl_cb) {
  hidl_vec<HwcDisplayCapability> capabilities;
  uint32_t count = 0;
  auto error = hwc_session_->GetDisplayCapabilities(display, &count, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<composer_V2_4::Error>(error), capabilities);
    return Void();
  }

  uint32_t count_2_4 = 0;
  error = hwc_session_->GetDisplayCapabilities_2_4(display, &count_2_4, nullptr);
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<composer_V2_4::Error>(error), capabilities);
    return Void();
  }

  capabilities.resize(count + count_2_4);
  error = hwc_session_->GetDisplayCapabilities(
      display, &count,
      reinterpret_cast<std::underlying_type<HwcDisplayCapability>::type *>(capabilities.data()));
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<composer_V2_4::Error>(error), {});
    return Void();
  }

  error = hwc_session_->GetDisplayCapabilities_2_4(
      display, &count_2_4,
      reinterpret_cast<std::underlying_type<HwcDisplayCapability>::type *>(capabilities.data() +
                                                                           count));
  if (error != HWC2_ERROR_NONE) {
    _hidl_cb(static_cast<composer_V2_4::Error>(error), {});
    return Void();
  }

  _hidl_cb(static_cast<composer_V2_4::Error>(error), capabilities);
  return Void();
}

Return<void> QtiComposerClient::getDisplayConnectionType(uint64_t display,
                                                         getDisplayConnectionType_cb _hidl_cb) {
  HwcDisplayConnectionType type;
  auto error = hwc_session_->GetDisplayConnectionType(display, &type);
  _hidl_cb(static_cast<composer_V2_4::Error>(error), type);
  return Void();
}

Return<void> QtiComposerClient::getDisplayAttribute_2_4(
    uint64_t display, uint32_t config, composer_V2_4::IComposerClient::Attribute attribute,
    getDisplayAttribute_2_4_cb _hidl_cb) {
  int32_t value = 0;
  auto error = hwc_session_->GetDisplayAttribute(display, config, attribute, &value);
  _hidl_cb(static_cast<composer_V2_4::Error>(error), value);
  return Void();
}

Return<void> QtiComposerClient::getDisplayVsyncPeriod(uint64_t display,
                                                      getDisplayVsyncPeriod_cb _hidl_cb) {
  VsyncPeriodNanos vsync_period;
  auto error = hwc_session_->GetDisplayVsyncPeriod(display, &vsync_period);
  _hidl_cb(static_cast<composer_V2_4::Error>(error), vsync_period);
  return Void();
}

Return<void> QtiComposerClient::setActiveConfigWithConstraints(
    uint64_t display, uint32_t config,
    const VsyncPeriodChangeConstraints &vsyncPeriodChangeConstraints,
    setActiveConfigWithConstraints_cb _hidl_cb) {
  VsyncPeriodChangeTimeline timeline;
  timeline.newVsyncAppliedTimeNanos = systemTime();
  timeline.refreshRequired = false;
  timeline.refreshTimeNanos = 0;

  auto error = hwc_session_->SetActiveConfigWithConstraints(
      display, config, &vsyncPeriodChangeConstraints, &timeline);
  _hidl_cb(static_cast<composer_V2_4::Error>(error), timeline);
  return Void();
}

Return<composer_V2_4::Error> QtiComposerClient::setAutoLowLatencyMode(uint64_t display, bool on) {
  if (mDisplayData.find(display) == mDisplayData.end()) {
    return composer_V2_4::Error::BAD_DISPLAY;
  }

  auto error = hwc_session_->SetAutoLowLatencyMode(display, on);

  return static_cast<composer_V2_4::Error>(error);
}

Return<void> QtiComposerClient::getSupportedContentTypes(uint64_t display,
                                                         getSupportedContentTypes_cb _hidl_cb) {
  hidl_vec<composer_V2_4::IComposerClient::ContentType> types;
  if (mDisplayData.find(display) == mDisplayData.end()) {
    _hidl_cb(composer_V2_4::Error::BAD_DISPLAY, types);
    return Void();
  }
  auto error = hwc_session_->GetSupportedContentTypes(display, &types);
  _hidl_cb(static_cast<composer_V2_4::Error>(error), types);
  return Void();
}

Return<composer_V2_4::Error> QtiComposerClient::setContentType(
    uint64_t display, composer_V2_4::IComposerClient::ContentType type) {
  if (mDisplayData.find(display) == mDisplayData.end()) {
    return composer_V2_4::Error::BAD_DISPLAY;
  }
  if (type == composer_V2_4::IComposerClient::ContentType::NONE) {
    return composer_V2_4::Error::NONE;
  }
  auto error = hwc_session_->SetContentType(display, type);

  return static_cast<composer_V2_4::Error>(error);
}

Return<void> QtiComposerClient::getLayerGenericMetadataKeys(
    getLayerGenericMetadataKeys_cb _hidl_cb) {
  hidl_vec<composer_V2_4::IComposerClient::LayerGenericMetadataKey> keys = {};
  _hidl_cb(composer_V2_4::Error::NONE, keys);
  return Void();
}

QtiComposerClient::CommandReader::CommandReader(QtiComposerClient& client)
  : mClient(client), mWriter(client.mWriter) {
}

bool QtiComposerClient::CommandReader::parseCommonCmd(
    IComposerClient::Command command, uint16_t length) {
  bool parsed = false;

  switch (command) {
  // Commands from ::android::hardware::graphics::composer::V2_1::IComposerClient follow.
  case IComposerClient::Command::SELECT_DISPLAY:
    parsed = parseSelectDisplay(length);
    // Displays will not be removed while processing the command queue.
    if (parsed && mClient.mDisplayData.find(mDisplay) == mClient.mDisplayData.end()) {
      ALOGW("Command::SELECT_DISPLAY: Display %" PRId64 "not found. Dropping commands.", mDisplay);
      mDisplay = sdm::HWCCallbacks::kNumDisplays;
    }
    break;
  case IComposerClient::Command::SELECT_LAYER:
    parsed = parseSelectLayer(length);
    break;
  case IComposerClient::Command::SET_COLOR_TRANSFORM:
    parsed = parseSetColorTransform(length);
    break;
  case IComposerClient::Command::SET_CLIENT_TARGET:
    parsed = parseSetClientTarget(length);
    break;
  case IComposerClient::Command::SET_OUTPUT_BUFFER:
    parsed = parseSetOutputBuffer(length);
    break;
  case IComposerClient::Command::VALIDATE_DISPLAY:
    parsed = parseValidateDisplay(length);
    break;
  case IComposerClient::Command::ACCEPT_DISPLAY_CHANGES:
    parsed = parseAcceptDisplayChanges(length);
    break;
  case IComposerClient::Command::PRESENT_DISPLAY:
    parsed = parsePresentDisplay(length);
    break;
  case IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY:
    parsed = parsePresentOrValidateDisplay(length);
    break;
  case IComposerClient::Command::SET_LAYER_CURSOR_POSITION:
    parsed = parseSetLayerCursorPosition(length);
    break;
  case IComposerClient::Command::SET_LAYER_BUFFER:
    parsed = parseSetLayerBuffer(length);
    break;
  case IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE:
    parsed = parseSetLayerSurfaceDamage(length);
    break;
  case IComposerClient::Command::SET_LAYER_BLEND_MODE:
    parsed = parseSetLayerBlendMode(length);
    break;
  case IComposerClient::Command::SET_LAYER_COLOR:
    parsed = parseSetLayerColor(length);
    break;
  case IComposerClient::Command::SET_LAYER_COMPOSITION_TYPE:
    parsed = parseSetLayerCompositionType(length);
    break;
  case IComposerClient::Command::SET_LAYER_DATASPACE:
    parsed = parseSetLayerDataspace(length);
    break;
  case IComposerClient::Command::SET_LAYER_DISPLAY_FRAME:
    parsed = parseSetLayerDisplayFrame(length);
    break;
  case IComposerClient::Command::SET_LAYER_PLANE_ALPHA:
    parsed = parseSetLayerPlaneAlpha(length);
    break;
  case IComposerClient::Command::SET_LAYER_SIDEBAND_STREAM:
    parsed = parseSetLayerSidebandStream(length);
    break;
  case IComposerClient::Command::SET_LAYER_SOURCE_CROP:
    parsed = parseSetLayerSourceCrop(length);
    break;
  case IComposerClient::Command::SET_LAYER_TRANSFORM:
    parsed = parseSetLayerTransform(length);
    break;
  case IComposerClient::Command::SET_LAYER_VISIBLE_REGION:
    parsed = parseSetLayerVisibleRegion(length);
    break;
  case IComposerClient::Command::SET_LAYER_Z_ORDER:
    parsed = parseSetLayerZOrder(length);
    break;
  // Commands from ::android::hardware::graphics::composer::V2_2::IComposerClient follow.
  case IComposerClient::Command::SET_LAYER_PER_FRAME_METADATA:
    parsed = parseSetLayerPerFrameMetadata(length);
    break;
  case IComposerClient::Command::SET_LAYER_FLOAT_COLOR:
    parsed = parseSetLayerFloatColor(length);
    break;
  // Commands from ::android::hardware::graphics::composer::V2_3::IComposerClient follow.
  case IComposerClient::Command::SET_LAYER_COLOR_TRANSFORM:
    parsed = parseSetLayerColorTransform(length);
    break;
  case IComposerClient::Command::SET_LAYER_PER_FRAME_METADATA_BLOBS:
    parsed = parseSetLayerPerFrameMetadataBlobs(length);
    break;
  default:
    parsed = false;
    break;
  }

  return parsed;
}

Error QtiComposerClient::CommandReader::parse() {
  IQtiComposerClient::Command qticommand;
  uint16_t length;

  while (!isEmpty()) {
    if (!beginCommand(qticommand, length)) {
      break;
    }

    bool parsed = false;
    switch (qticommand) {
      case IQtiComposerClient::Command::SET_LAYER_TYPE:
        parsed = parseSetLayerType(length);
        break;
      case IQtiComposerClient::Command::SET_DISPLAY_ELAPSE_TIME:
        parsed = parseSetDisplayElapseTime(length);
        break;
      default:
        parsed = parseCommonCmd(static_cast<IComposerClient::Command>(qticommand), length);
        break;
    }

    endCommand();

    if (!parsed) {
      ALOGE("failed to parse command 0x%x, length %" PRIu16,
          qticommand, length);
      break;
    }
  }

  return (isEmpty()) ? Error::NONE : Error::BAD_PARAMETER;
}

bool QtiComposerClient::CommandReader::parseSelectDisplay(uint16_t length) {
  if (length != CommandWriter::kSelectDisplayLength) {
    return false;
  }

  mDisplay = read64();
  mWriter.selectDisplay(mDisplay);

  return true;
}

bool QtiComposerClient::CommandReader::parseSelectLayer(uint16_t length) {
  if (length != CommandWriter::kSelectLayerLength) {
    return false;
  }

  mLayer = read64();

  return true;
}

bool QtiComposerClient::CommandReader::parseSetColorTransform(uint16_t length) {
  if (length != CommandWriter::kSetColorTransformLength) {
    return false;
  }

  float matrix[16];
  for (int i = 0; i < 16; i++) {
    matrix[i] = readFloat();
  }
  auto transform = readSigned();

  auto err = mClient.hwc_session_->SetColorTransform(mDisplay, matrix, transform);
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetClientTarget(uint16_t length) {
  // 4 parameters followed by N rectangles
  if ((length - 4) % 4 != 0) {
    return false;
  }

  bool useCache = false;
  auto slot = read();
  auto clientTarget = readHandle(useCache);
  shared_ptr<Fence> fence = nullptr;
  readFence(&fence, "fbt");
  auto dataspace = readSigned();
  auto damage = readRegion((length - 4) / 4);
  hwc_region region = {damage.size(), damage.data()};
  auto err = lookupBuffer(BufferCache::CLIENT_TARGETS, slot, useCache, clientTarget, &clientTarget);
  if (err == Error::NONE) {
    auto error = mClient.hwc_session_->SetClientTarget(mDisplay, clientTarget, fence,
        dataspace, region);
    err = static_cast<Error>(error);
    auto updateBufErr = updateBuffer(BufferCache::CLIENT_TARGETS, slot,
        useCache, clientTarget);
    if (err == Error::NONE) {
      err = updateBufErr;
    }
  }
  if (err != Error::NONE) {
    mWriter.setError(getCommandLoc(), err);
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetOutputBuffer(uint16_t length) {
  if (length != CommandWriter::kSetOutputBufferLength) {
    return false;
  }

  bool useCache;
  auto slot = read();
  auto outputBuffer = readHandle(useCache);
  shared_ptr<Fence> fence = nullptr;
  readFence(&fence, "outbuf");
  auto err = lookupBuffer(BufferCache::OUTPUT_BUFFERS, slot, useCache, outputBuffer, &outputBuffer);
  if (err == Error::NONE) {
    auto error = mClient.hwc_session_->SetOutputBuffer(mDisplay, outputBuffer, fence);
    err = static_cast<Error>(error);
    auto updateBufErr = updateBuffer(BufferCache::OUTPUT_BUFFERS, slot, useCache, outputBuffer);
    if (err == Error::NONE) {
      err = updateBufErr;
    }
  }

  if (err != Error::NONE) {
    mWriter.setError(getCommandLoc(), err);
  }

  return true;
}

Error QtiComposerClient::CommandReader::validateDisplay(Display display,
                                       std::vector<Layer>& changedLayers,
                                       std::vector<IComposerClient::Composition>& compositionTypes,
                                       uint32_t& displayRequestMask,
                                       std::vector<Layer>& requestedLayers,
                                       std::vector<uint32_t>& requestMasks) {
  uint32_t types_count = 0;
  uint32_t reqs_count = 0;

  auto err = mClient.hwc_session_->ValidateDisplay(mDisplay, &types_count, &reqs_count);
  if (err != HWC2_ERROR_NONE && err != HWC2_ERROR_HAS_CHANGES) {
    return static_cast<Error>(err);
  }

  err = mClient.hwc_session_->GetChangedCompositionTypes(mDisplay, &types_count, nullptr, nullptr);
  if (err != HWC2_ERROR_NONE) {
    return static_cast<Error>(err);
  }

  changedLayers.resize(types_count);
  compositionTypes.resize(types_count);
  err = mClient.hwc_session_->GetChangedCompositionTypes(mDisplay, &types_count,
                        changedLayers.data(),
                        reinterpret_cast<std::underlying_type<IComposerClient::Composition>::type*>(
                        compositionTypes.data()));

  if (err != HWC2_ERROR_NONE) {
    changedLayers.clear();
    compositionTypes.clear();
    return static_cast<Error>(err);
  }

  int32_t display_reqs = 0;
  err = mClient.hwc_session_->GetDisplayRequests(mDisplay, &display_reqs, &reqs_count, nullptr,
                                                 nullptr);
  if (err != HWC2_ERROR_NONE) {
    changedLayers.clear();
    compositionTypes.clear();
    return static_cast<Error>(err);
  }

  requestedLayers.resize(reqs_count);
  requestMasks.resize(reqs_count);
  err = mClient.hwc_session_->GetDisplayRequests(mDisplay, &display_reqs, &reqs_count,
                                                 requestedLayers.data(),
                                                 reinterpret_cast<int32_t*>(requestMasks.data()));
  if (err != HWC2_ERROR_NONE) {
    changedLayers.clear();
    compositionTypes.clear();

    requestedLayers.clear();
    requestMasks.clear();
    return static_cast<Error>(err);
  }

  displayRequestMask = display_reqs;

  return static_cast<Error>(err);
}

bool QtiComposerClient::CommandReader::parseValidateDisplay(uint16_t length) {
  if (length != CommandWriter::kValidateDisplayLength) {
    return false;
  }

  std::vector<Layer> changedLayers;
  std::vector<IComposerClient::Composition> compositionTypes;
  uint32_t displayRequestMask;
  std::vector<Layer> requestedLayers;
  std::vector<uint32_t> requestMasks;

  auto err = validateDisplay(mDisplay, changedLayers, compositionTypes, displayRequestMask,
                             requestedLayers, requestMasks);

  if (static_cast<Error>(err) == Error::NONE) {
    mWriter.setChangedCompositionTypes(changedLayers, compositionTypes);
    mWriter.setDisplayRequests(displayRequestMask, requestedLayers, requestMasks);
  } else {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseAcceptDisplayChanges(uint16_t length) {
  if (length != CommandWriter::kAcceptDisplayChangesLength) {
    return false;
  }

  auto err = mClient.hwc_session_->AcceptDisplayChanges(mDisplay);
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

Error QtiComposerClient::CommandReader::presentDisplay(Display display,
                                                  shared_ptr<Fence>* presentFence,
                                                  std::vector<Layer>& layers,
                                                  std::vector<shared_ptr<Fence>>& releaseFences) {
  int32_t err = mClient.hwc_session_->PresentDisplay(display, presentFence);
  if (err != HWC2_ERROR_NONE) {
    return static_cast<Error>(err);
  }

  uint32_t count = 0;
  err = mClient.hwc_session_->GetReleaseFences(display, &count, nullptr, nullptr);
  if (err != HWC2_ERROR_NONE) {
    ALOGW("failed to get release fences");
    return Error::NONE;
  }

  layers.resize(count);
  releaseFences.resize(count);
  err = mClient.hwc_session_->GetReleaseFences(display, &count, layers.data(), &releaseFences);
  if (err != HWC2_ERROR_NONE) {
    ALOGW("failed to get release fences");
    layers.clear();
    releaseFences.clear();
    return Error::NONE;
  }

  return static_cast<Error>(err);
}

bool QtiComposerClient::CommandReader::parsePresentDisplay(uint16_t length) {
  if (length != CommandWriter::kPresentDisplayLength) {
    return false;
  }

  shared_ptr<Fence> presentFence = nullptr;
  std::vector<Layer> layers;
  std::vector<shared_ptr<Fence>> fences;

  auto err = presentDisplay(mDisplay, &presentFence, layers, fences);
  if (err == Error::NONE) {
    mWriter.setPresentFence(presentFence);
    mWriter.setReleaseFences(layers, fences);
  } else {
    mWriter.setError(getCommandLoc(), err);
  }

  return true;
}

bool QtiComposerClient::CommandReader::parsePresentOrValidateDisplay(uint16_t length) {
  if (length != CommandWriter::kPresentOrValidateDisplayLength) {
     return false;
  }

  // First try to Present as is.
  mClient.getCapabilities();
  if (mClient.hasCapability(HWC2_CAPABILITY_SKIP_VALIDATE)) {
    shared_ptr<Fence> presentFence = nullptr;
    std::vector<Layer> layers;
    std::vector<shared_ptr<Fence>> fences;
    auto err = presentDisplay(mDisplay, &presentFence, layers, fences);
    if (err == Error::NONE) {
      mWriter.setPresentOrValidateResult(1);
      mWriter.setPresentFence(presentFence);
      mWriter.setReleaseFences(layers, fences);
      return true;
    }
  }

  // Present has failed. We need to fallback to validate
  std::vector<Layer> changedLayers;
  std::vector<IComposerClient::Composition> compositionTypes;
  uint32_t displayRequestMask = 0x0;
  std::vector<Layer> requestedLayers;
  std::vector<uint32_t> requestMasks;

  auto err = validateDisplay(mDisplay, changedLayers, compositionTypes, displayRequestMask,
                             requestedLayers, requestMasks);
  // mResources->setDisplayMustValidateState(mDisplay, false);
  if (err == Error::NONE) {
    mWriter.setPresentOrValidateResult(0);
    mWriter.setChangedCompositionTypes(changedLayers, compositionTypes);
    mWriter.setDisplayRequests(displayRequestMask, requestedLayers, requestMasks);
  } else {
    mWriter.setError(getCommandLoc(), err);
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerCursorPosition(uint16_t length) {
  if (length != CommandWriter::kSetLayerCursorPositionLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetCursorPosition(mDisplay, mLayer, readSigned(), readSigned());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerBuffer(uint16_t length) {
  if (length != CommandWriter::kSetLayerBufferLength) {
    return false;
  }

  bool useCache;
  auto slot = read();
  auto buffer = readHandle(useCache);
  shared_ptr<Fence> fence = nullptr;
  readFence(&fence, "layer");
  auto error = lookupBuffer(BufferCache::LAYER_BUFFERS, slot, useCache, buffer, &buffer);
  if (error == Error::NONE) {
    auto err = mClient.hwc_session_->SetLayerBuffer(mDisplay, mLayer, buffer, fence);
    error = static_cast<Error>(err);
    auto updateBufErr = updateBuffer(BufferCache::LAYER_BUFFERS, slot, useCache, buffer);
    if (static_cast<Error>(error) == Error::NONE) {
      error = updateBufErr;
    }
  }
  if (static_cast<Error>(error) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(error));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerSurfaceDamage(uint16_t length) {
  // N rectangles
  if (length % 4 != 0) {
    return false;
  }

  auto damage = readRegion(length / 4);
  hwc_region region = {damage.size(), damage.data()};
  auto err = mClient.hwc_session_->SetLayerSurfaceDamage(mDisplay, mLayer, region);
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerBlendMode(uint16_t length) {
  if (length != CommandWriter::kSetLayerBlendModeLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerBlendMode(mDisplay, mLayer, readSigned());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerColor(uint16_t length) {
  if (length != CommandWriter::kSetLayerColorLength) {
    return false;
  }
  auto color = readColor();
  hwc_color_t hwc_color{color.r, color.g, color.b, color.a};
  auto err = mClient.hwc_session_->SetLayerColor(mDisplay, mLayer, hwc_color);
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerCompositionType(uint16_t length) {
  if (length != CommandWriter::kSetLayerCompositionTypeLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerCompositionType(mDisplay, mLayer, readSigned());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerDataspace(uint16_t length) {
  if (length != CommandWriter::kSetLayerDataspaceLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerDataspace(mDisplay, mLayer, readSigned());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerDisplayFrame(uint16_t length) {
  if (length != CommandWriter::kSetLayerDisplayFrameLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerDisplayFrame(mDisplay, mLayer, readRect());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerPlaneAlpha(uint16_t length) {
  if (length != CommandWriter::kSetLayerPlaneAlphaLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerPlaneAlpha(mDisplay, mLayer, readFloat());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerSidebandStream(uint16_t length) {
  if (length != CommandWriter::kSetLayerSidebandStreamLength) {
    return false;
  }

  // Sideband stream is not supported
  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerSourceCrop(uint16_t length) {
  if (length != CommandWriter::kSetLayerSourceCropLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerSourceCrop(mDisplay, mLayer, readFRect());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerTransform(uint16_t length) {
  if (length != CommandWriter::kSetLayerTransformLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerTransform(mDisplay, mLayer, readSigned());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerVisibleRegion(uint16_t length) {
  // N rectangles
  if (length % 4 != 0) {
    return false;
  }

  auto region = readRegion(length / 4);
  hwc_region visibleRegion = {region.size(), region.data()};
  auto err = mClient.hwc_session_->SetLayerVisibleRegion(mDisplay, mLayer, visibleRegion);
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerZOrder(uint16_t length) {
  if (length != CommandWriter::kSetLayerZOrderLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerZOrder(mDisplay, mLayer, read());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerType(uint16_t length) {
  if (length != CommandWriter::kSetLayerTypeLength) {
    return false;
  }

  auto err = mClient.hwc_session_->SetLayerType(mDisplay, mLayer,
            static_cast<IQtiComposerClient::LayerType>(read()));
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerPerFrameMetadata(uint16_t length) {
  // (key, value) pairs
  if (length % 2 != 0) {
    return false;
  }

  std::vector<IComposerClient::PerFrameMetadata> metadata;
  metadata.reserve(length / 2);
  while (length > 0) {
    metadata.emplace_back(IComposerClient::PerFrameMetadata{
                          static_cast<IComposerClient::PerFrameMetadataKey>(readSigned()),
                          readFloat()});
    length -= 2;
  }

  std::vector<int32_t> keys;
  std::vector<float> values;
  keys.reserve(metadata.size());
  values.reserve(metadata.size());
  for (const auto& m : metadata) {
    keys.push_back(static_cast<int32_t>(m.key));
    values.push_back(m.value);
  }

  auto err = mClient.hwc_session_->SetLayerPerFrameMetadata(mDisplay, mLayer, metadata.size(),
                                                            keys.data(), values.data());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerFloatColor(uint16_t length) {
  if (length != CommandWriter::kSetLayerFloatColorLength) {
    return false;
  }

  // setLayerFloatColor is not supported
  auto err = Error::UNSUPPORTED;
  mWriter.setError(getCommandLoc(), static_cast<Error>(err));

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerColorTransform(uint16_t length) {
  if (length != CommandWriter::kSetLayerColorTransformLength) {
    return false;
  }

  float matrix[16];
  for (int i = 0; i < 16; i++) {
    matrix[i] = readFloat();
  }

  auto error = mClient.hwc_session_->SetLayerColorTransform(mDisplay, mLayer, matrix);
  if (static_cast<Error>(error) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(error));
  }

  return true;
}

bool QtiComposerClient::CommandReader::parseSetLayerPerFrameMetadataBlobs(uint16_t length) {
  // must have at least one metadata blob
  // of at least size 1 in queue (i.e {/*numBlobs=*/1, key, size, blob})
  if (length < 4) {
    return false;
  }

  uint32_t numBlobs = read();
  length--;
  std::vector<IComposerClient::PerFrameMetadataBlob> metadata;

  for (size_t i = 0; i < numBlobs; i++) {
    IComposerClient::PerFrameMetadataKey key =
      static_cast<IComposerClient::PerFrameMetadataKey>(readSigned());
    uint32_t blobSize = read();
    length -= 2;

    if (length * sizeof(uint32_t) < blobSize) {
      return false;
    }

    metadata.push_back({key, std::vector<uint8_t>()});
    IComposerClient::PerFrameMetadataBlob& metadataBlob = metadata.back();
    metadataBlob.blob.resize(blobSize);
    readBlob(blobSize, metadataBlob.blob.data());
  }

  std::vector<int32_t> keys;
  std::vector<uint32_t> sizes_of_metablob_;
  std::vector<uint8_t> blob_of_data_;
  keys.reserve(metadata.size());
  sizes_of_metablob_.reserve(metadata.size());
  for (const auto& m : metadata) {
    keys.push_back(static_cast<int32_t>(m.key));
    sizes_of_metablob_.push_back(m.blob.size());
    for (size_t i = 0; i < m.blob.size(); i++) {
      blob_of_data_.push_back(m.blob[i]);
    }
  }
  auto err = mClient.hwc_session_->SetLayerPerFrameMetadataBlobs(mDisplay, mLayer, metadata.size(),
                                                                 keys.data(),
                                                                 sizes_of_metablob_.data(),
                                                                 blob_of_data_.data());
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }
  return true;
}

bool QtiComposerClient::CommandReader::parseSetDisplayElapseTime(uint16_t length) {
  if (length < CommandWriter::kSetDisplayElapseTime) {
    return false;
  }
  uint64_t time = read64();

  auto err = mClient.hwc_session_->SetDisplayElapseTime(mDisplay, time);
  if (static_cast<Error>(err) != Error::NONE) {
    mWriter.setError(getCommandLoc(), static_cast<Error>(err));
  }

  return true;
}

hwc_rect_t QtiComposerClient::CommandReader::readRect() {
  return hwc_rect_t{
    readSigned(),
    readSigned(),
    readSigned(),
    readSigned(),
  };
}

std::vector<hwc_rect_t> QtiComposerClient::CommandReader::readRegion(size_t count) {
  std::vector<hwc_rect_t> region;
  region.reserve(count);
  while (count > 0) {
    region.emplace_back(readRect());
    count--;
  }

  return region;
}

hwc_frect_t QtiComposerClient::CommandReader::readFRect() {
  return hwc_frect_t{
    readFloat(),
    readFloat(),
    readFloat(),
    readFloat(),
  };
}

Error QtiComposerClient::CommandReader::lookupBufferCacheEntryLocked(BufferCache cache,
                                                                     uint32_t slot,
                                                                     BufferCacheEntry** outEntry) {
  auto dpy = mClient.mDisplayData.find(mDisplay);
  if (dpy == mClient.mDisplayData.end()) {
    return Error::BAD_DISPLAY;
  }

  BufferCacheEntry* entry = nullptr;
  switch (cache) {
  case BufferCache::CLIENT_TARGETS:
    if (slot < dpy->second.ClientTargets.size()) {
      entry = &dpy->second.ClientTargets[slot];
    }
    break;
  case BufferCache::OUTPUT_BUFFERS:
    if (slot < dpy->second.OutputBuffers.size()) {
      entry = &dpy->second.OutputBuffers[slot];
    }
    break;
  case BufferCache::LAYER_BUFFERS:
    {
      auto ly = dpy->second.Layers.find(mLayer);
      if (ly == dpy->second.Layers.end()) {
        return Error::BAD_LAYER;
      }
      if (slot < ly->second.Buffers.size()) {
        entry = &ly->second.Buffers[slot];
      }
    }
    break;
  case BufferCache::LAYER_SIDEBAND_STREAMS:
    {
      auto ly = dpy->second.Layers.find(mLayer);
      if (ly == dpy->second.Layers.end()) {
        return Error::BAD_LAYER;
      }
      if (slot == 0) {
        entry = &ly->second.SidebandStream;
      }
    }
    break;
  default:
    break;
  }

  if (!entry) {
    ALOGW("invalid buffer slot %" PRIu32, slot);
    return Error::BAD_PARAMETER;
  }

  *outEntry = entry;

  return Error::NONE;
}

Error QtiComposerClient::CommandReader::lookupBuffer(BufferCache cache, uint32_t slot,
                                                     bool useCache, buffer_handle_t handle,
                                                     buffer_handle_t* outHandle) {
  if (useCache) {
    std::lock_guard<std::mutex> lock(mClient.mDisplayDataMutex);

    BufferCacheEntry* entry;
    Error error = lookupBufferCacheEntryLocked(cache, slot, &entry);
    if (error != Error::NONE) {
      return error;
    }

    // input handle is ignored
    *outHandle = entry->getHandle();
  } else if (cache == BufferCache::LAYER_SIDEBAND_STREAMS) {
    if (handle) {
      *outHandle = native_handle_clone(handle);
      if (*outHandle == nullptr) {
        return Error::NO_RESOURCES;
      }
    }
  } else {
    if (!mHandleImporter.importBuffer(handle)) {
      return Error::NO_RESOURCES;
    }

    *outHandle = handle;
  }

  return Error::NONE;
}

Error QtiComposerClient::CommandReader::updateBuffer(BufferCache cache, uint32_t slot,
                                                     bool useCache, buffer_handle_t handle) {
  // handle was looked up from cache
  if (useCache) {
    return Error::NONE;
  }

  std::lock_guard<std::mutex> lock(mClient.mDisplayDataMutex);

  BufferCacheEntry* entry = nullptr;
  Error error = lookupBufferCacheEntryLocked(cache, slot, &entry);
  if (error != Error::NONE) {
    return error;
  }

  *entry = handle;
  return Error::NONE;
}
// Methods from ::android::hidl::base::V1_0::IBase follow.

IQtiComposerClient* HIDL_FETCH_IQtiComposerClient(const char* /* name */) {
  return QtiComposerClient::CreateQtiComposerClientInstance();
}

}  // namespace implementation
}  // namespace V3_0
}  // namespace composer
}  // namespace display
}  // namespace hardware
}  // namespace qti
}  // namespace vendor