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

Commit 44cc7867 authored by Harry Cutts's avatar Harry Cutts Committed by Android (Google) Code Review
Browse files

Merge changes from topic "revert-26154399-CXYXSETNUA" into main

* changes:
  Revert "uinput: use nanoseconds for delay durations"
  Revert "uinput: Specify timestamps when injecting events from evemu"
  Revert "uinput: delay from the end of the last delay"
parents 263343bf 5b70661c
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -154,8 +154,7 @@ will be unregistered. There is no explicit command for unregistering a device.

#### `delay`

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.
Add a delay to command processing

| Field         | Type          | Description                |
|:-------------:|:-------------:|:-------------------------- |
+9 −9
Original line number Diff line number Diff line
@@ -166,14 +166,14 @@ UinputDevice::~UinputDevice() {
    ::ioctl(mFd, UI_DEV_DESTROY);
}

void UinputDevice::injectEvent(std::chrono::microseconds timestamp, uint16_t type, uint16_t code,
                               int32_t value) {
void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
    struct input_event event = {};
    event.type = type;
    event.code = code;
    event.value = value;
    event.time.tv_sec = timestamp.count() / 1'000'000;
    event.time.tv_usec = timestamp.count() % 1'000'000;
    timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    TIMESPEC_TO_TIMEVAL(&event.time, &ts);

    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 closeUinputDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr)
    }
}

