diff options
| -rw-r--r-- | cmds/hid/jni/com_android_commands_hid_Device.cpp | 49 | ||||
| -rw-r--r-- | cmds/hid/jni/com_android_commands_hid_Device.h | 11 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Device.java | 88 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Event.java | 30 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Hid.java | 4 |
5 files changed, 144 insertions, 38 deletions
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 2cda57dd67e9..8b8d361edbd4 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -18,24 +18,22 @@ #include "com_android_commands_hid_Device.h" -#include <linux/uhid.h> - +#include <android-base/stringprintf.h> +#include <android/looper.h> #include <fcntl.h> #include <inttypes.h> -#include <unistd.h> -#include <cstdio> -#include <cstring> -#include <memory> - -#include <android/looper.h> #include <jni.h> +#include <linux/uhid.h> #include <log/log.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> +#include <unistd.h> -#include <android-base/stringprintf.h> +#include <cstdio> +#include <cstring> +#include <memory> // Log debug messages about the output. static constexpr bool DEBUG_OUTPUT = false; @@ -109,15 +107,15 @@ void DeviceCallback::onDeviceOpen() { void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { JNIEnv* env = getJNIEnv(); - env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport, - requestId, reportId); + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport, requestId, + reportId); checkAndClearException(env, "onDeviceGetReport"); } -void DeviceCallback::onDeviceSetReport(uint8_t rType, - const std::vector<uint8_t>& data) { +void DeviceCallback::onDeviceSetReport(uint32_t id, uint8_t rType, + const std::vector<uint8_t>& data) { JNIEnv* env = getJNIEnv(); - env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType, + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, id, rType, toJbyteArray(env, data).get()); checkAndClearException(env, "onDeviceSetReport"); } @@ -236,6 +234,14 @@ void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& writeEvent(mFd, ev, "UHID_GET_REPORT_REPLY"); } +void Device::sendSetReportReply(uint32_t id, bool success) const { + struct uhid_event ev = {}; + ev.type = UHID_SET_REPORT_REPLY; + ev.u.set_report_reply.id = id; + ev.u.set_report_reply.err = success ? 0 : EIO; + writeEvent(mFd, ev, "UHID_SET_REPORT_REPLY"); +} + int Device::handleEvents(int events) { if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { ALOGE("uhid node was closed or an error occurred. events=0x%x", events); @@ -249,7 +255,6 @@ int Device::handleEvents(int events) { mDeviceCallback->onDeviceError(); return 0; } - switch (ev.type) { case UHID_OPEN: { mDeviceCallback->onDeviceOpen(); @@ -271,7 +276,7 @@ int Device::handleEvents(int events) { ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id, set_report.rnum, toString(data).c_str()); } - mDeviceCallback->onDeviceSetReport(set_report.rtype, data); + mDeviceCallback->onDeviceSetReport(set_report.id, set_report.rtype, data); break; } case UHID_OUTPUT: { @@ -347,6 +352,15 @@ static void sendGetFeatureReportReply(JNIEnv* env, jclass /* clazz */, jlong ptr } } +static void sendSetReportReply(JNIEnv*, jclass /* clazz */, jlong ptr, jint id, jboolean success) { + uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr); + if (d) { + d->sendSetReportReply(id, success); + } else { + ALOGE("Could not send set report reply, Device* is null!"); + } +} + static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr); if (d) { @@ -362,6 +376,7 @@ static JNINativeMethod sMethods[] = { {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)}, {"nativeSendGetFeatureReportReply", "(JI[B)V", reinterpret_cast<void*>(sendGetFeatureReportReply)}, + {"nativeSendSetReportReply", "(JIZ)V", reinterpret_cast<void*>(sendSetReportReply)}, {"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)}, }; @@ -376,7 +391,7 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { uhid::gDeviceCallbackClassInfo.onDeviceGetReport = env->GetMethodID(clazz, "onDeviceGetReport", "(II)V"); uhid::gDeviceCallbackClassInfo.onDeviceSetReport = - env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V"); + env->GetMethodID(clazz, "onDeviceSetReport", "(IB[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceOutput = env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index d10a9aa3680c..9c6060d837e0 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -14,12 +14,11 @@ * limitations under the License. */ -#include <memory> -#include <vector> - +#include <android-base/unique_fd.h> #include <jni.h> -#include <android-base/unique_fd.h> +#include <memory> +#include <vector> namespace android { namespace uhid { @@ -31,7 +30,7 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); - void onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data); + void onDeviceSetReport(uint32_t id, uint8_t rType, const std::vector<uint8_t>& data); void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data); void onDeviceError(); @@ -50,9 +49,9 @@ public: ~Device(); void sendReport(const std::vector<uint8_t>& report) const; + void sendSetReportReply(uint32_t id, bool success) const; void sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const; void close(); - int handleEvents(int events); private: diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index 95b1e9a7b57f..0415037cfc9f 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -42,7 +42,8 @@ public class Device { private static final int MSG_OPEN_DEVICE = 1; private static final int MSG_SEND_REPORT = 2; private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3; - private static final int MSG_CLOSE_DEVICE = 4; + private static final int MSG_SEND_SET_REPORT_REPLY = 4; + private static final int MSG_CLOSE_DEVICE = 5; // Sync with linux uhid_event_type::UHID_OUTPUT private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6; @@ -56,21 +57,45 @@ public class Device { private final Map<ByteBuffer, byte[]> mOutputs; private final OutputStream mOutputStream; private long mTimeToSend; - private final Object mCond = new Object(); + /** + * The report id of the report received in UHID_EVENT_TYPE_SET_REPORT. + * Used for SET_REPORT_REPLY. + * This field gets overridden each time SET_REPORT is received. + */ + private int mResponseId; static { System.loadLibrary("hidcommand_jni"); } - private static native long nativeOpenDevice(String name, int id, int vid, int pid, int bus, - byte[] descriptor, DeviceCallback callback); + private static native long nativeOpenDevice( + String name, + int id, + int vid, + int pid, + int bus, + 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 nativeSendSetReportReply(long ptr, int id, boolean success); + private static native void nativeCloseDevice(long ptr); - public Device(int id, String name, int vid, int pid, int bus, byte[] descriptor, - byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) { + public Device( + int id, + String name, + int vid, + int pid, + int bus, + byte[] descriptor, + byte[] report, + SparseArray<byte[]> featureReports, + Map<ByteBuffer, byte[]> outputs) { mId = id; mThread = new HandlerThread("HidDeviceHandler"); mThread.start(); @@ -100,6 +125,17 @@ public class Device { mHandler.sendMessageAtTime(msg, mTimeToSend); } + public void setGetReportResponse(byte[] report) { + mFeatureReports.put(report[0], report); + } + + public void sendSetReportReply(boolean success) { + Message msg = + mHandler.obtainMessage(MSG_SEND_SET_REPORT_REPLY, mResponseId, success ? 1 : 0); + + mHandler.sendMessageAtTime(msg, mTimeToSend); + } + public void addDelay(int delay) { mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay; } @@ -111,7 +147,8 @@ public class Device { synchronized (mCond) { mCond.wait(); } - } catch (InterruptedException ignore) {} + } catch (InterruptedException ignore) { + } } private class DeviceHandler extends Handler { @@ -127,8 +164,15 @@ public class Device { switch (msg.what) { case MSG_OPEN_DEVICE: SomeArgs args = (SomeArgs) msg.obj; - mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3, - args.argi4, (byte[]) args.arg2, new DeviceCallback()); + mPtr = + nativeOpenDevice( + (String) args.arg1, + args.argi1, + args.argi2, + args.argi3, + args.argi4, + (byte[]) args.arg2, + new DeviceCallback()); pauseEvents(); break; case MSG_SEND_REPORT: @@ -145,6 +189,14 @@ public class Device { Log.e(TAG, "Tried to send feature report reply to closed device."); } break; + case MSG_SEND_SET_REPORT_REPLY: + if (mPtr != 0) { + final boolean success = msg.arg2 == 1; + nativeSendSetReportReply(mPtr, msg.arg1, success); + } else { + Log.e(TAG, "Tried to send set report reply to closed device."); + } + break; case MSG_CLOSE_DEVICE: if (mPtr != 0) { nativeCloseDevice(mPtr); @@ -173,14 +225,18 @@ public class Device { } private class DeviceCallback { + public void onDeviceOpen() { mHandler.resumeEvents(); } 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"); + Log.e( + TAG, + "Received GET_REPORT request for reportId=" + + reportId + + ", but 'feature_reports' section is not found"); return; } byte[] report = mFeatureReports.get(reportId); @@ -220,14 +276,15 @@ public class Device { } catch (IOException e) { throw new RuntimeException(e); } - } // native callback - public void onDeviceSetReport(byte rtype, byte[] data) { + public void onDeviceSetReport(int id, byte rType, byte[] data) { + // Used by sendSetReportReply() + mResponseId = id; // We don't need to reply for the SET_REPORT but just send it to HID output for test // verification. - sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rtype, data); + sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rType, data); } // native callback @@ -239,7 +296,8 @@ public class Device { } byte[] response = mOutputs.get(ByteBuffer.wrap(data)); if (response == null) { - Log.i(TAG, + Log.i( + TAG, "Requested response for output " + Arrays.toString(data) + " is not found"); return; } diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java index d4bf1d820a70..3efb79766b94 100644 --- a/cmds/hid/src/com/android/commands/hid/Event.java +++ b/cmds/hid/src/com/android/commands/hid/Event.java @@ -35,6 +35,8 @@ public class Event { public static final String COMMAND_REGISTER = "register"; public static final String COMMAND_DELAY = "delay"; public static final String COMMAND_REPORT = "report"; + public static final String COMMAND_SET_GET_REPORT_RESPONSE = "set_get_report_response"; + public static final String COMMAND_SEND_SET_REPORT_REPLY = "send_set_report_reply"; // These constants come from "include/uapi/linux/input.h" in the kernel enum Bus { @@ -62,6 +64,7 @@ public class Event { private SparseArray<byte[]> mFeatureReports; private Map<ByteBuffer, byte[]> mOutputs; private int mDuration; + private Boolean mReply; public int getId() { return mId; @@ -107,6 +110,10 @@ public class Event { return mDuration; } + public Boolean getReply() { + return mReply; + } + public String toString() { return "Event{id=" + mId + ", command=" + String.valueOf(mCommand) @@ -119,6 +126,7 @@ public class Event { + ", feature_reports=" + mFeatureReports.toString() + ", outputs=" + mOutputs.toString() + ", duration=" + mDuration + + ", success=" + mReply.toString() + "}"; } @@ -173,6 +181,10 @@ public class Event { mEvent.mDuration = duration; } + public void setReply(boolean success) { + mEvent.mReply = success; + } + public Event build() { if (mEvent.mId == -1) { throw new IllegalStateException("No event id"); @@ -183,6 +195,16 @@ public class Event { if (mEvent.mDescriptor == null) { throw new IllegalStateException("Device registration is missing descriptor"); } + } + if (COMMAND_SET_GET_REPORT_RESPONSE.equals(mEvent.mCommand)) { + if (mEvent.mReport == null) { + throw new IllegalStateException("Report command is missing response data"); + } + } + if (COMMAND_SEND_SET_REPORT_REPLY.equals(mEvent.mCommand)) { + if (mEvent.mReply == null) { + throw new IllegalStateException("Reply command is missing reply"); + } } else if (COMMAND_DELAY.equals(mEvent.mCommand)) { if (mEvent.mDuration <= 0) { throw new IllegalStateException("Delay has missing or invalid duration"); @@ -246,6 +268,9 @@ public class Event { case "duration": eb.setDuration(readInt()); break; + case "success": + eb.setReply(readBool()); + break; default: mReader.skipValue(); } @@ -292,6 +317,11 @@ public class Event { return Integer.decode(val); } + private boolean readBool() throws IOException { + String val = mReader.nextString(); + return Boolean.parseBoolean(val); + } + private Bus readBus() throws IOException { String val = mReader.nextString(); return Bus.valueOf(val.toUpperCase()); diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java index fac0ab2ef125..2db791fe90bd 100644 --- a/cmds/hid/src/com/android/commands/hid/Hid.java +++ b/cmds/hid/src/com/android/commands/hid/Hid.java @@ -93,6 +93,10 @@ public class Hid { d.addDelay(e.getDuration()); } else if (Event.COMMAND_REPORT.equals(e.getCommand())) { d.sendReport(e.getReport()); + } else if (Event.COMMAND_SET_GET_REPORT_RESPONSE.equals(e.getCommand())) { + d.setGetReportResponse(e.getReport()); + } else if (Event.COMMAND_SEND_SET_REPORT_REPLY.equals(e.getCommand())) { + d.sendSetReportReply(e.getReply()); } else { if (Event.COMMAND_REGISTER.equals(e.getCommand())) { error("Device id=" + e.getId() + " is already registered. Ignoring event."); |