blob: e673358dd91e8534726b4371c4e972651577c55d [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <source/InputSink.h>
#include <linux/input.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <glog/logging.h>
#include <https/SafeCallbackable.h>
#include <https/Support.h>
namespace android {
namespace {
// TODO de-dup this from vnc server and here
struct virtio_input_event {
uint16_t type;
uint16_t code;
int32_t value;
};
template <typename T>
struct InputEventBufferImpl : public InputEventBuffer {
InputEventBufferImpl() {
buffer_.reserve(6); // 6 is usually enough even for multi-touch
}
void addEvent(uint16_t type, uint16_t code, int32_t value) override {
buffer_.push_back({.type = type, .code = code, .value = value});
}
T* data() { return buffer_.data(); }
const void* data() const override { return buffer_.data(); }
std::size_t size() const override { return buffer_.size() * sizeof(T); }
private:
std::vector<T> buffer_;
};
} // namespace
InputSink::InputSink(std::shared_ptr<RunLoop> runLoop, int serverFd,
bool write_virtio_input)
: mRunLoop(runLoop),
mServerFd(serverFd),
mClientFd(-1),
mSendPending(false),
mWriteVirtioInput(write_virtio_input) {
if (mServerFd >= 0) {
makeFdNonblocking(mServerFd);
}
}
InputSink::~InputSink() {
if (mClientFd >= 0) {
mRunLoop->cancelSocket(mClientFd);
close(mClientFd);
mClientFd = -1;
}
if (mServerFd >= 0) {
mRunLoop->cancelSocket(mServerFd);
close(mServerFd);
mServerFd = -1;
}
}
void InputSink::start() {
if (mServerFd < 0) {
return;
}
mRunLoop->postSocketRecv(
mServerFd, makeSafeCallback(this, &InputSink::onServerConnection));
}
std::unique_ptr<InputEventBuffer> InputSink::getEventBuffer() const {
InputEventBuffer* raw_ptr;
if (mWriteVirtioInput) {
raw_ptr = new InputEventBufferImpl<virtio_input_event>();
} else {
raw_ptr = new InputEventBufferImpl<input_event>();
}
return std::unique_ptr<InputEventBuffer>(raw_ptr);
}
void InputSink::SendEvents(std::unique_ptr<InputEventBuffer> evt_buffer) {
sendRawEvents(evt_buffer->data(), evt_buffer->size());
}
void InputSink::onServerConnection() {
int s = accept(mServerFd, nullptr, nullptr);
if (s >= 0) {
if (mClientFd >= 0) {
LOG(INFO) << "Rejecting client, we already have one.";
// We already have a client.
close(s);
s = -1;
} else {
LOG(INFO) << "Accepted client socket " << s << ".";
makeFdNonblocking(s);
mClientFd = s;
}
}
mRunLoop->postSocketRecv(
mServerFd, makeSafeCallback(this, &InputSink::onServerConnection));
}
void InputSink::sendRawEvents(const void* evt_buffer, size_t size) {
if (size <= 0) return;
std::lock_guard autoLock(mLock);
if (mClientFd < 0) {
return;
}
size_t offset = mOutBuffer.size();
mOutBuffer.resize(offset + size);
memcpy(mOutBuffer.data() + offset, evt_buffer, size);
if (!mSendPending) {
mSendPending = true;
mRunLoop->postSocketSend(mClientFd,
makeSafeCallback(this, &InputSink::onSocketSend));
}
}
void InputSink::onSocketSend() {
std::lock_guard autoLock(mLock);
CHECK(mSendPending);
mSendPending = false;
if (mClientFd < 0) {
return;
}
ssize_t n;
while (!mOutBuffer.empty()) {
do {
n = ::send(mClientFd, mOutBuffer.data(), mOutBuffer.size(), 0);
} while (n < 0 && errno == EINTR);
if (n <= 0) {
break;
}
mOutBuffer.erase(mOutBuffer.begin(), mOutBuffer.begin() + n);
}
if ((n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || n == 0) {
LOG(ERROR) << "Client is gone.";
// Client is gone.
mRunLoop->cancelSocket(mClientFd);
close(mClientFd);
mClientFd = -1;
return;
}
if (!mOutBuffer.empty()) {
mSendPending = true;
mRunLoop->postSocketSend(mClientFd,
makeSafeCallback(this, &InputSink::onSocketSend));
}
}
} // namespace android