upload android base code part1

This commit is contained in:
August 2018-08-08 15:50:00 +08:00
parent e02f198e2d
commit 0a1de6c4b3
48159 changed files with 9071466 additions and 0 deletions

View file

@ -0,0 +1,120 @@
// Touchpad implementation.
src = [
"EvdevInjector.cpp",
"VirtualTouchpadEvdev.cpp",
]
shared_libs = [
"libbase",
"liblog",
"libutils",
]
header_libraries = [
"libdvr_headers"
]
cc_library {
srcs: src,
export_include_dirs: ["include"],
shared_libs: shared_libs,
header_libs: header_libraries,
cppflags: ["-std=c++11"],
cflags: ["-DLOG_TAG=\"VrVirtualTouchpad\""],
name: "libvirtualtouchpad",
tags: ["optional"],
}
// Touchpad unit tests.
test_static_libs = [
"libcutils",
"libvirtualtouchpad",
"libbase",
"liblog",
"libutils",
]
test_src_files = ["tests/VirtualTouchpad_test.cpp"]
cc_test {
srcs: test_src_files,
static_libs: test_static_libs,
header_libs: header_libraries,
cppflags = [
"-std=c++11",
],
host_ldlibs = [
"-llog",
],
name: "VirtualTouchpad_test",
stl: "libc++_static",
tags: [ "optional" ],
}
// Service.
service_src = [
"main.cpp",
"VirtualTouchpadService.cpp",
"aidl/android/dvr/VirtualTouchpadService.aidl",
]
service_static_libs = [
"libcutils",
"libvirtualtouchpad",
]
service_shared_libs = [
"libbase",
"libbinder",
"liblog",
"libutils",
]
cc_binary {
srcs: service_src,
static_libs: service_static_libs,
shared_libs: service_shared_libs,
header_libs: header_libraries,
cppflags: ["-std=c++11"],
cflags: [
"-DLOG_TAG=\"VrVirtualTouchpad\"",
],
host_ldlibs: ["-llog"],
name: "virtual_touchpad",
tags: ["optional"],
init_rc: ["virtual_touchpad.rc"],
compile_multilib: "64",
stl: "libc++_static",
}
// Touchpad client library.
client_src = [
"VirtualTouchpadClient.cpp",
"DvrVirtualTouchpadClient.cpp",
"aidl/android/dvr/VirtualTouchpadService.aidl",
]
client_shared_libs = [
"libbase",
"libbinder",
"liblog",
"libutils",
]
cc_library {
srcs: client_src,
shared_libs: client_shared_libs,
header_libs: header_libraries,
cppflags: ["-std=c++11"],
cflags: ["-DLOG_TAG=\"VirtualTouchpadClient\""],
host_ldlibs: ["-llog"],
name: "libvirtualtouchpadclient",
tags: ["optional"],
export_include_dirs: ["include"],
}

View file

