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

Bug: 291881033
Bug: 294275314
Test: manual
Change-Id: Ie522ff44aa1cb7ceb7f21a23d6dd68efd1845f7c
Merged-In: Ie522ff44aa1cb7ceb7f21a23d6dd68efd1845f7c
This commit is contained in:
Prabir Pradhan
2023-08-02 22:58:42 +00:00
parent 895fe8613c
commit 1c90b607eb
4 changed files with 90 additions and 9 deletions

View File

@@ -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

View File

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

View File

@@ -42,7 +42,8 @@ public class Event {
enum Command {
REGISTER("register"),
DELAY("delay"),
INJECT("inject");
INJECT("inject"),
SYNC("sync");
final String mCommandName;
@@ -77,6 +78,7 @@ public class Event {
private int mFfEffectsMax = 0;
private String mInputport;
private SparseArray<InputAbsInfo> mAbsInfo;
private String mSyncToken;
public int getId() {
return mId;
@@ -126,6 +128,10 @@ public class Event {
return mInputport;
}
public String getSyncToken() {
return mSyncToken;
}
/**
* Convert an event to String.
*/
@@ -206,6 +212,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");
@@ -225,10 +235,15 @@ public class Event {
}
}
case INJECT -> {
if (mEvent.mInjections == null) {
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");
}
}
}
return mEvent;
}
@@ -295,6 +310,9 @@ public class Event {
case "port":
eb.setInputport(mReader.nextString());
break;
case "syncToken":
eb.setSyncToken(mReader.nextString());
break;
default:
mReader.skipValue();
}

View File

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