blob: d14d58839aa034b646765bbcdc8bf2505666e705 [file] [log] [blame]
#include "screenshot_service.h"
#include <utils/Trace.h>
#include <pdx/default_transport/service_endpoint.h>
#include <private/android_filesystem_config.h>
#include <private/dvr/display_types.h>
#include <private/dvr/trusted_uids.h>
using android::pdx::Message;
using android::pdx::MessageInfo;
using android::pdx::default_transport::Endpoint;
using android::pdx::rpc::DispatchRemoteMethod;
using android::pdx::rpc::RemoteMethodError;
using android::pdx::rpc::RemoteMethodReturn;
namespace android {
namespace dvr {
ScreenshotService::~ScreenshotService() { instance_ = nullptr; }
pdx::Status<void> ScreenshotService::HandleMessage(pdx::Message& message) {
switch (message.GetOp()) {
case DisplayScreenshotRPC::GetFormat::Opcode:
DispatchRemoteMethod<DisplayScreenshotRPC::GetFormat>(
*this, &ScreenshotService::OnGetFormat, message);
return {};
case DisplayScreenshotRPC::TakeScreenshot::Opcode:
DispatchRemoteMethod<DisplayScreenshotRPC::TakeScreenshot>(
*this, &ScreenshotService::OnTakeScreenshot, message);
return {};
default:
return Service::HandleMessage(message);
}
}
int ScreenshotService::OnGetFormat(pdx::Message&) {
return HAL_PIXEL_FORMAT_RGB_888;
}
ScreenshotData ScreenshotService::OnTakeScreenshot(pdx::Message& message,
int layer_index) {
// Also allow AID_SHELL to support vrscreencap commands.
if (message.GetEffectiveUserId() != AID_SHELL &&
!IsTrustedUid(message.GetEffectiveUserId())) {
REPLY_ERROR_RETURN(message, EACCES, {});
}
AddWaiter(std::move(message), layer_index);
return {};
}
void ScreenshotService::AddWaiter(pdx::Message&& message, int layer_index) {
std::lock_guard<std::mutex> lock(mutex_);
waiters_.emplace_back(std::move(message), layer_index);
}
void ScreenshotService::TakeIfNeeded(
std::array<Layer*, HardwareComposer::kMaxHardwareLayers>& hw_layers,
Compositor& compositor) {
std::lock_guard<std::mutex> lock(mutex_);
// Send the buffer contents to all of our waiting clients.
for (auto& waiter : waiters_) {
if (waiter.IsDone())
continue;
if (waiter.layer_index() == 0) {
ALOGE(
"ScreenshotService::TakeIfNeeded: Capturing the composited display "
"output is not yet supported.");
waiter.Error(EINVAL);
continue;
}
if (waiter.layer_index() > 0) {
// Check for hardware layer screenshot requests.
// Hardware layers are requested with positive indices starting at 1.
const size_t layer_index = static_cast<size_t>(waiter.layer_index() - 1);
if (layer_index >= hw_layers.size()) {
waiter.Error(EINVAL);
continue;
}
auto buffer = hw_layers[layer_index]->GetBuffer();
if (!buffer) {
waiter.Error(ENOBUFS);
continue;
}
auto data = compositor.ReadBufferPixels(buffer);
if (data.empty()) {
waiter.Error(ENOBUFS);
continue;
}
Take(&waiter, data.data(), buffer->width(), buffer->height(),
buffer->width());
} else {
// Check for compositor input layer screenshot requests.
// Prewarp surfaces are requested with negative indices starting at -1.
const size_t layer_index = static_cast<size_t>(-waiter.layer_index() - 1);
if (layer_index >= compositor.GetLayerCount()) {
waiter.Error(EINVAL);
continue;
}
int width = 0;
int height = 0;
auto data = compositor.ReadLayerPixels(layer_index, &width, &height);
if (data.empty()) {
waiter.Error(ENOBUFS);
continue;
}
Take(&waiter, data.data(), width, height, width);
}
}
// Reply with error to requests that did not match up with a source layer.
for (auto& waiter : waiters_) {
if (!waiter.IsDone())
waiter.Error(EAGAIN);
}
waiters_.clear();
}
void ScreenshotWaiter::Reply(const ScreenshotData& screenshot) {
ALOGI("Returning screenshot: size=%zu recv_size=%zu",
screenshot.buffer.size(), message_.GetReceiveLength());
RemoteMethodReturn<DisplayScreenshotRPC::TakeScreenshot>(message_,
screenshot);
}
void ScreenshotWaiter::Error(int error) { RemoteMethodError(message_, error); }
void ScreenshotService::Take(ScreenshotWaiter* waiter, const void* rgba_data,
int32_t width, int32_t height, int buffer_stride) {
ATRACE_NAME(__PRETTY_FUNCTION__);
bool is_portrait = height > width;
if (is_portrait) {
std::swap(width, height);
}
int response_stride = width;
// Convert from RGBA to RGB and if in portrait, rotates to landscape; store
// the result in the response buffer.
ScreenshotData screenshot{width, height,
std::vector<uint8_t>(width * height * 3)};
const auto rgba_bytes = static_cast<const uint8_t*>(rgba_data);
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
// If the screenshot is in portrait mode, rotate into landscape mode.
const int response_index = is_portrait
? (height - j - 1) * response_stride + i
: j * response_stride + i;
const int buffer_index =
is_portrait ? i * buffer_stride + j : j * buffer_stride + i;
screenshot.buffer[response_index * 3 + 0] =
rgba_bytes[buffer_index * 4 + 0];
screenshot.buffer[response_index * 3 + 1] =
rgba_bytes[buffer_index * 4 + 1];
screenshot.buffer[response_index * 3 + 2] =
rgba_bytes[buffer_index * 4 + 2];
}
}
waiter->Reply(screenshot);
}
ScreenshotService::ScreenshotService()
: BASE("ScreenshotService",
Endpoint::Create(DisplayScreenshotRPC::kClientPath)) {
instance_ = this;
}
ScreenshotService* ScreenshotService::GetInstance() { return instance_; }
ScreenshotService* ScreenshotService::instance_ = nullptr;
} // namespace dvr
} // namespace android