Loading cmds/hid/jni/com_android_commands_hid_Device.cpp +31 −2 Original line number Diff line number Diff line Loading @@ -27,12 +27,13 @@ #include <cstring> #include <memory> #include <android/log.h> #include <android/looper.h> #include <jni.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> #include <android/looper.h> #include <android/log.h> #include <android-base/stringprintf.h> Loading @@ -49,6 +50,7 @@ static const char* UHID_PATH = "/dev/uhid"; static struct { jmethodID onDeviceOpen; jmethodID onDeviceGetReport; jmethodID onDeviceOutput; jmethodID onDeviceError; } gDeviceCallbackClassInfo; Loading @@ -64,6 +66,18 @@ static void checkAndClearException(JNIEnv* env, const char* methodName) { } } static ScopedLocalRef<jbyteArray> toJbyteArray(JNIEnv* env, const std::vector<uint8_t>& vector) { ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(vector.size())); if (array.get() == nullptr) { jniThrowException(env, "java/lang/OutOfMemoryError", nullptr); return array; } static_assert(sizeof(char) == sizeof(uint8_t)); env->SetByteArrayRegion(array.get(), 0, vector.size(), reinterpret_cast<const signed char*>(vector.data())); return array; } static std::string toString(const std::vector<uint8_t>& data) { std::string s = ""; for (uint8_t b : data) { Loading Loading @@ -101,6 +115,13 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { checkAndClearException(env, "onDeviceGetReport"); } void DeviceCallback::onDeviceOutput(const std::vector<uint8_t>& data) { JNIEnv* env = getJNIEnv(); env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, toJbyteArray(env, data).get()); checkAndClearException(env, "onDeviceOutput"); } JNIEnv* DeviceCallback::getJNIEnv() { JNIEnv* env; mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); Loading Loading @@ -240,6 +261,12 @@ int Device::handleEvents(int events) { set_report.rnum, toString(data).c_str()); break; } case UHID_OUTPUT: { struct uhid_output_req& output = ev.u.output; std::vector<uint8_t> data(output.data, output.data + output.size); mDeviceCallback->onDeviceOutput(data); break; } default: { LOGI("Unhandled event type: %" PRIu32, ev.type); break; Loading Loading @@ -332,6 +359,8 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { env->GetMethodID(clazz, "onDeviceOpen", "()V"); uhid::gDeviceCallbackClassInfo.onDeviceGetReport = env->GetMethodID(clazz, "onDeviceGetReport", "(II)V"); uhid::gDeviceCallbackClassInfo.onDeviceOutput = env->GetMethodID(clazz, "onDeviceOutput", "([B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = env->GetMethodID(clazz, "onDeviceError", "()V"); if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL || Loading cmds/hid/jni/com_android_commands_hid_Device.h +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); void onDeviceOutput(const std::vector<uint8_t>& data); void onDeviceError(); private: Loading cmds/hid/src/com/android/commands/hid/Device.java +35 −2 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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); Loading cmds/hid/src/com/android/commands/hid/Event.java +65 −10 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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() { Loading Loading @@ -75,6 +79,10 @@ public class Event { return mFeatureReports; } public Map<ByteBuffer, byte[]> getOutputs() { return mOutputs; } public int getDuration() { return mDuration; } Loading @@ -88,6 +96,7 @@ public class Event { + ", pid=" + mPid + ", report=" + Arrays.toString(mReport) + ", feature_reports=" + mFeatureReports.toString() + ", outputs=" + mOutputs.toString() + ", duration=" + mDuration + "}"; } Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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()) { Loading @@ -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) { consumeRemainingElements(); mReader.endArray(); throw new IllegalStateException("Encountered malformed data.", e); } finally { } 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); } return outputs; } private void consumeRemainingElements() throws IOException { Loading @@ -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); Loading cmds/hid/src/com/android/commands/hid/Hid.java +2 −7 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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); } Loading Loading
cmds/hid/jni/com_android_commands_hid_Device.cpp +31 −2 Original line number Diff line number Diff line Loading @@ -27,12 +27,13 @@ #include <cstring> #include <memory> #include <android/log.h> #include <android/looper.h> #include <jni.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> #include <android/looper.h> #include <android/log.h> #include <android-base/stringprintf.h> Loading @@ -49,6 +50,7 @@ static const char* UHID_PATH = "/dev/uhid"; static struct { jmethodID onDeviceOpen; jmethodID onDeviceGetReport; jmethodID onDeviceOutput; jmethodID onDeviceError; } gDeviceCallbackClassInfo; Loading @@ -64,6 +66,18 @@ static void checkAndClearException(JNIEnv* env, const char* methodName) { } } static ScopedLocalRef<jbyteArray> toJbyteArray(JNIEnv* env, const std::vector<uint8_t>& vector) { ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(vector.size())); if (array.get() == nullptr) { jniThrowException(env, "java/lang/OutOfMemoryError", nullptr); return array; } static_assert(sizeof(char) == sizeof(uint8_t)); env->SetByteArrayRegion(array.get(), 0, vector.size(), reinterpret_cast<const signed char*>(vector.data())); return array; } static std::string toString(const std::vector<uint8_t>& data) { std::string s = ""; for (uint8_t b : data) { Loading Loading @@ -101,6 +115,13 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { checkAndClearException(env, "onDeviceGetReport"); } void DeviceCallback::onDeviceOutput(const std::vector<uint8_t>& data) { JNIEnv* env = getJNIEnv(); env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, toJbyteArray(env, data).get()); checkAndClearException(env, "onDeviceOutput"); } JNIEnv* DeviceCallback::getJNIEnv() { JNIEnv* env; mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); Loading Loading @@ -240,6 +261,12 @@ int Device::handleEvents(int events) { set_report.rnum, toString(data).c_str()); break; } case UHID_OUTPUT: { struct uhid_output_req& output = ev.u.output; std::vector<uint8_t> data(output.data, output.data + output.size); mDeviceCallback->onDeviceOutput(data); break; } default: { LOGI("Unhandled event type: %" PRIu32, ev.type); break; Loading Loading @@ -332,6 +359,8 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { env->GetMethodID(clazz, "onDeviceOpen", "()V"); uhid::gDeviceCallbackClassInfo.onDeviceGetReport = env->GetMethodID(clazz, "onDeviceGetReport", "(II)V"); uhid::gDeviceCallbackClassInfo.onDeviceOutput = env->GetMethodID(clazz, "onDeviceOutput", "([B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = env->GetMethodID(clazz, "onDeviceError", "()V"); if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL || Loading
cmds/hid/jni/com_android_commands_hid_Device.h +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); void onDeviceOutput(const std::vector<uint8_t>& data); void onDeviceError(); private: Loading
cmds/hid/src/com/android/commands/hid/Device.java +35 −2 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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); Loading
cmds/hid/src/com/android/commands/hid/Event.java +65 −10 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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() { Loading Loading @@ -75,6 +79,10 @@ public class Event { return mFeatureReports; } public Map<ByteBuffer, byte[]> getOutputs() { return mOutputs; } public int getDuration() { return mDuration; } Loading @@ -88,6 +96,7 @@ public class Event { + ", pid=" + mPid + ", report=" + Arrays.toString(mReport) + ", feature_reports=" + mFeatureReports.toString() + ", outputs=" + mOutputs.toString() + ", duration=" + mDuration + "}"; } Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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()) { Loading @@ -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) { consumeRemainingElements(); mReader.endArray(); throw new IllegalStateException("Encountered malformed data.", e); } finally { } 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); } return outputs; } private void consumeRemainingElements() throws IOException { Loading @@ -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); Loading
cmds/hid/src/com/android/commands/hid/Hid.java +2 −7 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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); } Loading