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

Commit 02bc2a69 authored by Uwais Ashraf's avatar Uwais Ashraf Committed by Android (Google) Code Review
Browse files

Merge changes from topic "stylus-replay-from-file" into udc-qpr-dev

* changes:
  Uinput: Add sync command to get a response when the command is processed
  Uinput: Use enums for commands
parents f8b2f9be 1c90b607
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -153,6 +153,38 @@ Example:
}
```

4. `sync`

A command used to get a response once the command is processed. When several `inject` and `delay`
commands are used in a row, the `sync` command can be used to track the progress of the command
queue.

|    Field    |  Type   | Description                                  |
|:-----------:|:-------:|:---------------------------------------------|
|    `id`     | integer | Device ID                                    |
|  `command`  | string  | Must be set to "sync"                        |
| `syncToken` | string  | The token used to identify this sync command |

Example:

```json5
{
  "id": 1,
  "command": "syncToken",
  "syncToken": "finished_injecting_events"
}
```

This command will result in the following response when it is processed:

```json5
{
  "id": 1,
  "result": "sync",
  "syncToken": "finished_injecting_events"
}
```

### Notes
1. As soon as EOF is reached (either in interactive mode, or in file mode),
the device that was created will be unregistered. There is no
+37 −7
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ public class Device {
    private static final int MSG_OPEN_UINPUT_DEVICE = 1;
    private static final int MSG_CLOSE_UINPUT_DEVICE = 2;
    private static final int MSG_INJECT_EVENT = 3;
    private static final int MSG_SYNC_EVENT = 4;

    private final int mId;
    private final HandlerThread mThread;
@@ -118,6 +119,16 @@ public class Device {
        mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
    }

    /**
     * Synchronize the uinput command queue by writing a sync response with the provided syncToken
     * to the output stream when this event is processed.
     *
     * @param syncToken  The token for this sync command.
     */
    public void syncEvent(String syncToken) {
        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_SYNC_EVENT, syncToken), mTimeToSend);
    }

    /**
     * Close an uinput device.
     *
@@ -171,6 +182,9 @@ public class Device {
                        mCond.notify();
                    }
                    break;
                case MSG_SYNC_EVENT:
                    handleSyncEvent((String) msg.obj);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown device message");
            }
@@ -184,6 +198,18 @@ public class Device {
            getLooper().myQueue().removeSyncBarrier(mBarrierToken);
            mBarrierToken = 0;
        }

        private void handleSyncEvent(String syncToken) {
            final JSONObject json = new JSONObject();
            try {
                json.put("reason", "sync");
                json.put("id", mId);
                json.put("syncToken", syncToken);
            } catch (JSONException e) {
                throw new RuntimeException("Could not create JSON object ", e);
            }
            writeOutputObject(json);
        }
    }

    private class DeviceCallback {
@@ -211,7 +237,7 @@ public class Device {
        }

        public void onDeviceVibrating(int value) {
            JSONObject json = new JSONObject();
            final JSONObject json = new JSONObject();
            try {
                json.put("reason", "vibrating");
                json.put("id", mId);
@@ -219,12 +245,7 @@ public class Device {
            } catch (JSONException e) {
                throw new RuntimeException("Could not create JSON object ", e);
            }
            try {
                mOutputStream.write(json.toString().getBytes());
                mOutputStream.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            writeOutputObject(json);
        }

        public void onDeviceError() {
@@ -234,4 +255,13 @@ public class Device {
            msg.sendToTarget();
        }
    }

    private void writeOutputObject(JSONObject json) {
        try {
            mOutputStream.write(json.toString().getBytes());
            mOutputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
+55 −18
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.IntStream;

import src.com.android.commands.uinput.InputAbsInfo;
@@ -36,11 +37,21 @@ import src.com.android.commands.uinput.InputAbsInfo;
public class Event {
    private static final String TAG = "UinputEvent";

    public static final String COMMAND_REGISTER = "register";
    public static final String COMMAND_DELAY = "delay";
    public static final String COMMAND_INJECT = "inject";
    private static final int ABS_CNT = 64;

    enum Command {
        REGISTER("register"),
        DELAY("delay"),
        INJECT("inject"),
        SYNC("sync");

        final String mCommandName;

        Command(String command) {
            mCommandName = command;
        }
    }

    // These constants come from "include/uapi/linux/input.h" in the kernel
    enum Bus {
        USB(0x03), BLUETOOTH(0x05);
@@ -56,7 +67,7 @@ public class Event {
    }

    private int mId;
    private String mCommand;
    private Command mCommand;
    private String mName;
    private int mVid;
    private int mPid;
@@ -67,12 +78,13 @@ public class Event {
    private int mFfEffectsMax = 0;
    private String mInputport;
    private SparseArray<InputAbsInfo> mAbsInfo;
    private String mSyncToken;

    public int getId() {
        return mId;
    }

    public String getCommand() {
    public Command getCommand() {
        return mCommand;
    }

@@ -116,6 +128,10 @@ public class Event {
        return mInputport;
    }

    public String getSyncToken() {
        return mSyncToken;
    }

    /**
     * Convert an event to String.
     */
@@ -146,7 +162,14 @@ public class Event {
        }

        private void setCommand(String command) {
            mEvent.mCommand = command;
            Objects.requireNonNull(command, "Command must not be null");
            for (Command cmd : Command.values()) {
                if (cmd.mCommandName.equals(command)) {
                    mEvent.mCommand = cmd;
                    return;
                }
            }
            throw new IllegalStateException("Unrecognized command: " + command);
        }

        public void setName(String name) {
@@ -189,27 +212,38 @@ public class Event {
            mEvent.mInputport = port;
        }

        public void setSyncToken(String syncToken) {
            mEvent.mSyncToken = Objects.requireNonNull(syncToken, "Sync token must not be null");
        }

        public Event build() {
            if (mEvent.mId == -1) {
                throw new IllegalStateException("No event id");
            } else if (mEvent.mCommand == null) {
                throw new IllegalStateException("Event does not contain a command");
            }
            if (COMMAND_REGISTER.equals(mEvent.mCommand)) {
            switch (mEvent.mCommand) {
                case REGISTER -> {
                    if (mEvent.mConfiguration == null) {
                        throw new IllegalStateException(
                                "Device registration is missing configuration");
                    }
            } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
                }
                case DELAY -> {
                    if (mEvent.mDuration <= 0) {
                        throw new IllegalStateException("Delay has missing or invalid duration");
                    }
            } else if (COMMAND_INJECT.equals(mEvent.mCommand)) {
                }
                case INJECT -> {
                    if (mEvent.mInjections == null) {
                        throw new IllegalStateException("Inject command is missing injection data");
                    }
            } else {
                throw new IllegalStateException("Unknown command " + mEvent.mCommand);
                }
                case SYNC -> {
                    if (mEvent.mSyncToken == null) {
                        throw new IllegalStateException("Sync command is missing sync token");
                    }
                }
            }
            return mEvent;
        }
@@ -276,6 +310,9 @@ public class Event {
                            case "port":
                                eb.setInputport(mReader.nextString());
                                break;
                            case "syncToken":
                                eb.setSyncToken(mReader.nextString());
                                break;
                            default:
                                mReader.skipValue();
                        }
+16 −16
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Objects;

/**
 * Uinput class encapsulates execution of "uinput" command. It parses the provided input stream
@@ -96,28 +97,27 @@ public class Uinput {

    private void process(Event e) {
        final int index = mDevices.indexOfKey(e.getId());
        if (index >= 0) {
            Device d = mDevices.valueAt(index);
            if (Event.COMMAND_DELAY.equals(e.getCommand())) {
                d.addDelay(e.getDuration());
            } else if (Event.COMMAND_INJECT.equals(e.getCommand())) {
                d.injectEvent(e.getInjections());
            } else {
                if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
                    error("Device id=" + e.getId() + " is already registered. Ignoring event.");
                } else {
                    error("Unknown command \"" + e.getCommand() + "\". Ignoring event.");
                }
        if (index < 0) {
            if (e.getCommand() != Event.Command.REGISTER) {
                Log.e(TAG, "Unknown device id specified. Ignoring event.");
                return;
            }
        } else if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
            registerDevice(e);
        } else {
            Log.e(TAG, "Unknown device id specified. Ignoring event.");
            return;
        }

        final Device d = mDevices.valueAt(index);
        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.getDuration());
            case SYNC -> d.syncEvent(e.getSyncToken());
        }
    }

    private void registerDevice(Event e) {
        if (!Event.COMMAND_REGISTER.equals(e.getCommand())) {
        if (!Event.Command.REGISTER.equals(e.getCommand())) {
            throw new IllegalStateException(
                    "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
        }