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

Commit 6115d411 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Uinput: Add sync command to get a response when the command is processed

Bug: 294275314
Test: manual
Change-Id: Ie522ff44aa1cb7ceb7f21a23d6dd68efd1845f7c
parent 856c6577
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -128,7 +128,9 @@ stack, and `uinput` does not wait for that process to finish. Any commands sent
that time will be dropped. If you are controlling `uinput` by sending commands through standard
input from an app, you need to wait for [`onInputDeviceAdded`][onInputDeviceAdded] to be called on
an `InputDeviceListener` before issuing commands to the device. If you are passing a file to
`uinput`, add a `delay` after the `register` command to let registration complete.
`uinput`, add a `delay` after the `register` command to let registration complete. You can add a
`sync` in certain positions, like at the end of the file to get a response when all commands have
finished processing.

[onInputDeviceAdded]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html

@@ -187,6 +189,38 @@ keys would look like this:
}
```

### `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

The `getevent` utility can used to print out the key events for debugging purposes.
+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;
@@ -121,6 +122,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.
     *
@@ -174,6 +185,9 @@ public class Device {
                        mCond.notify();
                    }
                    break;
                case MSG_SYNC_EVENT:
                    handleSyncEvent((String) msg.obj);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown device message");
            }
@@ -187,6 +201,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 {
@@ -214,7 +240,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);
@@ -222,12 +248,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() {
@@ -238,6 +259,15 @@ public class Device {
        }
    }

    private void writeOutputObject(JSONObject json) {
        try {
            mOutputStream.write(json.toString().getBytes());
            mOutputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    static int getEvdevEventTypeByLabel(String label) {
        final var type = nativeGetEvdevEventTypeByLabel(label);
        if (type < 0) {
+20 −2
Original line number Diff line number Diff line
@@ -42,7 +42,8 @@ public class Event {
    enum Command {
        REGISTER("register"),
        DELAY("delay"),
        INJECT("inject");
        INJECT("inject"),
        SYNC("sync");

        final String mCommandName;

@@ -107,6 +108,7 @@ public class Event {
    private int mFfEffectsMax = 0;
    private String mInputport;
    private SparseArray<InputAbsInfo> mAbsInfo;
    private String mSyncToken;

    public int getId() {
        return mId;
@@ -156,6 +158,10 @@ public class Event {
        return mInputport;
    }

    public String getSyncToken() {
        return mSyncToken;
    }

    /**
     * Convert an event to String.
     */
@@ -236,6 +242,10 @@ 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");
@@ -259,6 +269,11 @@ public class Event {
                        throw new IllegalStateException("Inject command is missing injection data");
                    }
                }
                case SYNC -> {
                    if (mEvent.mSyncToken == null) {
                        throw new IllegalStateException("Sync command is missing sync token");
                    }
                }
            }
            return mEvent;
        }
@@ -325,6 +340,9 @@ public class Event {
                            case "port":
                                eb.setInputport(mReader.nextString());
                                break;
                            case "syncToken":
                                eb.setSyncToken(mReader.nextString());
                                break;
                            default:
                                mReader.skipValue();
                        }
+1 −0
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@ public class Uinput {
                    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());
        }
    }