Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7311c4a8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Support multiple virtual touchpads."

parents 78410012 0108af72
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -308,9 +308,8 @@ int EvdevInjector::EnableEventType(uint16_t type) {
}

void EvdevInjector::dumpInternal(String8& result) {
  result.append("[injector]\n");
  result.appendFormat("state = %d\n", static_cast<int>(state_));
  result.appendFormat("error = %d\n\n", error_);
  result.appendFormat("injector_state = %d\n", static_cast<int>(state_));
  result.appendFormat("injector_error = %d\n", error_);
}

}  // namespace dvr
+97 −65
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ namespace {
// 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 kDeviceName = "vr window manager virtual touchpad";
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;
@@ -32,122 +32,154 @@ static constexpr int32_t kSlots = 2;

sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
  VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev();
  touchpad->Reset();
  return sp<VirtualTouchpad>(touchpad);
}

status_t VirtualTouchpadEvdev::Attach() {
  if (!injector_) {
    owned_injector_.reset(new EvdevInjector());
    injector_ = owned_injector_.get();
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;
  }
}
  injector_->ConfigureBegin(kDeviceName, kDeviceBusType, kDeviceVendor,
                            kDeviceProduct, kDeviceVersion);
  injector_->ConfigureInputProperty(INPUT_PROP_DIRECT);
  injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
  injector_->ConfigureAbsSlots(kSlots);
  injector_->ConfigureKey(BTN_TOUCH);
  injector_->ConfigureKey(BTN_BACK);
  injector_->ConfigureEnd();
  return injector_->GetError();

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->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() {
  injector_->Close();
  injector_ = nullptr;
  owned_injector_.reset();
  last_device_x_ = INT32_MIN;
  last_device_y_ = INT32_MIN;
  touches_ = 0;
  last_motion_event_buttons_ = 0;
  Reset();
  return OK;
}

