chromium/ui/gfx/android/android_surface_control_compat.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/354829279): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "ui/gfx/android/android_surface_control_compat.h"

#include <android/data_space.h>
#include <android/hdr_metadata.h>
#include <dlfcn.h>

#include "base/android/build_info.h"
#include "base/atomic_sequence_num.h"
#include "base/debug/crash_logging.h"
#include "base/functional/bind.h"
#include "base/hash/md5_constexpr.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "skia/ext/skcolorspace_trfn.h"
#include "ui/gfx/color_space.h"

extern "C" {
typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
typedef void (*ASurfaceTransaction_OnComplete)(void* context,
                                               ASurfaceTransactionStats* stats);
typedef void (*ASurfaceTransaction_OnCommit)(void* context,
                                             ASurfaceTransactionStats* stats);

// ASurface
using pASurfaceControl_createFromWindow =
    ASurfaceControl* (*)(ANativeWindow* parent, const char* name);
using pASurfaceControl_create = ASurfaceControl* (*)(ASurfaceControl* parent,
                                                     const char* name);
using pASurfaceControl_fromJava = ASurfaceControl* (*)(JNIEnv*, jobject);
using pASurfaceControl_release = void (*)(ASurfaceControl*);

// ASurfaceTransaction enums
enum {
  ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
  ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
  ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
};

// ASurfaceTransaction
using pASurfaceTransaction_create = ASurfaceTransaction* (*)(void);
using pASurfaceTransaction_delete = void (*)(ASurfaceTransaction*);
using pASurfaceTransaction_apply = int64_t (*)(ASurfaceTransaction*);
using pASurfaceTransaction_setOnComplete =
    void (*)(ASurfaceTransaction*, void* ctx, ASurfaceTransaction_OnComplete);
using pASurfaceTransaction_setOnCommit = void (*)(ASurfaceTransaction*,
                                                  void* ctx,
                                                  ASurfaceTransaction_OnCommit);
using pASurfaceTransaction_setVisibility = void (*)(ASurfaceTransaction*,
                                                    ASurfaceControl*,
                                                    int8_t visibility);
using pASurfaceTransaction_setZOrder =
    void (*)(ASurfaceTransaction* transaction, ASurfaceControl*, int32_t z);
using pASurfaceTransaction_setBuffer =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl*,
             AHardwareBuffer*,
             int32_t fence_fd);
using pASurfaceTransaction_setGeometry =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             const ARect& src,
             const ARect& dst,
             int32_t transform);
using pASurfaceTransaction_setPosition =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             int32_t x,
             int32_t y);
using pASurfaceTransaction_setScale = void (*)(ASurfaceTransaction* transaction,
                                               ASurfaceControl* surface,
                                               float x_scale,
                                               float y_scale);
using pASurfaceTransaction_setCrop = void (*)(ASurfaceTransaction* transaction,
                                              ASurfaceControl* surface,
                                              const ARect& src);
using pASurfaceTransaction_setBufferTransparency =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             int8_t transparency);
using pASurfaceTransaction_setDamageRegion =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             const ARect rects[],
             uint32_t count);
using pASurfaceTransaction_setBufferDataSpace =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             ADataSpace data_space);
using pASurfaceTransaction_setHdrMetadata_cta861_3 =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             struct AHdrMetadata_cta861_3* metadata);
using pASurfaceTransaction_setHdrMetadata_smpte2086 =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface,
             struct AHdrMetadata_smpte2086* metadata);
using pASurfaceTransaction_setExtendedRangeBrightness =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface_control,
             float currentBufferRatio,
             float desiredRatio);
using pASurfaceTransaction_setFrameRate =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface_control,
             float frameRate,
             int8_t compatibility);
using pASurfaceTransaction_setFrameTimeline =
    void (*)(ASurfaceTransaction* transaction, int64_t vsync_id);
using pASurfaceTransaction_reparent = void (*)(ASurfaceTransaction*,
                                               ASurfaceControl* surface_control,
                                               ASurfaceControl* new_parent);
// ASurfaceTransactionStats
using pASurfaceTransactionStats_getPresentFenceFd =
    int (*)(ASurfaceTransactionStats* stats);
using pASurfaceTransactionStats_getLatchTime =
    int64_t (*)(ASurfaceTransactionStats* stats);
using pASurfaceTransactionStats_getASurfaceControls =
    void (*)(ASurfaceTransactionStats* stats,
             ASurfaceControl*** surface_controls,
             size_t* size);
using pASurfaceTransactionStats_releaseASurfaceControls =
    void (*)(ASurfaceControl** surface_controls);
using pASurfaceTransactionStats_getPreviousReleaseFenceFd =
    int (*)(ASurfaceTransactionStats* stats, ASurfaceControl* surface_control);
using pASurfaceTransaction_setEnableBackPressure =
    void (*)(ASurfaceTransaction* transaction,
             ASurfaceControl* surface_control,
             bool enable_back_pressure);
}

