diff options
author | Harry Cutts <hcutts@google.com> | 2023-11-14 14:56:07 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2023-11-14 14:56:07 +0000 |
commit | ffa80d0a853111efb9521a551a9496385aa5a9c6 (patch) | |
tree | bd22724d0f98fbd1c931a174233fc97298d2beea /cmds | |
parent | 5cfe5d02a59a0c6ecd0b906a346b77c3dbf0a114 (diff) | |
parent | 822e9c80deba25a93846ab539622322613eaab0b (diff) | |
download | base-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')
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()); } } |