Support multiple virtual touchpads.

The service now creates two uinput devices, named
"vr virtual touchpad 0" for VirtualTouchpad::PRIMARY
and "vr virtual touchpad 1" for VirtualTouchpad::VIRTUAL.

Bug: b/35992608
Test: expanded VirtualTouchpad_test unit test
Change-Id: I8749d559a56cfa9c8ec9a039ff7ad44d16f81915
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
index b4c0a52..a4ccdd0 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.cpp
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -308,9 +308,8 @@
 }
 
 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
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
index bc29408..92193d3 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -18,7 +18,7 @@
 // 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 @@
 
 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)
-                                     ? EvdevInjector::KEY_PRESS
-                                     : EvdevInjector::KEY_RELEASE);
-    injector_->SendSynReport();
+    touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
+                                             ? EvdevInjector::KEY_PRESS
+                                             : EvdevInjector::KEY_RELEASE);
+    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_) {
-    result.append("injector = none\n");
-    return;
+  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");
   }
-  result.appendFormat("injector = %s\n", owned_injector_ ? "normal" : "test");
-  result.appendFormat("touches = %d\n", 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);
 }
 
 }  // namespace dvr
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
index b158cec..dbaca9a 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
@@ -3,8 +3,8 @@
 
 #include <memory>
 
-#include "VirtualTouchpad.h"
 #include "EvdevInjector.h"
+#include "VirtualTouchpad.h"
 
 namespace android {
 namespace dvr {
@@ -25,31 +25,39 @@
   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:
-  // Except for testing, the |EvdevInjector| used to inject evdev events.
-  std::unique_ptr<EvdevInjector> owned_injector_;
+  // 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;
+    // 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_MIN;
-  int32_t last_device_y_ = INT32_MIN;
+    // 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_ = 0;
+    // 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_ = 0;
+    // 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;
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index b11a983..bc34850 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -88,17 +88,24 @@
 
 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,133 +124,169 @@
 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();
-  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 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];
 
-  // Check the system calls performed by initialization.
-  // 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 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(uidev, sizeof(uinput_user_dev));
-  expect.IoctlVoid(UI_DEV_CREATE);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  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 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();
-  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);
-  EXPECT_EQ(EINVAL, touch_status);
-  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, 1.0f, 1.0f, 1.0f);
+    EXPECT_EQ(EINVAL, 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.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);
-  EXPECT_EQ(0, touch_status);
-  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, 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 @@
   // 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);