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
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
|