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

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

Merge "Add UHID_OUTPUT handling to hid command"

parents 4f6f2a7a f6081119
Loading
Loading
Loading
Loading
+31 −2
Original line number Diff line number Diff line
@@ -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>

@@ -49,6 +50,7 @@ static const char* UHID_PATH = "/dev/uhid";
static struct {
    jmethodID onDeviceOpen;
    jmethodID onDeviceGetReport;
    jmethodID onDeviceOutput;
    jmethodID onDeviceError;
} gDeviceCallbackClassInfo;

@@ -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) {
@@ -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);
@@ -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;
@@ -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 ||
+1 −0
Original line number Diff line number Diff line
@@ -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:
+35 −2
Original line number Diff line number Diff line
@@ -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";

@@ -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();
@@ -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;
@@ -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) {
@@ -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);
+65 −10
Original line number Diff line number Diff line
@@ -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";
@@ -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() {
@@ -75,6 +79,10 @@ public class Event {
        return mFeatureReports;
    }

    public Map<ByteBuffer, byte[]> getOutputs() {
        return mOutputs;
    }

    public int getDuration() {
        return mDuration;
    }
@@ -88,6 +96,7 @@ public class Event {
            + ", pid=" + mPid
            + ", report=" + Arrays.toString(mReport)
            + ", feature_reports=" + mFeatureReports.toString()
            + ", outputs=" + mOutputs.toString()
            + ", duration=" + mDuration
            + "}";
    }
@@ -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;
        }
@@ -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;
@@ -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()) {
@@ -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 {
@@ -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);
+2 −7
Original line number Diff line number Diff line
@@ -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";
@@ -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);
    }