upload android base code part1
This commit is contained in:
parent
e02f198e2d
commit
0a1de6c4b3
48159 changed files with 9071466 additions and 0 deletions
|
@ -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"],
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
service virtual_touchpad /system/bin/virtual_touchpad
|
||||
class core
|
||||
user system
|
||||
group system input
|
||||
writepid /dev/cpuset/system/tasks
|
Loading…
Add table
Add a link
Reference in a new issue