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

Commit 72111e36 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add support to hid commandline tool for hid device output."

parents e0ae5453 68837973
Loading
Loading
Loading
Loading
+31 −28
Original line number Diff line number Diff line
@@ -27,9 +27,9 @@
#include <cstring>
#include <memory>

#include <android/log.h>
#include <android/looper.h>
#include <jni.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
@@ -37,10 +37,8 @@

#include <android-base/stringprintf.h>

#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<uint8_t>& data) {
void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& 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> Device::open(int32_t id, const char* name, int32_t vid,
                                     std::unique_ptr<DeviceCallback> 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> 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<DeviceCa
      : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
    ALooper* aLooper = ALooper_forThread();
    if (aLooper == NULL) {
        LOGE("Could not get ALooper, ALooper_forThread returned NULL");
        ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
        aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
    }
    ALooper_addFd(aLooper, mFd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
@@ -189,7 +187,7 @@ Device::~Device() {
    if (looper != NULL) {
        ALooper_removeFd(looper, mFd);
    } else {
        LOGE("Could not remove fd, ALooper_forThread() returned NULL!");
        ALOGE("Could not remove fd, ALooper_forThread() returned NULL!");
    }
    struct uhid_event ev = {};
    ev.type = UHID_DESTROY;
@@ -200,13 +198,13 @@ Device::~Device() {
static void writeEvent(int fd, struct uhid_event& ev, const char* messageType) {
    ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
    if (ret < 0 || ret != sizeof(ev)) {
        LOGE("Failed to send uhid_event %s: %s", messageType, strerror(errno));
        ALOGE("Failed to send uhid_event %s: %s", messageType, strerror(errno));
    }
}

void Device::sendReport(const std::vector<uint8_t>& 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<uint8_t>&

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<uint8_t> data(set_report.data, set_report.data + set_report.size);
            LOGI("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
            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<uint8_t> 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<uint8_t> 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;
    }

+1 −1
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ public:

    void onDeviceOpen();
    void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
    void onDeviceOutput(const std::vector<uint8_t>& data);
    void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data);
    void onDeviceError();

private:
+32 −1
Original line number Diff line number Diff line
@@ -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<byte[]> mFeatureReports;
    private final Map<ByteBuffer, byte[]> 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;