@ -0,0 +1,50 @@
#include "VirtualTouchpadClient.h"
#include "dvr/virtual_touchpad_client.h"
struct DvrVirtualTouchpad {};
#ifdef __cplusplus
extern "C" {
#endif
namespace {
android::dvr::VirtualTouchpad* FromC(DvrVirtualTouchpad* client) {
return reinterpret_cast<android::dvr::VirtualTouchpad*>(client);
}
} // namespace
DvrVirtualTouchpad* dvrVirtualTouchpadCreate() {
return reinterpret_cast<DvrVirtualTouchpad*>(
android::dvr::VirtualTouchpadClient::Create().release());
}
void dvrVirtualTouchpadDestroy(DvrVirtualTouchpad* client) {
delete FromC(client);
}
int dvrVirtualTouchpadAttach(DvrVirtualTouchpad* client) {
return FromC(client)->Attach();
}
int dvrVirtualTouchpadDetach(DvrVirtualTouchpad* client) {
return FromC(client)->Detach();
}
int dvrVirtualTouchpadTouch(DvrVirtualTouchpad* client, int touchpad, float x,
float y, float pressure) {
return FromC(client)->Touch(touchpad, x, y, pressure);
}
int dvrVirtualTouchpadButtonState(DvrVirtualTouchpad* client, int touchpad,
int buttons) {
return FromC(client)->ButtonState(touchpad, buttons);
}
int dvrVirtualTouchpadScroll(DvrVirtualTouchpad* client, int touchpad, float x,
float y) {
return FromC(client)->Scroll(touchpad, x, y);
}
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -0,0 +1,339 @@
#include "EvdevInjector.h"
#include <errno.h>
#include <inttypes.h>
#include <linux/input.h>
#include <log/log.h>
#include <string.h>
#include <sys/fcntl.h>
#include <unistd.h>
namespace android {
namespace dvr {
int EvdevInjector::UInput::Open() {
errno = 0;
fd_.reset(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
if (fd_.get() < 0) {
ALOGE("couldn't open uinput (r=%d errno=%d)", fd_.get(), errno);
}
return errno;
}
int EvdevInjector::UInput::Close() {
errno = 0;
fd_.reset();
return errno;
}
int EvdevInjector::UInput::Write(const void* buf, size_t count) {
ALOGV("UInput::Write(%zu, %02X...)", count, *static_cast<const char*>(buf));
errno = 0;
ssize_t r = write(fd_.get(), buf, count);
if (r != static_cast<ssize_t>(count)) {
ALOGE("write(%zu) failed (r=%zd errno=%d)", count, r, errno);
}
return errno;
}
int EvdevInjector::UInput::IoctlSetInt(int request, int value) {
ALOGV("UInput::IoctlSetInt(0x%X, 0x%X)", request, value);
errno = 0;
if (const int status = ioctl(fd_.get(), request, value)) {
ALOGE("ioctl(%d, 0x%X, 0x%X) failed (r=%d errno=%d)", fd_.get(), request,
value, status, errno);
}
return errno;
}
int EvdevInjector::UInput::IoctlVoid(int request) {
ALOGV("UInput::IoctlVoid(0x%X)", request);
errno = 0;
if (const int status = ioctl(fd_.get(), request)) {
ALOGE("ioctl(%d, 0x%X) failed (r=%d errno=%d)", fd_.get(), request, status,
errno);
}
return errno;
}
void EvdevInjector::Close() {
uinput_->Close();
state_ = State::CLOSED;
}
int EvdevInjector::ConfigureBegin(const char* device_name, int16_t bustype,
int16_t vendor, int16_t product,
int16_t version) {
ALOGV("ConfigureBegin %s 0x%04" PRIX16 " 0x%04" PRIX16 " 0x%04" PRIX16
" 0x%04" PRIX16 "",
device_name, bustype, vendor, product, version);
if (!device_name || strlen(device_name) >= UINPUT_MAX_NAME_SIZE) {
return Error(ERROR_DEVICE_NAME);
}
if (const int status = RequireState(State::NEW)) {
return status;
}
if (!uinput_) {
owned_uinput_.reset(new EvdevInjector::UInput());
uinput_ = owned_uinput_.get();
}
if (const int status = uinput_->Open()) {
// Without uinput we're dead in the water.
state_ = State::CLOSED;
return Error(status);
}
state_ = State::CONFIGURING;
// Initialize device setting structure.
memset(&uidev_, 0, sizeof(uidev_));
strncpy(uidev_.name, device_name, UINPUT_MAX_NAME_SIZE);
uidev_.id.bustype = bustype;
uidev_.id.vendor = vendor;
uidev_.id.product = product;
uidev_.id.version = version;
return 0;
}
int EvdevInjector::ConfigureInputProperty(int property) {
ALOGV("ConfigureInputProperty %d", property);
if (property < 0 || property >= INPUT_PROP_CNT) {
ALOGE("property 0x%X out of range [0,0x%X)", property, INPUT_PROP_CNT);
return Error(ERROR_PROPERTY_RANGE);
}
if (const int status = RequireState(State::CONFIGURING)) {
return status;
}
if (const int status = uinput_->IoctlSetInt(UI_SET_PROPBIT, property)) {
ALOGE("failed to set property %d", property);
return Error(status);
}
return 0;
}
int EvdevInjector::ConfigureKey(uint16_t key) {
ALOGV("ConfigureKey 0x%02" PRIX16 "", key);
if (key < 0 || key >= KEY_CNT) {
ALOGE("key 0x%X out of range [0,0x%X)", key, KEY_CNT);
return Error(ERROR_KEY_RANGE);
}
if (const int status = RequireState(State::CONFIGURING)) {
return status;
}
if (const int status = EnableEventType(EV_KEY)) {
return status;
}
if (const int status = uinput_->IoctlSetInt(UI_SET_KEYBIT, key)) {
ALOGE("failed to enable EV_KEY 0x%02" PRIX16 "", key);
return Error(status);
}
return 0;
}
int EvdevInjector::ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max,
int32_t fuzz, int32_t flat) {
ALOGV("ConfigureAbs 0x%" PRIX16 " %" PRId32 " %" PRId32 " %" PRId32
" %" PRId32 "",
abs_type, min, max, fuzz, flat);
if (abs_type < 0 || abs_type >= ABS_CNT) {
ALOGE("EV_ABS type 0x%" PRIX16 " out of range [0,0x%X)", abs_type, ABS_CNT);
return Error(ERROR_ABS_RANGE);
}
if (const int status = RequireState(State::CONFIGURING)) {
return status;
}
if (const int status = EnableEventType(EV_ABS)) {
return status;
}
if (const int status = uinput_->IoctlSetInt(UI_SET_ABSBIT, abs_type)) {
ALOGE("failed to enable EV_ABS 0x%" PRIX16 "", abs_type);
return Error(status);
}
uidev_.absmin[abs_type] = min;
uidev_.absmax[abs_type] = max;
uidev_.absfuzz[abs_type] = fuzz;
uidev_.absflat[abs_type] = flat;
return 0;
}
int EvdevInjector::ConfigureMultiTouchXY(int x0, int y0, int x1, int y1) {
if (const int status = ConfigureAbs(ABS_MT_POSITION_X, x0, x1, 0, 0)) {
return status;
}
if (const int status = ConfigureAbs(ABS_MT_POSITION_Y, y0, y1, 0, 0)) {
return status;
}
return 0;
}
int EvdevInjector::ConfigureAbsSlots(int slots) {
return ConfigureAbs(ABS_MT_SLOT, 0, slots, 0, 0);
}
int EvdevInjector::ConfigureRel(uint16_t rel_type) {
ALOGV("ConfigureRel 0x%" PRIX16 "", rel_type);
if (rel_type < 0 || rel_type >= REL_CNT) {
ALOGE("EV_REL type 0x%" PRIX16 " out of range [0,0x%X)", rel_type, REL_CNT);
return Error(ERROR_REL_RANGE);
}
if (const int status = RequireState(State::CONFIGURING)) {
return status;
}
if (const int status = EnableEventType(EV_REL)) {
return status;
}
if (const int status = uinput_->IoctlSetInt(UI_SET_RELBIT, rel_type)) {
ALOGE("failed to enable EV_REL 0x%" PRIX16 "", rel_type);
return Error(status);
}
return 0;
}
int EvdevInjector::ConfigureEnd() {
ALOGV("ConfigureEnd:");
ALOGV(" name=\"%s\"", uidev_.name);
ALOGV(" id.bustype=0x%04" PRIX16, uidev_.id.bustype);
ALOGV(" id.vendor=0x%04" PRIX16, uidev_.id.vendor);
ALOGV(" id.product=0x%04" PRIX16, uidev_.id.product);
ALOGV(" id.version=0x%04" PRIX16, uidev_.id.version);
ALOGV(" ff_effects_max=%" PRIu32, uidev_.ff_effects_max);
for (int i = 0; i < ABS_CNT; ++i) {
if (uidev_.absmin[i]) {
ALOGV(" absmin[%d]=%" PRId32, i, uidev_.absmin[i]);
}
if (uidev_.absmax[i]) {
ALOGV(" absmax[%d]=%" PRId32, i, uidev_.absmax[i]);
}
if (uidev_.absfuzz[i]) {
ALOGV(" absfuzz[%d]=%" PRId32, i, uidev_.absfuzz[i]);
}
if (uidev_.absflat[i]) {
ALOGV(" absflat[%d]=%" PRId32, i, uidev_.absflat[i]);
}
}
if (const int status = RequireState(State::CONFIGURING)) {
return status;
}
// Write out device settings.
if (const int status = uinput_->Write(&uidev_, sizeof uidev_)) {
ALOGE("failed to write device settings");
return Error(status);
}
// Create device node.
if (const int status = uinput_->IoctlVoid(UI_DEV_CREATE)) {
ALOGE("failed to create device node");
return Error(status);
}
state_ = State::READY;
return 0;
}
int EvdevInjector::Send(uint16_t type, uint16_t code, int32_t value) {
ALOGV("Send(0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32 ")", type, code, value);
if (const int status = RequireState(State::READY)) {
return status;
}
struct input_event event;
memset(&event, 0, sizeof(event));
event.type = type;
event.code = code;
event.value = value;
if (const int status = uinput_->Write(&event, sizeof(event))) {
ALOGE("failed to write event 0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32,
type, code, value);
return Error(status);
}
return 0;
}
int EvdevInjector::SendSynReport() { return Send(EV_SYN, SYN_REPORT, 0); }
int EvdevInjector::SendKey(uint16_t code, int32_t value) {
return Send(EV_KEY, code, value);
}
int EvdevInjector::SendAbs(uint16_t code, int32_t value) {
return Send(EV_ABS, code, value);
}
int EvdevInjector::SendRel(uint16_t code, int32_t value) {
return Send(EV_REL, code, value);
}
int EvdevInjector::SendMultiTouchSlot(int32_t slot) {
if (latest_slot_ != slot) {
if (const int status = SendAbs(ABS_MT_SLOT, slot)) {
return status;
}
latest_slot_ = slot;
}
return 0;
}
int EvdevInjector::SendMultiTouchXY(int32_t slot, int32_t id, int32_t x,
int32_t y) {
if (const int status = SendMultiTouchSlot(slot)) {
return status;
}
if (const int status = SendAbs(ABS_MT_TRACKING_ID, id)) {
return status;
}
if (const int status = SendAbs(ABS_MT_POSITION_X, x)) {
return status;
}
if (const int status = SendAbs(ABS_MT_POSITION_Y, y)) {
return status;
}
return 0;
}
int EvdevInjector::SendMultiTouchLift(int32_t slot) {
if (const int status = SendMultiTouchSlot(slot)) {
return status;
}
if (const int status = SendAbs(ABS_MT_TRACKING_ID, -1)) {
return status;
}
return 0;
}
int EvdevInjector::Error(int code) {
if (!error_) {
error_ = code;
}
return code;
}
int EvdevInjector::RequireState(State required_state) {
if (error_) {
return error_;
}
if (state_ != required_state) {
ALOGE("in state %d but require state %d", static_cast<int>(state_),
static_cast<int>(required_state));
return Error(ERROR_SEQUENCING);
}
return 0;
}
int EvdevInjector::EnableEventType(uint16_t type) {
if (const int status = RequireState(State::CONFIGURING)) {
return status;
}
if (enabled_event_types_.count(type) > 0) {
return 0;
}
if (const int status = uinput_->IoctlSetInt(UI_SET_EVBIT, type)) {
ALOGE("failed to enable event type 0x%X", type);
return Error(status);
}
enabled_event_types_.insert(type);
return 0;
}
void EvdevInjector::dumpInternal(String8& result) {
result.appendFormat("injector_state = %d\n", static_cast<int>(state_));
result.appendFormat("injector_error = %d\n", error_);
}
} // namespace dvr
} // namespace android

View file

@ -0,0 +1,148 @@
#ifndef ANDROID_DVR_EVDEV_INJECTOR_H
#define ANDROID_DVR_EVDEV_INJECTOR_H
#include <android-base/unique_fd.h>
#include <linux/uinput.h>
#include <utils/String8.h>
#include <cstdint>
#include <memory>
#include <unordered_set>
namespace android {
namespace dvr {
// Simulated evdev input device.
//
class EvdevInjector {
public:
// EvdevInjector-specific error codes are negative integers; other non-zero
// values returned from public routines are |errno| codes from underlying I/O.
// EvdevInjector maintains a 'sticky' error state, similar to |errno|, so that
// a caller can perform a sequence of operations and check for errors at the
// end using |GetError()|. In general, the first such error will be recorded
// and will suppress effects of further device operations until |ResetError()|
// is called.
//
enum : int {
ERROR_DEVICE_NAME = -1, // Invalid device name.
ERROR_PROPERTY_RANGE = -2, // |INPUT_PROP_*| code out of range.
ERROR_KEY_RANGE = -3, // |KEY_*|/|BTN_*| code out of range.
ERROR_ABS_RANGE = -4, // |ABS_*| code out of range.
ERROR_SEQUENCING = -5, // Configure/Send out of order.
ERROR_REL_RANGE = -6, // |REL_*| code out of range.
};
// Key event |value| is not defined in <linux/input.h>.
enum : int32_t { KEY_RELEASE = 0, KEY_PRESS = 1, KEY_REPEAT = 2 };
// UInput provides a shim to intercept /dev/uinput operations
// just above the system call level, for testing.
//
class UInput {
public:
UInput() {}
virtual ~UInput() {}
virtual int Open();
virtual int Close();
virtual int Write(const void* buf, size_t count);
virtual int IoctlVoid(int request);
virtual int IoctlSetInt(int request, int value);
private:
base::unique_fd fd_;
};
EvdevInjector() {}
~EvdevInjector() { Close(); }
void Close();
int GetError() const { return error_; }
void ResetError() { error_ = 0; }
// Configuration must be performed before sending any events.
// |ConfigureBegin()| must be called first, and |ConfigureEnd()| last,
// with zero or more other |Configure...()| calls in between in any order.
// Configure the basic evdev device properties; must be called first.
int ConfigureBegin(const char* device_name, int16_t bustype, int16_t vendor,
int16_t product, int16_t version);
// Configure an optional input device property.
// @param property One of the |INPUT_PROP_*| constants from <linux/input.h>.
int ConfigureInputProperty(int property);
// Configure an input key.
// @param key One of the |KEY_*| or |BTN_*| constants from <linux/input.h>.
int ConfigureKey(uint16_t key);
// Configure an absolute axis.
// @param abs_type One of the |KEY_*| or |BTN_*| constants from
// <linux/input.h>.
int ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max, int32_t fuzz,
int32_t flat);
// Configure the number of multitouch slots.
int ConfigureAbsSlots(int slots);
// Configure multitouch coordinate range.
int ConfigureMultiTouchXY(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
// Configure a relative axis.
// @param rel_type One of the |REL_*| constants from <linux/input.h>.
int ConfigureRel(uint16_t rel_type);
// Complete configuration and create the input device.
int ConfigureEnd();
// Send various events.
//
int Send(uint16_t type, uint16_t code, int32_t value);
int SendSynReport();
int SendKey(uint16_t code, int32_t value);
int SendAbs(uint16_t code, int32_t value);
int SendRel(uint16_t code, int32_t value);
int SendMultiTouchSlot(int32_t slot);
int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
int SendMultiTouchLift(int32_t slot);
void dumpInternal(String8& result);
protected:
// Must be called only between construction and ConfigureBegin().
inline void SetUInputForTesting(UInput* uinput) { uinput_ = uinput; }
// Caller must not retain pointer longer than EvdevInjector.
inline const uinput_user_dev* GetUiDevForTesting() const { return &uidev_; }
private:
// Phase to enforce that configuration is complete before events are sent.
enum class State { NEW, CONFIGURING, READY, CLOSED };
// Sets |error_| if it is not already set; returns |code|.
int Error(int code);
// Returns a nonzero error if the injector is not in the required |state|.
int RequireState(State state);
// Configures an event type if necessary.
// @param type One of the |EV_*| constants from <linux/input.h>.
int EnableEventType(uint16_t type);
// Active pointer to owned or testing UInput.
UInput* uinput_ = nullptr;
std::unique_ptr<UInput> owned_uinput_;
State state_ = State::NEW;
int error_ = 0;
uinput_user_dev uidev_;
std::unordered_set<uint16_t> enabled_event_types_;
int32_t latest_slot_ = -1;
EvdevInjector(const EvdevInjector&) = delete;
void operator=(const EvdevInjector&) = delete;
};
} // namespace dvr
} // namespace android
#endif // ANDROID_DVR_EVDEV_INJECTOR_H

View file

@ -0,0 +1,87 @@
#include "VirtualTouchpadClient.h"
#include <android/dvr/IVirtualTouchpadService.h>
#include <binder/IServiceManager.h>
namespace android {
namespace dvr {
namespace {
class VirtualTouchpadClientImpl : public VirtualTouchpadClient {
public:
VirtualTouchpadClientImpl() {}
~VirtualTouchpadClientImpl() override {
if (service_ != nullptr) {
Detach();
}
}
status_t Attach() {
if (service_ != nullptr) {
return ALREADY_EXISTS;
}
sp<IServiceManager> sm = defaultServiceManager();
if (sm == nullptr) {
ALOGE("no service manager");
return NO_INIT;
}
sp<IVirtualTouchpadService> service =
interface_cast<IVirtualTouchpadService>(
sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
if (service == nullptr) {
ALOGE("failed to get service");
return NAME_NOT_FOUND;
}
service_ = service;
return service_->attach().transactionError();
}
status_t Detach() {
if (service_ == nullptr) {
return NO_INIT;
}
status_t status = service_->detach().transactionError();
service_ = nullptr;
return status;
}
status_t Touch(int touchpad, float x, float y, float pressure) override {
if (service_ == nullptr) {
return NO_INIT;
}
return service_->touch(touchpad, x, y, pressure).transactionError();
}
status_t ButtonState(int touchpad, int buttons) override {
if (service_ == nullptr) {
return NO_INIT;
}
return service_->buttonState(touchpad, buttons).transactionError();
}
status_t Scroll(int touchpad, float x, float y) override {
if (service_ == nullptr) {
return NO_INIT;
}
return service_->scroll(touchpad, x, y).transactionError();
}
void dumpInternal(String8& result) override {
result.append("[virtual touchpad]\n");
result.appendFormat("connected = %s\n\n",
service_ != nullptr ? "true" : "false");
}
private:
sp<IVirtualTouchpadService> service_;
};
} // anonymous namespace
std::unique_ptr<VirtualTouchpad> VirtualTouchpadClient::Create() {
return std::unique_ptr<VirtualTouchpad>(new VirtualTouchpadClientImpl());
}
} // namespace dvr
} // namespace android

View file

@ -0,0 +1,218 @@
#include "VirtualTouchpadEvdev.h"
#include <android/input.h>
#include <inttypes.h>
#include <linux/input.h>
#include <log/log.h>
// References:
// [0] Multi-touch (MT) Protocol,
// https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
namespace android {
namespace dvr {
namespace {
// Virtual evdev device properties. The name is arbitrary, but Android can
// use it to look up device configuration, so it must be unique. Vendor and
// product values must be 0 to indicate an internal device and prevent a
// similar lookup that could conflict with a physical device.
static const char* const kDeviceNameFormat = "vr-virtual-touchpad-%d";
static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
static constexpr int16_t kDeviceVendor = 0;
static constexpr int16_t kDeviceProduct = 0;
static constexpr int16_t kDeviceVersion = 0x0001;
static constexpr int32_t kWidth = 0x10000;
static constexpr int32_t kHeight = 0x10000;
static constexpr int32_t kSlots = 2;
static constexpr float kScrollScale = 100.0f;
int32_t scale_relative_scroll(float x) {
return kScrollScale * x;
}
} // anonymous namespace
std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
std::unique_ptr<VirtualTouchpadEvdev> touchpad(new VirtualTouchpadEvdev());
touchpad->Reset();
return touchpad;
}
void VirtualTouchpadEvdev::Reset() {
for (auto& touchpad : touchpad_) {
if (touchpad.injector) {
touchpad.injector->Close();
}
touchpad.injector = nullptr;
touchpad.owned_injector.reset();
touchpad.last_device_x = INT32_MIN;
touchpad.last_device_y = INT32_MIN;
touchpad.touches = 0;
touchpad.last_motion_event_buttons = 0;
}
}
status_t VirtualTouchpadEvdev::Attach() {
status_t status = OK;
for (int i = 0; i < kTouchpads; ++i) {
Touchpad& touchpad = touchpad_[i];
if (!touchpad.injector) {
touchpad.owned_injector.reset(new EvdevInjector());
touchpad.injector = touchpad.owned_injector.get();
}
String8 DeviceName;
DeviceName.appendFormat(kDeviceNameFormat, i);
touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType,
kDeviceVendor, kDeviceProduct,
kDeviceVersion);
touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT);
touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
touchpad.injector->ConfigureAbsSlots(kSlots);
touchpad.injector->ConfigureRel(REL_WHEEL);
touchpad.injector->ConfigureRel(REL_HWHEEL);
touchpad.injector->ConfigureKey(BTN_TOUCH);
touchpad.injector->ConfigureKey(BTN_BACK);
touchpad.injector->ConfigureEnd();
if (const status_t configuration_status = touchpad.injector->GetError()) {
status = configuration_status;
}
}
return status;
}
status_t VirtualTouchpadEvdev::Detach() {
Reset();
return OK;
}
int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
float pressure) {
if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
return EINVAL;
}
int32_t device_x = x * kWidth;
int32_t device_y = y * kHeight;
Touchpad& touchpad = touchpad_[touchpad_id];
touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
device_y, touchpad.touches);
if (!touchpad.injector) {
return EvdevInjector::ERROR_SEQUENCING;
}
touchpad.injector->ResetError();
switch (touchpad.touches) {
case 0b00: // Hover continues.
if (device_x != touchpad.last_device_x ||
device_y != touchpad.last_device_y) {
touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
touchpad.injector->SendSynReport();
}
break;
case 0b01: // Touch begins.
// Press.
touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
touchpad.injector->SendSynReport();
break;
case 0b10: // Touch ends.
touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
touchpad.injector->SendMultiTouchLift(0);
touchpad.injector->SendSynReport();
break;
case 0b11: // Touch continues.
if (device_x != touchpad.last_device_x ||
device_y != touchpad.last_device_y) {
touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
touchpad.injector->SendSynReport();
}
break;
}
touchpad.last_device_x = device_x;
touchpad.last_device_y = device_y;
return touchpad.injector->GetError();
}
int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) {
if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
return EINVAL;
}
Touchpad& touchpad = touchpad_[touchpad_id];
const int changes = touchpad.last_motion_event_buttons ^ buttons;
if (!changes) {
return 0;
}
if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
return ENOTSUP;
}
ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
buttons);
if (!touchpad.injector) {
return EvdevInjector::ERROR_SEQUENCING;
}
touchpad.injector->ResetError();
if (changes & AMOTION_EVENT_BUTTON_BACK) {
touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
? EvdevInjector::KEY_PRESS
: EvdevInjector::KEY_RELEASE);
touchpad.injector->SendSynReport();
}
touchpad.last_motion_event_buttons = buttons;
return touchpad.injector->GetError();
}
int VirtualTouchpadEvdev::Scroll(int touchpad_id, float x, float y) {
if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
return EINVAL;
}
if ((x < -1.0f) || (x > 1.0f) || (y < -1.0f) || (y > 1.0f)) {
return EINVAL;
}
Touchpad& touchpad = touchpad_[touchpad_id];
if (!touchpad.injector) {
return EvdevInjector::ERROR_SEQUENCING;
}
touchpad.injector->ResetError();
const int32_t scaled_x = scale_relative_scroll(x);
const int32_t scaled_y = scale_relative_scroll(y);
ALOGV("(%f,%f) -> (%" PRId32 ",%" PRId32 ")", x, y, scaled_x, scaled_y);
if (scaled_x) {
touchpad.injector->SendRel(REL_HWHEEL, scaled_x);
}
if (scaled_y) {
touchpad.injector->SendRel(REL_WHEEL, scaled_y);
}
if (scaled_x || scaled_y) {
touchpad.injector->SendSynReport();
}
return touchpad.injector->GetError();
}
void VirtualTouchpadEvdev::dumpInternal(String8& result) {
for (int i = 0; i < kTouchpads; ++i) {
const auto& touchpad = touchpad_[i];
result.appendFormat("[virtual touchpad %d]\n", i);
if (!touchpad.injector) {
result.append("injector = none\n");
return;
}
result.appendFormat("injector = %s\n",
touchpad.owned_injector ? "normal" : "test");
result.appendFormat("touches = %d\n", touchpad.touches);
result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
touchpad.last_device_x, touchpad.last_device_y);
result.appendFormat("last_buttons = 0x%" PRIX32 "\n",
touchpad.last_motion_event_buttons);
touchpad.injector->dumpInternal(result);
result.append("\n");
}
}
} // namespace dvr
} // namespace android

