diff options
Diffstat (limited to 'cmds')
| -rw-r--r-- | cmds/uinput/README.md | 32 | ||||
| -rw-r--r-- | cmds/uinput/src/com/android/commands/uinput/Device.java | 44 | ||||
| -rw-r--r-- | cmds/uinput/src/com/android/commands/uinput/Event.java | 73 | ||||
| -rw-r--r-- | cmds/uinput/src/com/android/commands/uinput/Uinput.java | 32 |
4 files changed, 140 insertions, 41 deletions
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md index 47e1dad9ccd6..4d41dcd3712d 100644 --- a/cmds/uinput/README.md +++ b/cmds/uinput/README.md @@ -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 diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java index 732b33d60c15..2f362d7fabab 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Device.java +++ b/cmds/uinput/src/com/android/commands/uinput/Device.java @@ -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; @@ -119,6 +120,16 @@ public class Device { } /** + * 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); + } + } } diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java index 4b090f5a713c..91b4a7418c42 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Event.java +++ b/cmds/uinput/src/com/android/commands/uinput/Event.java @@ -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)) { - if (mEvent.mConfiguration == null) { - throw new IllegalStateException( - "Device registration is missing configuration"); + switch (mEvent.mCommand) { + case REGISTER -> { + if (mEvent.mConfiguration == null) { + throw new IllegalStateException( + "Device registration is missing configuration"); + } + } + case DELAY -> { + if (mEvent.mDuration <= 0) { + throw new IllegalStateException("Delay has missing or invalid duration"); + } } - } else if (COMMAND_DELAY.equals(mEvent.mCommand)) { - if (mEvent.mDuration <= 0) { - throw new IllegalStateException("Delay has missing or invalid duration"); + case INJECT -> { + if (mEvent.mInjections == null) { + throw new IllegalStateException("Inject command is missing injection data"); + } } - } else if (COMMAND_INJECT.equals(mEvent.mCommand)) { - if (mEvent.mInjections == null) { - throw new IllegalStateException("Inject command is missing injection data"); + case SYNC -> { + if (mEvent.mSyncToken == null) { + throw new IllegalStateException("Sync command is missing sync token"); + } } - } else { - throw new IllegalStateException("Unknown command " + mEvent.mCommand); } 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(); } diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java index 740578e878ac..47b7a354f330 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java +++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java @@ -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!"); } |