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);
     }
 }