View file

@ -0,0 +1,68 @@
#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
#define ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
#include "EvdevInjector.h"
#include "VirtualTouchpad.h"
namespace android {
namespace dvr {
class EvdevInjector;
// VirtualTouchpadEvdev implements a VirtualTouchpad by injecting evdev events.
//
class VirtualTouchpadEvdev : public VirtualTouchpad {
public:
static std::unique_ptr<VirtualTouchpad> Create();
~VirtualTouchpadEvdev() override {}
// VirtualTouchpad implementation:
status_t Attach() override;
status_t Detach() override;
status_t Touch(int touchpad, float x, float y, float pressure) override;
status_t ButtonState(int touchpad, int buttons) override;
status_t Scroll(int touchpad, float x, float y) override;
void dumpInternal(String8& result) override;
protected:
static constexpr int kTouchpads = 2;
VirtualTouchpadEvdev() {}
void Reset();
// Must be called only between construction (or Detach()) and Attach().
inline void SetEvdevInjectorForTesting(int touchpad,
EvdevInjector* injector) {
touchpad_[touchpad].injector = injector;
}
private:
// Per-touchpad state.
struct Touchpad {
// Except for testing, the |EvdevInjector| used to inject evdev events.
std::unique_ptr<EvdevInjector> owned_injector;
// Active pointer to |owned_injector_| or to a testing injector.
EvdevInjector* injector = nullptr;
// Previous (x, y) position in device space, to suppress redundant events.
int32_t last_device_x;
int32_t last_device_y;
// Records current touch state (0=up 1=down) in bit 0, and previous state
// in bit 1, to track transitions.
int touches;
// Previous injected button state, to detect changes.
int32_t last_motion_event_buttons;
};
Touchpad touchpad_[kTouchpads];
VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete;
void operator=(const VirtualTouchpadEvdev&) = delete;
};
} // namespace dvr
} // namespace android
#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H

