From 688379735d71e0002d8bcd8a20bcd509a6f13f0e Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Sun, 30 Aug 2020 12:36:22 -0700 Subject: [PATCH] Add support to hid commandline tool for hid device output. Add support to hid commandline tool for hid device output in JSON format. Hid Device output requests will be generated to allow hid based CTS tests to verify the results. Bug: 136215622 Test: atest CtsHardwareTestCases Change-Id: Ideaa1fcd023f1f3af2cade92e05418df6f9f8e6f --- .../jni/com_android_commands_hid_Device.cpp | 59 ++++++++++--------- .../hid/jni/com_android_commands_hid_Device.h | 2 +- .../src/com/android/commands/hid/Device.java | 33 ++++++++++- 3 files changed, 64 insertions(+), 30 deletions(-) diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 1e200c52a2075..437a87e8df54b 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -27,9 +27,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -37,10 +37,8 @@ #include -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) -#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +// Log debug messages about the output. +static constexpr bool DEBUG_OUTPUT = false; namespace android { namespace uhid { @@ -61,7 +59,7 @@ static int handleLooperEvents(int /* fd */, int events, void* data) { static void checkAndClearException(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { - LOGE("An exception was thrown by callback '%s'.", methodName); + ALOGE("An exception was thrown by callback '%s'.", methodName); env->ExceptionClear(); } } @@ -115,9 +113,9 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { checkAndClearException(env, "onDeviceGetReport"); } -void DeviceCallback::onDeviceOutput(const std::vector& data) { +void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector& data) { JNIEnv* env = getJNIEnv(); - env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType, toJbyteArray(env, data).get()); checkAndClearException(env, "onDeviceOutput"); } @@ -133,13 +131,13 @@ std::unique_ptr Device::open(int32_t id, const char* name, int32_t vid, std::unique_ptr callback) { size_t size = descriptor.size(); if (size > HID_MAX_DESCRIPTOR_SIZE) { - LOGE("Received invalid hid report with descriptor size %zu, skipping", size); + ALOGE("Received invalid hid report with descriptor size %zu, skipping", size); return nullptr; } android::base::unique_fd fd(::open(UHID_PATH, O_RDWR | O_CLOEXEC)); if (!fd.ok()) { - LOGE("Failed to open uhid: %s", strerror(errno)); + ALOGE("Failed to open uhid: %s", strerror(errno)); return nullptr; } @@ -159,14 +157,14 @@ std::unique_ptr Device::open(int32_t id, const char* name, int32_t vid, errno = 0; ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev))); if (ret < 0 || ret != sizeof(ev)) { - LOGE("Failed to create uhid node: %s", strerror(errno)); + ALOGE("Failed to create uhid node: %s", strerror(errno)); return nullptr; } // Wait for the device to actually be created. ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev))); if (ret < 0 || ev.type != UHID_START) { - LOGE("uhid node failed to start: %s", strerror(errno)); + ALOGE("uhid node failed to start: %s", strerror(errno)); return nullptr; } // using 'new' to access non-public constructor @@ -177,7 +175,7 @@ Device::Device(int32_t id, android::base::unique_fd fd, std::unique_ptr& report) const { if (report.size() > UHID_DATA_MAX) { - LOGE("Received invalid report of size %zu, skipping", report.size()); + ALOGE("Received invalid report of size %zu, skipping", report.size()); return; } @@ -230,14 +228,14 @@ void Device::sendGetFeatureReportReply(uint32_t id, const std::vector& 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); + ALOGE("uhid node was closed or an error occurred. events=0x%x", events); mDeviceCallback->onDeviceError(); return 0; } struct uhid_event ev; ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev))); if (ret < 0) { - LOGE("Failed to read from uhid node: %s", strerror(errno)); + ALOGE("Failed to read from uhid node: %s", strerror(errno)); mDeviceCallback->onDeviceError(); return 0; } @@ -254,23 +252,28 @@ int Device::handleEvents(int events) { case UHID_SET_REPORT: { const struct uhid_set_report_req& set_report = ev.u.set_report; if (set_report.size > UHID_DATA_MAX) { - LOGE("SET_REPORT contains too much data: size = %" PRIu16, set_report.size); + ALOGE("SET_REPORT contains too much data: size = %" PRIu16, set_report.size); return 0; } std::vector data(set_report.data, set_report.data + set_report.size); - LOGI("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id, - set_report.rnum, toString(data).c_str()); + if (DEBUG_OUTPUT) { + ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id, + set_report.rnum, toString(data).c_str()); + } break; } case UHID_OUTPUT: { struct uhid_output_req& output = ev.u.output; std::vector data(output.data, output.data + output.size); - mDeviceCallback->onDeviceOutput(data); + if (DEBUG_OUTPUT) { + ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str()); + } + mDeviceCallback->onDeviceOutput(output.rtype, data); break; } default: { - LOGI("Unhandled event type: %" PRIu32, ev.type); + ALOGI("Unhandled event type: %" PRIu32, ev.type); break; } } @@ -318,7 +321,7 @@ static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray ra if (d) { d->sendReport(report); } else { - LOGE("Could not send report, Device* is null!"); + ALOGE("Could not send report, Device* is null!"); } } @@ -329,7 +332,7 @@ static void sendGetFeatureReportReply(JNIEnv* env, jclass /* clazz */, jlong ptr std::vector report = getData(env, rawReport); d->sendGetFeatureReportReply(id, report); } else { - LOGE("Could not send get feature report reply, Device* is null!"); + ALOGE("Could not send get feature report reply, Device* is null!"); } } @@ -354,7 +357,7 @@ static JNINativeMethod sMethods[] = { int register_com_android_commands_hid_Device(JNIEnv* env) { jclass clazz = env->FindClass("com/android/commands/hid/Device$DeviceCallback"); if (clazz == NULL) { - LOGE("Unable to find class 'DeviceCallback'"); + ALOGE("Unable to find class 'DeviceCallback'"); return JNI_ERR; } uhid::gDeviceCallbackClassInfo.onDeviceOpen = @@ -362,12 +365,12 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { uhid::gDeviceCallbackClassInfo.onDeviceGetReport = env->GetMethodID(clazz, "onDeviceGetReport", "(II)V"); uhid::gDeviceCallbackClassInfo.onDeviceOutput = - env->GetMethodID(clazz, "onDeviceOutput", "([B)V"); + env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = env->GetMethodID(clazz, "onDeviceError", "()V"); if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL || uhid::gDeviceCallbackClassInfo.onDeviceError == NULL) { - LOGE("Unable to obtain onDeviceOpen or onDeviceError methods"); + ALOGE("Unable to obtain onDeviceOpen or onDeviceError methods"); return JNI_ERR; } diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index 7202b45adcde9..5483b40831a0d 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -31,7 +31,7 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); - void onDeviceOutput(const std::vector& data); + void onDeviceOutput(uint8_t rType, const std::vector& data); void onDeviceError(); private: diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index dade41511ae65..20b4bd86baec8 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -26,6 +26,12 @@ import android.util.SparseArray; import com.android.internal.os.SomeArgs; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Map; @@ -38,12 +44,16 @@ public class Device { private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3; private static final int MSG_CLOSE_DEVICE = 4; + // Sync with linux uhid_event_type::UHID_OUTPUT + private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6; + 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 mFeatureReports; private final Map mOutputs; + private final OutputStream mOutputStream; private long mTimeToSend; private final Object mCond = new Object(); @@ -66,6 +76,7 @@ public class Device { mHandler = new DeviceHandler(mThread.getLooper()); mFeatureReports = featureReports; mOutputs = outputs; + mOutputStream = System.out; SomeArgs args = SomeArgs.obtain(); args.argi1 = id; args.argi2 = vid; @@ -188,7 +199,27 @@ public class Device { } // native callback - public void onDeviceOutput(byte[] data) { + public void onDeviceOutput(byte rtype, byte[] data) { + JSONObject json = new JSONObject(); + try { + json.put("eventId", UHID_EVENT_TYPE_UHID_OUTPUT); + json.put("deviceId", mId); + json.put("reportType", rtype); + JSONArray dataArray = new JSONArray(); + for (int i = 0; i < data.length; i++) { + dataArray.put(data[i] & 0xFF); + } + json.put("reportData", dataArray); + } catch (JSONException e) { + throw new RuntimeException("Could not create JSON object ", e); + } + try { + mOutputStream.write(json.toString().getBytes()); + mOutputStream.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (mOutputs == null) { Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found"); return;