summaryrefslogtreecommitdiff
path: root/cmds
diff options
context:
space:
mode:
authorHarry Cutts <hcutts@google.com>2023-11-14 14:56:07 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-11-14 14:56:07 +0000
commitffa80d0a853111efb9521a551a9496385aa5a9c6 (patch)
treebd22724d0f98fbd1c931a174233fc97298d2beea /cmds
parent5cfe5d02a59a0c6ecd0b906a346b77c3dbf0a114 (diff)
parent822e9c80deba25a93846ab539622322613eaab0b (diff)
downloadbase-ffa80d0a853111efb9521a551a9496385aa5a9c6.tar.gz
Merge changes I9b9ae897,Iad98088b,Id6c7536a into main
* changes: uinput: improve Event.Builder API uinput: use enum valueOf method uinput: move Event.Reader to its own file
Diffstat (limited to 'cmds')
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Event.java467
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java333
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Uinput.java8
3 files changed, 399 insertions, 409 deletions
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9d8f1f400950..01486c0708ce 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -16,19 +16,10 @@
package com.android.commands.uinput;
-import android.util.JsonReader;
-import android.util.JsonToken;
-import android.util.Log;
import android.util.SparseArray;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.IntStream;
import src.com.android.commands.uinput.InputAbsInfo;
@@ -40,46 +31,44 @@ public class Event {
private static final String TAG = "UinputEvent";
enum Command {
- REGISTER("register"),
- DELAY("delay"),
- INJECT("inject"),
- SYNC("sync");
+ REGISTER,
+ DELAY,
+ INJECT,
+ SYNC,
+ }
- final String mCommandName;
+ // Constants representing evdev event types, from include/uapi/linux/input-event-codes.h in the
+ // kernel.
+ public static final int EV_KEY = 0x01;
+ public static final int EV_REL = 0x02;
+ public static final int EV_ABS = 0x03;
+ public static final int EV_MSC = 0x04;
+ public static final int EV_SW = 0x05;
+ public static final int EV_LED = 0x11;
+ public static final int EV_SND = 0x12;
+ public static final int EV_FF = 0x15;
+
+ public enum UinputControlCode {
+ UI_SET_EVBIT(100),
+ UI_SET_KEYBIT(101),
+ UI_SET_RELBIT(102),
+ UI_SET_ABSBIT(103),
+ UI_SET_MSCBIT(104),
+ UI_SET_LEDBIT(105),
+ UI_SET_SNDBIT(106),
+ UI_SET_FFBIT(107),
+ UI_SET_SWBIT(109),
+ UI_SET_PROPBIT(110);
- Command(String command) {
- mCommandName = command;
- }
- }
+ private final int mValue;
- private static final int EV_KEY = 0x01;
- private static final int EV_REL = 0x02;
- private static final int EV_ABS = 0x03;
- private static final int EV_MSC = 0x04;
- private static final int EV_SW = 0x05;
- private static final int EV_LED = 0x11;
- private static final int EV_SND = 0x12;
- private static final int EV_FF = 0x15;
-
- private enum UinputControlCode {
- UI_SET_EVBIT("UI_SET_EVBIT", 100),
- UI_SET_KEYBIT("UI_SET_KEYBIT", 101),
- UI_SET_RELBIT("UI_SET_RELBIT", 102),
- UI_SET_ABSBIT("UI_SET_ABSBIT", 103),
- UI_SET_MSCBIT("UI_SET_MSCBIT", 104),
- UI_SET_LEDBIT("UI_SET_LEDBIT", 105),
- UI_SET_SNDBIT("UI_SET_SNDBIT", 106),
- UI_SET_FFBIT("UI_SET_FFBIT", 107),
- UI_SET_SWBIT("UI_SET_SWBIT", 109),
- UI_SET_PROPBIT("UI_SET_PROPBIT", 110);
-
- final String mName;
- final int mValue;
-
- UinputControlCode(String name, int value) {
- this.mName = name;
+ UinputControlCode(int value) {
this.mValue = value;
}
+
+ public int getValue() {
+ return mValue;
+ }
}
// These constants come from "include/uapi/linux/input.h" in the kernel
@@ -104,9 +93,9 @@ public class Event {
private Bus mBus;
private int[] mInjections;
private SparseArray<int[]> mConfiguration;
- private int mDuration;
+ private int mDurationMillis;
private int mFfEffectsMax = 0;
- private String mInputport;
+ private String mInputPort;
private SparseArray<InputAbsInfo> mAbsInfo;
private String mSyncToken;
@@ -138,12 +127,20 @@ public class Event {
return mInjections;
}
+ /**
+ * Returns a {@link SparseArray} describing the event codes that should be registered for the
+ * device. The keys are uinput ioctl codes (such as those returned from {@link
+ * UinputControlCode#getValue()}, while the values are arrays of event codes to be enabled with
+ * those ioctls. For example, key 101 (corresponding to {@link UinputControlCode#UI_SET_KEYBIT})
+ * could have values 0x110 ({@code BTN_LEFT}, 0x111 ({@code BTN_RIGHT}), and 0x112
+ * ({@code BTN_MIDDLE}).
+ */
public SparseArray<int[]> getConfiguration() {
return mConfiguration;
}
- public int getDuration() {
- return mDuration;
+ public int getDurationMillis() {
+ return mDurationMillis;
}
public int getFfEffectsMax() {
@@ -155,7 +152,7 @@ public class Event {
}
public String getPort() {
- return mInputport;
+ return mInputPort;
}
public String getSyncToken() {
@@ -174,13 +171,13 @@ public class Event {
+ ", bus=" + mBus
+ ", events=" + Arrays.toString(mInjections)
+ ", configuration=" + mConfiguration
- + ", duration=" + mDuration
+ + ", duration=" + mDurationMillis + "ms"
+ ", ff_effects_max=" + mFfEffectsMax
- + ", port=" + mInputport
+ + ", port=" + mInputPort
+ "}";
}
- private static class Builder {
+ public static class Builder {
private Event mEvent;
Builder() {
@@ -191,15 +188,8 @@ public class Event {
mEvent.mId = id;
}
- private void setCommand(String 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 setCommand(String command) {
+ mEvent.mCommand = Command.valueOf(command.toUpperCase());
}
public void setName(String name) {
@@ -210,6 +200,12 @@ public class Event {
mEvent.mInjections = events;
}
+ /**
+ * Sets the event codes that should be registered with a {@code register} command.
+ *
+ * @param configuration An array of ioctls and event codes, as described at
+ * {@link Event#getConfiguration()}.
+ */
public void setConfiguration(SparseArray<int[]> configuration) {
mEvent.mConfiguration = configuration;
}
@@ -226,8 +222,8 @@ public class Event {
mEvent.mBus = bus;
}
- public void setDuration(int duration) {
- mEvent.mDuration = duration;
+ public void setDurationMillis(int durationMillis) {
+ mEvent.mDurationMillis = durationMillis;
}
public void setFfEffectsMax(int ffEffectsMax) {
@@ -238,8 +234,8 @@ public class Event {
mEvent.mAbsInfo = absInfo;
}
- public void setInputport(String port) {
- mEvent.mInputport = port;
+ public void setInputPort(String port) {
+ mEvent.mInputPort = port;
}
public void setSyncToken(String syncToken) {
@@ -260,7 +256,7 @@ public class Event {
}
}
case DELAY -> {
- if (mEvent.mDuration <= 0) {
+ if (mEvent.mDurationMillis <= 0) {
throw new IllegalStateException("Delay has missing or invalid duration");
}
}
@@ -278,343 +274,4 @@ public class Event {
return mEvent;
}
}
-
- /**
- * A class that parses the JSON event format from an input stream to build device events.
- */
- public static class Reader {
- private JsonReader mReader;
-
- public Reader(InputStreamReader in) {
- mReader = new JsonReader(in);
- mReader.setLenient(true);
- }
-
- /**
- * Get next event entry from JSON file reader.
- */
- public Event getNextEvent() throws IOException {
- Event e = null;
- while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
- Event.Builder eb = new Event.Builder();
- try {
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "id":
- eb.setId(readInt());
- break;
- case "command":
- eb.setCommand(mReader.nextString());
- break;
- case "name":
- eb.setName(mReader.nextString());
- break;
- case "vid":
- eb.setVid(readInt());
- break;
- case "pid":
- eb.setPid(readInt());
- break;
- case "bus":
- eb.setBus(readBus());
- break;
- case "events":
- int[] injections = readInjectedEvents().stream()
- .mapToInt(Integer::intValue).toArray();
- eb.setInjections(injections);
- break;
- case "configuration":
- eb.setConfiguration(readConfiguration());
- break;
- case "ff_effects_max":
- eb.setFfEffectsMax(readInt());
- break;
- case "abs_info":
- eb.setAbsInfo(readAbsInfoArray());
- break;
- case "duration":
- eb.setDuration(readInt());
- break;
- case "port":
- eb.setInputport(mReader.nextString());
- break;
- case "syncToken":
- eb.setSyncToken(mReader.nextString());
- break;
- default:
- mReader.skipValue();
- }
- }
- mReader.endObject();
- } catch (IllegalStateException ex) {
- error("Error reading in object, ignoring.", ex);
- consumeRemainingElements();
- mReader.endObject();
- continue;
- }
- e = eb.build();
- }
-
- return e;
- }
-
- private ArrayList<Integer> readInjectedEvents() throws IOException {
- ArrayList<Integer> data = new ArrayList<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- // Read events in groups of three, because we expect an event type, event code,
- // and event value.
- final int type = readEvdevEventType();
- data.add(type);
- data.add(readEvdevEventCode(type));
- data.add(readInt());
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return data;
- }
-
- private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
- switch (mReader.peek()) {
- case NUMBER: {
- return mReader.nextInt();
- }
- case STRING: {
- final var str = mReader.nextString();
- try {
- // Attempt to first parse the value as an int.
- return Integer.decode(str);
- } catch (NumberFormatException e) {
- // Then fall back to the supplied function.
- return stringToInt.apply(str);
- }
- }
- default: {
- throw new IllegalStateException(
- "Encountered malformed data. Expected int or string.");
- }
- }
- }
-
- private int readInt() throws IOException {
- return readValueAsInt((str) -> {
- throw new IllegalStateException("Encountered malformed data. Expected int.");
- });
- }
-
- private Bus readBus() throws IOException {
- String val = mReader.nextString();
- return Bus.valueOf(val.toUpperCase());
- }
-
- private SparseArray<int[]> readConfiguration()
- throws IllegalStateException, IOException {
- SparseArray<int[]> configuration = new SparseArray<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- UinputControlCode controlCode = null;
- IntStream data = null;
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "type":
- controlCode = readUinputControlCode();
- break;
- case "data":
- Objects.requireNonNull(controlCode,
- "Configuration 'type' must be specified before 'data'.");
- data = readDataForControlCode(controlCode)
- .stream().mapToInt(Integer::intValue);
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException(
- "Invalid key in device configuration: " + name);
- }
- }
- mReader.endObject();
- if (controlCode != null && data != null) {
- final int[] existing = configuration.get(controlCode.mValue);
- configuration.put(controlCode.mValue, existing == null ? data.toArray()
- : IntStream.concat(IntStream.of(existing), data).toArray());
- }
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return configuration;
- }
-
- private UinputControlCode readUinputControlCode() throws IOException {
- var code = readValueAsInt((controlTypeStr) -> {
- for (UinputControlCode controlCode : UinputControlCode.values()) {
- if (controlCode.mName.equals(controlTypeStr)) {
- return controlCode.mValue;
- }
- }
- return -1;
- });
- for (UinputControlCode controlCode : UinputControlCode.values()) {
- if (controlCode.mValue == code) {
- return controlCode;
- }
- }
- return null;
- }
-
- private List<Integer> readDataForControlCode(
- UinputControlCode controlCode) throws IOException {
- return switch (controlCode) {
- case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
- case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_KEY));
- case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_REL));
- case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_ABS));
- case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_MSC));
- case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_LED));
- case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SND));
- case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_FF));
- case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SW));
- case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
- };
- }
-
- interface IntValueReader {
- int readNextValue() throws IOException;
- }
-
- private ArrayList<Integer> readArrayAsInts(
- IntValueReader nextValueReader) throws IOException {
- ArrayList<Integer> data = new ArrayList<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- data.add(nextValueReader.readNextValue());
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return data;
- }
-
- private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
- InputAbsInfo absInfo = new InputAbsInfo();
- try {
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "value":
- absInfo.value = readInt();
- break;
- case "minimum":
- absInfo.minimum = readInt();
- break;
- case "maximum":
- absInfo.maximum = readInt();
- break;
- case "fuzz":
- absInfo.fuzz = readInt();
- break;
- case "flat":
- absInfo.flat = readInt();
- break;
- case "resolution":
- absInfo.resolution = readInt();
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Invalid key in abs info: " + name);
- }
- }
- mReader.endObject();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return absInfo;
- }
-
- private SparseArray<InputAbsInfo> readAbsInfoArray()
- throws IllegalStateException, IOException {
- SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- int type = 0;
- InputAbsInfo absInfo = null;
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "code":
- type = readEvdevEventCode(EV_ABS);
- break;
- case "info":
- absInfo = readAbsInfo();
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Invalid key in abs info array: "
- + name);
- }
- }
- mReader.endObject();
- if (absInfo != null) {
- infoArray.put(type, absInfo);
- }
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return infoArray;
- }
-
- private int readEvdevEventType() throws IOException {
- return readValueAsInt(Device::getEvdevEventTypeByLabel);
- }
-
- private int readEvdevEventCode(int type) throws IOException {
- return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
- }
-
- private int readEvdevInputProp() throws IOException {
- return readValueAsInt(Device::getEvdevInputPropByLabel);
- }
-
- private void consumeRemainingElements() throws IOException {
- while (mReader.hasNext()) {
- mReader.skipValue();
- }
- }
- }
-
- private static void error(String msg, Exception e) {
- System.out.println(msg);
- Log.e(TAG, msg);
- if (e != null) {
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
new file mode 100644
index 000000000000..53d0be819dae
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.uinput;
+
+import android.util.JsonReader;
+import android.util.JsonToken;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * A class that parses the JSON-like event format described in the README to build {@link Event}s.
+ */
+public class JsonStyleParser {
+ private static final String TAG = "UinputJsonStyleParser";
+
+ private JsonReader mReader;
+
+ public JsonStyleParser(InputStreamReader in) {
+ mReader = new JsonReader(in);
+ mReader.setLenient(true);
+ }
+
+ /**
+ * Gets the next event entry from the JSON file.
+ */
+ public Event getNextEvent() throws IOException {
+ Event e = null;
+ while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
+ Event.Builder eb = new Event.Builder();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "id" -> eb.setId(readInt());
+ case "command" -> eb.setCommand(mReader.nextString());
+ case "name" -> eb.setName(mReader.nextString());
+ case "vid" -> eb.setVid(readInt());
+ case "pid" -> eb.setPid(readInt());
+ case "bus" -> eb.setBus(readBus());
+ case "events" -> {
+ int[] injections = readInjectedEvents().stream()
+ .mapToInt(Integer::intValue).toArray();
+ eb.setInjections(injections);
+ }
+ case "configuration" -> eb.setConfiguration(readConfiguration());
+ case "ff_effects_max" -> eb.setFfEffectsMax(readInt());
+ case "abs_info" -> eb.setAbsInfo(readAbsInfoArray());
+ case "duration" -> eb.setDurationMillis(readInt());
+ case "port" -> eb.setInputPort(mReader.nextString());
+ case "syncToken" -> eb.setSyncToken(mReader.nextString());
+ default -> mReader.skipValue();
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException ex) {
+ error("Error reading in object, ignoring.", ex);
+ consumeRemainingElements();
+ mReader.endObject();
+ continue;
+ }
+ e = eb.build();
+ }
+
+ return e;
+ }
+
+ private ArrayList<Integer> readInjectedEvents() throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ // Read events in groups of three, because we expect an event type, event code,
+ // and event value.
+ final int type = readEvdevEventType();
+ data.add(type);
+ data.add(readEvdevEventCode(type));
+ data.add(readInt());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
+ switch (mReader.peek()) {
+ case NUMBER: {
+ return mReader.nextInt();
+ }
+ case STRING: {
+ final var str = mReader.nextString();
+ try {
+ // Attempt to first parse the value as an int.
+ return Integer.decode(str);
+ } catch (NumberFormatException e) {
+ // Then fall back to the supplied function.
+ return stringToInt.apply(str);
+ }
+ }
+ default: {
+ throw new IllegalStateException(
+ "Encountered malformed data. Expected int or string.");
+ }
+ }
+ }
+
+ private int readInt() throws IOException {
+ return readValueAsInt((str) -> {
+ throw new IllegalStateException("Encountered malformed data. Expected int.");
+ });
+ }
+
+ private Event.Bus readBus() throws IOException {
+ String val = mReader.nextString();
+ return Event.Bus.valueOf(val.toUpperCase());
+ }
+
+ private SparseArray<int[]> readConfiguration()
+ throws IllegalStateException, IOException {
+ SparseArray<int[]> configuration = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ Event.UinputControlCode controlCode = null;
+ IntStream data = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "type":
+ controlCode = readUinputControlCode();
+ break;
+ case "data":
+ Objects.requireNonNull(controlCode,
+ "Configuration 'type' must be specified before 'data'.");
+ data = readDataForControlCode(controlCode)
+ .stream().mapToInt(Integer::intValue);
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException(
+ "Invalid key in device configuration: " + name);
+ }
+ }
+ mReader.endObject();
+ if (controlCode != null && data != null) {
+ final int[] existing = configuration.get(controlCode.getValue());
+ configuration.put(controlCode.getValue(), existing == null ? data.toArray()
+ : IntStream.concat(IntStream.of(existing), data).toArray());
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return configuration;
+ }
+
+ private Event.UinputControlCode readUinputControlCode() throws IOException {
+ var code = readValueAsInt((controlTypeStr) -> {
+ try {
+ return Event.UinputControlCode.valueOf(controlTypeStr).getValue();
+ } catch (IllegalArgumentException ex) {
+ return -1;
+ }
+ });
+ for (Event.UinputControlCode controlCode : Event.UinputControlCode.values()) {
+ if (controlCode.getValue() == code) {
+ return controlCode;
+ }
+ }
+ return null;
+ }
+
+ private List<Integer> readDataForControlCode(
+ Event.UinputControlCode controlCode) throws IOException {
+ return switch (controlCode) {
+ case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
+ case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_KEY));
+ case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_REL));
+ case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_ABS));
+ case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_MSC));
+ case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_LED));
+ case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_SND));
+ case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_FF));
+ case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_SW));
+ case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
+ };
+ }
+
+ interface IntValueReader {
+ int readNextValue() throws IOException;
+ }
+
+ private ArrayList<Integer> readArrayAsInts(
+ IntValueReader nextValueReader) throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ data.add(nextValueReader.readNextValue());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
+ InputAbsInfo absInfo = new InputAbsInfo();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "value" -> absInfo.value = readInt();
+ case "minimum" -> absInfo.minimum = readInt();
+ case "maximum" -> absInfo.maximum = readInt();
+ case "fuzz" -> absInfo.fuzz = readInt();
+ case "flat" -> absInfo.flat = readInt();
+ case "resolution" -> absInfo.resolution = readInt();
+ default -> {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info: " + name);
+ }
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return absInfo;
+ }
+
+ private SparseArray<InputAbsInfo> readAbsInfoArray()
+ throws IllegalStateException, IOException {
+ SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ int type = 0;
+ InputAbsInfo absInfo = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "code" -> type = readEvdevEventCode(Event.EV_ABS);
+ case "info" -> absInfo = readAbsInfo();
+ default -> {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info array: "
+ + name);
+ }
+ }
+ }
+ mReader.endObject();
+ if (absInfo != null) {
+ infoArray.put(type, absInfo);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return infoArray;
+ }
+
+ private int readEvdevEventType() throws IOException {
+ return readValueAsInt(Device::getEvdevEventTypeByLabel);
+ }
+
+ private int readEvdevEventCode(int type) throws IOException {
+ return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
+ }
+
+ private int readEvdevInputProp() throws IOException {
+ return readValueAsInt(Device::getEvdevInputPropByLabel);
+ }
+
+ private void consumeRemainingElements() throws IOException {
+ while (mReader.hasNext()) {
+ mReader.skipValue();
+ }
+ }
+
+ private static void error(String msg, Exception e) {
+ System.out.println(msg);
+ Log.e(TAG, msg);
+ if (e != null) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 47b7a354f330..fe76abb9861d 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -35,7 +35,7 @@ import java.util.Objects;
public class Uinput {
private static final String TAG = "UINPUT";
- private final Event.Reader mReader;
+ private final JsonStyleParser mParser;
private final SparseArray<Device> mDevices;
private static void usage() {
@@ -74,7 +74,7 @@ public class Uinput {
private Uinput(InputStream in) {
mDevices = new SparseArray<Device>();
try {
- mReader = new Event.Reader(new InputStreamReader(in, "UTF-8"));
+ mParser = new JsonStyleParser(new InputStreamReader(in, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
@@ -83,7 +83,7 @@ public class Uinput {
private void run() {
try {
Event e = null;
- while ((e = mReader.getNextEvent()) != null) {
+ while ((e = mParser.getNextEvent()) != null) {
process(e);
}
} catch (IOException ex) {
@@ -111,7 +111,7 @@ public class Uinput {
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 DELAY -> d.addDelay(e.getDurationMillis());
case SYNC -> d.syncEvent(e.getSyncToken());
}
}