View file

@ -0,0 +1,145 @@
#include "VirtualTouchpadService.h"
#include <inttypes.h>
#include <binder/IPCThreadState.h>
#include <binder/PermissionCache.h>
#include <binder/Status.h>
#include <cutils/log.h>
#include <linux/input.h>
#include <private/android_filesystem_config.h>
#include <utils/Errors.h>
namespace android {
namespace dvr {
namespace {
const String16 kDumpPermission("android.permission.DUMP");
const String16 kTouchPermission("android.permission.RESTRICTED_VR_ACCESS");
} // anonymous namespace
VirtualTouchpadService::~VirtualTouchpadService() {
if (client_pid_) {
client_pid_ = 0;
touchpad_->Detach();
}
}
binder::Status VirtualTouchpadService::attach() {
pid_t pid;
if (!CheckTouchPermission(&pid)) {
return binder::Status::fromStatusT(PERMISSION_DENIED);
}
if (client_pid_ == pid) {
// The same client has called attach() twice with no intervening detach().
// This indicates a problem with the client, so return an error.
// However, since the client is already attached, any touchpad actions
// it takes will still work.
ALOGE("pid=%ld attached twice", static_cast<long>(pid));
return binder::Status::fromStatusT(ALREADY_EXISTS);
}
if (client_pid_ != 0) {
// Attach while another client is attached. This can happen if the client
// dies without cleaning up after itself, so move ownership to the current
// caller. If two actual clients have connected, the problem will be
// reported when the previous client performs any touchpad action.
ALOGE("pid=%ld replaces %ld", static_cast<long>(pid),
static_cast<long>(client_pid_));
client_pid_ = pid;
return binder::Status::ok();
}
client_pid_ = pid;
if (const status_t error = touchpad_->Attach()) {
return binder::Status::fromStatusT(error);
}
return binder::Status::ok();
}
binder::Status VirtualTouchpadService::detach() {
if (!CheckPermissions()) {
return binder::Status::fromStatusT(PERMISSION_DENIED);
}
client_pid_ = 0;
if (const status_t error = touchpad_->Detach()) {
return binder::Status::fromStatusT(error);
}
return binder::Status::ok();
}
binder::Status VirtualTouchpadService::touch(int touchpad, float x, float y,
float pressure) {
if (!CheckPermissions()) {
return binder::Status::fromStatusT(PERMISSION_DENIED);
}
if (const status_t error = touchpad_->Touch(touchpad, x, y, pressure)) {
return binder::Status::fromStatusT(error);
}
return binder::Status::ok();
}
binder::Status VirtualTouchpadService::buttonState(int touchpad, int buttons) {
if (!CheckPermissions()) {
return binder::Status::fromStatusT(PERMISSION_DENIED);
}
if (const status_t error = touchpad_->ButtonState(touchpad, buttons)) {
return binder::Status::fromStatusT(error);
}
return binder::Status::ok();
}
binder::Status VirtualTouchpadService::scroll(int touchpad, float x, float y) {
if (!CheckPermissions()) {
return binder::Status::fromStatusT(PERMISSION_DENIED);
}
if (const status_t error = touchpad_->Scroll(touchpad, x, y)) {
return binder::Status::fromStatusT(error);
}
return binder::Status::ok();
}
status_t VirtualTouchpadService::dump(
int fd, const Vector<String16>& args[[gnu::unused]]) {
String8 result;
const android::IPCThreadState* ipc = android::IPCThreadState::self();
const pid_t pid = ipc->getCallingPid();
const uid_t uid = ipc->getCallingUid();
if ((uid != AID_SHELL) &&
!PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
result.appendFormat("Permission denial: can't dump " LOG_TAG
" from pid=%ld, uid=%ld\n",
static_cast<long>(pid), static_cast<long>(uid));
} else {
result.appendFormat("[service]\nclient_pid = %ld\n\n",
static_cast<long>(client_pid_));
touchpad_->dumpInternal(result);
}
write(fd, result.string(), result.size());
return OK;
}
bool VirtualTouchpadService::CheckPermissions() {
pid_t pid;
if (!CheckTouchPermission(&pid)) {
return false;
}
if (client_pid_ != pid) {
ALOGE("pid=%ld is not owner", static_cast<long>(pid));
return false;
}
return true;
}
bool VirtualTouchpadService::CheckTouchPermission(pid_t* out_pid) {
const android::IPCThreadState* ipc = android::IPCThreadState::self();
*out_pid = ipc->getCallingPid();
const uid_t uid = ipc->getCallingUid();
const bool permission = PermissionCache::checkPermission(kTouchPermission, *out_pid, uid);
if (!permission) {
ALOGE("permission denied to pid=%ld uid=%ld", static_cast<long>(*out_pid),
static_cast<long>(uid));
}
return permission;
}
} // namespace dvr
} // namespace android

