| #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 |