Merge changes I3ceab49b,If486cdb7,I68d54c05 into main
* changes:
uinput: delay from the end of the last delay
uinput: Specify timestamps when injecting events from evemu
uinput: use nanoseconds for delay durations
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index b6e4e0d..f177586 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -154,7 +154,8 @@
#### `delay`
-Add a delay to command processing
+Add a delay between the processing of commands. The delay will be timed from when the last delay
+ended, rather than from the current time, to allow for more precise timings to be produced.
| Field | Type | Description |
|:-------------:|:-------------:|:-------------------------- |
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
index a78a465..bd61000 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -166,14 +166,14 @@
::ioctl(mFd, UI_DEV_DESTROY);
}
-void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+void UinputDevice::injectEvent(std::chrono::microseconds timestamp, uint16_t type, uint16_t code,
+ int32_t value) {
struct input_event event = {};
event.type = type;
event.code = code;
event.value = value;
- timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- TIMESPEC_TO_TIMEVAL(&event.time, &ts);
+ event.time.tv_sec = timestamp.count() / 1'000'000;
+ event.time.tv_usec = timestamp.count() % 1'000'000;
if (::write(mFd, &event, sizeof(input_event)) < 0) {
ALOGE("Could not write event %" PRIu16 " %" PRIu16 " with value %" PRId32 " : %s", type,
@@ -268,12 +268,12 @@
}
}
-static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint type, jint code,
- jint value) {
+static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jlong timestampMicros,
+ jint type, jint code, jint value) {
uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
if (d != nullptr) {
- d->injectEvent(static_cast<uint16_t>(type), static_cast<uint16_t>(code),
- static_cast<int32_t>(value));
+ d->injectEvent(std::chrono::microseconds(timestampMicros), static_cast<uint16_t>(type),
+ static_cast<uint16_t>(code), static_cast<int32_t>(value));
} else {
ALOGE("Could not inject event, Device* is null!");
}
@@ -330,7 +330,7 @@
"(Ljava/lang/String;IIIIIILjava/lang/String;"
"Lcom/android/commands/uinput/Device$DeviceCallback;)J",
reinterpret_cast<void*>(openUinputDevice)},
- {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
+ {"nativeInjectEvent", "(JJIII)V", reinterpret_cast<void*>(injectEvent)},
{"nativeConfigure", "(II[I)V", reinterpret_cast<void*>(configure)},
{"nativeSetAbsInfo", "(IILandroid/os/Parcel;)V", reinterpret_cast<void*>(setAbsInfo)},
{"nativeCloseUinputDevice", "(J)V", reinterpret_cast<void*>(closeUinputDevice)},
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h
index 9769a75..72c8647 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.h
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-#include <memory>
-#include <vector>
-
+#include <android-base/unique_fd.h>
#include <jni.h>
#include <linux/input.h>
-#include <android-base/unique_fd.h>
+#include <chrono>
+#include <memory>
+#include <vector>
+
#include "src/com/android/commands/uinput/InputAbsInfo.h"
namespace android {
@@ -53,7 +54,8 @@
virtual ~UinputDevice();
- void injectEvent(uint16_t type, uint16_t code, int32_t value);
+ void injectEvent(std::chrono::microseconds timestamp, uint16_t type, uint16_t code,
+ int32_t value);
int handleEvents(int events);
private:
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index 25d3a34..76ab475 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -55,7 +55,7 @@
private final SparseArray<InputAbsInfo> mAbsInfo;
private final OutputStream mOutputStream;
private final Object mCond = new Object();
- private long mTimeToSend;
+ private long mTimeToSendNanos;
static {
System.loadLibrary("uinputcommand_jni");
@@ -65,7 +65,8 @@
int productId, int versionId, int bus, int ffEffectsMax, String port,
DeviceCallback callback);
private static native void nativeCloseUinputDevice(long ptr);
- private static native void nativeInjectEvent(long ptr, int type, int code, int value);
+ private static native void nativeInjectEvent(long ptr, long timestampMicros, int type, int code,
+ int value);
private static native void nativeConfigure(int handle, int code, int[] configs);
private static native void nativeSetAbsInfo(int handle, int axisCode, Parcel axisParcel);
private static native int nativeGetEvdevEventTypeByLabel(String label);
@@ -101,27 +102,54 @@
}
mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
- mTimeToSend = SystemClock.uptimeMillis();
+ mTimeToSendNanos = SystemClock.uptimeNanos();
+ }
+
+ private long getTimeToSendMillis() {
+ // Since we can only specify delays in milliseconds but evemu timestamps are in
+ // microseconds, we have to round up the delays to avoid setting event timestamps
+ // which are in the future (which the kernel would silently reject and replace with
+ // the current time).
+ //
+ // This should be the same as (long) Math.ceil(mTimeToSendNanos / 1_000_000.0), except
+ // without the precision loss that comes from converting from long to double and back.
+ return mTimeToSendNanos / 1_000_000 + ((mTimeToSendNanos % 1_000_000 > 0) ? 1 : 0);
}
/**
* Inject uinput events to device
*
* @param events Array of raw uinput events.
+ * @param offsetMicros The difference in microseconds between the timestamps of the previous
+ * batch of events injected and this batch. If set to -1, the current
+ * timestamp will be used.
*/
- public void injectEvent(int[] events) {
+ public void injectEvent(int[] events, long offsetMicros) {
// if two messages are sent at identical time, they will be processed in order received
- Message msg = mHandler.obtainMessage(MSG_INJECT_EVENT, events);
- mHandler.sendMessageAtTime(msg, mTimeToSend);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = events;
+ args.argl1 = offsetMicros;
+ args.argl2 = mTimeToSendNanos;
+ Message msg = mHandler.obtainMessage(MSG_INJECT_EVENT, args);
+ mHandler.sendMessageAtTime(msg, getTimeToSendMillis());
}
/**
- * Impose a delay to the device for execution.
+ * Delay subsequent device activity by the specified amount of time.
*
- * @param delay Time to delay in unit of milliseconds.
+ * <p>Note that although the delay is specified in nanoseconds, due to limitations of {@link
+ * Handler}'s API, scheduling only occurs with millisecond precision. When scheduling an
+ * injection or sync, the time at which it is scheduled will be rounded up to the nearest
+ * millisecond. While this means that a particular injection cannot be scheduled precisely,
+ * rounding errors will not accumulate over time. For example, if five injections are scheduled
+ * with a delay of 1,200,000ns before each one, the total delay will be 6ms, as opposed to the
+ * 10ms it would have been if each individual delay had been rounded up (as {@link EvemuParser}
+ * would otherwise have to do to avoid sending timestamps that are in the future).
+ *
+ * @param delayNanos Time to delay in unit of nanoseconds.
*/
- public void addDelay(int delay) {
- mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
+ public void addDelayNanos(long delayNanos) {
+ mTimeToSendNanos += delayNanos;
}
/**
@@ -131,7 +159,8 @@
* @param syncToken The token for this sync command.
*/
public void syncEvent(String syncToken) {
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_SYNC_EVENT, syncToken), mTimeToSend);
+ mHandler.sendMessageAtTime(
+ mHandler.obtainMessage(MSG_SYNC_EVENT, syncToken), getTimeToSendMillis());
}
/**
@@ -140,7 +169,8 @@
*/
public void close() {
Message msg = mHandler.obtainMessage(MSG_CLOSE_UINPUT_DEVICE);
- mHandler.sendMessageAtTime(msg, Math.max(SystemClock.uptimeMillis(), mTimeToSend) + 1);
+ mHandler.sendMessageAtTime(
+ msg, Math.max(SystemClock.uptimeMillis(), getTimeToSendMillis()) + 1);
try {
synchronized (mCond) {
mCond.wait();
@@ -151,6 +181,7 @@
private class DeviceHandler extends Handler {
private long mPtr;
+ private long mLastInjectTimestampMicros = -1;
private int mBarrierToken;
DeviceHandler(Looper looper) {
@@ -160,7 +191,7 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_OPEN_UINPUT_DEVICE:
+ case MSG_OPEN_UINPUT_DEVICE: {
SomeArgs args = (SomeArgs) msg.obj;
String name = (String) args.arg1;
mPtr = nativeOpenUinputDevice(name, args.argi1 /* id */,
@@ -177,15 +208,44 @@
}
args.recycle();
break;
- case MSG_INJECT_EVENT:
- if (mPtr != 0) {
- int[] events = (int[]) msg.obj;
- for (int pos = 0; pos + 2 < events.length; pos += 3) {
- nativeInjectEvent(mPtr, events[pos], events[pos + 1], events[pos + 2]);
- }
+ }
+ case MSG_INJECT_EVENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ if (mPtr == 0) {
+ args.recycle();
+ break;
}
+ long offsetMicros = args.argl1;
+ if (mLastInjectTimestampMicros == -1 || offsetMicros == -1) {
+ // There's often a delay of a few milliseconds between the time specified to
+ // Handler.sendMessageAtTime and the handler actually being called, due to
+ // the way threads are scheduled. We don't take this into account when
+ // calling addDelayNanos between the first batch of event injections (when
+ // we set the "base timestamp" from which all others will be offset) and the
+ // second batch, meaning that the actual time between the handler calls for
+ // those batches may be less than the offset between their timestamps. When
+ // that happens, we would pass a timestamp for the second batch that's
+ // actually in the future. The kernel's uinput API rejects timestamps that
+ // are in the future and uses the current time instead, making the reported
+ // timestamps inconsistent with the recording we're replaying.
+ //
+ // To prevent this, we need to use the time we scheduled this first batch
+ // for (in microseconds, to avoid potential rounding up from
+ // getTimeToSendMillis), rather than the actual current time.
+ mLastInjectTimestampMicros = args.argl2 / 1000;
+ } else {
+ mLastInjectTimestampMicros += offsetMicros;
+ }
+
+ int[] events = (int[]) args.arg1;
+ for (int pos = 0; pos + 2 < events.length; pos += 3) {
+ nativeInjectEvent(mPtr, mLastInjectTimestampMicros, events[pos],
+ events[pos + 1], events[pos + 2]);
+ }
+ args.recycle();
break;
- case MSG_CLOSE_UINPUT_DEVICE:
+ }
+ case MSG_CLOSE_UINPUT_DEVICE: {
if (mPtr != 0) {
nativeCloseUinputDevice(mPtr);
getLooper().quitSafely();
@@ -198,11 +258,14 @@
mCond.notify();
}
break;
- case MSG_SYNC_EVENT:
+ }
+ case MSG_SYNC_EVENT: {
handleSyncEvent((String) msg.obj);
break;
- default:
+ }
+ default: {
throw new IllegalArgumentException("Unknown device message");
+ }
}
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
index 7652f24..da99162 100644
--- a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
@@ -44,7 +44,7 @@
* recordings, this will always be the same.
*/
private static final int DEVICE_ID = 1;
- private static final int REGISTRATION_DELAY_MILLIS = 500;
+ private static final int REGISTRATION_DELAY_NANOS = 500_000_000;
private static class CommentAwareReader {
private final LineNumberReader mReader;
@@ -152,7 +152,7 @@
final Event.Builder delayEb = new Event.Builder();
delayEb.setId(DEVICE_ID);
delayEb.setCommand(Event.Command.DELAY);
- delayEb.setDurationMillis(REGISTRATION_DELAY_MILLIS);
+ delayEb.setDurationNanos(REGISTRATION_DELAY_NANOS);
mQueuedEvents.add(delayEb.build());
}
@@ -175,7 +175,6 @@
throw new ParsingException(
"Invalid timestamp '" + parts[0] + "' (should contain a single '.')", mReader);
}
- // TODO(b/310958309): use timeMicros to set the timestamp on the event being sent.
final long timeMicros =
parseLong(timeParts[0], 10) * 1_000_000 + parseInt(timeParts[1], 10);
final Event.Builder eb = new Event.Builder();
@@ -192,21 +191,18 @@
return eb.build();
} else {
final long delayMicros = timeMicros - mLastEventTimeMicros;
- // The shortest delay supported by Handler.sendMessageAtTime (used for timings by the
- // Device class) is 1ms, so ignore time differences smaller than that.
- if (delayMicros < 1000) {
- mLastEventTimeMicros = timeMicros;
+ eb.setTimestampOffsetMicros(delayMicros);
+ if (delayMicros == 0) {
return eb.build();
- } else {
- // Send a delay now, and queue the actual event for the next call.
- mQueuedEvents.add(eb.build());
- mLastEventTimeMicros = timeMicros;
- final Event.Builder delayEb = new Event.Builder();
- delayEb.setId(DEVICE_ID);
- delayEb.setCommand(Event.Command.DELAY);
- delayEb.setDurationMillis((int) (delayMicros / 1000));
- return delayEb.build();
}
+ // Send a delay now, and queue the actual event for the next call.
+ mQueuedEvents.add(eb.build());
+ mLastEventTimeMicros = timeMicros;
+ final Event.Builder delayEb = new Event.Builder();
+ delayEb.setId(DEVICE_ID);
+ delayEb.setCommand(Event.Command.DELAY);
+ delayEb.setDurationNanos(delayMicros * 1000);
+ return delayEb.build();
}
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 0f16a27..9e7ee09 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -99,8 +99,9 @@
private int mVersionId;
private int mBusId;
private int[] mInjections;
+ private long mTimestampOffsetMicros = -1;
private SparseArray<int[]> mConfiguration;
- private int mDurationMillis;
+ private long mDurationNanos;
private int mFfEffectsMax = 0;
private String mInputPort;
private SparseArray<InputAbsInfo> mAbsInfo;
@@ -139,19 +140,28 @@
}
/**
+ * Returns the number of microseconds that should be added to the previous {@code INJECT}
+ * event's timestamp to produce the timestamp for this {@code INJECT} event. A value of -1
+ * indicates that the current timestamp should be used instead.
+ */
+ public long getTimestampOffsetMicros() {
+ return mTimestampOffsetMicros;
+ }
+
+ /**
* Returns a {@link SparseArray} describing the event codes that should be registered for the
* device. The keys are uinput ioctl codes (such as those returned from {@link
* UinputControlCode#getValue()}, while the values are arrays of event codes to be enabled with
* those ioctls. For example, key 101 (corresponding to {@link UinputControlCode#UI_SET_KEYBIT})
- * could have values 0x110 ({@code BTN_LEFT}, 0x111 ({@code BTN_RIGHT}), and 0x112
+ * could have values 0x110 ({@code BTN_LEFT}), 0x111 ({@code BTN_RIGHT}), and 0x112
* ({@code BTN_MIDDLE}).
*/
public SparseArray<int[]> getConfiguration() {
return mConfiguration;
}
- public int getDurationMillis() {
- return mDurationMillis;
+ public long getDurationNanos() {
+ return mDurationNanos;
}
public int getFfEffectsMax() {
@@ -182,7 +192,7 @@
+ ", busId=" + mBusId
+ ", events=" + Arrays.toString(mInjections)
+ ", configuration=" + mConfiguration
- + ", duration=" + mDurationMillis + "ms"
+ + ", duration=" + mDurationNanos + "ns"
+ ", ff_effects_max=" + mFfEffectsMax
+ ", port=" + mInputPort
+ "}";
@@ -211,6 +221,10 @@
mEvent.mInjections = events;
}
+ public void setTimestampOffsetMicros(long offsetMicros) {
+ mEvent.mTimestampOffsetMicros = offsetMicros;
+ }
+
/**
* Sets the event codes that should be registered with a {@code register} command.
*
@@ -237,8 +251,8 @@
mEvent.mBusId = busId;
}
- public void setDurationMillis(int durationMillis) {
- mEvent.mDurationMillis = durationMillis;
+ public void setDurationNanos(long durationNanos) {
+ mEvent.mDurationNanos = durationNanos;
}
public void setFfEffectsMax(int ffEffectsMax) {
@@ -271,7 +285,7 @@
}
}
case DELAY -> {
- if (mEvent.mDurationMillis <= 0) {
+ if (mEvent.mDurationNanos <= 0) {
throw new IllegalStateException("Delay has missing or invalid duration");
}
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
index ed3ff33..6994f0c 100644
--- a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -71,7 +71,8 @@
case "configuration" -> eb.setConfiguration(readConfiguration());
case "ff_effects_max" -> eb.setFfEffectsMax(readInt());
case "abs_info" -> eb.setAbsInfo(readAbsInfoArray());
- case "duration" -> eb.setDurationMillis(readInt());
+ // Duration is specified in milliseconds in the JSON-style format.
+ case "duration" -> eb.setDurationNanos(readInt() * 1_000_000L);
case "port" -> eb.setInputPort(mReader.nextString());
case "syncToken" -> eb.setSyncToken(mReader.nextString());
default -> mReader.skipValue();
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 04df279..760e981 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -134,8 +134,8 @@
switch (Objects.requireNonNull(e.getCommand())) {
case REGISTER ->
error("Device id=" + e.getId() + " is already registered. Ignoring event.");
- case INJECT -> d.injectEvent(e.getInjections());
- case DELAY -> d.addDelay(e.getDurationMillis());
+ case INJECT -> d.injectEvent(e.getInjections(), e.getTimestampOffsetMicros());
+ case DELAY -> d.addDelayNanos(e.getDurationNanos());
case SYNC -> d.syncEvent(e.getSyncToken());
}
}
diff --git a/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java b/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
index 06b0aac2..4dc4b68 100644
--- a/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
+++ b/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
@@ -183,16 +183,22 @@
}
private void assertInjectEvent(Event event, int eventType, int eventCode, int value) {
+ assertInjectEvent(event, eventType, eventCode, value, 0);
+ }
+
+ private void assertInjectEvent(Event event, int eventType, int eventCode, int value,
+ long timestampOffsetMicros) {
assertThat(event).isNotNull();
assertThat(event.getCommand()).isEqualTo(Event.Command.INJECT);
assertThat(event.getInjections()).asList()
.containsExactly(eventType, eventCode, value).inOrder();
+ assertThat(event.getTimestampOffsetMicros()).isEqualTo(timestampOffsetMicros);
}
- private void assertDelayEvent(Event event, int durationMillis) {
+ private void assertDelayEvent(Event event, int durationNanos) {
assertThat(event).isNotNull();
assertThat(event.getCommand()).isEqualTo(Event.Command.DELAY);
- assertThat(event.getDurationMillis()).isEqualTo(durationMillis);
+ assertThat(event.getDurationNanos()).isEqualTo(durationNanos);
}
@Test
@@ -207,7 +213,7 @@
EvemuParser parser = new EvemuParser(reader);
assertThat(parser.getNextEvent().getCommand()).isEqualTo(Event.Command.REGISTER);
assertThat(parser.getNextEvent().getCommand()).isEqualTo(Event.Command.DELAY);
- assertInjectEvent(parser.getNextEvent(), 0x2, 0x0, 1);
+ assertInjectEvent(parser.getNextEvent(), 0x2, 0x0, 1, -1);
assertInjectEvent(parser.getNextEvent(), 0x2, 0x1, -2);
assertInjectEvent(parser.getNextEvent(), 0x0, 0x0, 0);
}
@@ -228,17 +234,17 @@
assertThat(parser.getNextEvent().getCommand()).isEqualTo(Event.Command.REGISTER);
assertThat(parser.getNextEvent().getCommand()).isEqualTo(Event.Command.DELAY);
- assertInjectEvent(parser.getNextEvent(), 0x1, 0x15, 1);
+ assertInjectEvent(parser.getNextEvent(), 0x1, 0x15, 1, -1);
assertInjectEvent(parser.getNextEvent(), 0x0, 0x0, 0);
- assertDelayEvent(parser.getNextEvent(), 10);
+ assertDelayEvent(parser.getNextEvent(), 10_000_000);
- assertInjectEvent(parser.getNextEvent(), 0x1, 0x15, 0);
+ assertInjectEvent(parser.getNextEvent(), 0x1, 0x15, 0, 10_000);
assertInjectEvent(parser.getNextEvent(), 0x0, 0x0, 0);
- assertDelayEvent(parser.getNextEvent(), 1000);
+ assertDelayEvent(parser.getNextEvent(), 1_000_000_000);
- assertInjectEvent(parser.getNextEvent(), 0x1, 0x15, 1);
+ assertInjectEvent(parser.getNextEvent(), 0x1, 0x15, 1, 1_000_000);
assertInjectEvent(parser.getNextEvent(), 0x0, 0x0, 0);
}
@@ -477,7 +483,7 @@
assertThat(parser.getNextEvent().getCommand()).isEqualTo(Event.Command.DELAY);
- assertInjectEvent(parser.getNextEvent(), 0x3, 0x39, 0);
+ assertInjectEvent(parser.getNextEvent(), 0x3, 0x39, 0, -1);
assertInjectEvent(parser.getNextEvent(), 0x3, 0x35, 891);
assertInjectEvent(parser.getNextEvent(), 0x3, 0x36, 333);
assertInjectEvent(parser.getNextEvent(), 0x3, 0x3a, 56);
@@ -490,8 +496,8 @@
assertInjectEvent(parser.getNextEvent(), 0x3, 0x18, 56);
assertInjectEvent(parser.getNextEvent(), 0x0, 0x0, 0);
- assertDelayEvent(parser.getNextEvent(), 6);
+ assertDelayEvent(parser.getNextEvent(), 6_080_000);
- assertInjectEvent(parser.getNextEvent(), 0x3, 0x0035, 888);
+ assertInjectEvent(parser.getNextEvent(), 0x3, 0x0035, 888, 6_080);
}
}