View file

@ -0,0 +1,47 @@
#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
#define ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
#include <android/dvr/BnVirtualTouchpadService.h>
#include "VirtualTouchpad.h"
namespace android {
namespace dvr {
// VirtualTouchpadService implements the service side of
// the Binder interface defined in VirtualTouchpadService.aidl.
//
class VirtualTouchpadService : public BnVirtualTouchpadService {
public:
VirtualTouchpadService(std::unique_ptr<VirtualTouchpad> touchpad)
: touchpad_(std::move(touchpad)), client_pid_(0) {}
~VirtualTouchpadService() override;
protected:
// Implements IVirtualTouchpadService.
binder::Status attach() override;
binder::Status detach() override;
binder::Status touch(int touchpad, float x, float y, float pressure) override;
binder::Status buttonState(int touchpad, int buttons) override;
binder::Status scroll(int touchpad, float x, float y) override;
// Implements BBinder::dump().
status_t dump(int fd, const Vector<String16>& args) override;
private:
bool CheckPermissions();
bool CheckTouchPermission(pid_t* out_pid);
std::unique_ptr<VirtualTouchpad> touchpad_;
// Only one client at a time can use the virtual touchpad.
pid_t client_pid_;
VirtualTouchpadService(const VirtualTouchpadService&) = delete;
void operator=(const VirtualTouchpadService&) = delete;
};
} // namespace dvr
} // namespace android
#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H