namespace gfx {
namespace {

base::AtomicSequenceNumber g_next_transaction_id;

uint64_t g_agb_required_usage_bits = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;

#define LOAD_FUNCTION(lib, func)                             \
  do {                                                       \
    func##Fn = reinterpret_cast<p##func>(dlsym(lib, #func)); \
    if (!func##Fn) {                                         \
      supported = false;                                     \
      LOG(ERROR) << "Unable to load function " << #func;     \
    }                                                        \
  } while (0)

#define LOAD_FUNCTION_MAYBE(lib, func)                       \
  do {                                                       \
    func##Fn = reinterpret_cast<p##func>(dlsym(lib, #func)); \
  } while (0)

struct SurfaceControlMethods {
 public:
  static SurfaceControlMethods& GetImpl(bool load_functions) {
    static SurfaceControlMethods instance(load_functions);
    return instance;
  }

  static const SurfaceControlMethods& Get() {
    return GetImpl(/*load_functions=*/true);
  }

  void InitWithStubs() {
    struct TransactionStub {
      ASurfaceTransaction_OnComplete on_complete = nullptr;
      raw_ptr<void> on_complete_ctx = nullptr;
      ASurfaceTransaction_OnCommit on_commit = nullptr;
      raw_ptr<void> on_commit_ctx = nullptr;
    };

    ASurfaceTransaction_createFn = []() {
      return reinterpret_cast<ASurfaceTransaction*>(new TransactionStub);
    };
    ASurfaceTransaction_deleteFn = [](ASurfaceTransaction* transaction) {
      delete reinterpret_cast<TransactionStub*>(transaction);
    };
    ASurfaceTransaction_applyFn = [](ASurfaceTransaction* transaction) {
      auto* stub = reinterpret_cast<TransactionStub*>(transaction);

      if (stub->on_commit)
        stub->on_commit(stub->on_commit_ctx, nullptr);
      stub->on_commit = nullptr;
      stub->on_commit_ctx = nullptr;

      if (stub->on_complete)
        stub->on_complete(stub->on_complete_ctx, nullptr);
      stub->on_complete = nullptr;
      stub->on_complete_ctx = nullptr;

      return static_cast<int64_t>(0);
    };

    ASurfaceTransaction_setOnCompleteFn =
        [](ASurfaceTransaction* transaction, void* ctx,
           ASurfaceTransaction_OnComplete callback) {
          auto* stub = reinterpret_cast<TransactionStub*>(transaction);
          stub->on_complete = callback;
          stub->on_complete_ctx = ctx;
        };

    ASurfaceTransaction_setOnCommitFn =
        [](ASurfaceTransaction* transaction, void* ctx,
           ASurfaceTransaction_OnCommit callback) {
          auto* stub = reinterpret_cast<TransactionStub*>(transaction);
          stub->on_commit = callback;
          stub->on_commit_ctx = ctx;
        };
  }

