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

Commit f532c911 authored by Andrew Maganito's avatar Andrew Maganito
Browse files

Support for SensorHeadTrackerTest on Hid command

- Added support for SensorHeadTrackerTest. This does not affect other tests used by Hid Command tool.
- Added two new functions setGetReportResponse() and
  sendSetReportReply()

Bug: 228319672
Test: atest android.hardware.cts.SensorHeadTrackerTest
Change-Id: I83cfe1f71df57584dac7288ab092164a5c5dea3c
parent aded5264
Loading
Loading
Loading
Loading
+32 −17
Original line number Diff line number Diff line
@@ -18,24 +18,22 @@

#include "com_android_commands_hid_Device.h"

#include <linux/uhid.h>

#include <android-base/stringprintf.h>
#include <android/looper.h>
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <memory>

#include <android/looper.h>
#include <jni.h>
#include <linux/uhid.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include <unistd.h>

#include <android-base/stringprintf.h>
#include <cstdio>
#include <cstring>
#include <memory>

// Log debug messages about the output.
static constexpr bool DEBUG_OUTPUT = false;
@@ -109,15 +107,15 @@ void DeviceCallback::onDeviceOpen() {

void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) {
    JNIEnv* env = getJNIEnv();
    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport,
            requestId, reportId);
    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport, requestId,
                        reportId);
    checkAndClearException(env, "onDeviceGetReport");
}

void DeviceCallback::onDeviceSetReport(uint8_t rType,
void DeviceCallback::onDeviceSetReport(uint32_t id, uint8_t rType,
                                       const std::vector<uint8_t>& data) {
    JNIEnv* env = getJNIEnv();
    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType,
    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, id, rType,
                        toJbyteArray(env, data).get());
    checkAndClearException(env, "onDeviceSetReport");
}
@@ -236,6 +234,14 @@ void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>&
    writeEvent(mFd, ev, "UHID_GET_REPORT_REPLY");
}

void Device::sendSetReportReply(uint32_t id, bool success) const {
    struct uhid_event ev = {};
    ev.type = UHID_SET_REPORT_REPLY;
    ev.u.set_report_reply.id = id;
    ev.u.set_report_reply.err = success ? 0 : EIO;
    writeEvent(mFd, ev, "UHID_SET_REPORT_REPLY");
}