View file

@ -0,0 +1,48 @@
package android.dvr;
/** @hide */
interface VirtualTouchpadService
{
const String SERVICE_NAME = "virtual_touchpad";
/**
* Initialize the virtual touchpad.
*/
void attach() = 0;
/**
* Shut down the virtual touchpad.
*/
void detach() = 1;
/**
* Generate a simulated touch event.
*
* @param touchpad Selects touchpad.
* @param x Horizontal touch position.
* @param y Vertical touch position.
* @param pressure Touch pressure; use 0.0 for no touch (lift or hover).
*
* Position values in the range [0.0, 1.0) map to the screen.
*/
void touch(int touchpad, float x, float y, float pressure) = 2;
/**
* Generate a simulated touchpad button state event.
*
* @param touchpad Selects touchpad.
* @param buttons A union of MotionEvent BUTTON_* values.
*/
void buttonState(int touchpad, int buttons) = 3;
/**
* Generate a simulated scroll event.
*
* @param touchpad Selects touchpad.
* @param x Horizontal scroll increment.
* @param y Vertical scroll increment.
*
* Scroll values are in the range [-1.0, 1.0].
*/
void scroll(int touchpad, float x, float y) = 4;
}

View file

@ -0,0 +1,26 @@
# 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.
#
# Virtual touchpad for the primary display
device.internal = 1
touch.deviceType = touchScreen
# Have input flinger treat injected scroll events like a G1 ball
# rather than the default mouse wheel, because the latter requires
# a visible pointer for targeting.
device.type = rotaryEncoder
device.res = 1.0e+2
device.scalingFactor = 1.0e-2

View file

@ -0,0 +1,31 @@
# 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.
#
# Virtual touchpad for the VR virtual display
device.internal = 1
touch.deviceType = touchScreen
# Have input flinger treat injected scroll events like a G1 ball
# rather than the default mouse wheel, because the latter requires
# a visible pointer for targeting.
device.type = rotaryEncoder
device.res = 1.0e+2
device.scalingFactor = 1.0e-2
# This displayID matches the unique ID of the virtual display created for VR.
# This will indicate to input flinger than it should link this input device
# with the virtual display.
touch.displayId = virtual:android:277f1a09-b88d-4d1e-8716-796f114d080b

