diff options
| -rw-r--r-- | cmds/hid/jni/com_android_commands_hid_Device.cpp | 57 | ||||
| -rw-r--r-- | cmds/hid/jni/com_android_commands_hid_Device.h | 2 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Device.java | 35 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Event.java | 55 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Hid.java | 2 |
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); } |