  SurfaceControlMethods(bool load_functions) {
    if (!load_functions)
      return;

    void* main_dl_handle = dlopen("libandroid.so", RTLD_NOW);
    if (!main_dl_handle) {
      LOG(ERROR) << "Couldnt load android so";
      supported = false;
      return;
    }

    LOAD_FUNCTION(main_dl_handle, ASurfaceControl_createFromWindow);
    LOAD_FUNCTION(main_dl_handle, ASurfaceControl_create);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceControl_fromJava);
    LOAD_FUNCTION(main_dl_handle, ASurfaceControl_release);

    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_create);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_delete);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_apply);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setOnComplete);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setOnCommit);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_reparent);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setVisibility);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setZOrder);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBuffer);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setGeometry);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setPosition);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setScale);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setCrop);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBufferTransparency);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setDamageRegion);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBufferDataSpace);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setHdrMetadata_cta861_3);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setHdrMetadata_smpte2086);
    LOAD_FUNCTION_MAYBE(main_dl_handle,
                        ASurfaceTransaction_setExtendedRangeBrightness);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setFrameRate);
    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setFrameTimeline);

    LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getPresentFenceFd);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getLatchTime);
    LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getASurfaceControls);
    LOAD_FUNCTION(main_dl_handle,
                  ASurfaceTransactionStats_releaseASurfaceControls);
    LOAD_FUNCTION(main_dl_handle,
                  ASurfaceTransactionStats_getPreviousReleaseFenceFd);
    LOAD_FUNCTION_MAYBE(main_dl_handle,
                        ASurfaceTransaction_setEnableBackPressure);
  }

  ~SurfaceControlMethods() = default;

  bool supported = true;
  // Surface methods.
  pASurfaceControl_createFromWindow ASurfaceControl_createFromWindowFn;
  pASurfaceControl_create ASurfaceControl_createFn;
  pASurfaceControl_fromJava ASurfaceControl_fromJavaFn;
  pASurfaceControl_release ASurfaceControl_releaseFn;

  // Transaction methods.
  pASurfaceTransaction_create ASurfaceTransaction_createFn;
  pASurfaceTransaction_delete ASurfaceTransaction_deleteFn;
  pASurfaceTransaction_apply ASurfaceTransaction_applyFn;
  pASurfaceTransaction_setOnComplete ASurfaceTransaction_setOnCompleteFn;
  pASurfaceTransaction_setOnCommit ASurfaceTransaction_setOnCommitFn;
  pASurfaceTransaction_reparent ASurfaceTransaction_reparentFn;
  pASurfaceTransaction_setVisibility ASurfaceTransaction_setVisibilityFn;
  pASurfaceTransaction_setZOrder ASurfaceTransaction_setZOrderFn;
  pASurfaceTransaction_setBuffer ASurfaceTransaction_setBufferFn;
  pASurfaceTransaction_setGeometry ASurfaceTransaction_setGeometryFn;
  pASurfaceTransaction_setPosition ASurfaceTransaction_setPositionFn;
  pASurfaceTransaction_setScale ASurfaceTransaction_setScaleFn;
  pASurfaceTransaction_setCrop ASurfaceTransaction_setCropFn;
  pASurfaceTransaction_setBufferTransparency
      ASurfaceTransaction_setBufferTransparencyFn;
  pASurfaceTransaction_setDamageRegion ASurfaceTransaction_setDamageRegionFn;
  pASurfaceTransaction_setBufferDataSpace
      ASurfaceTransaction_setBufferDataSpaceFn;
  pASurfaceTransaction_setHdrMetadata_cta861_3
      ASurfaceTransaction_setHdrMetadata_cta861_3Fn;
  pASurfaceTransaction_setHdrMetadata_smpte2086
      ASurfaceTransaction_setHdrMetadata_smpte2086Fn;
  pASurfaceTransaction_setExtendedRangeBrightness
      ASurfaceTransaction_setExtendedRangeBrightnessFn;

  pASurfaceTransaction_setFrameRate ASurfaceTransaction_setFrameRateFn;
  pASurfaceTransaction_setFrameTimeline ASurfaceTransaction_setFrameTimelineFn;
  pASurfaceTransaction_setEnableBackPressure
      ASurfaceTransaction_setEnableBackPressureFn;

  // TransactionStats methods.
  pASurfaceTransactionStats_getPresentFenceFd
      ASurfaceTransactionStats_getPresentFenceFdFn;
  pASurfaceTransactionStats_getLatchTime
      ASurfaceTransactionStats_getLatchTimeFn;
  pASurfaceTransactionStats_getASurfaceControls
      ASurfaceTransactionStats_getASurfaceControlsFn;
  pASurfaceTransactionStats_releaseASurfaceControls
      ASurfaceTransactionStats_releaseASurfaceControlsFn;
  pASurfaceTransactionStats_getPreviousReleaseFenceFd
      ASurfaceTransactionStats_getPreviousReleaseFenceFdFn;
};

ARect RectToARect(const gfx::Rect& rect) {
  return ARect{rect.x(), rect.y(), rect.right(), rect.bottom()};
}

int32_t OverlayTransformToWindowTransform(gfx::OverlayTransform transform) {
  // Note that the gfx::OverlayTransform expresses rotations in anticlockwise
  // direction while the ANativeWindow rotations are in clockwise direction.
  switch (transform) {
    case gfx::OVERLAY_TRANSFORM_INVALID:
      DCHECK(false) << "Invalid Transform";
      return ANATIVEWINDOW_TRANSFORM_IDENTITY;
    case gfx::OVERLAY_TRANSFORM_NONE:
      return ANATIVEWINDOW_TRANSFORM_IDENTITY;
    case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
      return ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL;
    case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL:
      return ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL;
    case gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90:
      return ANATIVEWINDOW_TRANSFORM_ROTATE_270;
    case gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_180:
      return ANATIVEWINDOW_TRANSFORM_ROTATE_180;
    case gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_270:
      return ANATIVEWINDOW_TRANSFORM_ROTATE_90;
    case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL_CLOCKWISE_90:
      return ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
             ANATIVEWINDOW_TRANSFORM_ROTATE_90;
    case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL_CLOCKWISE_270:
      return ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
             ANATIVEWINDOW_TRANSFORM_ROTATE_90;
  };
  NOTREACHED_IN_MIGRATION();
  return ANATIVEWINDOW_TRANSFORM_IDENTITY;
}

inline ADataSpace operator|(ADataSpace a, ADataSpace b) {
  return static_cast<ADataSpace>(static_cast<int32_t>(a) |
                                 static_cast<int32_t>(b));
}

inline ADataSpace& operator|=(ADataSpace& a, ADataSpace b) {
  return a = static_cast<ADataSpace>(static_cast<int32_t>(a) |
                                     static_cast<int32_t>(b));
}

bool SetDataSpaceStandard(const gfx::ColorSpace& color_space,
                          ADataSpace& dataspace) {
  switch (color_space.GetPrimaryID()) {
    case gfx::ColorSpace::PrimaryID::BT709:
      dataspace |= STANDARD_BT709;
      return true;
    case gfx::ColorSpace::PrimaryID::BT470BG:
      dataspace |= STANDARD_BT601_625;
      return true;
    case gfx::ColorSpace::PrimaryID::SMPTE170M:
      dataspace |= STANDARD_BT601_525;
      return true;
    case gfx::ColorSpace::PrimaryID::BT2020:
      dataspace |= STANDARD_BT2020;
      return true;
    case gfx::ColorSpace::PrimaryID::P3:
      dataspace |= STANDARD_DCI_P3;
      return true;
    default:
      return false;
  }
}

