/* * Copyright (c) Hisilicon Technologies Co., Ltd.. 2016-2019. All rights reserved. * Description: bootvideo input */ #include "BootvideoInput.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "securec.h" #include "IAudio.h" #include "ITVManager.h" #undef LOG_TAG #define LOG_TAG "BootVideo" namespace android { // input devie name const std::string INPUT_DEVICE_NAME = "/dev/input/event0"; // key code const int KEYCODE_VOLUME_UP = 115; const int KEYCODE_VOLUME_DOWN = 114; const int KEYCODE_VOLUME_MUTE = 113; const int KEYCODE_OK = 28; // volume const int VOLUME_MAX_VALUE = 100; const int VOLUME_MIN_VALUE = 0; const int VOLUME_DEF_VALUE = 0; // ui envent const int UI_EVENT_NONE = 0; const int UI_EVENT_VOLUME = 1; const int UI_EVENT_MUTE = 2; // def vlaue const int DEF_PTHREAD_ID = -1; const int DEF_FILE_ID = -1; const unsigned int DEF_KEY_CODE = 0; // timestamp const int CONVERT_SEC_TO_MS = 1000; // sec to ms unit const int CONVERT_NSEC_TO_MS = 1000000; // nanosecond to ms unit const int DELAY_1000_MS = 1000; // delay 1000ms const int DELAY_100000_US = 100000; // delay 100000us, equal 100ms // define for input using ev_callback = std::function; constexpr size_t MAX_DEVICES = 16; constexpr size_t MAX_MISC_FDS = 16; static android::base::unique_fd g_epollFd; static epoll_event g_polledEvents[MAX_DEVICES + MAX_MISC_FDS]; static int g_polledEventsCount; constexpr size_t BITS_PER_LONG = sizeof(unsigned long) * 8; constexpr size_t BITS_TO_LONGS(size_t bits) { return ((bits + BITS_PER_LONG - 1) / BITS_PER_LONG); } struct FdInfo { android::base::unique_fd fd; ev_callback cb; }; static FdInfo g_evFdinfo[MAX_DEVICES + MAX_MISC_FDS]; static size_t g_evCount = 0; static size_t g_evDevCount = 0; static size_t g_evMiscCount = 0; // end for input static sem_t g_uirunLockout; sp g_bvUi = nullptr; sp g_bvPlayer = nullptr; pthread_t g_inputTid = DEF_PTHREAD_ID; pthread_t g_uiTid = DEF_PTHREAD_ID; pthread_t g_longPressTid = DEF_PTHREAD_ID; int g_fd = DEF_FILE_ID; unsigned int g_downCode = DEF_KEY_CODE; unsigned int g_upCode = DEF_KEY_CODE; int g_okCode = DEF_KEY_CODE; int g_volume = VOLUME_DEF_VALUE; int g_mute = 0; int g_inputRunning = 0; // input running state int g_needUi = UI_EVENT_NONE; int g_pthreadRet = 0; // create phtread ret char g_volumeIndex[PROPERTY_VALUE_MAX]; // property IAudio* g_audioManager = nullptr; void* LongPressRun(void*); void* InputRun(void*); void* UIRun(void*); inline long long GetCurrentTime() { struct timespec ts = {0, 0}; int res = clock_gettime(CLOCK_MONOTONIC, &ts); if (res == 0) { const long long tempSecToMs = ts.tv_sec * CONVERT_SEC_TO_MS; const long long tempNsecToMs = ts.tv_nsec / CONVERT_NSEC_TO_MS; return tempSecToMs + tempNsecToMs; } return 0; } int OpenInputDevice() { return open(INPUT_DEVICE_NAME.c_str(), O_RDONLY | O_NONBLOCK); } int GetVolume(const int& channel) { int volume = 0; if (g_audioManager) { volume = g_audioManager->getVolume(channel); ALOGE("GetVolume from mw:%d:%d", channel, volume); } return volume; } void SetVolume(const int& channel, const int& volume) { ALOGE("SetVolume to mw:%d:%d", channel, volume); if (g_audioManager) { g_audioManager->setVolume(channel, volume); } } void SetVolume() { // up/down key, cancel mute state if (g_mute) { g_mute = 0; property_set("persist.service.bootvideo.mute", std::to_string(g_mute).c_str()); } // set player volume status if (g_bvPlayer) { g_bvPlayer->SetVolume(g_mute, g_volume); } // set bootvideo volume property property_set("persist.service.bootvideo.volume", std::to_string(g_volume).c_str()); // set ui volume status g_needUi = UI_EVENT_VOLUME; g_bvUi->SetVolumeStatus(g_needUi, g_mute, g_volume); } void SetMute() { // mute key, flip mute state g_mute = !g_mute; // set bootvideo mute property property_set("persist.service.bootvideo.mute", std::to_string(g_mute).c_str()); // set player volume status if (g_bvPlayer) { g_bvPlayer->SetVolume(g_mute, g_volume); } // set ui volume status g_needUi = UI_EVENT_MUTE; if (g_bvUi) { g_bvUi->SetVolumeStatus(g_needUi, g_mute, g_volume); } } void PostVolume(unsigned int value) { bool isMuteCode = false; switch (value) { case KEYCODE_VOLUME_UP: g_volume++; if (g_volume > VOLUME_MAX_VALUE) { g_volume = VOLUME_MAX_VALUE; } break; case KEYCODE_VOLUME_DOWN: g_volume--; if (g_volume < VOLUME_MIN_VALUE) { g_volume = VOLUME_MIN_VALUE; } break; case KEYCODE_VOLUME_MUTE: isMuteCode = true; break; case KEYCODE_OK: g_okCode = 1; break; default: break; } if (isMuteCode) { SetMute(); } else if (value == KEYCODE_VOLUME_UP || value == KEYCODE_VOLUME_DOWN) { SetVolume(); } } bool IsPressKeyOK() { if (g_okCode == 1) { return true; } return false; } void ResetPressKeyOK() { if (g_okCode == 1) { g_okCode = DEF_KEY_CODE; } return; } void ProcessLongPress() { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); g_pthreadRet = pthread_create(&g_longPressTid, &attr, LongPressRun, nullptr); if (g_pthreadRet < 0) { ALOGE("pthread_create long_press_run fail..."); } pthread_attr_destroy(&attr); } void ProcessEvent(struct input_event event) { if (event.code <= 0) { return; } if (event.value == 1) { // Down g_downCode = event.code; ProcessLongPress(); } else { // Up if (event.code == g_downCode) { g_upCode = event.code; } else { ALOGE("Error code: %d", event.code); } if (g_longPressTid != DEF_PTHREAD_ID) { // up envent should be destroy the thread pthread_join(g_longPressTid, nullptr); g_longPressTid = DEF_PTHREAD_ID; } } // key event, post g_uirunLockout int ret = sem_post(&g_uirunLockout); if (ret < 0) { ALOGE("key event post g_uirunLockout fail"); } } int EvGetInput(int fd, uint32_t epevents, input_event* ev) { if (epevents & EPOLLIN) { ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev))); if (r == sizeof(*ev)) { return 0; } } return -1; } int EvWait(int timeout) { g_polledEventsCount = epoll_wait(g_epollFd, g_polledEvents, g_evCount, timeout); if (g_polledEventsCount <= 0) { return -1; } return 0; } void EvDispatch(void) { for (int n = 0; n < g_polledEventsCount; n++) { FdInfo* fdi = static_cast(g_polledEvents[n].data.ptr); const ev_callback& cb = fdi->cb; if (cb) { cb(fdi->fd, g_polledEvents[n].events); } } } int InputCallback(int fd, uint32_t epevents) { struct input_event event; if (EvGetInput(fd, epevents, &event)) { return -1; } if (event.type == EV_KEY) { ProcessEvent(event); } return 0; } void EvExit(void) { while (g_evCount > 0) { g_evFdinfo[--g_evCount].fd.reset(); } g_evMiscCount = 0; g_evDevCount = 0; g_epollFd.reset(); } int EvInit(ev_callback inputCb) { g_epollFd.reset(); base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC)); if (epoll_fd == -1) { return -1; } std::unique_ptr dir(opendir("/dev/input"), closedir); if (!dir) { return -1; } bool epollCtlFailed = false; dirent* de; while ((de = readdir(dir.get())) != nullptr) { if (strncmp(de->d_name, "event", 5)) continue; base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC)); if (fd == -1) continue; // Use unsigned long to match ioctl's parameter type. unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT // Read the evbits of the input device. if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { continue; } epoll_event ev; ev.events = EPOLLIN | EPOLLWAKEUP; ev.data.ptr = &g_evFdinfo[g_evCount]; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { epollCtlFailed = true; continue; } g_evFdinfo[g_evCount].fd.reset(fd.release()); g_evFdinfo[g_evCount].cb = inputCb; g_evCount++; g_evDevCount++; if (g_evDevCount == MAX_DEVICES) break; } if (epollCtlFailed && !g_evCount) { return -1; } g_epollFd.reset(epoll_fd.release()); return 0; } void InitAudioManager() { ITVManager *m_tvmw = TVCreator::create(); if (m_tvmw) { g_audioManager = m_tvmw->getAudio(); } } void* InputRun(void*) { if (g_audioManager == nullptr) { InitAudioManager(); } EvInit(std::bind(InputCallback, std::placeholders::_1, std::placeholders::_2)); const int timeout = 500; while (g_inputRunning) { if (!EvWait(timeout)) { EvDispatch(); } } EvExit(); return nullptr; } void* UIRun(void*) { long long startTime = 0; int uiMode = UI_EVENT_NONE; if (g_bvUi == nullptr) { ALOGE("UI is NULL"); return nullptr; } while (g_inputRunning) { usleep(DELAY_100000_US); if (g_needUi != UI_EVENT_NONE) { // store the ui mode uiMode = g_needUi; // clear the globle ui event g_needUi = UI_EVENT_NONE; // reset the start time startTime = GetCurrentTime(); } if (uiMode != UI_EVENT_NONE) { if ((GetCurrentTime() - startTime) < DELAY_1000_MS) { continue; } else if (uiMode == UI_EVENT_MUTE) { continue; } else { uiMode = UI_EVENT_NONE; } } if (g_bvUi != nullptr) { g_bvUi->SetVolumeStatus(uiMode, g_mute, g_volume); } int ret = sem_wait(&g_uirunLockout); if (ret < 0) { ALOGE("UIRun wait g_uirunLockout fail"); } } return nullptr; } void* LongPressRun(void*) { long long startTime = GetCurrentTime(); while (g_downCode > 0 && g_upCode == DEF_KEY_CODE) { if ((GetCurrentTime() - startTime) > DELAY_1000_MS) { // long press PostVolume(g_downCode); } usleep(DELAY_100000_US); if (g_inputRunning == 0) { return nullptr; } } if ((GetCurrentTime() - startTime) <= DELAY_1000_MS) { // short press PostVolume(g_downCode); } // reset g_downCode = DEF_KEY_CODE; g_upCode = DEF_KEY_CODE; return nullptr; } void StartInput(int volume, android::sp bUi, android::sp bPlayer) { g_volume = volume; g_bvUi = bUi; g_bvPlayer = bPlayer; if ((g_fd = OpenInputDevice()) < 0) { ALOGE("Fail to open device: %s\n", INPUT_DEVICE_NAME.c_str()); } g_inputRunning = 1; // init g_uirunLockout int ret = sem_init(&g_uirunLockout, 0, 0); if (ret < 0) { ALOGE("uirun_lockout sem_init fail"); g_inputRunning = 0; close(g_fd); g_fd = DEF_FILE_ID; return; } // start to read input event pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); g_pthreadRet = pthread_create(&g_inputTid, &attr, InputRun, nullptr); if (g_pthreadRet < 0) { ALOGE("pthread_create input_run fail..."); } g_pthreadRet = pthread_create(&g_uiTid, &attr, UIRun, nullptr); if (g_pthreadRet < 0) { ALOGE("pthread_create input_run fail..."); } pthread_attr_destroy(&attr); } void StopInput() { g_bvUi = nullptr; g_bvPlayer = nullptr; g_inputRunning = 0; int ret = sem_post(&g_uirunLockout); if (ret < 0) { ALOGE("StopInput post uirun_lockout fail"); } if (g_inputTid != DEF_PTHREAD_ID) { pthread_join(g_inputTid, nullptr); } if (g_uiTid != DEF_PTHREAD_ID) { pthread_join(g_uiTid, nullptr); } // stop thread and reset id sem_destroy(&g_uirunLockout); g_inputTid = DEF_PTHREAD_ID; g_uiTid = DEF_PTHREAD_ID; close(g_fd); g_fd = DEF_FILE_ID; } } // namespace android