View file

@ -0,0 +1,88 @@
#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
#define ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
#include "dvr/virtual_touchpad_client.h"
#include <memory>
#include <utils/Errors.h>
#include <utils/String8.h>
namespace android {
namespace dvr {
// Provides a virtual touchpad for injecting events into the input system.
//
class VirtualTouchpad {
public:
enum : int {
PRIMARY = DVR_VIRTUAL_TOUCHPAD_PRIMARY,
VIRTUAL = DVR_VIRTUAL_TOUCHPAD_VIRTUAL,
};
virtual ~VirtualTouchpad() {}
// Create a virtual touchpad.
// Implementations should provide this, and hide their constructors.
// For the user, switching implementations should be as simple as changing
// the class whose |Create()| is called.
// Implementations should be minimial; major resource allocation should
// be performed in Attach().
static std::unique_ptr<VirtualTouchpad> Create() {
return nullptr;
}
// Initialize a virtual touchpad.
virtual status_t Attach() = 0;
// Shut down a virtual touchpad.
virtual status_t Detach() = 0;
// Generate a simulated touch event.
//
// @param touchpad Touchpad selector index.
// @param x Horizontal touch position.
// @param y Vertical touch position.
// Values must be in the range [0.0, 1.0).
// @param pressure Touch pressure.
// Positive values represent contact; use 1.0f if contact
// is binary. Use 0.0f for no contact.
// @returns OK on success.
//
virtual status_t Touch(int touchpad, float x, float y, float pressure) = 0;
// Generate a simulated touchpad button state.
//
// @param touchpad Touchpad selector index.
// @param buttons A union of MotionEvent BUTTON_* values.
// @returns OK on success.
//
// Currently only BUTTON_BACK is supported, as the implementation
// restricts itself to operations actually required by VrWindowManager.
//
virtual status_t ButtonState(int touchpad, int buttons) = 0;
// Generate a simulated scroll event.
//
// @param touchpad Touchpad selector index.
// @param x Horizontal scroll increment.
// @param y Vertical scroll increment.
// Values must be in the range [-1.0, 1.0].
// @returns OK on success.
//
virtual status_t Scroll(int touchpad, float x, float y) = 0;
// Report state for 'dumpsys'.
virtual void dumpInternal(String8& result) = 0;
protected:
VirtualTouchpad() {}
private:
VirtualTouchpad(const VirtualTouchpad&) = delete;
void operator=(const VirtualTouchpad&) = delete;
};
} // namespace dvr
} // namespace android
#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H

View file

@ -0,0 +1,35 @@
#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
#define ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
#include "VirtualTouchpad.h"
namespace android {
namespace dvr {
// VirtualTouchpadClient implements a VirtualTouchpad by connecting to
// a VirtualTouchpadService over Binder.
//
class VirtualTouchpadClient : public VirtualTouchpad {
public:
// VirtualTouchpad implementation:
static std::unique_ptr<VirtualTouchpad> Create();
status_t Attach() override;
status_t Detach() override;
status_t Touch(int touchpad, float x, float y, float pressure) override;
status_t ButtonState(int touchpad, int buttons) override;
status_t Scroll(int touchpad, float x, float y) override;
void dumpInternal(String8& result) override;
protected:
VirtualTouchpadClient() {}
~VirtualTouchpadClient() override {}
private:
VirtualTouchpadClient(const VirtualTouchpadClient&) = delete;
void operator=(const VirtualTouchpadClient&) = delete;
};
} // namespace dvr
} // namespace android
#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H

View file

@ -0,0 +1,79 @@
#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_C_CLIENT_H
#define ANDROID_DVR_VIRTUAL_TOUCHPAD_C_CLIENT_H
#include <dvr/dvr_api.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
// Creates a new virtual touchpad client.
//
// @return Pointer to the created virtual touchpad client; nullptr on failure.
//
DvrVirtualTouchpad* dvrVirtualTouchpadCreate();
// Destroys a virtual touchpad client.
//
// @param client Pointer to the virtual touchpad client to be destroyed.
//
void dvrVirtualTouchpadDestroy(DvrVirtualTouchpad* client);
// Initialize the virtual touchpad.
//
// In the current server implementation, attachment creates and configures
// the kernel virtual touchpad device(s). A single client may be attached
// and detached repeatedly, e.g. on entering and leaving VR mode.
//
// @param client Pointer to the virtual touchpad client to be attached.
// @return Zero on success, status_t-style error code on failure.
//
int dvrVirtualTouchpadAttach(DvrVirtualTouchpad* client);
// Shut down the virtual touchpad.
//
// @param client Pointer to the virtual touchpad client to be detached.
// @return Zero on success, status_t-style error code on failure.
//
int dvrVirtualTouchpadDetach(DvrVirtualTouchpad* client);
// Generate a simulated touch event.
//
// @param client Pointer to the virtual touchpad client.
// @param touchpad Selects touchpad.
// @param x Horizontal touch position.
// @param y Vertical touch position.
// @param pressure Touch pressure; use 0.0 for no touch (lift or hover).
// @return Zero on success, status_t-style error code on failure.
//
int dvrVirtualTouchpadTouch(DvrVirtualTouchpad* client, int touchpad, float x,
float y, float pressure);
// Generate a simulated touchpad button state event.
//
// @param client Pointer to the virtual touchpad client.
// @param touchpad Selects touchpad.
// @param buttons A union of MotionEvent BUTTON_* values.
// @return Zero on success, status_t-style error code on failure.
//
int dvrVirtualTouchpadButtonState(DvrVirtualTouchpad* client, int touchpad,
int buttons);
// Generate a simulated scroll event.
//
// @param client Pointer to the virtual touchpad client.
// @param touchpad Selects touchpad.
// @param x Horizontal scroll increment.
// @param y Vertical scroll increment.
// @return Zero on success, status_t-style error code on failure.
//
int dvrVirtualTouchpadScroll(DvrVirtualTouchpad* client, int touchpad, float x,
float y);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H

View file

@ -0,0 +1,33 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <log/log.h>
#include "VirtualTouchpadEvdev.h"
#include "VirtualTouchpadService.h"
int main() {
ALOGI("Starting");
android::sp<android::dvr::VirtualTouchpadService> touchpad_service =
new android::dvr::VirtualTouchpadService(
android::dvr::VirtualTouchpadEvdev::Create());
signal(SIGPIPE, SIG_IGN);
android::sp<android::ProcessState> ps(android::ProcessState::self());
ps->setThreadPoolMaxThreadCount(4);
ps->startThreadPool();
ps->giveThreadPoolName();
android::sp<android::IServiceManager> sm(android::defaultServiceManager());
const android::status_t service_status =
sm->addService(android::String16(touchpad_service->SERVICE_NAME()),
touchpad_service, false /*allowIsolated*/);
if (service_status != android::OK) {
ALOGE("virtual touchpad service not added: %d",
static_cast<int>(service_status));
exit(2);
}
android::IPCThreadState::self()->joinThreadPool();
return 0;
}