bool SetDataSpaceTransfer(const gfx::ColorSpace& color_space,
                          ADataSpace& dataspace,
                          float& extended_range_brightness_ratio) {
  extended_range_brightness_ratio = 1.f;
  switch (color_space.GetTransferID()) {
    case gfx::ColorSpace::TransferID::SMPTE170M:
      dataspace |= TRANSFER_SMPTE_170M;
      return true;
    case gfx::ColorSpace::TransferID::LINEAR_HDR:
      dataspace |= TRANSFER_LINEAR;
      return true;
    case gfx::ColorSpace::TransferID::PQ:
      dataspace |= TRANSFER_ST2084;
      return true;
    case gfx::ColorSpace::TransferID::HLG:
      dataspace |= TRANSFER_HLG;
      return true;
    case gfx::ColorSpace::TransferID::SRGB:
      dataspace |= TRANSFER_SRGB;
      return true;
    case gfx::ColorSpace::TransferID::BT709:
      // We use SRGB for BT709. See |ColorSpace::GetTransferFunction()| for
      // details.
      dataspace |= TRANSFER_SRGB;
      return true;
    default: {
      skcms_TransferFunction trfn;
      // Detect scaled versions of sRGB and linear for HDR content.
      if (color_space.GetTransferFunction(&trfn)) {
        if (skia::IsScaledTransferFunction(SkNamedTransferFnExt::kSRGB, trfn,
                                           &extended_range_brightness_ratio)) {
          dataspace |= TRANSFER_SRGB;
          return true;
        }
        if (skia::IsScaledTransferFunction(SkNamedTransferFn::kLinear, trfn,
                                           &extended_range_brightness_ratio)) {
          dataspace |= TRANSFER_LINEAR;
          return true;
        }
      }
      return false;
    }
  }
}

bool SetDataSpaceRange(const gfx::ColorSpace& color_space,
                       float extended_range_brightness_ratio,
                       float desired_brightness_ratio,
                       ADataSpace& dataspace) {
  switch (color_space.GetRangeID()) {
    case gfx::ColorSpace::RangeID::FULL:
      if (extended_range_brightness_ratio > 1.f ||
          desired_brightness_ratio > 1.f) {
        dataspace |= RANGE_EXTENDED;
      } else {
        dataspace |= RANGE_FULL;
      }
      return true;
    case gfx::ColorSpace::RangeID::LIMITED:
      dataspace |= RANGE_LIMITED;
      return true;
    default:
      return false;
  };
}

SurfaceControl::TransactionStats ToTransactionStats(
    ASurfaceTransactionStats* stats) {
  SurfaceControl::TransactionStats transaction_stats;

  // In unit tests we don't have stats.
  if (!stats)
    return transaction_stats;

  transaction_stats.present_fence = base::ScopedFD(
      SurfaceControlMethods::Get().ASurfaceTransactionStats_getPresentFenceFdFn(
          stats));
  transaction_stats.latch_time =
      base::TimeTicks() +
      base::Nanoseconds(
          SurfaceControlMethods::Get().ASurfaceTransactionStats_getLatchTimeFn(
              stats));
  if (transaction_stats.latch_time == base::TimeTicks())
    transaction_stats.latch_time = base::TimeTicks::Now();

  ASurfaceControl** surface_controls = nullptr;
  size_t size = 0u;
  SurfaceControlMethods::Get().ASurfaceTransactionStats_getASurfaceControlsFn(
      stats, &surface_controls, &size);
  transaction_stats.surface_stats.resize(size);
  for (size_t i = 0u; i < size; ++i) {
    transaction_stats.surface_stats[i].surface = surface_controls[i];
    int fence_fd = SurfaceControlMethods::Get()
                       .ASurfaceTransactionStats_getPreviousReleaseFenceFdFn(
                           stats, surface_controls[i]);
    if (fence_fd != -1) {
      transaction_stats.surface_stats[i].fence = base::ScopedFD(fence_fd);
    }
  }
  SurfaceControlMethods::Get()
      .ASurfaceTransactionStats_releaseASurfaceControlsFn(surface_controls);

  return transaction_stats;
}

struct TransactionAckCtx {
  int id = 0;
  SurfaceControl::Transaction::OnCompleteCb callback;
  SurfaceControl::Transaction::OnCommitCb latch_callback;
};

uint64_t GetTraceIdForTransaction(int transaction_id) {
  constexpr uint64_t kMask =
      base::MD5Hash64Constexpr("SurfaceControl::Transaction");
  return kMask ^ transaction_id;
}

// Note that the framework API states that this callback can be dispatched on
// any thread (in practice it should be a binder thread).
void OnTransactionCompletedOnAnyThread(void* context,
                                       ASurfaceTransactionStats* stats) {
  auto* ack_ctx = static_cast<TransactionAckCtx*>(context);
  auto transaction_stats = ToTransactionStats(stats);
  TRACE_EVENT_NESTABLE_ASYNC_END0("gpu,benchmark", "SurfaceControlTransaction",
                                  ack_ctx->id);
  TRACE_EVENT_WITH_FLOW0(
      "toplevel.flow", "gfx::SurfaceControlTransaction completed",
      GetTraceIdForTransaction(ack_ctx->id), TRACE_EVENT_FLAG_FLOW_IN);

  std::move(ack_ctx->callback).Run(std::move(transaction_stats));
  delete ack_ctx;
}

