Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e2fd20d authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add support for UHID_GET_REPORT requests"

parents 310b9ea8 d47c0131
Loading
Loading
Loading
Loading
+50 −7
Original line number Diff line number Diff line
@@ -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 ||
+2 −0
Original line number Diff line number Diff line
@@ -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);
+33 −2
Original line number Diff line number Diff line
@@ -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);
+55 −0
Original line number Diff line number Diff line
@@ -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();
+1 −1
Original line number Diff line number Diff line
@@ -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);
    }