summaryrefslogtreecommitdiff
path: root/cmds/hid/src
diff options
context:
space:
mode:
author Siarhei Vishniakou <svv@google.com> 2019-12-06 22:43:28 -0800
committer Siarhei Vishniakou <svv@google.com> 2019-12-10 13:47:08 -0800
commitf6081119aa48d1d54ec5bd2d0e2ce336af99f97b (patch)
treebfc0144d6cee118fee3cc92aa3619d62986fd2b8 /cmds/hid/src
parentc490e7acdeaf682275cb687684e82ac984ab4a61 (diff)
Add UHID_OUTPUT handling to hid command
Some joysticks may send UHID_OUTPUT requests during probe. They expect to receive a report in response. Provide a facility to specify the expected outputs from the driver, and a way to respond to each of these requests. Typically, this would be something like "driver: please use full report mode", "joystick: ACK". If the ACK is not received by the driver, the probe would fail, and input device would never get registered. Bug: 135136477 Test: atest NintendoSwitchProTest Change-Id: Ic2c7a73d3d4bf759a1a104324687cd01646f256e
Diffstat (limited to 'cmds/hid/src')
-rw-r--r--cmds/hid/src/com/android/commands/hid/Device.java37
-rw-r--r--cmds/hid/src/com/android/commands/hid/Event.java75
-rw-r--r--cmds/hid/src/com/android/commands/hid/Hid.java9
3 files changed, 102 insertions, 19 deletions
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 616d411ef7bb..874604ceb5e4 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -20,13 +20,16 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.MessageQueue;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Map;
+
public class Device {
private static final String TAG = "HidDevice";
@@ -40,6 +43,7 @@ public class Device {
private final DeviceHandler mHandler;
// mFeatureReports is limited to 256 entries, because the report number is 8-bit
private final SparseArray<byte[]> mFeatureReports;
+ private final Map<ByteBuffer, byte[]> mOutputs;
private long mTimeToSend;
private final Object mCond = new Object();
@@ -55,12 +59,13 @@ public class Device {
private static native void nativeCloseDevice(long ptr);
public Device(int id, String name, int vid, int pid, byte[] descriptor,
- byte[] report, SparseArray<byte[]> featureReports) {
+ byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
mThread.start();
mHandler = new DeviceHandler(mThread.getLooper());
mFeatureReports = featureReports;
+ mOutputs = outputs;
SomeArgs args = SomeArgs.obtain();
args.argi1 = id;
args.argi2 = vid;
@@ -160,6 +165,11 @@ public class Device {
}
public void onDeviceGetReport(int requestId, int reportId) {
+ if (mFeatureReports == null) {
+ Log.e(TAG, "Received GET_REPORT request for reportId=" + reportId
+ + ", but 'feature_reports' section is not found");
+ return;
+ }
byte[] report = mFeatureReports.get(reportId);
if (report == null) {
@@ -176,6 +186,29 @@ public class Device {
mHandler.sendMessageAtTime(msg, mTimeToSend);
}
+ // native callback
+ public void onDeviceOutput(byte[] data) {
+ if (mOutputs == null) {
+ Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found");
+ return;
+ }
+ byte[] response = mOutputs.get(ByteBuffer.wrap(data));
+ if (response == null) {
+ Log.i(TAG,
+ "Requested response for output " + Arrays.toString(data) + " is not found");
+ return;
+ }
+
+ Message msg;
+ msg = mHandler.obtainMessage(MSG_SEND_REPORT, response);
+
+ // Message is set to asynchronous so it won't be blocked by synchronization
+ // barrier during UHID_OPEN. This is necessary for drivers that do
+ // UHID_OUTPUT requests during probe, and expect a response right away.
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, mTimeToSend);
+ }
+
public void onDeviceError() {
Log.e(TAG, "Device error occurred, closing /dev/uhid");
Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE);
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 746e37289076..62587a70f10d 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -21,10 +21,13 @@ import android.util.JsonToken;
import android.util.Log;
import android.util.SparseArray;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
public class Event {
private static final String TAG = "HidEvent";
@@ -41,6 +44,7 @@ public class Event {
private int mPid;
private byte[] mReport;
private SparseArray<byte[]> mFeatureReports;
+ private Map<ByteBuffer, byte[]> mOutputs;
private int mDuration;
public int getId() {
@@ -75,6 +79,10 @@ public class Event {
return mFeatureReports;
}
+ public Map<ByteBuffer, byte[]> getOutputs() {
+ return mOutputs;
+ }
+
public int getDuration() {
return mDuration;
}
@@ -88,6 +96,7 @@ public class Event {
+ ", pid=" + mPid
+ ", report=" + Arrays.toString(mReport)
+ ", feature_reports=" + mFeatureReports.toString()
+ + ", outputs=" + mOutputs.toString()
+ ", duration=" + mDuration
+ "}";
}
@@ -123,6 +132,10 @@ public class Event {
mEvent.mFeatureReports = reports;
}
+ public void setOutputs(Map<ByteBuffer, byte[]> outputs) {
+ mEvent.mOutputs = outputs;
+ }
+
public void setVid(int vid) {
mEvent.mVid = vid;
}
@@ -199,6 +212,9 @@ public class Event {
case "feature_reports":
eb.setFeatureReports(readFeatureReports());
break;
+ case "outputs":
+ eb.setOutputs(readOutputs());
+ break;
case "duration":
eb.setDuration(readInt());
break;
@@ -250,7 +266,7 @@ public class Event {
private SparseArray<byte[]> readFeatureReports()
throws IllegalStateException, IOException {
- SparseArray<byte[]> featureReports = new SparseArray();
+ SparseArray<byte[]> featureReports = new SparseArray<>();
try {
mReader.beginArray();
while (mReader.hasNext()) {
@@ -276,17 +292,60 @@ public class Event {
}
}
mReader.endObject();
- if (data != null)
+ if (data != null) {
featureReports.put(id, data);
+ }
}
mReader.endArray();
- } catch (IllegalStateException|NumberFormatException e) {
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return featureReports;
+ }
+
+ private Map<ByteBuffer, byte[]> readOutputs()
+ throws IllegalStateException, IOException {
+ Map<ByteBuffer, byte[]> outputs = new HashMap<>();
+
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ byte[] output = null;
+ byte[] response = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "description":
+ // Description is only used to keep track of the output responses
+ mReader.nextString();
+ break;
+ case "output":
+ output = readData();
+ break;
+ case "response":
+ response = readData();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in outputs: " + name);
+ }
+ }
+ mReader.endObject();
+ if (output != null) {
+ outputs.put(ByteBuffer.wrap(output), response);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
consumeRemainingElements();
mReader.endArray();
throw new IllegalStateException("Encountered malformed data.", e);
- } finally {
- return featureReports;
}
+ return outputs;
}
private void consumeRemainingElements() throws IOException {
@@ -296,10 +355,6 @@ public class Event {
}
}
- private static void error(String msg) {
- error(msg, null);
- }
-
private static void error(String msg, Exception e) {
System.out.println(msg);
Log.e(TAG, msg);
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 54ac1b0733ff..0ee2cc45932f 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -16,22 +16,17 @@
package com.android.commands.hid;
-import android.util.JsonReader;
-import android.util.JsonToken;
import android.util.Log;
import android.util.SparseArray;
import libcore.io.IoUtils;
-import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
public class Hid {
private static final String TAG = "HID";
@@ -119,7 +114,7 @@ public class Hid {
}
int id = e.getId();
Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
- e.getDescriptor(), e.getReport(), e.getFeatureReports());
+ e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
mDevices.append(id, d);
}