// Note that the framework API states that this callback can be dispatched on
// any thread (in practice it should be a binder thread).
void OnTransactiOnCommittedOnAnyThread(void* context,
                                       ASurfaceTransactionStats* stats) {
  auto* ack_ctx = static_cast<TransactionAckCtx*>(context);
  TRACE_EVENT_INSTANT0("gpu,benchmark", "SurfaceControlTransaction committed",
                       TRACE_EVENT_SCOPE_THREAD);

  std::move(ack_ctx->latch_callback).Run();
  delete ack_ctx;
}

}  // namespace

// static
bool SurfaceControl::IsSupported() {
  const auto* build_info = base::android::BuildInfo::GetInstance();

  // Disabled on Samsung devices due to a platform bug fixed in R.
  int min_sdk_version = base::android::SDK_VERSION_Q;
  if (base::EqualsCaseInsensitiveASCII(build_info->manufacturer(), "samsung"))
    min_sdk_version = base::android::SDK_VERSION_R;

  if (build_info->sdk_int() < min_sdk_version)
    return false;

  CHECK(SurfaceControlMethods::Get().supported);
  return true;
}

bool SurfaceControl::SupportsColorSpace(const gfx::ColorSpace& color_space) {
  float desired_brightness_ratio = 1.f;
  ADataSpace dataspace = ADATASPACE_UNKNOWN;
  float extended_range_brightness_ratio = 1.f;
  return ColorSpaceToADataSpace(color_space, desired_brightness_ratio,
                                dataspace, extended_range_brightness_ratio);
}

bool SurfaceControl::ColorSpaceToADataSpace(
    const gfx::ColorSpace& color_space,
    float desired_brightness_ratio,
    ADataSpace& out_dataspace,
    float& out_extended_range_brightness_ratio) {
  out_dataspace = ADATASPACE_UNKNOWN;
  out_extended_range_brightness_ratio = 1.f;

  if (!color_space.IsValid()) {
    out_dataspace = ADATASPACE_SRGB;
    return true;
  }

  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
      base::android::SDK_VERSION_S) {
    if (color_space == gfx::ColorSpace::CreateExtendedSRGB()) {
      out_dataspace = STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED;
      return true;
    }

    ADataSpace dataspace = ADATASPACE_UNKNOWN;
    float extended_range_brightness_ratio = 1.f;
    if (!SetDataSpaceStandard(color_space, dataspace)) {
      return false;
    }
    if (!SetDataSpaceTransfer(color_space, dataspace,
                              extended_range_brightness_ratio)) {
      return false;
    }
    if (!SetDataSpaceRange(color_space, extended_range_brightness_ratio,
                           desired_brightness_ratio, dataspace)) {
      return false;
    }
    out_dataspace = dataspace;
    out_extended_range_brightness_ratio = extended_range_brightness_ratio;
    return true;
  }

  if (!color_space.IsValid() || color_space == gfx::ColorSpace::CreateSRGB()) {
    out_dataspace = ADATASPACE_SRGB;
    return true;
  }

  if (color_space == gfx::ColorSpace::CreateSRGBLinear()) {
    out_dataspace = ADATASPACE_SCRGB_LINEAR;
    return true;
  }

  if (color_space == gfx::ColorSpace::CreateDisplayP3D65()) {
    out_dataspace = ADATASPACE_DISPLAY_P3;
    return true;
  }

  return false;
}

uint64_t SurfaceControl::RequiredUsage() {
  if (!IsSupported())
    return 0u;
  return g_agb_required_usage_bits;
}

void SurfaceControl::EnableQualcommUBWC() {
  g_agb_required_usage_bits |= AHARDWAREBUFFER_USAGE_VENDOR_0;
}

bool SurfaceControl::SupportsSetFrameRate() {
  // TODO(khushalsagar): Assert that this function is always available on R.
  return IsSupported() &&
         SurfaceControlMethods::Get().ASurfaceTransaction_setFrameRateFn !=
             nullptr;
}

bool SurfaceControl::SupportsOnCommit() {
  return IsSupported() &&
         SurfaceControlMethods::Get().ASurfaceTransaction_setOnCommitFn !=
             nullptr;
}

bool SurfaceControl::SupportsSetFrameTimeline() {
  return IsSupported() &&
         SurfaceControlMethods::Get().ASurfaceTransaction_setFrameTimelineFn !=
             nullptr;
}

bool SurfaceControl::SupportsSurfacelessControl() {
  return IsSupported() &&
         !!SurfaceControlMethods::Get().ASurfaceControl_fromJavaFn;
}

bool SurfaceControl::SupportsSetEnableBackPressure() {
  return IsSupported() &&
         SurfaceControlMethods::Get()
                 .ASurfaceTransaction_setEnableBackPressureFn != nullptr;
}

