/* * Copyright (C) 2018 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_NDEBUG 0 #define LOG_TAG "android.hardware.power@1.2-service.wahoo-libperfmgr" #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) #include <fcntl.h> #include <poll.h> #include <sys/eventfd.h> #include <time.h> #include <unistd.h> #include <utils/Log.h> #include <utils/Trace.h> #include "InteractionHandler.h" #define FB_IDLE_PATH "/sys/class/graphics/fb0/idle_state" #define MAX_LENGTH 64 #define MSINSEC 1000L #define USINMS 1000000L InteractionHandler::InteractionHandler(std::shared_ptr<HintManager> const & hint_manager) : mState(INTERACTION_STATE_UNINITIALIZED), mWaitMs(100), mMinDurationMs(1400), mMaxDurationMs(5650), mDurationMs(0), mHintManager(hint_manager) { } InteractionHandler::~InteractionHandler() { Exit(); } bool InteractionHandler::Init() { std::lock_guard<std::mutex> lk(mLock); if (mState != INTERACTION_STATE_UNINITIALIZED) return true; mIdleFd = open(FB_IDLE_PATH, O_RDONLY); if (mIdleFd < 0) { ALOGE("Unable to open idle state path (%d)", errno); return false; } mEventFd = eventfd(0, EFD_NONBLOCK); if (mEventFd < 0) { ALOGE("Unable to create event fd (%d)", errno); close(mIdleFd); return false; } mState = INTERACTION_STATE_IDLE; mThread = std::unique_ptr<std::thread>( new std::thread(&InteractionHandler::Routine, this)); return true; } void InteractionHandler::Exit() { std::unique_lock<std::mutex> lk(mLock); if (mState == INTERACTION_STATE_UNINITIALIZED) return; AbortWaitLocked(); mState = INTERACTION_STATE_UNINITIALIZED; lk.unlock(); mCond.notify_all(); mThread->join(); close(mEventFd); close(mIdleFd); } void InteractionHandler::PerfLock() { ALOGV("%s: acquiring perf lock", __func__); if (!mHintManager->DoHint("INTERACTION")) { ALOGE("%s: do hint INTERACTION failed", __func__); } ATRACE_INT("interaction_lock", 1); } void InteractionHandler::PerfRel() { ALOGV("%s: releasing perf lock", __func__); if (!mHintManager->EndHint("INTERACTION")) { ALOGE("%s: end hint INTERACTION failed", __func__); } ATRACE_INT("interaction_lock", 0); } long long InteractionHandler::CalcTimespecDiffMs(struct timespec start, struct timespec end) { long long diff_in_us = 0; diff_in_us += (end.tv_sec - start.tv_sec) * MSINSEC; diff_in_us += (end.tv_nsec - start.tv_nsec) / USINMS; return diff_in_us; } void InteractionHandler::Acquire(int32_t duration) { ATRACE_CALL(); std::lock_guard<std::mutex> lk(mLock); if (mState == INTERACTION_STATE_UNINITIALIZED) { ALOGW("%s: called while uninitialized", __func__); return; } int inputDuration = duration + 650; int finalDuration; if (inputDuration > mMaxDurationMs) finalDuration = mMaxDurationMs; else if (inputDuration > mMinDurationMs) finalDuration = inputDuration; else finalDuration = mMinDurationMs; struct timespec cur_timespec; clock_gettime(CLOCK_MONOTONIC, &cur_timespec); if (mState != INTERACTION_STATE_IDLE && finalDuration <= mDurationMs) { long long elapsed_time = CalcTimespecDiffMs(mLastTimespec, cur_timespec); // don't hint if previous hint's duration covers this hint's duration if (elapsed_time <= (mDurationMs - finalDuration)) { ALOGV("%s: Previous duration (%d) cover this (%d) elapsed: %lld", __func__, mDurationMs, finalDuration, elapsed_time); return; } } mLastTimespec = cur_timespec; mDurationMs = finalDuration; ALOGV("%s: input: %d final duration: %d", __func__, duration, finalDuration); if (mState == INTERACTION_STATE_WAITING) AbortWaitLocked(); else if (mState == INTERACTION_STATE_IDLE) PerfLock(); mState = INTERACTION_STATE_INTERACTION; mCond.notify_one(); } void InteractionHandler::Release() { std::lock_guard<std::mutex> lk(mLock); if (mState == INTERACTION_STATE_WAITING) { ATRACE_CALL(); PerfRel(); mState = INTERACTION_STATE_IDLE; } else { // clear any wait aborts pending in event fd uint64_t val; ssize_t ret = read(mEventFd, &val, sizeof(val)); ALOGW_IF(ret < 0, "%s: failed to clear eventfd (%zd, %d)", __func__, ret, errno); } } // should be called while locked void InteractionHandler::AbortWaitLocked() { uint64_t val = 1; ssize_t ret = write(mEventFd, &val, sizeof(val)); if (ret != sizeof(val)) ALOGW("Unable to write to event fd (%zd)", ret); } void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) { char data[MAX_LENGTH]; ssize_t ret; struct pollfd pfd[2]; ATRACE_CALL(); ALOGV("%s: wait:%d timeout:%d", __func__, wait_ms, timeout_ms); pfd[0].fd = mEventFd; pfd[0].events = POLLIN; pfd[1].fd = mIdleFd; pfd[1].events = POLLPRI | POLLERR; ret = poll(pfd, 1, wait_ms); if (ret > 0) { ALOGV("%s: wait aborted", __func__); return; } else if (ret < 0) { ALOGE("%s: error in poll while waiting", __func__); return; } ret = pread(mIdleFd, data, sizeof(data), 0); if (!ret) { ALOGE("%s: Unexpected EOF!", __func__); return; } if (!strncmp(data, "idle", 4)) { ALOGV("%s: already idle", __func__); return; } ret = poll(pfd, 2, timeout_ms); if (ret < 0) ALOGE("%s: Error on waiting for idle (%zd)", __func__, ret); else if (ret == 0) ALOGV("%s: timed out waiting for idle", __func__); else if (pfd[0].revents) ALOGV("%s: wait for idle aborted", __func__); else if (pfd[1].revents) ALOGV("%s: idle detected", __func__); } void InteractionHandler::Routine() { std::unique_lock<std::mutex> lk(mLock, std::defer_lock); while (true) { lk.lock(); mCond.wait(lk, [&] { return mState != INTERACTION_STATE_IDLE; }); if (mState == INTERACTION_STATE_UNINITIALIZED) return; mState = INTERACTION_STATE_WAITING; lk.unlock(); WaitForIdle(mWaitMs, mDurationMs); Release(); } }