int Device::handleEvents(int events) {
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        ALOGE("uhid node was closed or an error occurred. events=0x%x", events);
@@ -249,7 +255,6 @@ int Device::handleEvents(int events) {
        mDeviceCallback->onDeviceError();
        return 0;
    }

    switch (ev.type) {
        case UHID_OPEN: {
            mDeviceCallback->onDeviceOpen();
@@ -271,7 +276,7 @@ int Device::handleEvents(int events) {
                ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
                      set_report.rnum, toString(data).c_str());
            }
            mDeviceCallback->onDeviceSetReport(set_report.rtype, data);
            mDeviceCallback->onDeviceSetReport(set_report.id, set_report.rtype, data);
            break;
        }
        case UHID_OUTPUT: {
@@ -347,6 +352,15 @@ static void sendGetFeatureReportReply(JNIEnv* env, jclass /* clazz */, jlong ptr
    }
}

static void sendSetReportReply(JNIEnv*, jclass /* clazz */, jlong ptr, jint id, jboolean success) {
    uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
    if (d) {
        d->sendSetReportReply(id, success);
    } else {
        ALOGE("Could not send set report reply, Device* is null!");
    }
}

static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
    uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
    if (d) {
@@ -362,6 +376,7 @@ static JNINativeMethod sMethods[] = {
        {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
        {"nativeSendGetFeatureReportReply", "(JI[B)V",
         reinterpret_cast<void*>(sendGetFeatureReportReply)},
        {"nativeSendSetReportReply", "(JIZ)V", reinterpret_cast<void*>(sendSetReportReply)},
        {"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)},
};

@@ -376,7 +391,7 @@ int register_com_android_commands_hid_Device(JNIEnv* env) {
    uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
            env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
    uhid::gDeviceCallbackClassInfo.onDeviceSetReport =
            env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V");
            env->GetMethodID(clazz, "onDeviceSetReport", "(IB[B)V");
    uhid::gDeviceCallbackClassInfo.onDeviceOutput =
            env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
    uhid::gDeviceCallbackClassInfo.onDeviceError =
+5 −6
Original line number Diff line number Diff line
@@ -14,12 +14,11 @@
 * limitations under the License.
 */

#include <memory>
#include <vector>

#include <android-base/unique_fd.h>
#include <jni.h>

#include <android-base/unique_fd.h>
#include <memory>
#include <vector>

namespace android {
namespace uhid {
@@ -31,7 +30,7 @@ public:

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

@@ -50,9 +49,9 @@ public:
    ~Device();

    void sendReport(const std::vector<uint8_t>& report) const;
    void sendSetReportReply(uint32_t id, bool success) const;
    void sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const;
    void close();

    int handleEvents(int events);

private:
+73 −15
Original line number Diff line number Diff line
@@ -42,7 +42,8 @@ public class Device {
    private static final int MSG_OPEN_DEVICE = 1;
    private static final int MSG_SEND_REPORT = 2;
    private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3;
    private static final int MSG_CLOSE_DEVICE = 4;
    private static final int MSG_SEND_SET_REPORT_REPLY = 4;
    private static final int MSG_CLOSE_DEVICE = 5;

    // Sync with linux uhid_event_type::UHID_OUTPUT
    private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6;
@@ -56,21 +57,45 @@ public class Device {
    private final Map<ByteBuffer, byte[]> mOutputs;
    private final OutputStream mOutputStream;
    private long mTimeToSend;

    private final Object mCond = new Object();
    /**
     * The report id of the report received in UHID_EVENT_TYPE_SET_REPORT.
     * Used for SET_REPORT_REPLY.
     * This field gets overridden each time SET_REPORT is received.
     */
    private int mResponseId;

    static {
        System.loadLibrary("hidcommand_jni");
    }

    private static native long nativeOpenDevice(String name, int id, int vid, int pid, int bus,
            byte[] descriptor, DeviceCallback callback);
    private static native long nativeOpenDevice(
            String name,
            int id,
            int vid,
            int pid,
            int bus,
            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 nativeSendSetReportReply(long ptr, int id, boolean success);

    private static native void nativeCloseDevice(long ptr);

    public Device(int id, String name, int vid, int pid, int bus, byte[] descriptor,
            byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) {
    public Device(
            int id,
            String name,
            int vid,
            int pid,
            int bus,
            byte[] descriptor,
            byte[] report,
            SparseArray<byte[]> featureReports,
            Map<ByteBuffer, byte[]> outputs) {
        mId = id;
        mThread = new HandlerThread("HidDeviceHandler");
        mThread.start();
@@ -100,6 +125,17 @@ public class Device {
        mHandler.sendMessageAtTime(msg, mTimeToSend);
    }

    public void setGetReportResponse(byte[] report) {
        mFeatureReports.put(report[0], report);
    }

    public void sendSetReportReply(boolean success) {
        Message msg =
                mHandler.obtainMessage(MSG_SEND_SET_REPORT_REPLY, mResponseId, success ? 1 : 0);

        mHandler.sendMessageAtTime(msg, mTimeToSend);
    }

    public void addDelay(int delay) {
        mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
    }
@@ -111,7 +147,8 @@ public class Device {
            synchronized (mCond) {
                mCond.wait();
            }
        } catch (InterruptedException ignore) {}
        } catch (InterruptedException ignore) {
        }
    }

    private class DeviceHandler extends Handler {
@@ -127,8 +164,15 @@ public class Device {
            switch (msg.what) {
                case MSG_OPEN_DEVICE:
                    SomeArgs args = (SomeArgs) msg.obj;
                    mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3,
                            args.argi4, (byte[]) args.arg2, new DeviceCallback());
                    mPtr =
                            nativeOpenDevice(
                                    (String) args.arg1,
                                    args.argi1,
                                    args.argi2,
                                    args.argi3,
                                    args.argi4,
                                    (byte[]) args.arg2,
                                    new DeviceCallback());
                    pauseEvents();
                    break;
                case MSG_SEND_REPORT:
@@ -145,6 +189,14 @@ public class Device {
                        Log.e(TAG, "Tried to send feature report reply to closed device.");
                    }
                    break;
                case MSG_SEND_SET_REPORT_REPLY:
                    if (mPtr != 0) {
                        final boolean success = msg.arg2 == 1;
                        nativeSendSetReportReply(mPtr, msg.arg1, success);
                    } else {
                        Log.e(TAG, "Tried to send set report reply to closed device.");
                    }
                    break;
                case MSG_CLOSE_DEVICE:
                    if (mPtr != 0) {
                        nativeCloseDevice(mPtr);
@@ -173,13 +225,17 @@ public class Device {
    }

    private class DeviceCallback {

        public void onDeviceOpen() {
            mHandler.resumeEvents();
        }

        public void onDeviceGetReport(int requestId, int reportId) {
            if (mFeatureReports == null) {
                Log.e(TAG, "Received GET_REPORT request for reportId=" + reportId
                Log.e(
                        TAG,
                        "Received GET_REPORT request for reportId="
                                + reportId
                                + ", but 'feature_reports' section is not found");
                return;
            }
@@ -220,14 +276,15 @@ public class Device {
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }

        // native callback
        public void onDeviceSetReport(byte rtype, byte[] data) {
        public void onDeviceSetReport(int id, byte rType, byte[] data) {
            // Used by sendSetReportReply()
            mResponseId = id;
            // We don't need to reply for the SET_REPORT but just send it to HID output for test
            // verification.
            sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rtype, data);
            sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rType, data);
        }

        // native callback
@@ -239,7 +296,8 @@ public class Device {
            }
            byte[] response = mOutputs.get(ByteBuffer.wrap(data));
            if (response == null) {
                Log.i(TAG,
                Log.i(
                        TAG,
                        "Requested response for output " + Arrays.toString(data) + " is not found");
                return;
            }
+30 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ public class Event {
    public static final String COMMAND_REGISTER = "register";
    public static final String COMMAND_DELAY = "delay";
    public static final String COMMAND_REPORT = "report";
    public static final String COMMAND_SET_GET_REPORT_RESPONSE = "set_get_report_response";
    public static final String COMMAND_SEND_SET_REPORT_REPLY = "send_set_report_reply";

    // These constants come from "include/uapi/linux/input.h" in the kernel
    enum Bus {
@@ -62,6 +64,7 @@ public class Event {
    private SparseArray<byte[]> mFeatureReports;
    private Map<ByteBuffer, byte[]> mOutputs;
    private int mDuration;
    private Boolean mReply;

    public int getId() {
        return mId;
@@ -107,6 +110,10 @@ public class Event {
        return mDuration;
    }

    public Boolean getReply() {
        return mReply;
    }

    public String toString() {
        return "Event{id=" + mId
            + ", command=" + String.valueOf(mCommand)
@@ -119,6 +126,7 @@ public class Event {
            + ", feature_reports=" + mFeatureReports.toString()
            + ", outputs=" + mOutputs.toString()
            + ", duration=" + mDuration
            + ", success=" + mReply.toString()
            + "}";
    }

@@ -173,6 +181,10 @@ public class Event {
            mEvent.mDuration = duration;
        }

        public void setReply(boolean success) {
            mEvent.mReply = success;
        }

        public Event build() {
            if (mEvent.mId == -1) {
                throw new IllegalStateException("No event id");
@@ -183,6 +195,16 @@ public class Event {
                if (mEvent.mDescriptor == null) {
                    throw new IllegalStateException("Device registration is missing descriptor");
                }
            }
            if (COMMAND_SET_GET_REPORT_RESPONSE.equals(mEvent.mCommand)) {
                if (mEvent.mReport == null) {
                    throw new IllegalStateException("Report command is missing response data");
                }
            }
            if (COMMAND_SEND_SET_REPORT_REPLY.equals(mEvent.mCommand)) {
                if (mEvent.mReply == null) {
                    throw new IllegalStateException("Reply command is missing reply");
                }
            } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
                if (mEvent.mDuration <= 0) {
                    throw new IllegalStateException("Delay has missing or invalid duration");
@@ -246,6 +268,9 @@ public class Event {
                            case "duration":
                                eb.setDuration(readInt());
                                break;
                            case "success":
                                eb.setReply(readBool());
                                break;
                            default:
                                mReader.skipValue();
                        }
@@ -292,6 +317,11 @@ public class Event {
            return Integer.decode(val);
        }

        private boolean readBool() throws IOException {
            String val = mReader.nextString();
            return Boolean.parseBoolean(val);
        }

        private Bus readBus() throws IOException {
            String val = mReader.nextString();
            return Bus.valueOf(val.toUpperCase());
+4 −0
Original line number Diff line number Diff line
@@ -93,6 +93,10 @@ public class Hid {
                d.addDelay(e.getDuration());
            } else if (Event.COMMAND_REPORT.equals(e.getCommand())) {
                d.sendReport(e.getReport());
            } else if (Event.COMMAND_SET_GET_REPORT_RESPONSE.equals(e.getCommand())) {
                d.setGetReportResponse(e.getReport());
            } else if (Event.COMMAND_SEND_SET_REPORT_REPLY.equals(e.getCommand())) {
                d.sendSetReportReply(e.getReply());
            } else {
                if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
                    error("Device id=" + e.getId() + " is already registered. Ignoring event.");