void SurfaceControl::SetStubImplementationForTesting() {
  SurfaceControlMethods::GetImpl(/*load_functions=*/false).InitWithStubs();
}

void SurfaceControl::ApplyTransaction(ASurfaceTransaction* transaction) {
  SurfaceControlMethods::Get().ASurfaceTransaction_applyFn(transaction);
}

scoped_refptr<SurfaceControl::Surface> SurfaceControl::Surface::WrapUnowned(
    ASurfaceControl* surface) {
  scoped_refptr<SurfaceControl::Surface> result =
      base::MakeRefCounted<SurfaceControl::Surface>();
  result->surface_ = surface;
  return result;
}

SurfaceControl::Surface::Surface() = default;

SurfaceControl::Surface::Surface(const Surface& parent, const char* name) {
  owned_surface_ = SurfaceControlMethods::Get().ASurfaceControl_createFn(
      parent.surface(), name);
  if (!owned_surface_)
    LOG(ERROR) << "Failed to create ASurfaceControl : " << name;
  surface_ = owned_surface_;
}

SurfaceControl::Surface::Surface(ANativeWindow* parent, const char* name) {
  owned_surface_ =
      SurfaceControlMethods::Get().ASurfaceControl_createFromWindowFn(parent,
                                                                      name);
  if (!owned_surface_)
    LOG(ERROR) << "Failed to create ASurfaceControl : " << name;
  surface_ = owned_surface_;
}

SurfaceControl::Surface::Surface(
    JNIEnv* env,
    const base::android::JavaRef<jobject>& j_surface_control) {
  CHECK(SupportsSurfacelessControl());
  owned_surface_ = SurfaceControlMethods::Get().ASurfaceControl_fromJavaFn(
      env, j_surface_control.obj());
  if (!owned_surface_) {
    LOG(ERROR) << "Failed to obtain ASurfaceControl from java";
    return;
  }
  surface_ = owned_surface_;
}

SurfaceControl::Surface::~Surface() {
  if (owned_surface_)
    SurfaceControlMethods::Get().ASurfaceControl_releaseFn(owned_surface_);
}

SurfaceControl::SurfaceStats::SurfaceStats() = default;
SurfaceControl::SurfaceStats::~SurfaceStats() = default;

SurfaceControl::SurfaceStats::SurfaceStats(SurfaceStats&& other) = default;
SurfaceControl::SurfaceStats& SurfaceControl::SurfaceStats::operator=(
    SurfaceStats&& other) = default;

SurfaceControl::TransactionStats::TransactionStats() = default;
SurfaceControl::TransactionStats::~TransactionStats() = default;

SurfaceControl::TransactionStats::TransactionStats(TransactionStats&& other) =
    default;
SurfaceControl::TransactionStats& SurfaceControl::TransactionStats::operator=(
    TransactionStats&& other) = default;

SurfaceControl::Transaction::Transaction()
    : id_(g_next_transaction_id.GetNext()) {
  transaction_ = SurfaceControlMethods::Get().ASurfaceTransaction_createFn();
  DCHECK(transaction_);
}

SurfaceControl::Transaction::~Transaction() {
  DestroyIfNeeded();
}

void SurfaceControl::Transaction::DestroyIfNeeded() {
  if (!transaction_)
    return;
  if (need_to_apply_)
    SurfaceControlMethods::Get().ASurfaceTransaction_applyFn(transaction_);
  SurfaceControlMethods::Get().ASurfaceTransaction_deleteFn(transaction_);
  transaction_ = nullptr;
}

SurfaceControl::Transaction::Transaction(Transaction&& other)
    : id_(other.id_),
      transaction_(other.transaction_),
      on_commit_cb_(std::move(other.on_commit_cb_)),
      on_complete_cb_(std::move(other.on_complete_cb_)),
      need_to_apply_(other.need_to_apply_) {
  other.transaction_ = nullptr;
  other.id_ = 0;
  other.need_to_apply_ = false;
}

SurfaceControl::Transaction& SurfaceControl::Transaction::operator=(
    Transaction&& other) {
  if (this == &other)
    return *this;

  DestroyIfNeeded();

  transaction_ = other.transaction_;
  id_ = other.id_;
  on_commit_cb_ = std::move(other.on_commit_cb_);
  on_complete_cb_ = std::move(other.on_complete_cb_);
  need_to_apply_ = other.need_to_apply_;

  other.transaction_ = nullptr;
  other.id_ = 0;
  other.need_to_apply_ = false;
  return *this;
}

void SurfaceControl::Transaction::SetVisibility(const Surface& surface,
                                                bool show) {
  SurfaceControlMethods::Get().ASurfaceTransaction_setVisibilityFn(
      transaction_, surface.surface(), show);
}

void SurfaceControl::Transaction::SetZOrder(const Surface& surface, int32_t z) {
  SurfaceControlMethods::Get().ASurfaceTransaction_setZOrderFn(
      transaction_, surface.surface(), z);
}