static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jlong timestampMicros,
                        jint type, jint code, jint value) {
static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint type, jint code,
                        jint value) {
    uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
    if (d != nullptr) {
        d->injectEvent(std::chrono::microseconds(timestampMicros), static_cast<uint16_t>(type),
                       static_cast<uint16_t>(code), static_cast<int32_t>(value));
        d->injectEvent(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 @@ static JNINativeMethod sMethods[] = {
         "(Ljava/lang/String;IIIIIILjava/lang/String;"
         "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
         reinterpret_cast<void*>(openUinputDevice)},
        {"nativeInjectEvent", "(JJIII)V", reinterpret_cast<void*>(injectEvent)},
        {"nativeInjectEvent", "(JIII)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)},
+5 −7
Original line number Diff line number Diff line
@@ -14,14 +14,13 @@
 * limitations under the License.
 */

#include <android-base/unique_fd.h>
#include <jni.h>
#include <linux/input.h>

#include <chrono>
#include <memory>
#include <vector>

#include <jni.h>
#include <linux/input.h>

#include <android-base/unique_fd.h>
#include "src/com/android/commands/uinput/InputAbsInfo.h"

namespace android {
@@ -54,8 +53,7 @@ public:

    virtual ~UinputDevice();

    void injectEvent(std::chrono::microseconds timestamp, uint16_t type, uint16_t code,
                     int32_t value);
    void injectEvent(uint16_t type, uint16_t code, int32_t value);
    int handleEvents(int events);

private:
+22 −85
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ public class Device {
    private final SparseArray<InputAbsInfo> mAbsInfo;
    private final OutputStream mOutputStream;
    private final Object mCond = new Object();
    private long mTimeToSendNanos;
    private long mTimeToSend;

    static {
        System.loadLibrary("uinputcommand_jni");
@@ -65,8 +65,7 @@ public class Device {
            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, long timestampMicros, int type, int code,
                                                 int value);
    private static native void nativeInjectEvent(long ptr, 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);
@@ -102,54 +101,27 @@ public class Device {
        }

        mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
        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);
        mTimeToSend = SystemClock.uptimeMillis();
    }

    /**
     * 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, long offsetMicros) {
    public void injectEvent(int[] events) {
        // if two messages are sent at identical time, they will be processed in order received
        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());
        Message msg = mHandler.obtainMessage(MSG_INJECT_EVENT, events);
        mHandler.sendMessageAtTime(msg, mTimeToSend);
    }

    /**
     * Delay subsequent device activity by the specified amount of time.
     * Impose a delay to the device for execution.
     *
     * <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.
     * @param delay  Time to delay in unit of milliseconds.
     */
    public void addDelayNanos(long delayNanos) {
        mTimeToSendNanos += delayNanos;
    public void addDelay(int delay) {
        mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
    }

    /**
@@ -159,8 +131,7 @@ public class Device {
     * @param syncToken  The token for this sync command.
     */
    public void syncEvent(String syncToken) {
        mHandler.sendMessageAtTime(
                mHandler.obtainMessage(MSG_SYNC_EVENT, syncToken), getTimeToSendMillis());
        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_SYNC_EVENT, syncToken), mTimeToSend);
    }

    /**
@@ -169,8 +140,7 @@ public class Device {
     */
    public void close() {
        Message msg = mHandler.obtainMessage(MSG_CLOSE_UINPUT_DEVICE);
        mHandler.sendMessageAtTime(
                msg, Math.max(SystemClock.uptimeMillis(), getTimeToSendMillis()) + 1);
        mHandler.sendMessageAtTime(msg, Math.max(SystemClock.uptimeMillis(), mTimeToSend) + 1);
        try {
            synchronized (mCond) {
                mCond.wait();
@@ -181,7 +151,6 @@ public class Device {

    private class DeviceHandler extends Handler {
        private long mPtr;
        private long mLastInjectTimestampMicros = -1;
        private int mBarrierToken;

        DeviceHandler(Looper looper) {
@@ -191,7 +160,7 @@ public class Device {
        @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 */,
@@ -208,44 +177,15 @@ public class Device {
                    }
                    args.recycle();
                    break;
                }
                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;
                case MSG_INJECT_EVENT:
                    if (mPtr != 0) {
                        int[] events = (int[]) msg.obj;
                        for (int pos = 0; pos + 2 < events.length; pos += 3) {
                        nativeInjectEvent(mPtr, mLastInjectTimestampMicros, events[pos],
                                events[pos + 1], events[pos + 2]);
                            nativeInjectEvent(mPtr, events[pos], events[pos + 1], events[pos + 2]);
                        }
                    args.recycle();
                    break;
                    }
                case MSG_CLOSE_UINPUT_DEVICE: {
                    break;
                case MSG_CLOSE_UINPUT_DEVICE:
                    if (mPtr != 0) {
                        nativeCloseUinputDevice(mPtr);
                        getLooper().quitSafely();
@@ -258,16 +198,13 @@ public class Device {
                        mCond.notify();
                    }
                    break;
                }
                case MSG_SYNC_EVENT: {
                case MSG_SYNC_EVENT:
                    handleSyncEvent((String) msg.obj);
                    break;
                }
                default: {
                default:
                    throw new IllegalArgumentException("Unknown device message");
            }
        }
        }

        public void pauseEvents() {
            mBarrierToken = getLooper().myQueue().postSyncBarrier();
+16 −12
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ public class EvemuParser implements EventParser {
     * recordings, this will always be the same.
     */
    private static final int DEVICE_ID = 1;
    private static final int REGISTRATION_DELAY_NANOS = 500_000_000;
    private static final int REGISTRATION_DELAY_MILLIS = 500;

    private static class CommentAwareReader {
        private final LineNumberReader mReader;
@@ -152,7 +152,7 @@ public class EvemuParser implements EventParser {
        final Event.Builder delayEb = new Event.Builder();
        delayEb.setId(DEVICE_ID);
        delayEb.setCommand(Event.Command.DELAY);
        delayEb.setDurationNanos(REGISTRATION_DELAY_NANOS);
        delayEb.setDurationMillis(REGISTRATION_DELAY_MILLIS);
        mQueuedEvents.add(delayEb.build());
    }

@@ -175,6 +175,7 @@ public class EvemuParser implements EventParser {
            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();
@@ -191,20 +192,23 @@ public class EvemuParser implements EventParser {
            return eb.build();
        } else {
            final long delayMicros = timeMicros - mLastEventTimeMicros;
            eb.setTimestampOffsetMicros(delayMicros);
            if (delayMicros == 0) {
            // 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;
                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.setDurationNanos(delayMicros * 1000);
                delayEb.setDurationMillis((int) (delayMicros / 1000));
                return delayEb.build();
            }
        }
    }

    private Event parseRegistrationEvent() throws IOException {
        // The registration details at the start of a recording are specified by a set of lines
Loading