int VirtualTouchpadEvdev::Touch(int touchpad, float x, float y,
int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
                                float pressure) {
  (void)touchpad;  // TODO(b/35992608) Support multiple touchpad devices.
  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
    return EINVAL;
  }
  if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
    return EINVAL;
  }
  int32_t device_x = x * kWidth;
  int32_t device_y = y * kHeight;
  touches_ = ((touches_ & 1) << 1) | (pressure > 0);
  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, touches_);
        device_y, touchpad.touches);

  if (!injector_) {
  if (!touchpad.injector) {
    return EvdevInjector::ERROR_SEQUENCING;
  }
  injector_->ResetError();
  switch (touches_) {
  touchpad.injector->ResetError();
  switch (touchpad.touches) {
    case 0b00:  // Hover continues.
      if (device_x != last_device_x_ || device_y != last_device_y_) {
        injector_->SendMultiTouchXY(0, 0, device_x, device_y);
        injector_->SendSynReport();
      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.
      injector_->SendMultiTouchXY(0, 0, device_x, device_y);
      injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
      injector_->SendSynReport();
      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.
      injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
      injector_->SendMultiTouchLift(0);
      injector_->SendSynReport();
      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
      touchpad.injector->SendMultiTouchLift(0);
      touchpad.injector->SendSynReport();
      break;
    case 0b11:  // Touch continues.
      if (device_x != last_device_x_ || device_y != last_device_y_) {
        injector_->SendMultiTouchXY(0, 0, device_x, device_y);
        injector_->SendSynReport();
      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;
  }
  last_device_x_ = device_x;
  last_device_y_ = device_y;
  touchpad.last_device_x = device_x;
  touchpad.last_device_y = device_y;

  return injector_->GetError();
  return touchpad.injector->GetError();
}

int VirtualTouchpadEvdev::ButtonState(int touchpad, int buttons) {
  (void)touchpad;  // TODO(b/35992608) Support multiple touchpad devices.
  const int changes = last_motion_event_buttons_ ^ buttons;
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, last_motion_event_buttons_,
  ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
        buttons);

  if (!injector_) {
  if (!touchpad.injector) {
    return EvdevInjector::ERROR_SEQUENCING;
  }
  injector_->ResetError();
  touchpad.injector->ResetError();
  if (changes & AMOTION_EVENT_BUTTON_BACK) {
    injector_->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
    touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
                                             ? EvdevInjector::KEY_PRESS
                                             : EvdevInjector::KEY_RELEASE);
    injector_->SendSynReport();
    touchpad.injector->SendSynReport();
  }
  last_motion_event_buttons_ = buttons;
  return injector_->GetError();
  touchpad.last_motion_event_buttons = buttons;
  return touchpad.injector->GetError();
}

void VirtualTouchpadEvdev::dumpInternal(String8& result) {
  result.append("[virtual touchpad]\n");
  if (!injector_) {
  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", owned_injector_ ? "normal" : "test");
  result.appendFormat("touches = %d\n", touches_);
    result.appendFormat("injector = %s\n",
                        touchpad.owned_injector ? "normal" : "test");
    result.appendFormat("touches = %d\n", touchpad.touches);
    result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
                      last_device_x_, last_device_y_);
  result.appendFormat("last_buttons = 0x%" PRIX32 "\n\n",
                      last_motion_event_buttons_);
  injector_->dumpInternal(result);
                        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
+28 −20
Original line number Diff line number Diff line
@@ -3,8 +3,8 @@

#include <memory>

#include "VirtualTouchpad.h"
#include "EvdevInjector.h"
#include "VirtualTouchpad.h"

namespace android {
namespace dvr {
@@ -25,31 +25,39 @@ class VirtualTouchpadEvdev : public VirtualTouchpad {
  void dumpInternal(String8& result) override;

 protected:
  static constexpr int kTouchpads = 2;

  VirtualTouchpadEvdev() {}
  ~VirtualTouchpadEvdev() override {}
  void Reset();

  // Must be called only between construction and Attach().
  inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
    injector_ = injector;
  // 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_;
    std::unique_ptr<EvdevInjector> owned_injector;

    // Active pointer to |owned_injector_| or to a testing injector.
  EvdevInjector* injector_ = nullptr;
    EvdevInjector* injector = nullptr;

    // Previous (x, y) position in device space, to suppress redundant events.
  int32_t last_device_x_ = INT32_MIN;
  int32_t last_device_y_ = INT32_MIN;
    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_ = 0;
    int touches;

    // Previous injected button state, to detect changes.
  int32_t last_motion_event_buttons_ = 0;
    int32_t last_motion_event_buttons;
  };
  Touchpad touchpad_[kTouchpads];

  VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete;
  void operator=(const VirtualTouchpadEvdev&) = delete;
+119 −78
Original line number Diff line number Diff line
@@ -88,17 +88,24 @@ class UInputRecorder : public UInputForTesting {

class EvdevInjectorForTesting : public EvdevInjector {
 public:
  EvdevInjectorForTesting(UInput& uinput) { SetUInputForTesting(&uinput); }
  EvdevInjectorForTesting() { SetUInputForTesting(&record); }
  const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
  UInputRecorder record;
};

class VirtualTouchpadForTesting : public VirtualTouchpadEvdev {
 public:
  static sp<VirtualTouchpad> Create(EvdevInjectorForTesting& injector) {
  static sp<VirtualTouchpad> Create() { return sp<VirtualTouchpad>(New()); }
  static VirtualTouchpadForTesting* New() {
    VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
    touchpad->SetEvdevInjectorForTesting(&injector);
    return sp<VirtualTouchpad>(touchpad);
    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) {
@@ -117,29 +124,38 @@ void DumpDifference(const char* expect, const char* actual) {
class VirtualTouchpadTest : public testing::Test {};

TEST_F(VirtualTouchpadTest, Goodness) {
  sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New());
  UInputRecorder expect;
  UInputRecorder record;
  EvdevInjectorForTesting injector(record);
  sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));

  status_t touch_status = touchpad->Attach();
  EXPECT_EQ(0, touch_status);

  // Check some aspects of uinput_user_dev.
  const uinput_user_dev* uidev = injector.GetUiDev();
  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) {
      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):
@@ -155,95 +171,122 @@ TEST_F(VirtualTouchpadTest, Goodness) {
    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
    // From ConfigureEnd():
  expect.Write(uidev, sizeof(uinput_user_dev));
    expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev));
    expect.IoctlVoid(UI_DEV_CREATE);
  EXPECT_EQ(expect.GetString(), record.GetString());
    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
  }

  expect.Reset();
  record.Reset();
  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0, 0, 0);
  EXPECT_EQ(0, touch_status);
  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);
  EXPECT_EQ(expect.GetString(), record.GetString());
  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();
  record.Reset();
  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, 0.5f);
  EXPECT_EQ(0, touch_status);
  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);
  EXPECT_EQ(expect.GetString(), record.GetString());
  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();
  record.Reset();
  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.99f, 0.99f, 0.99f);
  EXPECT_EQ(0, touch_status);
  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);
  EXPECT_EQ(expect.GetString(), record.GetString());
  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();
  record.Reset();
  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.0f, 1.0f, 1.0f);
  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(), record.GetString());
    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
  }

  expect.Reset();
  record.Reset();
  touch_status =
      touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, -0.01f);
  EXPECT_EQ(0, touch_status);
  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);
  EXPECT_EQ(expect.GetString(), record.GetString());
  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();
  record.Reset();
  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
                                       AMOTION_EVENT_BUTTON_BACK);
  EXPECT_EQ(0, touch_status);
  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
  EXPECT_EQ(expect.GetString(), record.GetString());
  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();
  record.Reset();
  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
                                       AMOTION_EVENT_BUTTON_BACK);
  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(), record.GetString());
    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
  }

  expect.Reset();
  record.Reset();
  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, 0);
  EXPECT_EQ(0, touch_status);
  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
  EXPECT_EQ(expect.GetString(), record.GetString());
  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();
  record.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);
  expect.Close();
  EXPECT_EQ(expect.GetString(), record.GetString());
  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
    SCOPED_TRACE(t);
    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
  }
}

TEST_F(VirtualTouchpadTest, Badness) {
  sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New());
  UInputRecorder expect;
  UInputRecorder record;
  EvdevInjectorForTesting injector(record);
  sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));
  UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record;

  status_t touch_status = touchpad->Attach();
  EXPECT_EQ(0, touch_status);
@@ -252,11 +295,9 @@ TEST_F(VirtualTouchpadTest, Badness) {
  // and should not result in any system calls.
  expect.Reset();
  record.Reset();
  touch_status =
      touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
  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);
  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);