void SurfaceControl::Transaction::SetBuffer(const Surface& surface,
                                            AHardwareBuffer* buffer,
                                            base::ScopedFD fence_fd) {
  SurfaceControlMethods::Get().ASurfaceTransaction_setBufferFn(
      transaction_, surface.surface(), buffer,
      fence_fd.is_valid() ? fence_fd.release() : -1);
  // In T OS, setBuffer call setOnComplete internally, so Apply() is required to
  // decrease ref count of SurfaceControl.
  // TODO(crbug.com/40249006): remove this if AOSP fix the issue
  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
      base::android::SDK_VERSION_T) {
    need_to_apply_ = true;
  }
}

void SurfaceControl::Transaction::SetGeometry(const Surface& surface,
                                              const gfx::Rect& src,
                                              const gfx::Rect& dst,
                                              gfx::OverlayTransform transform) {
  SurfaceControlMethods::Get().ASurfaceTransaction_setGeometryFn(
      transaction_, surface.surface(), RectToARect(src), RectToARect(dst),
      OverlayTransformToWindowTransform(transform));
}

void SurfaceControl::Transaction::SetPosition(const Surface& surface,
                                              const gfx::Point& position) {
  CHECK(SurfaceControlMethods::Get().ASurfaceTransaction_setPositionFn);
  SurfaceControlMethods::Get().ASurfaceTransaction_setPositionFn(
      transaction_, surface.surface(), position.x(), position.y());
}

void SurfaceControl::Transaction::SetScale(const Surface& surface,
                                           const float sx,
                                           float sy) {
  CHECK(SurfaceControlMethods::Get().ASurfaceTransaction_setScaleFn);
  SurfaceControlMethods::Get().ASurfaceTransaction_setScaleFn(
      transaction_, surface.surface(), sx, sy);
}

void SurfaceControl::Transaction::SetCrop(const Surface& surface,
                                          const gfx::Rect& rect) {
  CHECK(SurfaceControlMethods::Get().ASurfaceTransaction_setCropFn);
  SurfaceControlMethods::Get().ASurfaceTransaction_setCropFn(
      transaction_, surface.surface(), RectToARect(rect));
}

void SurfaceControl::Transaction::SetFrameTimelineId(int64_t vsync_id) {
  CHECK(SurfaceControlMethods::Get().ASurfaceTransaction_setFrameTimelineFn);
  SurfaceControlMethods::Get().ASurfaceTransaction_setFrameTimelineFn(
      transaction_, vsync_id);
}

void SurfaceControl::Transaction::SetOpaque(const Surface& surface,
                                            bool opaque) {
  int8_t transparency = opaque ? ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE
                               : ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT;
  SurfaceControlMethods::Get().ASurfaceTransaction_setBufferTransparencyFn(
      transaction_, surface.surface(), transparency);
}

void SurfaceControl::Transaction::SetDamageRect(const Surface& surface,
                                                const gfx::Rect& rect) {
  auto a_rect = RectToARect(rect);
  SurfaceControlMethods::Get().ASurfaceTransaction_setDamageRegionFn(
      transaction_, surface.surface(), &a_rect, 1u);
}

void SurfaceControl::Transaction::SetColorSpace(
    const Surface& surface,
    const gfx::ColorSpace& color_space,
    const std::optional<HDRMetadata>& metadata) {
  // Populate the data space and brightness ratios.
  ADataSpace data_space = ADATASPACE_UNKNOWN;
  float extended_range_brightness_ratio = 1.f;
  float desired_brightness_ratio = 1.f;
  if (metadata && metadata->extended_range &&
      SurfaceControlMethods::Get()
          .ASurfaceTransaction_setExtendedRangeBrightnessFn) {
    desired_brightness_ratio = metadata->extended_range->desired_headroom;
  }
  ColorSpaceToADataSpace(color_space, desired_brightness_ratio, data_space,
                         extended_range_brightness_ratio);

  // Log the data space in crash keys for debugging crbug.com/997592.
  static auto* kCrashKey = base::debug::AllocateCrashKeyString(
      "data_space_for_buffer", base::debug::CrashKeySize::Size256);
  auto crash_key_value = base::NumberToString(data_space);
  base::debug::ScopedCrashKeyString scoped_crash_key(kCrashKey,
                                                     crash_key_value);

  SurfaceControlMethods::Get().ASurfaceTransaction_setBufferDataSpaceFn(
      transaction_, surface.surface(), data_space);

  const bool extended_range = (data_space & RANGE_MASK) == RANGE_EXTENDED;

  // Set the HDR metadata for not extended SRGB case.
  if (metadata && !extended_range) {
    if (const auto& gfx_cta_861_3 = metadata->cta_861_3) {
      AHdrMetadata_cta861_3 cta861_3 = {
          .maxContentLightLevel =
              static_cast<float>(gfx_cta_861_3->max_content_light_level),
          .maxFrameAverageLightLevel =
              static_cast<float>(gfx_cta_861_3->max_frame_average_light_level)};
      SurfaceControlMethods::Get()
          .ASurfaceTransaction_setHdrMetadata_cta861_3Fn(
              transaction_, surface.surface(), &cta861_3);
    }

    if (const auto& gfx_smpte_st_2086 = metadata->smpte_st_2086) {
      const auto& primaries = gfx_smpte_st_2086->primaries;
      AHdrMetadata_smpte2086 smpte2086 = {
          .displayPrimaryRed = {.x = primaries.fRX, .y = primaries.fRY},
          .displayPrimaryGreen = {.x = primaries.fGX, .y = primaries.fGY},
          .displayPrimaryBlue = {.x = primaries.fBX, .y = primaries.fBY},
          .whitePoint = {.x = primaries.fWX, .y = primaries.fWY},
          .maxLuminance = gfx_smpte_st_2086->luminance_max,
          .minLuminance = gfx_smpte_st_2086->luminance_min};
      SurfaceControlMethods::Get()
          .ASurfaceTransaction_setHdrMetadata_smpte2086Fn(
              transaction_, surface.surface(), &smpte2086);
    }
  } else {
    SurfaceControlMethods::Get().ASurfaceTransaction_setHdrMetadata_cta861_3Fn(
        transaction_, surface.surface(), nullptr);
    SurfaceControlMethods::Get().ASurfaceTransaction_setHdrMetadata_smpte2086Fn(
        transaction_, surface.surface(), nullptr);
  }

  // Set brightness points for extended range.
  if (extended_range) {
    CHECK(SurfaceControlMethods::Get()
              .ASurfaceTransaction_setExtendedRangeBrightnessFn);
    SurfaceControlMethods::Get()
        .ASurfaceTransaction_setExtendedRangeBrightnessFn(
            transaction_, surface.surface(), extended_range_brightness_ratio,
            desired_brightness_ratio);
  } else {
    // If extended range brightness is supported, we need reset it to default
    // values.
    if (SurfaceControlMethods::Get()
            .ASurfaceTransaction_setExtendedRangeBrightnessFn) {
      SurfaceControlMethods::Get()
          .ASurfaceTransaction_setExtendedRangeBrightnessFn(
              transaction_, surface.surface(), 1.0f, 1.0f);
    }
  }
}