View file

@ -0,0 +1,332 @@
#include <android/input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "EvdevInjector.h"
#include "VirtualTouchpadEvdev.h"
namespace android {
namespace dvr {
namespace {
class UInputForTesting : public EvdevInjector::UInput {
public:
~UInputForTesting() override {}
void WriteInputEvent(uint16_t type, uint16_t code, int32_t value) {
struct input_event event;
memset(&event, 0, sizeof(event));
event.type = type;
event.code = code;
event.value = value;
Write(&event, sizeof(event));
}
};
// Recording test implementation of UInput.
//
class UInputRecorder : public UInputForTesting {
public:
UInputRecorder() {}
~UInputRecorder() override {}
const std::string& GetString() const { return s_; }
void Reset() { s_.clear(); }
// UInput overrides:
int Open() override {
s_ += "o;";
return 0;
}
int Close() override {
s_ += "c;";
return 0;
}
int Write(const void* buf, size_t count) override {
s_ += "w(";
s_ += Encode(&count, sizeof(count));
s_ += ",";
s_ += Encode(buf, count);
s_ += ");";
return 0;
}
int IoctlVoid(int request) override {
s_ += "i(";
s_ += Encode(&request, sizeof(request));
s_ += ");";
return 0;
}
int IoctlSetInt(int request, int value) override {
s_ += "i(";
s_ += Encode(&request, sizeof(request));
s_ += ",";
s_ += Encode(&value, sizeof(value));
s_ += ");";
return 0;
}
private:
std::string s_;
std::string Encode(const void* buf, size_t count) {
const char* in = static_cast<const char*>(buf);
char out[2 * count + 1];
for (size_t i = 0; i < count; ++i) {
snprintf(&out[2 * i], 3, "%02X", in[i]);
}
return out;
}
};
class EvdevInjectorForTesting : public EvdevInjector {
public:
EvdevInjectorForTesting() { SetUInputForTesting(&record); }
const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
UInputRecorder record;
};
class VirtualTouchpadForTesting : public VirtualTouchpadEvdev {
public:
static std::unique_ptr<VirtualTouchpad> Create() {
return std::unique_ptr<VirtualTouchpad>(New());
}
static VirtualTouchpadForTesting* New() {
VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
touchpad->Reset();
for (int t = 0; t < kTouchpads; ++t) {
touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]);
}
return touchpad;
}
int GetTouchpadCount() const { return kTouchpads; }
EvdevInjectorForTesting injector[kTouchpads];
};
void DumpDifference(const char* expect, const char* actual) {
printf(" common: ");
while (*expect && *expect == *actual) {
putchar(*expect);
++expect;
++actual;
}
printf("\n expect: %s\n", expect);
printf(" actual: %s\n", actual);
}
} // anonymous namespace
class VirtualTouchpadTest : public testing::Test {};
TEST_F(VirtualTouchpadTest, Goodness) {
std::unique_ptr<VirtualTouchpadForTesting> touchpad(
VirtualTouchpadForTesting::New());
UInputRecorder expect;
status_t touch_status = touchpad->Attach();
EXPECT_EQ(0, touch_status);
// Check some aspects of uinput_user_dev.
const uinput_user_dev* uidev;
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
uidev = touchpad->injector[t].GetUiDev();
String8 name;
name.appendFormat("vr-virtual-touchpad-%d", t);
EXPECT_EQ(name, uidev->name);
for (int i = 0; i < ABS_CNT; ++i) {
EXPECT_EQ(0, uidev->absmin[i]);
EXPECT_EQ(0, uidev->absfuzz[i]);
EXPECT_EQ(0, uidev->absflat[i]);
if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y &&
i != ABS_MT_SLOT) {
EXPECT_EQ(0, uidev->absmax[i]);
}
}
}
const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
const int32_t slots = uidev->absmax[ABS_MT_SLOT];
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
// Check the system calls performed by initialization.
expect.Reset();
// From ConfigureBegin():
expect.Open();
// From ConfigureInputProperty(INPUT_PROP_DIRECT):
expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
// From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
// From ConfigureAbsSlots(kSlots):
expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
// From ConfigureRel(REL_WHEEL):
expect.IoctlSetInt(UI_SET_EVBIT, EV_REL);
expect.IoctlSetInt(UI_SET_RELBIT, REL_WHEEL);
// From ConfigureRel(REL_HWHEEL):
expect.IoctlSetInt(UI_SET_RELBIT, REL_HWHEEL);
// From ConfigureKey(BTN_TOUCH):
expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
// From ConfigureEnd():
expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev));
expect.IoctlVoid(UI_DEV_CREATE);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0, 0, 0);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height);
expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f);
EXPECT_EQ(EINVAL, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE);
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->ButtonState(t, 0);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.Close();
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
}
touch_status = touchpad->Detach();
EXPECT_EQ(0, touch_status);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
}
TEST_F(VirtualTouchpadTest, Badness) {
std::unique_ptr<VirtualTouchpadForTesting> touchpad(
VirtualTouchpadForTesting::New());
UInputRecorder expect;
UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record;
status_t touch_status = touchpad->Attach();
EXPECT_EQ(0, touch_status);
// Touch off-screen should return an error,
// and should not result in any system calls.
expect.Reset();
record.Reset();
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
EXPECT_NE(OK, touch_status);
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f);
EXPECT_NE(OK, touch_status);
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f);
EXPECT_NE(OK, touch_status);
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 1.75f, 1.0f);
EXPECT_NE(OK, touch_status);
EXPECT_EQ(expect.GetString(), record.GetString());
// Unsupported button should return an error,
// and should not result in any system calls.
expect.Reset();
record.Reset();
touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
AMOTION_EVENT_BUTTON_FORWARD);
EXPECT_NE(OK, touch_status);
EXPECT_EQ(expect.GetString(), record.GetString());
// Repeated attach is an error.
touch_status = touchpad->Attach();
EXPECT_NE(0, touch_status);
}
} // namespace dvr
} // namespace android

View file

@ -0,0 +1,5 @@
service virtual_touchpad /system/bin/virtual_touchpad
class core
user system
group system input
writepid /dev/cpuset/system/tasks