summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.cpp57
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.h2
-rw-r--r--cmds/hid/src/com/android/commands/hid/Device.java35
-rw-r--r--cmds/hid/src/com/android/commands/hid/Event.java55
-rw-r--r--cmds/hid/src/com/android/commands/hid/Hid.java2
5 files changed, 141 insertions, 10 deletions
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index b3e287bae76a..d4fdf85491d3 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -45,6 +45,7 @@ static const char* UHID_PATH = "/dev/uhid";
static struct {
jmethodID onDeviceOpen;
+ jmethodID onDeviceGetReport;
jmethodID onDeviceError;
} gDeviceCallbackClassInfo;
@@ -82,6 +83,13 @@ void DeviceCallback::onDeviceOpen() {
checkAndClearException(env, "onDeviceOpen");
}
+void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport,
+ requestId, reportId);
+ checkAndClearException(env, "onDeviceGetReport");
+}
+
JNIEnv* DeviceCallback::getJNIEnv() {
JNIEnv* env;
mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
@@ -103,8 +111,7 @@ Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
return nullptr;
}
- struct uhid_event ev;
- memset(&ev, 0, sizeof(ev));
+ struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
memcpy(&ev.u.create2.rd_data, descriptor.data(),
@@ -152,8 +159,7 @@ Device::~Device() {
} else {
LOGE("Could not remove fd, ALooper_forThread() returned NULL!");
}
- struct uhid_event ev;
- memset(&ev, 0, sizeof(ev));
+ struct uhid_event ev = {};
ev.type = UHID_DESTROY;
TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
::close(mFd);
@@ -166,8 +172,7 @@ void Device::sendReport(const std::vector<uint8_t>& report) const {
return;
}
- struct uhid_event ev;
- memset(&ev, 0, sizeof(ev));
+ struct uhid_event ev = {};
ev.type = UHID_INPUT2;
ev.u.input2.size = report.size();
memcpy(&ev.u.input2.data, report.data(), report.size() * sizeof(ev.u.input2.data[0]));
@@ -177,6 +182,20 @@ void Device::sendReport(const std::vector<uint8_t>& report) const {
}
}
+void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const {
+ struct uhid_event ev = {};
+ ev.type = UHID_GET_REPORT_REPLY;
+ ev.u.get_report_reply.id = id;
+ ev.u.get_report_reply.err = report.size() == 0 ? EIO : 0;
+ ev.u.get_report_reply.size = report.size();
+ memcpy(&ev.u.get_report_reply.data, report.data(),
+ report.size() * sizeof(ev.u.get_report_reply.data[0]));
+ ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
+ if (ret < 0 || ret != sizeof(ev)) {
+ LOGE("Failed to send hid event (UHID_GET_REPORT_REPLY): %s", strerror(errno));
+ }
+}
+
int Device::handleEvents(int events) {
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
LOGE("uhid node was closed or an error occurred. events=0x%x", events);
@@ -193,6 +212,11 @@ int Device::handleEvents(int events) {
if (ev.type == UHID_OPEN) {
mDeviceCallback->onDeviceOpen();
+ } else if (ev.type == UHID_GET_REPORT) {
+ mDeviceCallback->onDeviceGetReport(ev.u.get_report.id, ev.u.get_report.rnum);
+ } else if (ev.type == UHID_SET_REPORT) {
+ LOGE("UHID_SET_REPORT is currently not supported");
+ return 0;
}
return 1;
@@ -201,9 +225,13 @@ int Device::handleEvents(int events) {
} // namespace uhid
std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) {
+ std::vector<uint8_t> data;
+ if (javaArray == nullptr) {
+ return data;
+ }
+
ScopedByteArrayRO scopedArray(env, javaArray);
size_t size = scopedArray.size();
- std::vector<uint8_t> data;
data.reserve(size);
for (size_t i = 0; i < size; i++) {
data.push_back(static_cast<uint8_t>(scopedArray[i]));
@@ -237,6 +265,17 @@ static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray ra
}
}
+static void sendGetFeatureReportReply(JNIEnv* env, jclass /* clazz */, jlong ptr, jint id,
+ jbyteArray rawReport) {
+ uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
+ if (d) {
+ std::vector<uint8_t> report = getData(env, rawReport);
+ d->sendGetFeatureReportReply(id, report);
+ } else {
+ LOGE("Could not send get feature report reply, Device* is null!");
+ }
+}
+
static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
if (d) {
@@ -250,6 +289,8 @@ static JNINativeMethod sMethods[] = {
"Lcom/android/commands/hid/Device$DeviceCallback;)J",
reinterpret_cast<void*>(openDevice) },
{ "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
+ { "nativeSendGetFeatureReportReply", "(JI[B)V",
+ reinterpret_cast<void*>(sendGetFeatureReportReply) },
{ "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
};
@@ -261,6 +302,8 @@ int register_com_android_commands_hid_Device(JNIEnv* env) {
}
uhid::gDeviceCallbackClassInfo.onDeviceOpen =
env->GetMethodID(clazz, "onDeviceOpen", "()V");
+ uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
+ env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
env->GetMethodID(clazz, "onDeviceError", "()V");
if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 61a1f760697f..892c7cd12953 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -28,6 +28,7 @@ public:
~DeviceCallback();
void onDeviceOpen();
+ void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
void onDeviceError();
private:
@@ -45,6 +46,7 @@ public:
~Device();
void sendReport(const std::vector<uint8_t>& report) const;
+ void sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const;
void close();
int handleEvents(int events);
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 8c52a8ed1e09..616d411ef7bb 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -23,6 +23,7 @@ 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;
@@ -31,11 +32,14 @@ public class Device {
private static final int MSG_OPEN_DEVICE = 1;
private static final int MSG_SEND_REPORT = 2;
- private static final int MSG_CLOSE_DEVICE = 3;
+ private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3;
+ private static final int MSG_CLOSE_DEVICE = 4;
private final int mId;
private final HandlerThread mThread;
private final DeviceHandler mHandler;
+ // mFeatureReports is limited to 256 entries, because the report number is 8-bit
+ private final SparseArray<byte[]> mFeatureReports;
private long mTimeToSend;
private final Object mCond = new Object();
@@ -47,13 +51,16 @@ public class Device {
private static native long nativeOpenDevice(String name, int id, int vid, int pid,
byte[] descriptor, DeviceCallback callback);
private static native void nativeSendReport(long ptr, byte[] data);
+ private static native void nativeSendGetFeatureReportReply(long ptr, int id, byte[] data);
private static native void nativeCloseDevice(long ptr);
- public Device(int id, String name, int vid, int pid, byte[] descriptor, byte[] report) {
+ public Device(int id, String name, int vid, int pid, byte[] descriptor,
+ byte[] report, SparseArray<byte[]> featureReports) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
mThread.start();
mHandler = new DeviceHandler(mThread.getLooper());
+ mFeatureReports = featureReports;
SomeArgs args = SomeArgs.obtain();
args.argi1 = id;
args.argi2 = vid;
@@ -113,6 +120,13 @@ public class Device {
Log.e(TAG, "Tried to send report to closed device.");
}
break;
+ case MSG_SEND_GET_FEATURE_REPORT_REPLY:
+ if (mPtr != 0) {
+ nativeSendGetFeatureReportReply(mPtr, msg.arg1, (byte[]) msg.obj);
+ } else {
+ Log.e(TAG, "Tried to send feature report reply to closed device.");
+ }
+ break;
case MSG_CLOSE_DEVICE:
if (mPtr != 0) {
nativeCloseDevice(mPtr);
@@ -145,6 +159,23 @@ public class Device {
mHandler.resumeEvents();
}
+ public void onDeviceGetReport(int requestId, int reportId) {
+ byte[] report = mFeatureReports.get(reportId);
+
+ if (report == null) {
+ Log.e(TAG, "Requested feature report " + reportId + " is not specified");
+ }
+
+ Message msg;
+ msg = mHandler.obtainMessage(MSG_SEND_GET_FEATURE_REPORT_REPLY, requestId, 0, report);
+
+ // 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_GET_REPORT requests during probe.
+ 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 c6a37bd3c48f..746e37289076 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -19,6 +19,7 @@ package com.android.commands.hid;
import android.util.JsonReader;
import android.util.JsonToken;
import android.util.Log;
+import android.util.SparseArray;
import java.io.InputStreamReader;
import java.io.IOException;
@@ -39,6 +40,7 @@ public class Event {
private int mVid;
private int mPid;
private byte[] mReport;
+ private SparseArray<byte[]> mFeatureReports;
private int mDuration;
public int getId() {
@@ -69,6 +71,10 @@ public class Event {
return mReport;
}
+ public SparseArray<byte[]> getFeatureReports() {
+ return mFeatureReports;
+ }
+
public int getDuration() {
return mDuration;
}
@@ -81,6 +87,7 @@ public class Event {
+ ", vid=" + mVid
+ ", pid=" + mPid
+ ", report=" + Arrays.toString(mReport)
+ + ", feature_reports=" + mFeatureReports.toString()
+ ", duration=" + mDuration
+ "}";
}
@@ -112,6 +119,10 @@ public class Event {
mEvent.mReport = report;
}
+ public void setFeatureReports(SparseArray<byte[]> reports) {
+ mEvent.mFeatureReports = reports;
+ }
+
public void setVid(int vid) {
mEvent.mVid = vid;
}
@@ -185,6 +196,9 @@ public class Event {
case "report":
eb.setReport(readData());
break;
+ case "feature_reports":
+ eb.setFeatureReports(readFeatureReports());
+ break;
case "duration":
eb.setDuration(readInt());
break;
@@ -234,6 +248,47 @@ public class Event {
return Integer.decode(val);
}
+ private SparseArray<byte[]> readFeatureReports()
+ throws IllegalStateException, IOException {
+ SparseArray<byte[]> featureReports = new SparseArray();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ // If "id" is not specified, it defaults to 0, which means
+ // report does not contain report ID (based on HID specs).
+ int id = 0;
+ byte[] data = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "id":
+ id = readInt();
+ break;
+ case "data":
+ data = readData();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in feature report: "
+ + name);
+ }
+ }
+ mReader.endObject();
+ if (data != null)
+ featureReports.put(id, data);
+ }
+ mReader.endArray();
+ } catch (IllegalStateException|NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ } finally {
+ return featureReports;
+ }
+ }
+
private void consumeRemainingElements() throws IOException {
while (mReader.hasNext()) {
mReader.skipValue();
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 234e47f12dee..54ac1b0733ff 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -119,7 +119,7 @@ public class Hid {
}
int id = e.getId();
Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
- e.getDescriptor(), e.getReport());
+ e.getDescriptor(), e.getReport(), e.getFeatureReports());
mDevices.append(id, d);
}