blob: 60bb2be1821811cf08de742b2ea6fa79163b7d31 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/arc/video_accelerator/protected_buffer_manager.h"
#include "base/bits.h"
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "base/sys_info.h"
#include "mojo/public/cpp/system/buffer.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
#define VLOGF(level) VLOG(level) << __func__ << "(): "
namespace arc {
namespace {
// Size of the pixmap to be used as the dummy handle for protected buffers.
constexpr gfx::Size kDummyBufferSize(32, 32);
} // namespace
ProtectedBufferHandle::ProtectedBufferHandle(
base::OnceClosure destruction_cb,
const base::SharedMemoryHandle& shm_handle)
: shm_handle_(shm_handle), destruction_cb_(std::move(destruction_cb)) {
DCHECK(shm_handle_.IsValid());
DCHECK(shm_handle.OwnershipPassesToIPC());
}
ProtectedBufferHandle::ProtectedBufferHandle(
base::OnceClosure destruction_cb,
const gfx::NativePixmapHandle& native_pixmap_handle)
: native_pixmap_handle_(native_pixmap_handle),
destruction_cb_(std::move(destruction_cb)) {
DCHECK(!native_pixmap_handle_.fds.empty());
for (const auto& fd : native_pixmap_handle_.fds)
DCHECK(fd.auto_close);
}
ProtectedBufferHandle::~ProtectedBufferHandle() {
if (shm_handle_.OwnershipPassesToIPC())
shm_handle_.Close();
for (const auto& fd : native_pixmap_handle_.fds) {
// Close the fd by wrapping it in a ScopedFD and letting
// it fall out of scope.
base::ScopedFD scoped_fd(fd.fd);
}
std::move(destruction_cb_).Run();
}
base::SharedMemoryHandle ProtectedBufferHandle::shm_handle() const {
base::SharedMemoryHandle handle = shm_handle_;
handle.SetOwnershipPassesToIPC(false);
return handle;
}
gfx::NativePixmapHandle ProtectedBufferHandle::native_pixmap_handle() const {
return native_pixmap_handle_;
}
class ProtectedBufferManager::ProtectedBuffer {
public:
virtual ~ProtectedBuffer() {}
// Downcasting methods to return duplicated handles to the underlying
// protected buffers for each buffer type, or empty/null handles if not
// applicable.
virtual base::SharedMemoryHandle DuplicateSharedMemoryHandle() const {
return base::SharedMemoryHandle();
}
virtual gfx::NativePixmapHandle DuplicateNativePixmapHandle() const {
return gfx::NativePixmapHandle();
}
// Downcasting method to return a scoped_refptr to the underlying
// NativePixmap, or null if not applicable.
virtual scoped_refptr<gfx::NativePixmap> GetNativePixmap() const {
return nullptr;
}
protected:
explicit ProtectedBuffer(scoped_refptr<gfx::NativePixmap> dummy_handle)
: dummy_handle_(std::move(dummy_handle)) {}
private:
scoped_refptr<gfx::NativePixmap> dummy_handle_;
DISALLOW_COPY_AND_ASSIGN(ProtectedBuffer);
};
class ProtectedBufferManager::ProtectedSharedMemory
: public ProtectedBufferManager::ProtectedBuffer {
public:
~ProtectedSharedMemory() override;
// Allocate a ProtectedSharedMemory buffer of |size| bytes.
static std::unique_ptr<ProtectedSharedMemory> Create(
scoped_refptr<gfx::NativePixmap> dummy_handle,
size_t size);
base::SharedMemoryHandle DuplicateSharedMemoryHandle() const override {
return base::SharedMemory::DuplicateHandle(shmem_->handle());
}
private:
explicit ProtectedSharedMemory(scoped_refptr<gfx::NativePixmap> dummy_handle);
std::unique_ptr<base::SharedMemory> shmem_;
};
ProtectedBufferManager::ProtectedSharedMemory::ProtectedSharedMemory(
scoped_refptr<gfx::NativePixmap> dummy_handle)
: ProtectedBuffer(std::move(dummy_handle)) {}
ProtectedBufferManager::ProtectedSharedMemory::~ProtectedSharedMemory() {}
// static
std::unique_ptr<ProtectedBufferManager::ProtectedSharedMemory>
ProtectedBufferManager::ProtectedSharedMemory::Create(
scoped_refptr<gfx::NativePixmap> dummy_handle,
size_t size) {
std::unique_ptr<ProtectedSharedMemory> protected_shmem(
new ProtectedSharedMemory(std::move(dummy_handle)));
size_t aligned_size =
base::bits::Align(size, base::SysInfo::VMAllocationGranularity());
mojo::ScopedSharedBufferHandle mojo_shared_buffer =
mojo::SharedBufferHandle::Create(aligned_size);
if (!mojo_shared_buffer->is_valid()) {
VLOGF(1) << "Failed to allocate shared memory";
return nullptr;
}
base::SharedMemoryHandle shm_handle;
MojoResult mojo_result = mojo::UnwrapSharedMemoryHandle(
std::move(mojo_shared_buffer), &shm_handle, nullptr, nullptr);
if (mojo_result != MOJO_RESULT_OK) {
VLOGF(1) << "Failed to unwrap a mojo shared memory handle";
return nullptr;
}
protected_shmem->shmem_ =
std::make_unique<base::SharedMemory>(shm_handle, false);
return protected_shmem;
}
class ProtectedBufferManager::ProtectedNativePixmap
: public ProtectedBufferManager::ProtectedBuffer {
public:
~ProtectedNativePixmap() override;
// Allocate a ProtectedNativePixmap of |format| and |size|.
static std::unique_ptr<ProtectedNativePixmap> Create(
scoped_refptr<gfx::NativePixmap> dummy_handle,
gfx::BufferFormat format,
const gfx::Size& size);
gfx::NativePixmapHandle DuplicateNativePixmapHandle() const override {
return native_pixmap_->ExportHandle();
}
scoped_refptr<gfx::NativePixmap> GetNativePixmap() const override {
return native_pixmap_;
}
private:
explicit ProtectedNativePixmap(scoped_refptr<gfx::NativePixmap> dummy_handle);
scoped_refptr<gfx::NativePixmap> native_pixmap_;
};
ProtectedBufferManager::ProtectedNativePixmap::ProtectedNativePixmap(
scoped_refptr<gfx::NativePixmap> dummy_handle)
: ProtectedBuffer(std::move(dummy_handle)) {}
ProtectedBufferManager::ProtectedNativePixmap::~ProtectedNativePixmap() {}
// static
std::unique_ptr<ProtectedBufferManager::ProtectedNativePixmap>
ProtectedBufferManager::ProtectedNativePixmap::Create(
scoped_refptr<gfx::NativePixmap> dummy_handle,
gfx::BufferFormat format,
const gfx::Size& size) {
std::unique_ptr<ProtectedNativePixmap> protected_pixmap(
new ProtectedNativePixmap(std::move(dummy_handle)));
ui::OzonePlatform* platform = ui::OzonePlatform::GetInstance();
ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
protected_pixmap->native_pixmap_ =
factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, size, format,
gfx::BufferUsage::SCANOUT_VDA_WRITE);
if (!protected_pixmap->native_pixmap_) {
VLOGF(1) << "Failed allocating a native pixmap";
return nullptr;
}
return protected_pixmap;
}
ProtectedBufferManager::ProtectedBufferManager() : weak_factory_(this) {
VLOGF(2);
weak_this_ = weak_factory_.GetWeakPtr();
}
ProtectedBufferManager::~ProtectedBufferManager() {
VLOGF(2);
}
std::unique_ptr<ProtectedBufferHandle>
ProtectedBufferManager::AllocateProtectedSharedMemory(base::ScopedFD dummy_fd,
size_t size) {
VLOGF(2) << "dummy_fd: " << dummy_fd.get() << ", size: " << size;
// Import the |dummy_fd| to produce a unique id for it.
uint32_t id;
auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
if (!pixmap)
return nullptr;
base::AutoLock lock(buffer_map_lock_);
if (buffer_map_.find(id) != buffer_map_.end()) {
VLOGF(1) << "A protected buffer for this handle already exists";
return nullptr;
}
// Allocate a protected buffer and associate it with the dummy pixmap.
// The pixmap needs to be stored to ensure the id remains the same for
// the entire lifetime of the dummy pixmap.
auto protected_shmem = ProtectedSharedMemory::Create(pixmap, size);
if (!protected_shmem) {
VLOGF(1) << "Failed allocating a protected shared memory buffer";
return nullptr;
}
auto shm_handle = protected_shmem->DuplicateSharedMemoryHandle();
if (!shm_handle.IsValid()) {
VLOGF(1) << "Failed duplicating SharedMemoryHandle";
return nullptr;
}
// Store the buffer in the buffer_map_, and return a handle to it to the
// client. The buffer will be permanently removed from the map when the
// handle is destroyed.
VLOGF(2) << "New protected shared memory buffer, handle id: " << id;
auto protected_buffer_handle = std::make_unique<ProtectedBufferHandle>(
base::BindOnce(&ProtectedBufferManager::RemoveEntry, weak_this_, id),
shm_handle);
// This will always succeed as we find() first above.
buffer_map_.emplace(id, std::move(protected_shmem));
return protected_buffer_handle;
}
std::unique_ptr<ProtectedBufferHandle>
ProtectedBufferManager::AllocateProtectedNativePixmap(base::ScopedFD dummy_fd,
gfx::BufferFormat format,
const gfx::Size& size) {
VLOGF(2) << "dummy_fd: " << dummy_fd.get() << ", format: " << (int)format
<< ", size: " << size.ToString();
// Import the |dummy_fd| to produce a unique id for it.
uint32_t id = 0;
auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
if (!pixmap)
return nullptr;
base::AutoLock lock(buffer_map_lock_);
if (buffer_map_.find(id) != buffer_map_.end()) {
VLOGF(1) << "A protected buffer for this handle already exists";
return nullptr;
}
// Allocate a protected buffer and associate it with the dummy pixmap.
// The pixmap needs to be stored to ensure the id remains the same for
// the entire lifetime of the dummy pixmap.
auto protected_pixmap = ProtectedNativePixmap::Create(pixmap, format, size);
if (!protected_pixmap) {
VLOGF(1) << "Failed allocating a protected native pixmap";
return nullptr;
}
auto native_pixmap_handle = protected_pixmap->DuplicateNativePixmapHandle();
if (native_pixmap_handle.planes.empty()) {
VLOGF(1) << "Failed duplicating NativePixmapHandle";
return nullptr;
}
// Store the buffer in the buffer_map_, and return a handle to it to the
// client. The buffer will be permanently removed from the map when the
// handle is destroyed.
VLOGF(2) << "New protected native pixmap, handle id: " << id;
auto protected_buffer_handle = std::make_unique<ProtectedBufferHandle>(
base::BindOnce(&ProtectedBufferManager::RemoveEntry, weak_this_, id),
native_pixmap_handle);
// This will always succeed as we find() first above.
buffer_map_.emplace(id, std::move(protected_pixmap));
return protected_buffer_handle;
}
base::SharedMemoryHandle
ProtectedBufferManager::GetProtectedSharedMemoryHandleFor(
base::ScopedFD dummy_fd) {
uint32_t id = 0;
auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
base::AutoLock lock(buffer_map_lock_);
const auto& iter = buffer_map_.find(id);
if (iter == buffer_map_.end())
return base::SharedMemoryHandle();
return iter->second->DuplicateSharedMemoryHandle();
}
gfx::NativePixmapHandle
ProtectedBufferManager::GetProtectedNativePixmapHandleFor(
base::ScopedFD dummy_fd) {
uint32_t id = 0;
auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
base::AutoLock lock(buffer_map_lock_);
const auto& iter = buffer_map_.find(id);
if (iter == buffer_map_.end())
return gfx::NativePixmapHandle();
return iter->second->DuplicateNativePixmapHandle();
}
scoped_refptr<gfx::NativePixmap>
ProtectedBufferManager::GetProtectedNativePixmapFor(
const gfx::NativePixmapHandle& handle) {
// Only the first fd is used for lookup.
if (handle.fds.empty())
return nullptr;
base::ScopedFD dummy_fd(HANDLE_EINTR(dup(handle.fds[0].fd)));
uint32_t id = 0;
auto pixmap = ImportDummyFd(std::move(dummy_fd), &id);
base::AutoLock lock(buffer_map_lock_);
const auto& iter = buffer_map_.find(id);
if (iter == buffer_map_.end())
return nullptr;
auto native_pixmap = iter->second->GetNativePixmap();
if (native_pixmap) {
for (const auto& fd : handle.fds)
base::ScopedFD scoped_fd(fd.fd);
}
return native_pixmap;
}
scoped_refptr<gfx::NativePixmap> ProtectedBufferManager::ImportDummyFd(
base::ScopedFD dummy_fd,
uint32_t* id) const {
// 0 is an invalid handle id.
*id = 0;
// Import dummy_fd to acquire its unique id.
// CreateNativePixmapFromHandle() takes ownership and will close the handle
// also on failure.
gfx::NativePixmapHandle pixmap_handle;
pixmap_handle.fds.emplace_back(
base::FileDescriptor(dummy_fd.release(), true));
pixmap_handle.planes.emplace_back(gfx::NativePixmapPlane());
ui::OzonePlatform* platform = ui::OzonePlatform::GetInstance();
ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
scoped_refptr<gfx::NativePixmap> pixmap =
factory->CreateNativePixmapForProtectedBufferHandle(
gfx::kNullAcceleratedWidget, kDummyBufferSize, gfx::BufferFormat::R_8,
pixmap_handle);
if (!pixmap) {
VLOGF(1) << "Failed importing dummy handle";
return nullptr;
}
*id = pixmap->GetUniqueId();
if (*id == 0) {
VLOGF(1) << "Failed acquiring unique id for handle";
return nullptr;
}
return pixmap;
}
void ProtectedBufferManager::RemoveEntry(uint32_t id) {
VLOGF(2) << "id: " << id;
base::AutoLock lock(buffer_map_lock_);
auto num_erased = buffer_map_.erase(id);
if (num_erased != 1)
VLOGF(1) << "No buffer id " << id << " to destroy";
}
} // namespace arc