void SurfaceControl::Transaction::SetFrameRate(const Surface& surface,
                                               float frame_rate) {
  DCHECK(SupportsSetFrameRate());

  // We always used fixed source here since a non-default value is only used for
  // videos which have a fixed playback rate.
  SurfaceControlMethods::Get().ASurfaceTransaction_setFrameRateFn(
      transaction_, surface.surface(), frame_rate,
      ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
}

void SurfaceControl::Transaction::SetParent(const Surface& surface,
                                            Surface* new_parent) {
  SurfaceControlMethods::Get().ASurfaceTransaction_reparentFn(
      transaction_, surface.surface(),
      new_parent ? new_parent->surface() : nullptr);
}

void SurfaceControl::Transaction::SetOnCompleteCb(
    OnCompleteCb cb,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  TRACE_EVENT_WITH_FLOW0(
      "toplevel.flow", "gfx::SurfaceControl::Transaction::SetOnCompleteCb",
      GetTraceIdForTransaction(id_), TRACE_EVENT_FLAG_FLOW_OUT);

  DCHECK(!on_complete_cb_);
  on_complete_cb_ = base::BindPostTask(std::move(task_runner), std::move(cb));
}

void SurfaceControl::Transaction::SetOnCommitCb(
    OnCommitCb cb,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  DCHECK(!on_commit_cb_);
  on_commit_cb_ = base::BindPostTask(std::move(task_runner), std::move(cb));
}

void SurfaceControl::Transaction::Apply() {
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("gpu,benchmark",
                                    "SurfaceControlTransaction", id_);

  PrepareCallbacks();
  SurfaceControlMethods::Get().ASurfaceTransaction_applyFn(transaction_);
  need_to_apply_ = false;
}

ASurfaceTransaction* SurfaceControl::Transaction::GetTransaction() {
  PrepareCallbacks();
  need_to_apply_ = false;
  return transaction_;
}

void SurfaceControl::Transaction::PrepareCallbacks() {
  if (on_commit_cb_) {
    TransactionAckCtx* ack_ctx = new TransactionAckCtx;
    ack_ctx->latch_callback = std::move(on_commit_cb_);
    ack_ctx->id = id_;

    SurfaceControlMethods::Get().ASurfaceTransaction_setOnCommitFn(
        transaction_, ack_ctx, &OnTransactiOnCommittedOnAnyThread);
    // setOnCommit and setOnComplete increase ref count of SurfaceControl and
    // Apply() is required to decrease the ref count.
    need_to_apply_ = true;
  }

  if (on_complete_cb_) {
    TransactionAckCtx* ack_ctx = new TransactionAckCtx;
    ack_ctx->callback = std::move(on_complete_cb_);
    ack_ctx->id = id_;

    SurfaceControlMethods::Get().ASurfaceTransaction_setOnCompleteFn(
        transaction_, ack_ctx, &OnTransactionCompletedOnAnyThread);
    need_to_apply_ = true;
  }
}

void SurfaceControl::Transaction::SetEnableBackPressure(const Surface& surface,
                                                        bool enable) {
  SurfaceControlMethods::Get().ASurfaceTransaction_setEnableBackPressureFn(
      transaction_, surface.surface(), enable);
}

}  // namespace gfx