You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

523 lines
13 KiB

/*
* Copyright (c) Hisilicon Technologies Co., Ltd.. 2016-2019. All rights reserved.
* Description: bootvideo input
*/
#include "BootvideoInput.h"
#include <string>
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/times.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <cutils/properties.h>
#include <linux/input.h>
#include <functional>
#include <memory>
#include <android-base/unique_fd.h>
#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<int(int fd, uint32_t epevents)>;
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<BootvideoUI> g_bvUi = nullptr;
sp<BootvideoPlayer> 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<FdInfo*>(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, decltype(&closedir)> 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<android::BootvideoUI> bUi,
android::sp<android::BootvideoPlayer> 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