/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "android.hardware.usb.aidl-service" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Usb.h" using android::base::GetProperty; using android::base::Trim; namespace aidl { namespace android { namespace hardware { namespace usb { // Set by the signal handler to destroy the thread volatile bool destroyThread; constexpr char kConsole[] = "init.svc.console"; constexpr char kDetectedPath[] = "/sys/class/power_supply/usb/moisture_detected"; constexpr char kDisableContatminantDetection[] = "vendor.usb.contaminantdisable"; constexpr char kEnabledPath[] = "/sys/class/power_supply/usb/moisture_detection_enabled"; constexpr char kTypecPath[] = "/sys/class/typec"; void queryVersionHelper(android::hardware::usb::Usb *usb, std::vector *currentPortStatus); ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable, int64_t in_transactionId) { bool result = true; std::vector currentPortStatus; string pullup; ALOGI("Userspace turn %s USB data signaling. opID:%ld", in_enable ? "on" : "off", in_transactionId); if (in_enable) { if (ReadFileToString(PULLUP_PATH, &pullup)) { pullup = Trim(pullup); if (pullup != kGadgetName) { if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) { ALOGE("Gadget cannot be pulled up"); result = false; } } } if (!WriteStringToFile("1", USB_DATA_PATH)) { ALOGE("Not able to turn on usb connection notification"); result = false; } } else { if (ReadFileToString(PULLUP_PATH, &pullup)) { pullup = Trim(pullup); if (pullup == kGadgetName) { if (!WriteStringToFile("none", PULLUP_PATH)) { ALOGE("Gadget cannot be pulled down"); result = false; } } } if (!WriteStringToFile("0", USB_DATA_PATH)) { ALOGE("Not able to turn off usb connection notification"); result = false; } } if (result) { mUsbDataEnabled = in_enable; } pthread_mutex_lock(&mLock); if (mCallback != NULL) { ScopedAStatus ret = mCallback->notifyEnableUsbDataStatus( in_portName, in_enable, result ? Status::SUCCESS : Status::ERROR, in_transactionId); if (!ret.isOk()) ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); queryVersionHelper(this, ¤tPortStatus); return ScopedAStatus::ok(); } ScopedAStatus Usb::enableUsbDataWhileDocked(const string& in_portName, int64_t in_transactionId) { std::vector currentPortStatus; ALOGI("Userspace enableUsbDataWhileDocked opID:%ld", in_transactionId); pthread_mutex_lock(&mLock); if (mCallback != NULL) { ScopedAStatus ret = mCallback->notifyEnableUsbDataWhileDockedStatus( in_portName, Status::NOT_SUPPORTED, in_transactionId); if (!ret.isOk()) ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); queryVersionHelper(this, ¤tPortStatus); return ScopedAStatus::ok(); } ScopedAStatus Usb::resetUsbPort(const std::string& in_portName, int64_t in_transactionId) { bool result = true; std::vector currentPortStatus; ALOGI("Userspace reset USB Port. opID:%ld", in_transactionId); if (!WriteStringToFile("none", PULLUP_PATH)) { ALOGI("Gadget cannot be pulled down"); result = false; } pthread_mutex_lock(&mLock); if (mCallback != NULL) { ::ndk::ScopedAStatus ret = mCallback->notifyResetUsbPortStatus( in_portName, result ? Status::SUCCESS : Status::ERROR, in_transactionId); if (!ret.isOk()) ALOGE("notifyTransactionStatus error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); return ::ndk::ScopedAStatus::ok(); } ScopedAStatus Usb::limitPowerTransfer(const string& in_portName, bool in_limit, int64_t in_transactionId) { std::vector currentPortStatus; bool sessionFail = false, success; pthread_mutex_lock(&mLock); ALOGI("limitPowerTransfer limit:%c opId:%ld", in_limit ? 'y' : 'n', in_transactionId); if (in_limit) { success = WriteStringToFile("0", SINK_CURRENT_LIMIT_PATH); if (!success) { ALOGE("Failed to set sink current limit"); sessionFail = true; } } success = WriteStringToFile(in_limit ? "1" : "0", SINK_LIMIT_ENABLE_PATH); if (!success) { ALOGE("Failed to %s sink current limit: %s", in_limit ? "enable" : "disable", SINK_LIMIT_ENABLE_PATH); sessionFail = true; } success = WriteStringToFile(in_limit ? "1" : "0", SOURCE_LIMIT_ENABLE_PATH); if (!success) { ALOGE("Failed to %s source current limit: %s", in_limit ? "enable" : "disable", SOURCE_LIMIT_ENABLE_PATH); sessionFail = true; } if (mCallback != NULL && in_transactionId >= 0) { ScopedAStatus ret = mCallback->notifyLimitPowerTransferStatus( in_portName, in_limit, sessionFail ? Status::ERROR : Status::SUCCESS, in_transactionId); if (!ret.isOk()) ALOGE("limitPowerTransfer error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); queryVersionHelper(this, ¤tPortStatus); return ScopedAStatus::ok(); } Status queryMoistureDetectionStatus(std::vector *currentPortStatus) { string enabled, status, path, DetectedPath; (*currentPortStatus)[0].supportedContaminantProtectionModes .push_back(ContaminantProtectionMode::FORCE_DISABLE); (*currentPortStatus)[0].contaminantProtectionStatus = ContaminantProtectionStatus::NONE; (*currentPortStatus)[0].contaminantDetectionStatus = ContaminantDetectionStatus::DISABLED; (*currentPortStatus)[0].supportsEnableContaminantPresenceDetection = true; (*currentPortStatus)[0].supportsEnableContaminantPresenceProtection = false; if (!ReadFileToString(kEnabledPath, &enabled)) { ALOGE("Failed to open moisture_detection_enabled"); return Status::ERROR; } enabled = Trim(enabled); if (enabled == "1") { if (!ReadFileToString(kDetectedPath, &status)) { ALOGE("Failed to open moisture_detected"); return Status::ERROR; } status = Trim(status); if (status == "1") { (*currentPortStatus)[0].contaminantDetectionStatus = ContaminantDetectionStatus::DETECTED; (*currentPortStatus)[0].contaminantProtectionStatus = ContaminantProtectionStatus::FORCE_DISABLE; } else { (*currentPortStatus)[0].contaminantDetectionStatus = ContaminantDetectionStatus::NOT_DETECTED; } } ALOGI("ContaminantDetectionStatus:%d ContaminantProtectionStatus:%d", (*currentPortStatus)[0].contaminantDetectionStatus, (*currentPortStatus)[0].contaminantProtectionStatus); return Status::SUCCESS; } string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) { string node("/sys/class/typec/" + portName); switch (tag) { case PortRole::dataRole: return node + "/data_role"; case PortRole::powerRole: return node + "/power_role"; case PortRole::mode: return node + "/port_type"; default: return ""; } } string convertRoletoString(PortRole role) { if (role.getTag() == PortRole::powerRole) { if (role.get() == PortPowerRole::SOURCE) return "source"; else if (role.get() == PortPowerRole::SINK) return "sink"; } else if (role.getTag() == PortRole::dataRole) { if (role.get() == PortDataRole::HOST) return "host"; if (role.get() == PortDataRole::DEVICE) return "device"; } else if (role.getTag() == PortRole::mode) { if (role.get() == PortMode::UFP) return "sink"; if (role.get() == PortMode::DFP) return "source"; } return "none"; } void extractRole(string *roleName) { std::size_t first, last; first = roleName->find("["); last = roleName->find("]"); if (first != string::npos && last != string::npos) { *roleName = roleName->substr(first + 1, last - first - 1); } } void switchToDrp(const string &portName) { string filename = appendRoleNodeHelper(string(portName.c_str()), PortRole::mode); FILE *fp; if (filename != "") { fp = fopen(filename.c_str(), "w"); if (fp != NULL) { int ret = fputs("dual", fp); fclose(fp); if (ret == EOF) ALOGE("Fatal: Error while switching back to drp"); } else { ALOGE("Fatal: Cannot open file to switch back to drp"); } } else { ALOGE("Fatal: invalid node type"); } } bool switchMode(const string &portName, const PortRole &in_role, struct Usb *usb) { string filename = appendRoleNodeHelper(string(portName.c_str()), in_role.getTag()); string written; FILE *fp; bool roleSwitch = false; if (filename == "") { ALOGE("Fatal: invalid node type"); return false; } fp = fopen(filename.c_str(), "w"); if (fp != NULL) { // Hold the lock here to prevent loosing connected signals // as once the file is written the partner added signal // can arrive anytime. pthread_mutex_lock(&usb->mPartnerLock); usb->mPartnerUp = false; int ret = fputs(convertRoletoString(in_role).c_str(), fp); fclose(fp); if (ret != EOF) { struct timespec to; struct timespec now; wait_again: clock_gettime(CLOCK_MONOTONIC, &now); to.tv_sec = now.tv_sec + PORT_TYPE_TIMEOUT; to.tv_nsec = now.tv_nsec; int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to); // There are no uevent signals which implies role swap timed out. if (err == ETIMEDOUT) { ALOGI("uevents wait timedout"); // Validity check. } else if (!usb->mPartnerUp) { goto wait_again; // Role switch succeeded since usb->mPartnerUp is true. } else { roleSwitch = true; } } else { ALOGI("Role switch failed while wrting to file"); } pthread_mutex_unlock(&usb->mPartnerLock); } if (!roleSwitch) switchToDrp(string(portName.c_str())); return roleSwitch; } Usb::Usb() : mLock(PTHREAD_MUTEX_INITIALIZER), mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER), mPartnerLock(PTHREAD_MUTEX_INITIALIZER), mPartnerUp(false), mUsbDataEnabled(true) { pthread_condattr_t attr; if (pthread_condattr_init(&attr)) { ALOGE("pthread_condattr_init failed: %s", strerror(errno)); abort(); } if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) { ALOGE("pthread_condattr_setclock failed: %s", strerror(errno)); abort(); } if (pthread_cond_init(&mPartnerCV, &attr)) { ALOGE("pthread_cond_init failed: %s", strerror(errno)); abort(); } if (pthread_condattr_destroy(&attr)) { ALOGE("pthread_condattr_destroy failed: %s", strerror(errno)); abort(); } } ScopedAStatus Usb::switchRole(const string& in_portName, const PortRole& in_role, int64_t in_transactionId) { string filename = appendRoleNodeHelper(string(in_portName.c_str()), in_role.getTag()); string written; FILE *fp; bool roleSwitch = false; if (filename == "") { ALOGE("Fatal: invalid node type"); return ScopedAStatus::ok(); } pthread_mutex_lock(&mRoleSwitchLock); ALOGI("filename write: %s role:%s", filename.c_str(), convertRoletoString(in_role).c_str()); if (in_role.getTag() == PortRole::mode) { roleSwitch = switchMode(in_portName, in_role, this); } else { fp = fopen(filename.c_str(), "w"); if (fp != NULL) { int ret = fputs(convertRoletoString(in_role).c_str(), fp); fclose(fp); if ((ret != EOF) && ReadFileToString(filename, &written)) { written = Trim(written); extractRole(&written); ALOGI("written: %s", written.c_str()); if (written == convertRoletoString(in_role)) { roleSwitch = true; } else { ALOGE("Role switch failed"); } } else { ALOGE("failed to update the new role"); } } else { ALOGE("fopen failed"); } } pthread_mutex_lock(&mLock); if (mCallback != NULL) { ScopedAStatus ret = mCallback->notifyRoleSwitchStatus( in_portName, in_role, roleSwitch ? Status::SUCCESS : Status::ERROR, in_transactionId); if (!ret.isOk()) ALOGE("RoleSwitchStatus error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); pthread_mutex_unlock(&mRoleSwitchLock); return ScopedAStatus::ok(); } Status getAccessoryConnected(const string &portName, string *accessory) { string filename = "/sys/class/typec/" + portName + "-partner/accessory_mode"; if (!ReadFileToString(filename, accessory)) { ALOGE("getAccessoryConnected: Failed to open filesystem node: %s", filename.c_str()); return Status::ERROR; } *accessory = Trim(*accessory); return Status::SUCCESS; } Status getCurrentRoleHelper(const string &portName, bool connected, PortRole *currentRole) { string filename; string roleName; string accessory; if (currentRole->getTag() == PortRole::powerRole) { filename = "/sys/class/typec/" + portName + "/power_role"; currentRole->set(PortPowerRole::NONE); } else if (currentRole->getTag() == PortRole::dataRole) { filename = "/sys/class/typec/" + portName + "/data_role"; currentRole->set(PortDataRole::NONE); } else if (currentRole->getTag() == PortRole::mode) { filename = "/sys/class/typec/" + portName + "/data_role"; currentRole->set(PortMode::NONE); } else { return Status::ERROR; } if (!connected) return Status::SUCCESS; if (currentRole->getTag() == PortRole::mode) { if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) { return Status::ERROR; } if (accessory == "analog_audio") { currentRole->set(PortMode::AUDIO_ACCESSORY); return Status::SUCCESS; } else if (accessory == "debug") { currentRole->set(PortMode::DEBUG_ACCESSORY); return Status::SUCCESS; } } if (!ReadFileToString(filename, &roleName)) { ALOGE("getCurrentRole: Failed to open filesystem node: %s", filename.c_str()); return Status::ERROR; } roleName = Trim(roleName); extractRole(&roleName); if (roleName == "source") { currentRole->set(PortPowerRole::SOURCE); } else if (roleName == "sink") { currentRole->set(PortPowerRole::SINK); } else if (roleName == "host") { if (currentRole->getTag() == PortRole::dataRole) currentRole->set(PortDataRole::HOST); else currentRole->set(PortMode::DFP); } else if (roleName == "device") { if (currentRole->getTag() == PortRole::dataRole) currentRole->set(PortDataRole::DEVICE); else currentRole->set(PortMode::UFP); } else if (roleName != "none") { /* case for none has already been addressed. * so we check if the role isn't none. */ return Status::UNRECOGNIZED_ROLE; } return Status::SUCCESS; } Status getTypeCPortNamesHelper(std::unordered_map *names) { DIR *dp; dp = opendir(kTypecPath); if (dp != NULL) { struct dirent *ep; while ((ep = readdir(dp))) { if (ep->d_type == DT_LNK) { if (string::npos == string(ep->d_name).find("-partner")) { std::unordered_map::const_iterator portName = names->find(ep->d_name); if (portName == names->end()) { names->insert({ep->d_name, false}); } } else { (*names)[std::strtok(ep->d_name, "-")] = true; } } } closedir(dp); return Status::SUCCESS; } ALOGE("Failed to open /sys/class/typec"); return Status::ERROR; } bool canSwitchRoleHelper(const string &portName) { string filename = "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery"; string supportsPD; if (ReadFileToString(filename, &supportsPD)) { supportsPD = Trim(supportsPD); if (supportsPD == "yes") { return true; } } return false; } Status getPortStatusHelper(android::hardware::usb::Usb *usb, std::vector *currentPortStatus) { std::unordered_map names; Status result = getTypeCPortNamesHelper(&names); int i = -1; if (result == Status::SUCCESS) { currentPortStatus->resize(names.size()); for (std::pair port : names) { i++; ALOGI("%s", port.first.c_str()); (*currentPortStatus)[i].portName = port.first; PortRole currentRole; currentRole.set(PortPowerRole::NONE); if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS){ (*currentPortStatus)[i].currentPowerRole = currentRole.get(); } else { ALOGE("Error while retrieving portNames"); goto done; } currentRole.set(PortDataRole::NONE); if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) { (*currentPortStatus)[i].currentDataRole = currentRole.get(); } else { ALOGE("Error while retrieving current port role"); goto done; } currentRole.set(PortMode::NONE); if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) { (*currentPortStatus)[i].currentMode = currentRole.get(); } else { ALOGE("Error while retrieving current data role"); goto done; } (*currentPortStatus)[i].canChangeMode = true; (*currentPortStatus)[i].canChangeDataRole = port.second ? canSwitchRoleHelper(port.first) : false; (*currentPortStatus)[i].canChangePowerRole = port.second ? canSwitchRoleHelper(port.first) : false; (*currentPortStatus)[i].supportedModes.push_back(PortMode::DRP); if (!usb->mUsbDataEnabled) { (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::DISABLED_FORCE); } else { (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::ENABLED); } (*currentPortStatus)[i].powerBrickStatus = PowerBrickStatus::UNKNOWN; ALOGI("%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d " "usbDataEnabled:%d", i, port.first.c_str(), port.second, (*currentPortStatus)[i].canChangeMode, (*currentPortStatus)[i].canChangeDataRole, (*currentPortStatus)[i].canChangePowerRole, usb->mUsbDataEnabled ? 1 : 0); } return Status::SUCCESS; } done: return Status::ERROR; } Status queryPowerTransferStatus(std::vector *currentPortStatus) { string enabled; if (!ReadFileToString(SINK_LIMIT_ENABLE_PATH, &enabled)) { ALOGE("Failed to open limit_sink_enable"); return Status::ERROR; } enabled = Trim(enabled); (*currentPortStatus)[0].powerTransferLimited = enabled == "1"; ALOGI("powerTransferLimited:%d", (*currentPortStatus)[0].powerTransferLimited ? 1 : 0); return Status::SUCCESS; } void queryVersionHelper(android::hardware::usb::Usb *usb, std::vector *currentPortStatus) { Status status; pthread_mutex_lock(&usb->mLock); status = getPortStatusHelper(usb, currentPortStatus); queryMoistureDetectionStatus(currentPortStatus); queryPowerTransferStatus(currentPortStatus); if (usb->mCallback != NULL) { ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus, status); if (!ret.isOk()) ALOGE("queryPortStatus error %s", ret.getDescription().c_str()); } else { ALOGI("Notifying userspace skipped. Callback is NULL"); } pthread_mutex_unlock(&usb->mLock); } ScopedAStatus Usb::queryPortStatus(int64_t in_transactionId) { std::vector currentPortStatus; queryVersionHelper(this, ¤tPortStatus); pthread_mutex_lock(&mLock); if (mCallback != NULL) { ScopedAStatus ret = mCallback->notifyQueryPortStatus( "all", Status::SUCCESS, in_transactionId); if (!ret.isOk()) ALOGE("notifyQueryPortStatus error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); return ScopedAStatus::ok(); } ScopedAStatus Usb::enableContaminantPresenceDetection(const string& in_portName, bool in_enable, int64_t in_transactionId) { string disable = GetProperty(kDisableContatminantDetection, ""); std::string status = GetProperty(kConsole, ""); std::vector currentPortStatus; bool success = true; if (status != "running" && disable != "true") success = WriteStringToFile(in_enable ? "1" : "0", kEnabledPath); pthread_mutex_lock(&mLock); if (mCallback != NULL) { ScopedAStatus ret = mCallback->notifyContaminantEnabledStatus( in_portName, in_enable, success ? Status::SUCCESS : Status::ERROR, in_transactionId); if (!ret.isOk()) ALOGE("notifyContaminantEnabledStatus error %s", ret.getDescription().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); queryVersionHelper(this, ¤tPortStatus); return ScopedAStatus::ok(); } struct data { int uevent_fd; ::aidl::android::hardware::usb::Usb *usb; }; static void uevent_event(uint32_t /*epevents*/, struct data *payload) { char msg[UEVENT_MSG_LEN + 2]; char *cp; int n; n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN); if (n <= 0) return; if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ return; msg[n] = '\0'; msg[n + 1] = '\0'; cp = msg; while (*cp) { if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) { ALOGI("partner added"); pthread_mutex_lock(&payload->usb->mPartnerLock); payload->usb->mPartnerUp = true; pthread_cond_signal(&payload->usb->mPartnerCV); pthread_mutex_unlock(&payload->usb->mPartnerLock); } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_")) || !strncmp(cp, "POWER_SUPPLY_MOISTURE_DETECTED", strlen("POWER_SUPPLY_MOISTURE_DETECTED"))) { std::vector currentPortStatus; queryVersionHelper(payload->usb, ¤tPortStatus); // Role switch is not in progress and port is in disconnected state if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) { for (unsigned long i = 0; i < currentPortStatus.size(); i++) { DIR *dp = opendir(string("/sys/class/typec/" + string(currentPortStatus[i].portName.c_str()) + "-partner").c_str()); if (dp == NULL) { switchToDrp(currentPortStatus[i].portName); } else { closedir(dp); } } pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); } break; } /* advance to after the next \0 */ while (*cp++) { } } } void *work(void *param) { int epoll_fd, uevent_fd; struct epoll_event ev; int nevents = 0; struct data payload; ALOGE("creating thread"); uevent_fd = uevent_open_socket(64 * 1024, true); if (uevent_fd < 0) { ALOGE("uevent_init: uevent_open_socket failed\n"); return NULL; } payload.uevent_fd = uevent_fd; payload.usb = (::aidl::android::hardware::usb::Usb *)param; fcntl(uevent_fd, F_SETFL, O_NONBLOCK); ev.events = EPOLLIN; ev.data.ptr = (void *)uevent_event; epoll_fd = epoll_create(64); if (epoll_fd == -1) { ALOGE("epoll_create failed; errno=%d", errno); goto error; } if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) { ALOGE("epoll_ctl failed; errno=%d", errno); goto error; } while (!destroyThread) { struct epoll_event events[64]; nevents = epoll_wait(epoll_fd, events, 64, -1); if (nevents == -1) { if (errno == EINTR) continue; ALOGE("usb epoll_wait failed; errno=%d", errno); break; } for (int n = 0; n < nevents; ++n) { if (events[n].data.ptr) (*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events, &payload); } } ALOGI("exiting worker thread"); error: close(uevent_fd); if (epoll_fd >= 0) close(epoll_fd); return NULL; } void sighandler(int sig) { if (sig == SIGUSR1) { destroyThread = true; ALOGI("destroy set"); return; } signal(SIGUSR1, sighandler); } ScopedAStatus Usb::setCallback(const shared_ptr& in_callback) { pthread_mutex_lock(&mLock); if ((mCallback == NULL && in_callback == NULL) || (mCallback != NULL && in_callback != NULL)) { mCallback = in_callback; pthread_mutex_unlock(&mLock); return ScopedAStatus::ok(); } mCallback = in_callback; ALOGI("registering callback"); if (mCallback == NULL) { if (!pthread_kill(mPoll, SIGUSR1)) { pthread_join(mPoll, NULL); ALOGI("pthread destroyed"); } pthread_mutex_unlock(&mLock); return ScopedAStatus::ok(); } destroyThread = false; signal(SIGUSR1, sighandler); /* * Create a background thread if the old callback value is NULL * and being updated with a new value. */ if (pthread_create(&mPoll, NULL, work, this)) { ALOGE("pthread creation failed %d", errno); mCallback = NULL; } pthread_mutex_unlock(&mLock); return ScopedAStatus::ok(); } } // namespace usb } // namespace hardware } // namespace android } // aidl