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

Commit 1aa8a96f authored by Febin Thattil's avatar Febin Thattil Committed by Android (Google) Code Review
Browse files

Merge "[AOA FFS Implementation] Add Vendor Control Request Monitor" into main

parents e488d58e f73343a5
Loading
Loading
Loading
Loading
+186 −1
Original line number Diff line number Diff line
@@ -32,12 +32,12 @@

#include "MtpDescriptors.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
#include "jni.h"
#include "utils/Log.h"

#define DRIVER_NAME "/dev/usb_accessory"
#define EPOLL_MAX_EVENTS 4
#define FFS_NUM_EVENTS 5
#define USB_STATE_MAX_LEN 20

namespace android
@@ -177,6 +177,191 @@ public:
};
static std::unique_ptr<NativeGadgetMonitorThread> sGadgetMonitorThread;

/*
 * NativeVendorControlRequestMonitorThread starts a new thread to monitor vendor
 * control requests. It issues state changes for accessory mode as required.
 */
class NativeVendorControlRequestMonitorThread {
    android::base::unique_fd mMonitorFd;
    int mShutdownPipefd[2];
    std::thread mThread;
    jobject mCallbackObj;

    void handleControlRequest(int fd, const struct usb_ctrlrequest *setup) {
        JNIEnv *env = AndroidRuntime::getJNIEnv();

        uint8_t type = setup->bRequestType;
        uint8_t code = setup->bRequest;
        uint16_t length = setup->wLength;
        uint16_t index = setup->wIndex;
        uint16_t value = setup->wValue;
        std::vector<char> buf;
        buf.resize(length + 1);
        std::string accessoryControlState;

        if ((type & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
            switch (code) {
                // TODO(b/421809234): - Add support for accessory mode requests.
                // TODO(b/421807206): - Add support for accessory HID requests.

                default:
                    ALOGE("Unrecognized USB vendor request! %d", (int)code);
                    goto fail;
            }
        } else {
            ALOGE("Unrecognized USB request type %d", (int)type);
            goto fail;
        }

        if (type & USB_DIR_IN) {
            if (::write(fd, buf.data(), length) != length) {
                ALOGE("Usb error ctrlreq write data");
                goto fail;
            }
        }

        return;
    fail:
        // stall control endpoint by applying opposite i/o
        if (type & USB_DIR_IN) {
            if (::read(fd, buf.data(), 0) != -1 || errno != EL2HLT) {
                ALOGE("Couldn't halt ep0 on in request");
            }
        } else {
            if (::write(fd, buf.data(), 0) != -1 || errno != EL2HLT) {
                ALOGE("Couldn't halt ep0 on out request");
            }
        }
    }

    void teardown() {
        // Add teardown for vendor control requests being handled.

        ALOGI("Vendor control request monitor teardown");
    }

    int setupEpoll(android::base::unique_fd &epollFd) {
        struct epoll_event ev;

        ev.data.fd = mMonitorFd.get();
        ev.events = EPOLLIN;
        if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, mMonitorFd.get(), &ev) != 0) {
            ALOGE("epoll_ctl failed for ctrl request monitor fd; %s", strerror(errno));
            return errno;
        }

        ev.data.fd = mShutdownPipefd[0];
        ev.events = EPOLLIN;
        if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, mShutdownPipefd[0], &ev) != 0) {
            ALOGE("epoll_ctl failed for ctrl request pipe fd; %s", strerror(errno));
            return errno;
        }

        return 0;
    }

    void monitorLoop() {
        android::base::unique_fd epollFd(epoll_create(EPOLL_MAX_EVENTS));
        std::vector<struct usb_functionfs_event> ffs_events(FFS_NUM_EVENTS);

        ALOGI("Monitoring vendor control requests...");

        if (epollFd.get() == -1) {
            ALOGE("Vendor control request monitor epoll_create failed; %s", strerror(errno));
            return;
        }

        if (setupEpoll(epollFd) != 0) {
            ALOGE("Vendor control request monitor setupEpoll failed!");
            return;
        }

        JNIEnv *env = nullptr;
        JavaVMAttachArgs aargs = {JNI_VERSION_1_4, "NativeVendorControlRequestMonitorThread",
                                  nullptr};
        if (gvm->AttachCurrentThread(&env, &aargs) != JNI_OK || env == nullptr) {
            ALOGE("Couldn't attach thread");
            return;
        }

        struct epoll_event events[EPOLL_MAX_EVENTS];
        int nevents = 0;
        while (true) {
            nevents = epoll_wait(epollFd.get(), events, EPOLL_MAX_EVENTS, -1);

            if (nevents < 0) {
                if (errno != EINTR)
                    ALOGE("Vendor control request monitor epoll_wait failed; %s", strerror(errno));
                continue;
            }

            for (int i = 0; i < nevents; ++i) {
                int fd = events[i].data.fd;
                if (fd == mShutdownPipefd[0]) {
                    ALOGE("Vendor control request monitor loop exiting...");
                    goto exit;
                } else if (fd == mMonitorFd.get()) {
                    if (events[i].events & EPOLLIN) {
                        struct usb_functionfs_event *event = ffs_events.data();
                        int nbytes = TEMP_FAILURE_RETRY(
                                ::read(fd, event,
                                       ffs_events.size() * sizeof(usb_functionfs_event)));
                        if (nbytes == -1) {
                            ALOGE("error reading Usb control events");
                            continue;
                        }
                        for (size_t n = nbytes / sizeof(*event); n; --n, ++event) {
                            switch (event->type) {
                                case FUNCTIONFS_SETUP:
                                    handleControlRequest(fd, &event->u.setup);
                                    break;
                                case FUNCTIONFS_UNBIND:
                                    teardown();
                                    break;
                                default:
                                    continue;
                            }
                        }
                    }
                }
            }
        }

    exit:
        auto res = gvm->DetachCurrentThread();
        ALOGE("Detaching thread");
        ALOGE_IF(res != JNI_OK, "Couldn't detach thread");
        return;
    }

    void stop() {
        if (mThread.joinable()) {
            int c = 'q';
            write(mShutdownPipefd[1], &c, 1);
            mThread.join();
        }
    }

    DISALLOW_COPY_AND_ASSIGN(NativeVendorControlRequestMonitorThread);

public:
    explicit NativeVendorControlRequestMonitorThread(jobject obj,
                                                     android::base::unique_fd monitorFd)
          : mMonitorFd(std::move(monitorFd)){
        mCallbackObj = AndroidRuntime::getJNIEnv()->NewGlobalRef(obj);
        pipe(mShutdownPipefd);
        mThread = std::thread(&NativeVendorControlRequestMonitorThread::monitorLoop, this);
    }

    ~NativeVendorControlRequestMonitorThread() {
        stop();
        close(mShutdownPipefd[0]);
        close(mShutdownPipefd[1]);
        AndroidRuntime::getJNIEnv()->DeleteGlobalRef(mCallbackObj);
    }
};
static std::unique_ptr<NativeVendorControlRequestMonitorThread> sVendorControlRequestMonitorThread;

static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index)
{
    char buffer[256];