sdm660-common: power-libperfmgr: Switch to AIDL Power HAL

hardware/google/pixel/power-libperfmgr from android-11.0.0_r25

SQUASHED:
Revert all HIDL interface and nuke previous changes
Import Pixel libperfmgr AIDL Power HAL
Adapt and rebrand for xiaomi_sdm660
Remove Google-specific display LPM control
Remove Google-specific camera and audio hints
Remove VR hints handling
Remove audio hints handling
Remove dumpstate support
Initialize powerHAL when boot is completed
Add support for tap-to-wake feature control
Add sepolicy rules for power-libperfmgr
Enable power-libperfmgr

Signed-off-by: Subhajeet Muhuri <subhajeet.muhuri@aosip.dev>
This commit is contained in:
Subhajeet Muhuri 2021-02-25 20:31:33 +05:30 committed by OdSazib
parent 4aec2ab1c3
commit 7c3beb85c9
No known key found for this signature in database
GPG key ID: B678DBD07079B021
44 changed files with 506 additions and 3932 deletions

View file

@ -14,7 +14,7 @@
// limitations under the License.
cc_library {
name: "libdisppower-xiaomi_sdm660",
name: "libdisppower",
proprietary: true,
srcs: [
"disp-power/InteractionHandler.cpp",
@ -28,38 +28,26 @@ cc_library {
],
}
cc_library_headers {
name: "xiaomi_sdm660_power_headers",
vendor: true,
export_include_dirs: ["hidl"],
}
cc_binary {
name: "android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr",
name: "android.hardware.power-service.xiaomi_sdm660-libperfmgr",
relative_install_path: "hw",
vintf_fragments: ["hidl/android.hardware.power@1.3-service.xiaomi_sdm660.xml"],
init_rc: ["hidl/android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr.rc"],
srcs: [
"hidl/service.cpp",
"hidl/Power.cpp",
"hidl/power-helper.c",
],
cflags: [
"-Wall",
"-Werror",
],
init_rc: ["aidl/android.hardware.power-service.xiaomi_sdm660-libperfmgr.rc"],
vintf_fragments: ["aidl/android.hardware.power-service.xiaomi_sdm660.xml"],
vendor: true,
shared_libs: [
"android.hardware.power-ndk_platform",
"libbase",
"libhidlbase",
"libcutils",
"liblog",
"libutils",
"libcutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
"libdisppower-xiaomi_sdm660",
"libbinder_ndk",
"libdisppower",
"libperfmgr",
"pixel-power-ext-ndk_platform",
],
srcs: [
"aidl/service.cpp",
"aidl/Power.cpp",
"aidl/PowerExt.cpp",
],
proprietary: true,
}

View file

@ -0,0 +1,170 @@
/*
* Copyright (C) 2020 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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "android.hardware.power-service.xiaomi_sdm660-libperfmgr"
#include "Power.h"
#include <mutex>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#ifndef TAP_TO_WAKE_NODE
#define TAP_TO_WAKE_NODE "/sys/touchpanel/double_tap"
#endif
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
Power::Power(std::shared_ptr<HintManager> hm)
: mHintManager(hm),
mInteractionHandler(nullptr),
mSustainedPerfModeOn(false) {
mInteractionHandler = std::make_unique<InteractionHandler>(mHintManager);
mInteractionHandler->Init();
std::string state = ::android::base::GetProperty(kPowerHalStateProp, "");
if (state == "SUSTAINED_PERFORMANCE") {
ALOGI("Initialize with SUSTAINED_PERFORMANCE on");
mHintManager->DoHint("SUSTAINED_PERFORMANCE");
mSustainedPerfModeOn = true;
} else {
ALOGI("Initialize PowerHAL");
}
state = ::android::base::GetProperty(kPowerHalRenderingProp, "");
if (state == "EXPENSIVE_RENDERING") {
ALOGI("Initialize with EXPENSIVE_RENDERING on");
mHintManager->DoHint("EXPENSIVE_RENDERING");
}
// Now start to take powerhint
ALOGI("PowerHAL ready to process hints");
}
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
ATRACE_INT(toString(type).c_str(), enabled);
switch (type) {
case Mode::DOUBLE_TAP_TO_WAKE:
{
::android::base::WriteStringToFile(enabled ? "1" : "0", TAP_TO_WAKE_NODE, true);
[[fallthrough]];
}
case Mode::LOW_POWER:
break;
case Mode::SUSTAINED_PERFORMANCE:
if (enabled) {
mHintManager->DoHint("SUSTAINED_PERFORMANCE");
}
mSustainedPerfModeOn = true;
break;
case Mode::LAUNCH:
if (mSustainedPerfModeOn) {
break;
}
[[fallthrough]];
case Mode::FIXED_PERFORMANCE:
[[fallthrough]];
case Mode::EXPENSIVE_RENDERING:
[[fallthrough]];
case Mode::INTERACTIVE:
[[fallthrough]];
case Mode::DEVICE_IDLE:
[[fallthrough]];
case Mode::DISPLAY_INACTIVE:
[[fallthrough]];
default:
if (enabled) {
mHintManager->DoHint(toString(type));
} else {
mHintManager->EndHint(toString(type));
}
break;
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(toString(type));
// LOW_POWER handled insides PowerHAL specifically
if (type == Mode::DOUBLE_TAP_TO_WAKE) {
supported = true;
}
LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs;
ATRACE_INT(toString(type).c_str(), durationMs);
switch (type) {
case Boost::INTERACTION:
if (mSustainedPerfModeOn) {
break;
}
mInteractionHandler->Acquire(durationMs);
break;
case Boost::DISPLAY_UPDATE_IMMINENT:
[[fallthrough]];
case Boost::ML_ACC:
[[fallthrough]];
default:
if (mSustainedPerfModeOn) {
break;
}
if (durationMs > 0) {
mHintManager->DoHint(toString(type), std::chrono::milliseconds(durationMs));
} else if (durationMs == 0) {
mHintManager->DoHint(toString(type));
} else {
mHintManager->EndHint(toString(type));
}
break;
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(toString(type));
LOG(INFO) << "Power boost " << toString(type) << " isBoostSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <atomic>
#include <memory>
#include <thread>
#include <aidl/android/hardware/power/BnPower.h>
#include <perfmgr/HintManager.h>
#include "disp-power/InteractionHandler.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::InteractionHandler;
using ::aidl::android::hardware::power::Boost;
using ::aidl::android::hardware::power::Mode;
using ::android::perfmgr::HintManager;
class Power : public ::aidl::android::hardware::power::BnPower {
public:
Power(std::shared_ptr<HintManager> hm);
ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override;
ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
ndk::ScopedAStatus isBoostSupported(Boost type, bool *_aidl_return) override;
private:
std::shared_ptr<HintManager> mHintManager;
std::unique_ptr<InteractionHandler> mInteractionHandler;
std::atomic<bool> mSustainedPerfModeOn;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2020 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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "android.hardware.power-service.xiaomi_sdm660.ext-libperfmgr"
#include "PowerExt.h"
#include <mutex>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <utils/Log.h>
#include <utils/Trace.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) {
LOG(DEBUG) << "PowerExt setMode: " << mode << " to: " << enabled;
ATRACE_INT(mode.c_str(), enabled);
if (enabled) {
mHintManager->DoHint(mode);
} else {
mHintManager->EndHint(mode);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(mode);
LOG(INFO) << "PowerExt mode " << mode << " isModeSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) {
LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs;
ATRACE_INT(boost.c_str(), durationMs);
if (durationMs > 0) {
mHintManager->DoHint(boost, std::chrono::milliseconds(durationMs));
} else if (durationMs == 0) {
mHintManager->DoHint(boost);
} else {
mHintManager->EndHint(boost);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerExt::isBoostSupported(const std::string &boost, bool *_aidl_return) {
bool supported = mHintManager->IsHintSupported(boost);
LOG(INFO) << "PowerExt boost " << boost << " isBoostSupported: " << supported;
*_aidl_return = supported;
return ndk::ScopedAStatus::ok();
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <atomic>
#include <memory>
#include <thread>
#include <aidl/google/hardware/power/extension/pixel/BnPowerExt.h>
#include <perfmgr/HintManager.h>
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::perfmgr::HintManager;
class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt {
public:
PowerExt(std::shared_ptr<HintManager> hm) : mHintManager(hm) {}
ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override;
ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override;
ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override;
ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override;
private:
std::shared_ptr<HintManager> mHintManager;
};
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl

View file

@ -0,0 +1,15 @@
service vendor.power-hal-aidl /vendor/bin/hw/android.hardware.power-service.xiaomi_sdm660-libperfmgr
class hal
user root
group system
priority -20
# restart powerHAL when framework died
on property:init.svc.zygote=restarting && property:vendor.powerhal.state=*
setprop vendor.powerhal.state ""
setprop vendor.powerhal.rendering ""
restart vendor.power-hal-aidl
# initialize powerHAL when boot is completed
on property:sys.boot_completed=1
setprop vendor.powerhal.init 1

View file

@ -0,0 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.power</name>
<fqname>IPower/default</fqname>
</hal>
</manifest>

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2020 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.power-service.xiaomi_sdm660-libperfmgr"
#include <thread>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "Power.h"
#include "PowerExt.h"
using aidl::google::hardware::power::impl::pixel::Power;
using aidl::google::hardware::power::impl::pixel::PowerExt;
using ::android::perfmgr::HintManager;
constexpr char kPowerHalConfigPath[] = "/vendor/etc/powerhint.json";
constexpr char kPowerHalInitProp[] = "vendor.powerhal.init";
int main() {
LOG(INFO) << "Xiaomi SDM660 Power HAL AIDL Service with Extension is starting.";
// Parse config but do not start the looper
std::shared_ptr<HintManager> hm = HintManager::GetFromJSON(kPowerHalConfigPath, false);
if (!hm) {
LOG(FATAL) << "Invalid config: " << kPowerHalConfigPath;
}
// single thread
ABinderProcess_setThreadPoolMaxThreadCount(0);
// core service
std::shared_ptr<Power> pw = ndk::SharedRefBase::make<Power>(hm);
ndk::SpAIBinder pwBinder = pw->asBinder();
// extension service
std::shared_ptr<PowerExt> pwExt = ndk::SharedRefBase::make<PowerExt>(hm);
// attach the extension to the same binder we will be registering
CHECK(STATUS_OK == AIBinder_setExtension(pwBinder.get(), pwExt->asBinder().get()));
const std::string instance = std::string() + Power::descriptor + "/default";
binder_status_t status = AServiceManager_addService(pw->asBinder().get(), instance.c_str());
CHECK(status == STATUS_OK);
LOG(INFO) << "Xiaomi SDM660 Power HAL AIDL Service with Extension is started.";
std::thread initThread([&]() {
::android::base::WaitForProperty(kPowerHalInitProp, "1");
hm->Start();
});
initThread.detach();
ABinderProcess_joinThreadPool();
// should not reach
LOG(ERROR) << "Xiaomi SDM660 Power HAL AIDL Service with Extension just died.";
return EXIT_FAILURE;
}

View file

@ -14,7 +14,8 @@
* limitations under the License.
*/
#define LOG_TAG "android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr"
#define LOG_TAG "android.hardware.power@-service.xiaomi_sdm660-libperfmgr"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include <fcntl.h>
#include <poll.h>
@ -64,14 +65,15 @@ bool InteractionHandler::Init() {
if (mState != INTERACTION_STATE_UNINITIALIZED)
return true;
mIdleFd = fb_idle_open();
int fd = fb_idle_open();
if (fd < 0)
return false;
mIdleFd = fd;
mEventFd = eventfd(0, EFD_NONBLOCK);
if (mEventFd < 0) {
ALOGE("Unable to create event fd (%d)", errno);
if (mIdleFd >= 0) {
close(mIdleFd);
}
close(mIdleFd);
return false;
}
@ -94,9 +96,7 @@ void InteractionHandler::Exit() {
mThread->join();
close(mEventFd);
if (mIdleFd >= 0) {
close(mIdleFd);
}
close(mIdleFd);
}
void InteractionHandler::PerfLock() {
@ -104,6 +104,7 @@ void InteractionHandler::PerfLock() {
if (!mHintManager->DoHint("INTERACTION")) {
ALOGE("%s: do hint INTERACTION failed", __func__);
}
ATRACE_INT("interaction_lock", 1);
}
void InteractionHandler::PerfRel() {
@ -111,6 +112,7 @@ void InteractionHandler::PerfRel() {
if (!mHintManager->EndHint("INTERACTION")) {
ALOGE("%s: end hint INTERACTION failed", __func__);
}
ATRACE_INT("interaction_lock", 0);
}
size_t InteractionHandler::CalcTimespecDiffMs(struct timespec start, struct timespec end) {
@ -121,6 +123,7 @@ size_t InteractionHandler::CalcTimespecDiffMs(struct timespec start, struct time
}
void InteractionHandler::Acquire(int32_t duration) {
ATRACE_CALL();
std::lock_guard<std::mutex> lk(mLock);
if (mState == INTERACTION_STATE_UNINITIALIZED) {
@ -166,6 +169,7 @@ void InteractionHandler::Acquire(int32_t duration) {
void InteractionHandler::Release() {
std::lock_guard<std::mutex> lk(mLock);
if (mState == INTERACTION_STATE_WAITING) {
ATRACE_CALL();
PerfRel();
mState = INTERACTION_STATE_IDLE;
} else {
@ -190,6 +194,7 @@ void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) {
ssize_t ret;
struct pollfd pfd[2];
ATRACE_CALL();
ALOGV("%s: wait:%d timeout:%d", __func__, wait_ms, timeout_ms);
@ -207,18 +212,6 @@ void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) {
return;
}
if (mIdleFd < 0) {
ret = poll(pfd, 1, timeout_ms);
if (ret > 0) {
ALOGV("%s: wait for duration aborted", __func__);
return;
} else if (ret < 0) {
ALOGE("%s: Error on waiting for duration (%zd)", __func__, ret);
return;
}
return;
}
ret = pread(mIdleFd, data, sizeof(data), 0);
if (!ret) {
ALOGE("%s: Unexpected EOF!", __func__);

View file

@ -1,287 +0,0 @@
/*
* 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_TAG "android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr"
#include "Power.h"
#include <mutex>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include "power-helper.h"
/* RPM runs at 19.2Mhz. Divide by 19200 for msec */
#define RPM_CLK 19200
#ifndef TAP_TO_WAKE_NODE
#define TAP_TO_WAKE_NODE "/sys/touchpanel/double_tap"
#endif
extern struct stat_pair rpm_stat_map[];
namespace android {
namespace hardware {
namespace power {
namespace V1_3 {
namespace implementation {
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::power::V1_0::Feature;
using ::android::hardware::power::V1_0::PowerStatePlatformSleepState;
using ::android::hardware::power::V1_0::Status;
using ::android::hardware::power::V1_1::PowerStateSubsystem;
using ::android::hardware::power::V1_1::PowerStateSubsystemSleepState;
constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio";
constexpr char kPowerHalInitProp[] = "vendor.powerhal.init";
constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
constexpr char kPowerHalConfigPath[] = "/vendor/etc/powerhint.json";
Power::Power()
: mHintManager(nullptr),
mInteractionHandler(nullptr),
mSustainedPerfModeOn(false),
mReady(false) {
mInitThread = std::thread([this]() {
android::base::WaitForProperty(kPowerHalInitProp, "1");
mHintManager = HintManager::GetFromJSON(kPowerHalConfigPath);
if (!mHintManager) {
LOG(FATAL) << "Invalid config: " << kPowerHalConfigPath;
}
mInteractionHandler = std::make_unique<InteractionHandler>(mHintManager);
mInteractionHandler->Init();
std::string state = android::base::GetProperty(kPowerHalStateProp, "");
if (state == "SUSTAINED_PERFORMANCE") {
ALOGI("Initialize with SUSTAINED_PERFORMANCE on");
mHintManager->DoHint("SUSTAINED_PERFORMANCE");
mSustainedPerfModeOn = true;
} else {
ALOGI("Initialize PowerHAL");
}
state = android::base::GetProperty(kPowerHalAudioProp, "");
if (state == "AUDIO_LOW_LATENCY") {
ALOGI("Initialize with AUDIO_LOW_LATENCY on");
mHintManager->DoHint("AUDIO_LOW_LATENCY");
}
state = android::base::GetProperty(kPowerHalRenderingProp, "");
if (state == "EXPENSIVE_RENDERING") {
ALOGI("Initialize with EXPENSIVE_RENDERING on");
mHintManager->DoHint("EXPENSIVE_RENDERING");
}
// Now start to take powerhint
mReady.store(true);
ALOGI("PowerHAL ready to process hints");
});
mInitThread.detach();
}
// Methods from ::android::hardware::power::V1_0::IPower follow.
Return<void> Power::setInteractive(bool /* interactive */) {
return Void();
}
Return<void> Power::powerHint(PowerHint_1_0 hint, int32_t data) {
if (!mReady) {
return Void();
}
ALOGD_IF(hint != PowerHint_1_0::INTERACTION, "%s: %d",
android::hardware::power::V1_0::toString(hint).c_str(), static_cast<int>(data));
switch (hint) {
case PowerHint_1_0::INTERACTION:
if (mSustainedPerfModeOn) {
ALOGV("%s: ignoring due to other active perf hints", __func__);
} else {
mInteractionHandler->Acquire(data);
}
break;
case PowerHint_1_0::SUSTAINED_PERFORMANCE:
if (data && !mSustainedPerfModeOn) {
mHintManager->DoHint("SUSTAINED_PERFORMANCE");
mSustainedPerfModeOn = true;
} else if (!data && mSustainedPerfModeOn) {
mHintManager->EndHint("SUSTAINED_PERFORMANCE");
mSustainedPerfModeOn = false;
}
break;
case PowerHint_1_0::LAUNCH:
if (mSustainedPerfModeOn) {
ALOGV("%s: ignoring due to other active perf hints", __func__);
} else {
if (data) {
// Hint until canceled
mHintManager->DoHint("LAUNCH");
} else {
mHintManager->EndHint("LAUNCH");
}
}
break;
case PowerHint_1_0::LOW_POWER:
break;
default:
break;
}
return Void();
}
Return<void> Power::setFeature(Feature feature, bool activate) {
switch (feature) {
#ifdef TAP_TO_WAKE_NODE
case Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE:
::android::base::WriteStringToFile(activate ? "1" : "0", TAP_TO_WAKE_NODE, true);
break;
#endif
default:
break;
}
return Void();
}
Return<void> Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) {
hidl_vec<PowerStatePlatformSleepState> states;
uint64_t stats[MAX_PLATFORM_STATS * MAX_RPM_PARAMS] = {0};
uint64_t *values;
struct PowerStatePlatformSleepState *state;
int ret;
states.resize(PLATFORM_SLEEP_MODES_COUNT);
ret = extract_platform_stats(stats);
if (ret != 0) {
states.resize(0);
goto done;
}
/* Update statistics for XO_shutdown */
state = &states[RPM_MODE_XO];
state->name = "XO_shutdown";
values = stats + (RPM_MODE_XO * MAX_RPM_PARAMS);
state->residencyInMsecSinceBoot = values[1];
state->totalTransitions = values[0];
state->supportedOnlyInSuspend = false;
state->voters.resize(XO_VOTERS);
for(size_t i = 0; i < XO_VOTERS; i++) {
int voter = static_cast<int>(i + XO_VOTERS_START);
state->voters[i].name = rpm_stat_map[voter].label;
values = stats + (voter * MAX_RPM_PARAMS);
state->voters[i].totalTimeInMsecVotedForSinceBoot = values[0] / RPM_CLK;
state->voters[i].totalNumberOfTimesVotedSinceBoot = values[1];
}
/* Update statistics for VMIN state */
state = &states[RPM_MODE_VMIN];
state->name = "VMIN";
values = stats + (RPM_MODE_VMIN * MAX_RPM_PARAMS);
state->residencyInMsecSinceBoot = values[1];
state->totalTransitions = values[0];
state->supportedOnlyInSuspend = false;
state->voters.resize(VMIN_VOTERS);
//Note: No filling of state voters since VMIN_VOTERS = 0
done:
_hidl_cb(states, Status::SUCCESS);
return Void();
}
// Methods from ::android::hardware::power::V1_1::IPower follow.
Return<void> Power::getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) {
hidl_vec<PowerStateSubsystem> subsystems;
subsystems.resize(0);
_hidl_cb(subsystems, Status::SUCCESS);
return Void();
}
Return<void> Power::powerHintAsync(PowerHint_1_0 hint, int32_t data) {
// just call the normal power hint in this oneway function
return powerHint(hint, data);
}
// Methods from ::android::hardware::power::V1_2::IPower follow.
Return<void> Power::powerHintAsync_1_2(PowerHint_1_2 hint, int32_t data) {
if (!mReady) {
return Void();
}
ALOGD_IF(hint >= PowerHint_1_2::AUDIO_STREAMING, "%s: %d",
android::hardware::power::V1_2::toString(hint).c_str(), static_cast<int>(data));
switch (hint) {
case PowerHint_1_2::AUDIO_LOW_LATENCY:
if (data) {
// Hint until canceled
mHintManager->DoHint("AUDIO_LOW_LATENCY");
} else {
mHintManager->EndHint("AUDIO_LOW_LATENCY");
}
break;
case PowerHint_1_2::AUDIO_STREAMING:
if (mSustainedPerfModeOn) {
ALOGV("%s: ignoring due to other active perf hints", __func__);
} else {
if (data) {
mHintManager->DoHint("AUDIO_STREAMING");
} else {
mHintManager->EndHint("AUDIO_STREAMING");
}
}
break;
default:
return powerHint(static_cast<PowerHint_1_0>(hint), data);
}
return Void();
}
// Methods from ::android::hardware::power::V1_3::IPower follow.
Return<void> Power::powerHintAsync_1_3(PowerHint_1_3 hint, int32_t data) {
if (!mReady) {
return Void();
}
if (hint == PowerHint_1_3::EXPENSIVE_RENDERING) {
if (mSustainedPerfModeOn) {
ALOGV("%s: ignoring due to other active perf hints", __func__);
} else {
if (data > 0) {
mHintManager->DoHint("EXPENSIVE_RENDERING");
} else {
mHintManager->EndHint("EXPENSIVE_RENDERING");
}
}
} else {
return powerHintAsync_1_2(static_cast<PowerHint_1_2>(hint), data);
}
return Void();
}
} // namespace implementation
} // namespace V1_3
} // namespace power
} // namespace hardware
} // namespace android

View file

@ -1,84 +0,0 @@
/*
* 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.
*/
#ifndef POWER_LIBPERFMGR_POWER_H_
#define POWER_LIBPERFMGR_POWER_H_
#include <atomic>
#include <memory>
#include <thread>
#include <android/hardware/power/1.3/IPower.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <perfmgr/HintManager.h>
#include "disp-power/InteractionHandler.h"
namespace android {
namespace hardware {
namespace power {
namespace V1_3 {
namespace implementation {
using ::InteractionHandler;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::power::V1_0::Feature;
using ::android::hardware::power::V1_3::IPower;
using PowerHint_1_0 = ::android::hardware::power::V1_0::PowerHint;
using PowerHint_1_2 = ::android::hardware::power::V1_2::PowerHint;
using PowerHint_1_3 = ::android::hardware::power::V1_3::PowerHint;
using ::android::perfmgr::HintManager;
class Power : public IPower {
public:
// Methods from ::android::hardware::power::V1_0::IPower follow.
Power();
Return<void> setInteractive(bool /* interactive */) override;
Return<void> powerHint(PowerHint_1_0 hint, int32_t data) override;
Return<void> setFeature(Feature feature, bool activate) override;
Return<void> getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) override;
// Methods from ::android::hardware::power::V1_1::IPower follow.
Return<void> getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) override;
Return<void> powerHintAsync(PowerHint_1_0 hint, int32_t data) override;
// Methods from ::android::hardware::power::V1_2::IPower follow.
Return<void> powerHintAsync_1_2(PowerHint_1_2 hint, int32_t data) override;
// Methods from ::android::hardware::power::V1_3::IPower follow.
Return<void> powerHintAsync_1_3(PowerHint_1_3 hint, int32_t data) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
private:
std::shared_ptr<HintManager> mHintManager;
std::unique_ptr<InteractionHandler> mInteractionHandler;
std::atomic<bool> mSustainedPerfModeOn;
std::atomic<bool> mReady;
std::thread mInitThread;
};
} // namespace implementation
} // namespace V1_3
} // namespace power
} // namespace hardware
} // namespace android
#endif // POWER_LIBPERFMGR_POWER_H_

View file

@ -1,20 +0,0 @@
service vendor.power-hal-1-3 /vendor/bin/hw/android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr
class hal
user root
group system
priority -20
interface android.hardware.power@1.0::IPower default
interface android.hardware.power@1.1::IPower default
interface android.hardware.power@1.2::IPower default
interface android.hardware.power@1.3::IPower default
# restart powerHAL when framework died
on property:init.svc.zygote=restarting && property:vendor.powerhal.state=*
setprop vendor.powerhal.state ""
setprop vendor.powerhal.audio ""
setprop vendor.powerhal.rendering ""
restart vendor.power-hal-1-3
# initialize powerHAL when boot is completed
on property:sys.boot_completed=1
setprop vendor.powerhal.init 1

View file

@ -1,11 +0,0 @@
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.power</name>
<transport>hwbinder</transport>
<version>1.3</version>
<interface>
<name>IPower</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

View file

@ -1,156 +0,0 @@
/*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_NIDEBUG 0
#define LOG_TAG "android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr"
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <log/log.h>
#include "power-helper.h"
#ifndef RPM_SYSTEM_STAT
#define RPM_SYSTEM_STAT "/d/system_stats"
#endif
#define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
#define LINE_SIZE 128
const char *rpm_stat_params[MAX_RPM_PARAMS] = {
"count",
"actual last sleep(msec)",
};
const char *master_stat_params[MAX_RPM_PARAMS] = {
"Accumulated XO duration",
"XO Count",
};
struct stat_pair rpm_stat_map[] = {
{ RPM_MODE_XO, "RPM Mode:vlow", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
{ RPM_MODE_VMIN, "RPM Mode:vmin", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
{ VOTER_APSS, "APSS", master_stat_params, ARRAY_SIZE(master_stat_params) },
{ VOTER_MPSS, "MPSS", master_stat_params, ARRAY_SIZE(master_stat_params) },
{ VOTER_ADSP, "ADSP", master_stat_params, ARRAY_SIZE(master_stat_params) },
{ VOTER_SLPI, "SLPI", master_stat_params, ARRAY_SIZE(master_stat_params) },
};
static int parse_stats(const char **params, size_t params_size,
uint64_t *list, FILE *fp) {
ssize_t nread;
size_t len = LINE_SIZE;
char *line;
size_t params_read = 0;
size_t i;
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
return -ENOMEM;
}
while ((params_read < params_size) &&
(nread = getline(&line, &len, fp) > 0)) {
char *key = line + strspn(line, " \t");
char *value = strchr(key, ':');
if (!value || (value > (line + len)))
continue;
*value++ = '\0';
for (i = 0; i < params_size; i++) {
if (!strcmp(key, params[i])) {
list[i] = strtoull(value, NULL, 0);
params_read++;
break;
}
}
}
free(line);
return 0;
}
static int extract_stats(uint64_t *list, char *file,
struct stat_pair *map, size_t map_size) {
FILE *fp;
ssize_t read;
size_t len = LINE_SIZE;
char *line;
size_t i, stats_read = 0;
int ret = 0;
fp = fopen(file, "re");
if (fp == NULL) {
ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
return -errno;
}
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
fclose(fp);
return -ENOMEM;
}
while ((stats_read < map_size) && (read = getline(&line, &len, fp) != -1)) {
size_t begin = strspn(line, " \t");
for (i = 0; i < map_size; i++) {
if (!strncmp(line + begin, map[i].label, strlen(map[i].label))) {
stats_read++;
break;
}
}
if (i == map_size)
continue;
ret = parse_stats(map[i].parameters, map[i].num_parameters,
&list[map[i].stat * MAX_RPM_PARAMS], fp);
if (ret < 0)
break;
}
free(line);
fclose(fp);
return ret;
}
int extract_platform_stats(uint64_t *list) {
return extract_stats(list, RPM_SYSTEM_STAT, rpm_stat_map, ARRAY_SIZE(rpm_stat_map));
}

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __POWER_HELPER_H__
#define __POWER_HELPER_H__
#ifdef __cplusplus
extern "C" {
#endif
enum stats_type {
//Platform Stats
RPM_MODE_XO = 0,
RPM_MODE_VMIN,
RPM_MODE_MAX,
XO_VOTERS_START = RPM_MODE_MAX,
VOTER_APSS = XO_VOTERS_START,
VOTER_MPSS,
VOTER_ADSP,
VOTER_SLPI,
MAX_PLATFORM_STATS,
};
#define PLATFORM_SLEEP_MODES_COUNT RPM_MODE_MAX
#define MAX_RPM_PARAMS 2
#define XO_VOTERS (MAX_PLATFORM_STATS - XO_VOTERS_START)
#define VMIN_VOTERS 0
struct stat_pair {
enum stats_type stat;
const char *label;
const char **parameters;
size_t num_parameters;
};
int extract_platform_stats(uint64_t *list);
#ifdef __cplusplus
}
#endif
#endif //__POWER_HELPER_H__

View file

@ -1,59 +0,0 @@
/*
* 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_TAG "android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr"
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>
#include "Power.h"
using android::OK;
using android::sp;
using android::status_t;
// libhwbinder:
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
// Generated HIDL files
using android::hardware::power::V1_3::IPower;
using android::hardware::power::V1_3::implementation::Power;
int main(int /* argc */, char ** /* argv */) {
ALOGI("Power HAL Service 1.3 for Xiaomi SDM660 is starting.");
android::sp<IPower> service = new Power();
if (service == nullptr) {
ALOGE("Can not create an instance of Power HAL Iface, exiting.");
return 1;
}
android::hardware::setMinSchedulerPolicy(service, SCHED_NORMAL, -20);
configureRpcThreadpool(1, true /*callerWillJoin*/);
status_t status = service->registerAsService();
if (status != OK) {
ALOGE("Could not register service for Power HAL Iface (%d), exiting.", status);
return 1;
}
ALOGI("Power Service is ready");
joinRpcThreadpool();
// In normal operation, we don't expect the thread pool to exit
ALOGE("Power Service is shutting down");
return 1;
}

View file

@ -1,78 +0,0 @@
//
// Copyright (C) 2017 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.
//
cc_defaults {
name: "libperfmgr_defaults",
local_include_dirs: ["include"],
shared_libs: [
"libbase",
"libcutils",
"libutils",
],
static_libs: [
"libjsoncpp",
],
cflags: [
"-Wall",
"-Werror",
],
tidy: true,
tidy_checks: [
"android-*",
"cert-*",
"clang-analyzer-security*",
],
tidy_flags: [
"-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
],
}
cc_library {
name: "libperfmgr",
vendor_available: true,
defaults: ["libperfmgr_defaults"],
export_include_dirs: ["include"],
srcs: [
"RequestGroup.cc",
"Node.cc",
"FileNode.cc",
"PropertyNode.cc",
"NodeLooperThread.cc",
"HintManager.cc",
]
}
cc_test {
name: "libperfmgr_test",
defaults: ["libperfmgr_defaults"],
static_libs: ["libperfmgr"],
srcs: [
"tests/RequestGroupTest.cc",
"tests/FileNodeTest.cc",
"tests/PropertyNodeTest.cc",
"tests/NodeLooperThreadTest.cc",
"tests/HintManagerTest.cc",
]
}
cc_binary {
name: "perfmgr_config_verifier",
defaults: ["libperfmgr_defaults"],
static_libs: ["libperfmgr"],
srcs: [
"tools/ConfigVerifier.cc",
]
}

View file

@ -1,122 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "libperfmgr"
#include "perfmgr/FileNode.h"
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <utils/Trace.h>
namespace android {
namespace perfmgr {
FileNode::FileNode(std::string name, std::string node_path,
std::vector<RequestGroup> req_sorted,
std::size_t default_val_index, bool reset_on_init,
bool hold_fd)
: Node(std::move(name), std::move(node_path), std::move(req_sorted),
default_val_index, reset_on_init),
hold_fd_(hold_fd),
warn_timeout_(
android::base::GetBoolProperty("ro.debuggable", false) ? 5ms : 50ms) {
}
std::chrono::milliseconds FileNode::Update(bool log_error) {
std::size_t value_index = default_val_index_;
std::chrono::milliseconds expire_time = std::chrono::milliseconds::max();
// Find the highest outstanding request's expire time
for (std::size_t i = 0; i < req_sorted_.size(); i++) {
if (req_sorted_[i].GetExpireTime(&expire_time)) {
value_index = i;
break;
}
}
// Update node only if request index changes
if (value_index != current_val_index_ || reset_on_init_) {
ATRACE_BEGIN(GetName().c_str());
const std::string& req_value =
req_sorted_[value_index].GetRequestValue();
android::base::Timer t;
fd_.reset(TEMP_FAILURE_RETRY(
open(node_path_.c_str(), O_WRONLY | O_CLOEXEC | O_TRUNC)));
if (fd_ == -1 || !android::base::WriteStringToFd(req_value, fd_)) {
if (log_error) {
LOG(WARNING) << "Failed to write to node: " << node_path_
<< " with value: " << req_value << ", fd: " << fd_;
}
// Retry in 500ms or sooner
expire_time = std::min(expire_time, std::chrono::milliseconds(500));
} else {
// For regular file system, we need fsync
fsync(fd_);
// Some dev node requires file to remain open during the entire hint
// duration e.g. /dev/cpu_dma_latency, so fd_ is intentionally kept
// open during any requested value other than default one. If
// request a default value, node will write the value and then
// release the fd.
if ((!hold_fd_) || value_index == default_val_index_) {
fd_.reset();
}
auto duration = t.duration();
if (duration > warn_timeout_) {
LOG(WARNING) << "Slow writing to file: '" << node_path_
<< "' with value: '" << req_value
<< "' took: " << duration.count() << " ms";
}
// Update current index only when succeed
current_val_index_ = value_index;
reset_on_init_ = false;
}
ATRACE_END();
}
return expire_time;
}
bool FileNode::GetHoldFd() const {
return hold_fd_;
}
void FileNode::DumpToFd(int fd) const {
std::string node_value;
if (!android::base::ReadFileToString(node_path_, &node_value)) {
LOG(ERROR) << "Failed to read node path: " << node_path_;
}
node_value = android::base::Trim(node_value);
std::string buf(android::base::StringPrintf(
"%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(),
current_val_index_, node_value.c_str()));
if (!android::base::WriteStringToFd(buf, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
for (std::size_t i = 0; i < req_sorted_.size(); i++) {
req_sorted_[i].DumpToFd(
fd, android::base::StringPrintf("\t\tReq%zu:\t", i));
}
}
} // namespace perfmgr
} // namespace android

View file

@ -1,392 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "libperfmgr"
#include "perfmgr/HintManager.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <json/reader.h>
#include <json/value.h>
#include <algorithm>
#include <set>
#include "perfmgr/FileNode.h"
#include "perfmgr/PropertyNode.h"
namespace android {
namespace perfmgr {
bool HintManager::ValidateHint(const std::string& hint_type) const {
if (nm_.get() == nullptr) {
LOG(ERROR) << "NodeLooperThread not present";
return false;
}
return IsHintSupported(hint_type);
}
bool HintManager::IsHintSupported(const std::string& hint_type) const {
if (actions_.find(hint_type) == actions_.end()) {
LOG(INFO) << "Hint type not present in actions: " << hint_type;
return false;
}
return true;
}
bool HintManager::DoHint(const std::string& hint_type) {
LOG(VERBOSE) << "Do Powerhint: " << hint_type;
return ValidateHint(hint_type)
? nm_->Request(actions_.at(hint_type), hint_type)
: false;
}
bool HintManager::DoHint(const std::string& hint_type,
std::chrono::milliseconds timeout_ms_override) {
LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
<< timeout_ms_override.count() << "ms";
if (!ValidateHint(hint_type)) {
return false;
}
std::vector<NodeAction> actions_override = actions_.at(hint_type);
for (auto& action : actions_override) {
action.timeout_ms = timeout_ms_override;
}
return nm_->Request(actions_override, hint_type);
}
bool HintManager::EndHint(const std::string& hint_type) {
LOG(VERBOSE) << "End Powerhint: " << hint_type;
return ValidateHint(hint_type)
? nm_->Cancel(actions_.at(hint_type), hint_type)
: false;
}
bool HintManager::IsRunning() const {
return (nm_.get() == nullptr) ? false : nm_->isRunning();
}
std::vector<std::string> HintManager::GetHints() const {
std::vector<std::string> hints;
for (auto const& action : actions_) {
hints.push_back(action.first);
}
return hints;
}
void HintManager::DumpToFd(int fd) {
std::string header(
"========== Begin perfmgr nodes ==========\n"
"Node Name\t"
"Node Path\t"
"Current Index\t"
"Current Value\n");
if (!android::base::WriteStringToFd(header, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
nm_->DumpToFd(fd);
std::string footer("========== End perfmgr nodes ==========\n");
if (!android::base::WriteStringToFd(footer, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
fsync(fd);
}
bool HintManager::Start() {
return nm_->Start();
}
std::unique_ptr<HintManager> HintManager::GetFromJSON(
const std::string& config_path, bool start) {
std::string json_doc;
if (!android::base::ReadFileToString(config_path, &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path;
return nullptr;
}
std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
if (nodes.empty()) {
LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
return nullptr;
}
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions(json_doc, nodes);
if (actions.empty()) {
LOG(ERROR) << "Failed to parse Actions section from " << config_path;
return nullptr;
}
sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
std::unique_ptr<HintManager> hm =
std::make_unique<HintManager>(std::move(nm), actions);
LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
if (start) {
hm->Start();
}
return hm;
}
std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
const std::string& json_doc) {
// function starts
std::vector<std::unique_ptr<Node>> nodes_parsed;
std::set<std::string> nodes_name_parsed;
std::set<std::string> nodes_path_parsed;
Json::Value root;
Json::Reader reader;
if (!reader.parse(json_doc, root)) {
LOG(ERROR) << "Failed to parse JSON config";
return nodes_parsed;
}
Json::Value nodes = root["Nodes"];
for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
std::string name = nodes[i]["Name"].asString();
LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
if (name.empty()) {
LOG(ERROR) << "Failed to read "
<< "Node[" << i << "]'s Name";
nodes_parsed.clear();
return nodes_parsed;
}
auto result = nodes_name_parsed.insert(name);
if (!result.second) {
LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
nodes_parsed.clear();
return nodes_parsed;
}
std::string path = nodes[i]["Path"].asString();
LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
if (path.empty()) {
LOG(ERROR) << "Failed to read "
<< "Node[" << i << "]'s Path";
nodes_parsed.clear();
return nodes_parsed;
}
result = nodes_path_parsed.insert(path);
if (!result.second) {
LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
nodes_parsed.clear();
return nodes_parsed;
}
bool is_file = true;
std::string node_type = nodes[i]["Type"].asString();
LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
if (node_type.empty()) {
LOG(ERROR) << "Failed to read "
<< "Node[" << i << "]'s Type, set to 'File' as default";
} else if (node_type == "File") {
is_file = true;
} else if (node_type == "Property") {
is_file = false;
} else {
LOG(ERROR) << "Invalid Node[" << i
<< "]'s Type: only File and Property supported.";
nodes_parsed.clear();
return nodes_parsed;
}
std::vector<RequestGroup> values_parsed;
std::set<std::string> values_set_parsed;
Json::Value values = nodes[i]["Values"];
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
std::string value = values[j].asString();
LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
auto result = values_set_parsed.insert(value);
if (!result.second) {
LOG(ERROR) << "Duplicate value parsed in Node[" << i
<< "]'s Value[" << j << "]";
nodes_parsed.clear();
return nodes_parsed;
}
if (is_file && value.empty()) {
LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
<< "]";
nodes_parsed.clear();
return nodes_parsed;
}
values_parsed.emplace_back(value);
}
if (values_parsed.size() < 1) {
LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
nodes_parsed.clear();
return nodes_parsed;
}
Json::UInt64 default_index = values_parsed.size() - 1;
if (nodes[i]["DefaultIndex"].empty() ||
!nodes[i]["DefaultIndex"].isUInt64()) {
LOG(INFO) << "Failed to read Node[" << i
<< "]'s DefaultIndex, set to last index: "
<< default_index;
} else {
default_index = nodes[i]["DefaultIndex"].asUInt64();
}
if (default_index > values_parsed.size() - 1) {
default_index = values_parsed.size() - 1;
LOG(ERROR) << "Node[" << i
<< "]'s DefaultIndex out of bound, max value index: "
<< default_index;
nodes_parsed.clear();
return nodes_parsed;
}
LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
bool reset = false;
if (nodes[i]["ResetOnInit"].empty() ||
!nodes[i]["ResetOnInit"].isBool()) {
LOG(INFO) << "Failed to read Node[" << i
<< "]'s ResetOnInit, set to 'false'";
} else {
reset = nodes[i]["ResetOnInit"].asBool();
}
LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
<< reset << std::noboolalpha;
if (is_file) {
bool hold_fd = false;
if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
LOG(INFO) << "Failed to read Node[" << i
<< "]'s HoldFd, set to 'false'";
} else {
hold_fd = nodes[i]["HoldFd"].asBool();
}
LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
<< hold_fd << std::noboolalpha;
nodes_parsed.emplace_back(std::make_unique<FileNode>(
name, path, values_parsed,
static_cast<std::size_t>(default_index), reset, hold_fd));
} else {
nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
name, path, values_parsed,
static_cast<std::size_t>(default_index), reset));
}
}
LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
return nodes_parsed;
}
std::map<std::string, std::vector<NodeAction>> HintManager::ParseActions(
const std::string& json_doc,
const std::vector<std::unique_ptr<Node>>& nodes) {
// function starts
std::map<std::string, std::vector<NodeAction>> actions_parsed;
Json::Value root;
Json::Reader reader;
if (!reader.parse(json_doc, root)) {
LOG(ERROR) << "Failed to parse JSON config";
return actions_parsed;
}
Json::Value actions = root["Actions"];
std::size_t total_parsed = 0;
std::map<std::string, std::size_t> nodes_index;
for (std::size_t i = 0; i < nodes.size(); ++i) {
nodes_index[nodes[i]->GetName()] = i;
}
for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
const std::string& hint_type = actions[i]["PowerHint"].asString();
LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
if (hint_type.empty()) {
LOG(ERROR) << "Failed to read "
<< "Action[" << i << "]'s PowerHint";
actions_parsed.clear();
return actions_parsed;
}
std::string node_name = actions[i]["Node"].asString();
LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
std::size_t node_index;
if (nodes_index.find(node_name) == nodes_index.end()) {
LOG(ERROR) << "Failed to find "
<< "Action[" << i << "]'s Node from Nodes section: ["
<< node_name << "]";
actions_parsed.clear();
return actions_parsed;
}
node_index = nodes_index[node_name];
std::string value_name = actions[i]["Value"].asString();
LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
std::size_t value_index = 0;
if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
<< " is not defined in Node[" << node_name << "]";
actions_parsed.clear();
return actions_parsed;
}
LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
Json::UInt64 duration = 0;
if (actions[i]["Duration"].empty() ||
!actions[i]["Duration"].isUInt64()) {
LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
actions_parsed.clear();
return actions_parsed;
} else {
duration = actions[i]["Duration"].asUInt64();
}
LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
if (actions_parsed.find(hint_type) == actions_parsed.end()) {
actions_parsed[hint_type] = std::vector<NodeAction>{
{node_index, value_index, std::chrono::milliseconds(duration)}};
} else {
for (const auto& action : actions_parsed[hint_type]) {
if (action.node_index == node_index) {
LOG(ERROR)
<< "Action[" << i
<< "]'s NodeIndex is duplicated with another Action";
actions_parsed.clear();
return actions_parsed;
}
}
actions_parsed[hint_type].emplace_back(
node_index, value_index, std::chrono::milliseconds(duration));
}
++total_parsed;
}
LOG(INFO) << total_parsed << " Actions parsed successfully";
for (const auto& action : actions_parsed) {
LOG(INFO) << "PowerHint " << action.first << " has "
<< action.second.size() << " actions parsed";
}
return actions_parsed;
}
} // namespace perfmgr
} // namespace android

View file

@ -1,97 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "libperfmgr"
#include "perfmgr/Node.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
namespace android {
namespace perfmgr {
Node::Node(std::string name, std::string node_path,
std::vector<RequestGroup> req_sorted, std::size_t default_val_index,
bool reset_on_init)
: name_(std::move(name)),
node_path_(std::move(node_path)),
req_sorted_(std::move(req_sorted)),
default_val_index_(default_val_index),
reset_on_init_(reset_on_init),
current_val_index_(default_val_index) {}
bool Node::AddRequest(std::size_t value_index, const std::string& hint_type,
ReqTime end_time) {
if (value_index >= req_sorted_.size()) {
LOG(ERROR) << "Value index out of bound: " << value_index
<< " ,size: " << req_sorted_.size();
return false;
}
// Add/Update request to the new end_time for the specific hint_type
req_sorted_[value_index].AddRequest(hint_type, end_time);
return true;
}
bool Node::RemoveRequest(const std::string& hint_type) {
bool ret = false;
// Remove all requests for the specific hint_type
for (auto& value : req_sorted_) {
ret = value.RemoveRequest(hint_type) || ret;
}
return ret;
}
const std::string& Node::GetName() const {
return name_;
}
const std::string& Node::GetPath() const {
return node_path_;
}
bool Node::GetValueIndex(const std::string& value, std::size_t* index) const {
bool found = false;
for (std::size_t i = 0; i < req_sorted_.size(); i++) {
if (req_sorted_[i].GetRequestValue() == value) {
*index = i;
found = true;
break;
}
}
return found;
}
std::size_t Node::GetDefaultIndex() const {
return default_val_index_;
}
bool Node::GetResetOnInit() const {
return reset_on_init_;
}
std::vector<std::string> Node::GetValues() const {
std::vector<std::string> values;
for (const auto& value : req_sorted_) {
values.emplace_back(value.GetRequestValue());
}
return values;
}
} // namespace perfmgr
} // namespace android

View file

@ -1,153 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "libperfmgr"
#include "perfmgr/NodeLooperThread.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <utils/Trace.h>
namespace android {
namespace perfmgr {
bool NodeLooperThread::Request(const std::vector<NodeAction>& actions,
const std::string& hint_type) {
if (::android::Thread::exitPending()) {
LOG(WARNING) << "NodeLooperThread is exiting";
return false;
}
if (!::android::Thread::isRunning()) {
LOG(WARNING) << "NodeLooperThread is not running, request " << hint_type;
}
bool ret = true;
::android::AutoMutex _l(lock_);
for (const auto& a : actions) {
if (a.node_index >= nodes_.size()) {
LOG(ERROR) << "Node index out of bound: " << a.node_index
<< " ,size: " << nodes_.size();
ret = false;
} else {
// End time set to steady time point max
ReqTime end_time = ReqTime::max();
// Timeout is non-zero
if (a.timeout_ms != std::chrono::milliseconds::zero()) {
auto now = std::chrono::steady_clock::now();
// Overflow protection in case timeout_ms is too big to overflow
// time point which is unsigned integer
if (std::chrono::duration_cast<std::chrono::milliseconds>(
ReqTime::max() - now) > a.timeout_ms) {
end_time = now + a.timeout_ms;
}
}
ret = nodes_[a.node_index]->AddRequest(a.value_index, hint_type,
end_time) &&
ret;
}
}
wake_cond_.signal();
return ret;
}
bool NodeLooperThread::Cancel(const std::vector<NodeAction>& actions,
const std::string& hint_type) {
if (::android::Thread::exitPending()) {
LOG(WARNING) << "NodeLooperThread is exiting";
return false;
}
if (!::android::Thread::isRunning()) {
LOG(WARNING) << "NodeLooperThread is not running, cancel " << hint_type;
}
bool ret = true;
::android::AutoMutex _l(lock_);
for (const auto& a : actions) {
if (a.node_index >= nodes_.size()) {
LOG(ERROR) << "Node index out of bound: " << a.node_index
<< " ,size: " << nodes_.size();
ret = false;
} else {
nodes_[a.node_index]->RemoveRequest(hint_type);
}
}
wake_cond_.signal();
return ret;
}
void NodeLooperThread::DumpToFd(int fd) {
::android::AutoMutex _l(lock_);
for (auto& n : nodes_) {
n->DumpToFd(fd);
}
}
bool NodeLooperThread::threadLoop() {
::android::AutoMutex _l(lock_);
std::chrono::milliseconds timeout_ms = kMaxUpdatePeriod;
// Update 2 passes: some node may have dependency in other node
// e.g. update cpufreq min to VAL while cpufreq max still set to
// a value lower than VAL, is expected to fail in first pass
ATRACE_BEGIN("update_nodes");
for (auto& n : nodes_) {
n->Update(false);
}
for (auto& n : nodes_) {
timeout_ms = std::min(n->Update(true), timeout_ms);
}
ATRACE_END();
nsecs_t sleep_timeout_ns = std::numeric_limits<nsecs_t>::max();
if (timeout_ms.count() < sleep_timeout_ns / 1000 / 1000) {
sleep_timeout_ns = timeout_ms.count() * 1000 * 1000;
}
// VERBOSE level won't print by default in user/userdebug build
LOG(VERBOSE) << "NodeLooperThread will wait for " << sleep_timeout_ns
<< "ns";
ATRACE_BEGIN("wait");
wake_cond_.waitRelative(lock_, sleep_timeout_ns);
ATRACE_END();
return true;
}
bool NodeLooperThread::Start() {
auto ret = this->run("NodeLooperThread", PRIORITY_HIGHEST);
if (ret != NO_ERROR) {
LOG(ERROR) << "NodeLooperThread start failed: " << ret;
} else {
LOG(INFO) << "NodeLooperThread started";
}
return ret == NO_ERROR;
}
void NodeLooperThread::Stop() {
if (::android::Thread::isRunning()) {
LOG(INFO) << "NodeLooperThread stopping";
{
::android::AutoMutex _l(lock_);
wake_cond_.signal();
::android::Thread::requestExit();
}
::android::Thread::join();
LOG(INFO) << "NodeLooperThread stopped";
}
}
} // namespace perfmgr
} // namespace android

View file

@ -1,84 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "libperfmgr"
#include "perfmgr/PropertyNode.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <utils/Trace.h>
namespace android {
namespace perfmgr {
PropertyNode::PropertyNode(std::string name, std::string node_path,
std::vector<RequestGroup> req_sorted,
std::size_t default_val_index, bool reset_on_init)
: Node(std::move(name), std::move(node_path), std::move(req_sorted),
default_val_index, reset_on_init) {}
std::chrono::milliseconds PropertyNode::Update(bool) {
std::size_t value_index = default_val_index_;
std::chrono::milliseconds expire_time = std::chrono::milliseconds::max();
// Find the highest outstanding request's expire time
for (std::size_t i = 0; i < req_sorted_.size(); i++) {
if (req_sorted_[i].GetExpireTime(&expire_time)) {
value_index = i;
break;
}
}
// Update node only if request index changes
if (value_index != current_val_index_ || reset_on_init_) {
ATRACE_BEGIN(GetName().c_str());
const std::string& req_value =
req_sorted_[value_index].GetRequestValue();
if (!android::base::SetProperty(node_path_, req_value)) {
LOG(WARNING) << "Failed to set property to : " << node_path_
<< " with value: " << req_value;
} else {
// Update current index only when succeed
current_val_index_ = value_index;
reset_on_init_ = false;
}
ATRACE_END();
}
return expire_time;
}
void PropertyNode::DumpToFd(int fd) const {
std::string node_value = android::base::GetProperty(node_path_, "");
std::string buf(android::base::StringPrintf(
"%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(),
current_val_index_, node_value.c_str()));
if (!android::base::WriteStringToFd(buf, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
for (std::size_t i = 0; i < req_sorted_.size(); i++) {
req_sorted_[i].DumpToFd(
fd, android::base::StringPrintf("\t\tReq%zu:\t", i));
}
}
} // namespace perfmgr
} // namespace android

View file

@ -1,84 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "libperfmgr"
#include "perfmgr/RequestGroup.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <sstream>
namespace android {
namespace perfmgr {
bool RequestGroup::AddRequest(const std::string& hint_type, ReqTime end_time) {
if (request_map_.find(hint_type) == request_map_.end()) {
request_map_.emplace(hint_type, end_time);
return true;
} else {
if (request_map_[hint_type] < end_time) {
request_map_[hint_type] = end_time;
}
return false;
}
}
bool RequestGroup::RemoveRequest(const std::string& hint_type) {
return request_map_.erase(hint_type);
}
const std::string& RequestGroup::GetRequestValue() const {
return request_value_;
}
bool RequestGroup::GetExpireTime(std::chrono::milliseconds* expire_time) {
ReqTime now = std::chrono::steady_clock::now();
*expire_time = std::chrono::milliseconds::max();
bool active = false;
for (auto it = request_map_.begin(); it != request_map_.end();) {
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
it->second - now);
if (duration <= std::chrono::milliseconds::zero()) {
it = request_map_.erase(it);
} else {
*expire_time = std::min(duration, *expire_time);
active = true;
++it;
}
}
return active;
}
void RequestGroup::DumpToFd(int fd, const std::string& prefix) const {
std::ostringstream dump_buf;
ReqTime now = std::chrono::steady_clock::now();
for (auto it = request_map_.begin(); it != request_map_.end(); it++) {
auto remaining_duration =
std::chrono::duration_cast<std::chrono::milliseconds>(it->second -
now);
dump_buf << prefix << it->first << "\t" << remaining_duration.count()
<< "\t" << request_value_ << "\n";
}
if (!android::base::WriteStringToFd(dump_buf.str(), fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
}
} // namespace perfmgr
} // namespace android

View file

@ -1,12 +0,0 @@
{
"presubmit": [
{
"name": "libperfmgr_test"
}
],
"pts-experimental": [
{
"name": "libperfmgr_test"
}
]
}

View file

@ -1,126 +0,0 @@
{
"definitions": {},
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"id": "config_schema.json",
"required": [
"Nodes",
"Actions"
],
"properties": {
"Nodes": {
"type": "array",
"id": "/properties/Nodes",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "object",
"id": "/properties/Nodes/items",
"required": [
"Name",
"Path",
"Values"
],
"properties": {
"Name": {
"type": "string",
"id": "/properties/Nodes/items/properties/Name",
"title": "The Name Schema.",
"description": "The name of the node.",
"minLength": 1
},
"Path": {
"type": "string",
"id": "/properties/Nodes/items/properties/Path",
"title": "The Path Schema.",
"description": "For File type node, it is filesystem path of the file; for Property type node, it is the key of the property.",
"minLength": 1
},
"Values": {
"type": "array",
"id": "/properties/Nodes/items/properties/Values",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"id": "/properties/Nodes/items/properties/Values/items",
"title": "The Values Schema.",
"description": "The Values array lists all possible values that can be set in the Actions section, and the list of values is sorted based on their priority, with the highest priority first."
}
},
"DefaultIndex": {
"type": "integer",
"id": "/properties/Nodes/items/properties/DefaultIndex",
"title": "The Default Index Schema.",
"description": "The default index of the node, if not present, it will be set to max index of Values.",
"minimum": 0
},
"ResetOnInit": {
"type": "boolean",
"id": "/properties/Nodes/items/properties/ResetOnInit",
"title": "The Reset On Init Schema.",
"description": "Flag if node will be set to default value on initialization; if not present, it will be set to false."
},
"Type": {
"type": "string",
"id": "/properties/Nodes/items/properties/Type",
"title": "The type Schema.",
"description": "Type of Node (File or Property), if not present, it will be set to File."
},
"HoldFd": {
"type": "boolean",
"id": "/properties/Nodes/items/properties/HoldFd",
"title": "The Hold Fd Schema.",
"description": "Flag if node will hold the file descriptor on non-default values; if not present, it will be set to false. This is only honoured for File type node."
}
}
}
},
"Actions": {
"type": "array",
"id": "/properties/Actions",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "object",
"id": "/properties/Actions/items",
"required": [
"PowerHint",
"Node",
"ValueIndex",
"Duration"
],
"properties": {
"PowerHint": {
"type": "string",
"id": "/properties/Actions/items/properties/PowerHint",
"title": "The PowerHint Schema.",
"description": "The PowerHint name of the action.",
"minLength": 1
},
"Node": {
"type": "string",
"id": "/properties/Actions/items/properties/Node",
"title": "The Node Schema.",
"description": "The Node name of the action, which is defined in Nodes.",
"minLength": 1
},
"Value": {
"type": "string",
"id": "/properties/Actions/items/properties/Value",
"title": "The Value Index Schema.",
"description": "The value of action, which is defined in Nodes.",
"minLength": 1
},
"Duration": {
"type": "integer",
"id": "/properties/Actions/items/properties/Duration",
"title": "The Duration Schema.",
"description": "The number of milliseconds that this action will be active (zero means forever).",
"minimum": 0
}
}
}
}
}
}

View file

@ -1,56 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LIBPERFMGR_FILENODE_H_
#define ANDROID_LIBPERFMGR_FILENODE_H_
#include <android-base/unique_fd.h>
#include <cstddef>
#include <string>
#include <vector>
#include "perfmgr/Node.h"
namespace android {
namespace perfmgr {
// FileNode represents file
class FileNode : public Node {
public:
FileNode(std::string name, std::string node_path,
std::vector<RequestGroup> req_sorted, std::size_t default_val_index,
bool reset_on_init, bool hold_fd = false);
std::chrono::milliseconds Update(bool log_error) override;
bool GetHoldFd() const;
void DumpToFd(int fd) const override;
private:
FileNode(const Node& other) = delete;
FileNode& operator=(Node const&) = delete;
const bool hold_fd_;
const std::chrono::milliseconds warn_timeout_;
android::base::unique_fd fd_;
};
} // namespace perfmgr
} // namespace android
#endif // ANDROID_LIBPERFMGR_FILENODE_H_

View file

@ -1,98 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LIBPERFMGR_HINTMANAGER_H_
#define ANDROID_LIBPERFMGR_HINTMANAGER_H_
#include <cstddef>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "perfmgr/NodeLooperThread.h"
namespace android {
namespace perfmgr {
// HintManager is the external interface of the library to be used by PowerHAL
// to do power hints with sysfs nodes. HintManager maintains a representation of
// the actions that are parsed from the configuration file as a mapping from a
// PowerHint to the set of actions that are performed for that PowerHint.
class HintManager {
public:
HintManager(sp<NodeLooperThread> nm,
const std::map<std::string, std::vector<NodeAction>>& actions)
: nm_(std::move(nm)), actions_(actions) {}
~HintManager() {
if (nm_.get() != nullptr) nm_->Stop();
}
// Return true if the sysfs manager thread is running.
bool IsRunning() const;
// Do hint based on hint_type which defined as PowerHint in the actions
// section of the JSON config. Return true with valid hint_type and also
// NodeLooperThread::Request succeeds; otherwise return false.
bool DoHint(const std::string& hint_type);
// Do hint with the override time for all actions defined for the given
// hint_type. Return true with valid hint_type and also
// NodeLooperThread::Request succeeds; otherwise return false.
bool DoHint(const std::string& hint_type,
std::chrono::milliseconds timeout_ms_override);
// End hint early. Return true with valid hint_type and also
// NodeLooperThread::Cancel succeeds; otherwise return false.
bool EndHint(const std::string& hint_type);
// Query if given hint supported.
bool IsHintSupported(const std::string& hint_type) const;
// Static method to construct HintManager from the JSON config file.
static std::unique_ptr<HintManager> GetFromJSON(
const std::string& config_path, bool start = true);
// Return available hints managed by HintManager
std::vector<std::string> GetHints() const;
// Dump internal status to fd
void DumpToFd(int fd);
// Start thread loop
bool Start();
protected:
static std::vector<std::unique_ptr<Node>> ParseNodes(
const std::string& json_doc);
static std::map<std::string, std::vector<NodeAction>> ParseActions(
const std::string& json_doc,
const std::vector<std::unique_ptr<Node>>& nodes);
private:
HintManager(HintManager const&) = delete;
void operator=(HintManager const&) = delete;
bool ValidateHint(const std::string& hint_type) const;
sp<NodeLooperThread> nm_;
const std::map<std::string, std::vector<NodeAction>> actions_;
};
} // namespace perfmgr
} // namespace android
#endif // ANDROID_LIBPERFMGR_HINTMANAGER_H_

View file

@ -1,92 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LIBPERFMGR_NODE_H_
#define ANDROID_LIBPERFMGR_NODE_H_
#include <android-base/unique_fd.h>
#include <cstddef>
#include <string>
#include <vector>
#include "perfmgr/RequestGroup.h"
namespace android {
namespace perfmgr {
// The Node class provides an interface for adding and cancelling powerhint
// requests, as well as checking the next time that an in-progress powerhint
// request will expire. There are additional methods for getting the Nodes name
// and the index of a value, which may be used for initialization, debugging,
// and request management. The core of the Node class is a vector of
// RequestGroups named req_sorted_, which is used to track the in-progress
// requests on the node. Each entry in the vector corresponds to a possible
// value for the node, in priority order. For example, the first entry in the
// vector for the cpu0 cluster represents the in-progress requests to boost the
// clusters frequency to the highest available value. The next entry represents
// the in-progress requests to boost the clusters frequency to the next highest
// value. For each value, there may be multiple requests because different
// powerhints may request the same value, and the requests may have different
// expiration times. All of the in-progress powerhints for a given value are
// collected in a RequestGroup. Node class is not thread safe so it needs
// protection from caller e.g. NodeLooperThread.
class Node {
public:
virtual ~Node() {}
// Return true if successfully add a request
bool AddRequest(std::size_t value_index, const std::string& hint_type,
ReqTime end_time);
// Return true if successfully remove a request
bool RemoveRequest(const std::string& hint_type);
// Return the nearest expire time of active requests; return
// std::chrono::milliseconds::max() if no active request on Node; update
// node's controlled file node value and the current value index based on
// active request.
virtual std::chrono::milliseconds Update(bool log_error) = 0;
const std::string& GetName() const;
const std::string& GetPath() const;
std::vector<std::string> GetValues() const;
std::size_t GetDefaultIndex() const;
bool GetResetOnInit() const;
bool GetValueIndex(const std::string& value, std::size_t* index) const;
virtual void DumpToFd(int fd) const = 0;
protected:
Node(std::string name, std::string node_path,
std::vector<RequestGroup> req_sorted, std::size_t default_val_index,
bool reset_on_init);
Node(const Node& other) = delete;
Node& operator=(Node const&) = delete;
const std::string name_;
const std::string node_path_;
// request vector, one entry per possible value, sorted by priority.
std::vector<RequestGroup> req_sorted_;
const std::size_t default_val_index_;
// node will be explicitly initialized when first time called Update().
bool reset_on_init_;
std::size_t current_val_index_;
};
} // namespace perfmgr
} // namespace android
#endif // ANDROID_LIBPERFMGR_NODE_H_

View file

@ -1,104 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LIBPERFMGR_NODELOOPERTHREAD_H_
#define ANDROID_LIBPERFMGR_NODELOOPERTHREAD_H_
#include <utils/Thread.h>
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "perfmgr/Node.h"
namespace android {
namespace perfmgr {
// The NodeAction specifies the sysfs node, the value to be assigned, and the
// timeout for this action:
struct NodeAction {
NodeAction(std::size_t node_index, std::size_t value_index,
std::chrono::milliseconds timeout_ms)
: node_index(node_index),
value_index(value_index),
timeout_ms(timeout_ms) {}
std::size_t node_index;
std::size_t value_index;
std::chrono::milliseconds timeout_ms; // 0ms for forever
};
// The NodeLooperThread is responsible for managing each of the sysfs nodes
// specified in the configuration. At initialization, the NodeLooperThrea holds
// a vector containing the nodes defined in the configuration. The NodeManager
// gets powerhint requests and cancellations from the HintManager, maintains
// state about the current set of powerhint requests on each sysfs node, and
// decides how to apply the requests. The NodeLooperThread contains a ThreadLoop
// to maintain the sysfs nodes, and that thread is woken up both to handle
// powerhint requests and when the timeout expires for an in-progress powerhint.
class NodeLooperThread : public ::android::Thread {
public:
explicit NodeLooperThread(std::vector<std::unique_ptr<Node>> nodes)
: Thread(false), nodes_(std::move(nodes)) {}
virtual ~NodeLooperThread() { Stop(); }
// Need call Stop() as the threadloop will hold a strong pointer
// itself and wait for Condition fired or timeout (60s) before
// the out looper can call deconstructor to Stop() thread
void Stop();
// Return true when successfully adds request from actions for the hint_type
// in each individual node. Return false if any of the actions has either
// invalid node index or value index.
bool Request(const std::vector<NodeAction>& actions,
const std::string& hint_type);
// Return when successfully cancels request from actions for the hint_type
// in each individual node. Return false if any of the actions has invalid
// node index.
bool Cancel(const std::vector<NodeAction>& actions,
const std::string& hint_type);
// Dump all nodes to fd
void DumpToFd(int fd);
// Return true when successfully started the looper thread
bool Start();
private:
NodeLooperThread(NodeLooperThread const&) = delete;
void operator=(NodeLooperThread const&) = delete;
bool threadLoop() override;
static constexpr auto kMaxUpdatePeriod = std::chrono::milliseconds::max();
std::vector<std::unique_ptr<Node>> nodes_; // parsed from Config
// conditional variable from C++ standard library can be affected by wall
// time change as it is using CLOCK_REAL (b/35756266). The component should
// not be impacted by wall time, thus need use Android specific Condition
// class for waking up threadloop.
::android::Condition wake_cond_;
// lock to protect nodes_
::android::Mutex lock_;
};
} // namespace perfmgr
} // namespace android
#endif // ANDROID_LIBPERFMGR_NODELOOPERTHREAD_H_

View file

@ -1,48 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LIBPERFMGR_PROPERTYNODE_H_
#define ANDROID_LIBPERFMGR_PROPERTYNODE_H_
#include <cstddef>
#include <string>
#include <vector>
#include "perfmgr/Node.h"
namespace android {
namespace perfmgr {
// PropertyNode represents managed system properties
class PropertyNode : public Node {
public:
PropertyNode(std::string name, std::string node_path,
std::vector<RequestGroup> req_sorted,
std::size_t default_val_index, bool reset_on_init);
std::chrono::milliseconds Update(bool log_error) override;
void DumpToFd(int fd) const override;
private:
PropertyNode(const Node& other) = delete;
PropertyNode& operator=(Node const&) = delete;
};
} // namespace perfmgr
} // namespace android
#endif // ANDROID_LIBPERFMGR_PROPERTYNODE_H_

View file

@ -1,67 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LIBPERFMGR_REQUESTGROUP_H_
#define ANDROID_LIBPERFMGR_REQUESTGROUP_H_
#include <chrono>
#include <map>
#include <string>
#include <utility>
namespace android {
namespace perfmgr {
using ReqTime = std::chrono::time_point<std::chrono::steady_clock>;
// The RequestGroup type represents the set of requests for a given value on a
// particular sysfs node, and the interface is simple: there is a function to
// add requests, a function to remove requests, and a function to check for the
// next expiration time if there is an outstanding request, and a function to
// check the requested value. There may only be one request per PowerHint, so
// the representation is simple: a map from PowerHint to the expiration time for
// that hint.
class RequestGroup {
public:
RequestGroup(std::string request_value) // NOLINT(runtime/explicit)
: request_value_(std::move(request_value)) {}
// Remove expired request in the map and return true when request_map_ is
// not empty, false when request_map_ is empty; also update expire_time with
// nearest timeout in request_map_ or std::chrono::milliseconds::max() when
// request_map_ is empty.
bool GetExpireTime(std::chrono::milliseconds* expire_time);
// Return the request value.
const std::string& GetRequestValue() const;
// Return true for adding request, false for extending expire time of
// existing active request on given hint_type.
bool AddRequest(const std::string& hint_type, ReqTime end_time);
// Return true for removing request, false if request is not active on given
// hint_type. If request exits and the new end_time is less than the active
// time, expire time will not be updated; also returns false.
bool RemoveRequest(const std::string& hint_type);
// Dump internal status to fd
void DumpToFd(int fd, const std::string& prefix) const;
private:
const std::string request_value_;
std::map<std::string, ReqTime> request_map_;
};
} // namespace perfmgr
} // namespace android
#endif // ANDROID_LIBPERFMGR_REQUESTGROUP_H_

View file

@ -1,234 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <thread>
#include "perfmgr/FileNode.h"
namespace android {
namespace perfmgr {
using namespace std::chrono_literals;
constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
constexpr auto kSLEEP_TOLERANCE_MS = 2ms;
static inline void _VerifyPathValue(const std::string& path,
const std::string& value) {
std::string s;
EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
EXPECT_EQ(value, s);
}
// Test init with no default value
TEST(FileNodeTest, NoInitDefaultTest) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
t.Update(false);
_VerifyPathValue(tf.path, "");
}
// Test init with default value
TEST(FileNodeTest, InitDefaultTest) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
t.Update(false);
_VerifyPathValue(tf.path, "value1");
TemporaryFile tf2;
FileNode t2("t2", tf2.path, {{"value0"}, {"value1"}, {"value2"}}, 0, true);
t2.Update(false);
_VerifyPathValue(tf2.path, "value0");
}
// Test DumpToFd
TEST(FileNodeTest, DumpToFdTest) {
TemporaryFile tf;
FileNode t("test_dump", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1,
true);
t.Update(false);
TemporaryFile dumptf;
t.DumpToFd(dumptf.fd);
fsync(dumptf.fd);
std::string buf(
android::base::StringPrintf("test_dump\t%s\t1\tvalue1\n", tf.path));
_VerifyPathValue(dumptf.path, buf);
}
// Test GetValueIndex
TEST(FileNodeTest, GetValueIndexTest) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
std::size_t index = 0;
EXPECT_TRUE(t.GetValueIndex("value2", &index));
EXPECT_EQ(2u, index);
index = 1234;
EXPECT_FALSE(t.GetValueIndex("NON_EXIST", &index));
EXPECT_EQ(1234u, index);
}
// Test GetValues
TEST(FileNodeTest, GetValuesTest) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
std::vector values = t.GetValues();
EXPECT_EQ(3u, values.size());
EXPECT_EQ("value0", values[0]);
EXPECT_EQ("value1", values[1]);
EXPECT_EQ("value2", values[2]);
}
// Test get more properties
TEST(FileNodeTest, GetPropertiesTest) {
std::string test_name = "TESTREQ_1";
std::string test_path = "TEST_PATH";
FileNode t(test_name, test_path, {}, 0, false, true);
EXPECT_EQ(test_name, t.GetName());
EXPECT_EQ(test_path, t.GetPath());
EXPECT_EQ(0u, t.GetValues().size());
EXPECT_EQ(0u, t.GetDefaultIndex());
EXPECT_FALSE(t.GetResetOnInit());
EXPECT_TRUE(t.GetHoldFd());
}
// Test add request fail and retry
TEST(FileNodeTest, AddRequestTestFail) {
FileNode t("t", "/sys/android/nonexist_node_test",
{{"value0"}, {"value1"}, {"value2"}}, 2, true);
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 200ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 2000ms));
expire_time = t.Update(true);
// Retry in 500 ms
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
}
// Test add request
TEST(FileNodeTest, AddRequestTest) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
_VerifyPathValue(tf.path, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Let high prio request timeout, now only request @ value1 active
std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value1");
EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Let all requests timeout, now default value2
std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value2");
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
}
// Test remove request
TEST(FileNodeTest, RemoveRequestTest) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
_VerifyPathValue(tf.path, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove high prio request, now only request @ value1 active
t.RemoveRequest("LAUNCH");
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove request, now default value2
t.RemoveRequest("INTERACTION");
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value2");
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
}
// Test add request with holding fd
TEST(FileNodeTest, AddRequestTestHoldFdOverride) {
TemporaryFile tf;
FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true,
true);
EXPECT_TRUE(t.GetHoldFd());
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
_VerifyPathValue(tf.path, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 shorter
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 100ms));
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 longer
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 300ms));
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value0");
EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove high prio request, now only request @ value1 active
t.RemoveRequest("LAUNCH");
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove request, now default value2
t.RemoveRequest("INTERACTION");
expire_time = t.Update(true);
_VerifyPathValue(tf.path, "value2");
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
}
} // namespace perfmgr
} // namespace android

View file

@ -1,575 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <thread>
#include "perfmgr/FileNode.h"
#include "perfmgr/HintManager.h"
#include "perfmgr/PropertyNode.h"
namespace android {
namespace perfmgr {
using namespace std::chrono_literals;
constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
// JSON_CONFIG
// {
// "Nodes": [
// {
// "Name": "CPUCluster0MinFreq",
// "Path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq",
// "Values": [
// "1512000",
// "1134000",
// "384000"
// ],
// "DefaultIndex": 2,
// "ResetOnInit": true
// },
// {
// "Name": "CPUCluster1MinFreq",
// "Path": "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq",
// "Values": [
// "1512000",
// "1134000",
// "384000"
// ],
// "HoldFd": true
// },
// {
// "Name": "ModeProperty",
// "Path": "vendor.pwhal.mode",
// "Values": [
// "HIGH",
// "LOW",
// "NONE"
// ],
// "Type": "Property"
// }
// ],
// "Actions": [
// {
// "PowerHint": "INTERACTION",
// "Node": "CPUCluster1MinFreq",
// "Value": "1134000",
// "Duration": 800
// },
// {
// "PowerHint": "INTERACTION",
// "Node": "ModeProperty",
// "Value": "LOW",
// "Duration": 800
// },
// {
// "PowerHint": "LAUNCH",
// "Node": "CPUCluster0MinFreq",
// "Value": "1134000",
// "Duration": 500
// },
// {
// "PowerHint": "LAUNCH",
// "Node": "ModeProperty",
// "Value": "HIGH",
// "Duration": 500
// },
// {
// "PowerHint": "LAUNCH",
// "Node": "CPUCluster1MinFreq",
// "Value": "1512000",
// "Duration": 2000
// }
// ]
// }
constexpr char kJSON_RAW[] =
"{\"Nodes\":[{\"Name\":\"CPUCluster0MinFreq\",\"Path\":\"/sys/devices/"
"system/cpu/cpu0/cpufreq/"
"scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
"\"DefaultIndex\":2,\"ResetOnInit\":true},{\"Name\":\"CPUCluster1MinFreq\","
"\"Path\":\"/sys/devices/system/cpu/cpu4/cpufreq/"
"scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
"\"HoldFd\":true},{\"Name\":\"ModeProperty\",\"Path\":\"vendor.pwhal."
"mode\",\"Values\":[\"HIGH\",\"LOW\",\"NONE\"],\"Type\":\"Property\"}],"
"\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":"
"\"CPUCluster1MinFreq\",\"Value\":\"1134000\",\"Duration\":800},{"
"\"PowerHint\":\"INTERACTION\",\"Node\":\"ModeProperty\",\"Value\":\"LOW\","
"\"Duration\":800},{\"PowerHint\":\"LAUNCH\",\"Node\":"
"\"CPUCluster0MinFreq\",\"Value\":\"1134000\",\"Duration\":500},{"
"\"PowerHint\":\"LAUNCH\",\"Node\":\"ModeProperty\",\"Value\":\"HIGH\","
"\"Duration\":500},{\"PowerHint\":\"LAUNCH\",\"Node\":"
"\"CPUCluster1MinFreq\",\"Value\":\"1512000\",\"Duration\":2000}]}";
class HintManagerTest : public ::testing::Test, public HintManager {
protected:
HintManagerTest()
: HintManager(nullptr,
std::map<std::string, std::vector<NodeAction>>{}) {
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
prop_ = "vendor.pwhal.mode";
}
virtual void SetUp() {
// Set up 3 dummy nodes
std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
nodes_.emplace_back(new FileNode(
"n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2,
false));
files_.emplace_back(std::move(tf));
tf = std::make_unique<TemporaryFile>();
nodes_.emplace_back(new FileNode(
"n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2,
true));
files_.emplace_back(std::move(tf));
nodes_.emplace_back(new PropertyNode(
"n2", prop_, {{"n2_value0"}, {"n2_value1"}, {"n2_value2"}}, 2,
true));
nm_ = new NodeLooperThread(std::move(nodes_));
// Set up dummy actions
// "INTERACTION"
// Node0, value1, 800ms
// Node1, value1, forever
// Node2, value1, 800ms
// "LAUNCH"
// Node0, value0, forever
// Node1, value0, 400ms
// Node2, value0, 400ms
actions_ = std::map<std::string, std::vector<NodeAction>>{
{"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}, {2, 1, 800ms}}},
{"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}, {2, 0, 400ms}}}};
// Prepare dummy files to replace the nodes' path in example json_doc
files_.emplace_back(std::make_unique<TemporaryFile>());
files_.emplace_back(std::make_unique<TemporaryFile>());
// replace file path
json_doc_ = kJSON_RAW;
std::string from =
"/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), files_[0 + 2]->path);
from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq";
start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
EXPECT_TRUE(android::base::SetProperty(prop_, ""))
<< "failed to clear property";
}
virtual void TearDown() {
actions_.clear();
nodes_.clear();
files_.clear();
nm_ = nullptr;
}
sp<NodeLooperThread> nm_;
std::map<std::string, std::vector<NodeAction>> actions_;
std::vector<std::unique_ptr<Node>> nodes_;
std::vector<std::unique_ptr<TemporaryFile>> files_;
std::string json_doc_;
std::string prop_;
};
static inline void _VerifyPropertyValue(const std::string& path,
const std::string& value) {
std::string s = android::base::GetProperty(path, "");
EXPECT_EQ(value, s);
}
static inline void _VerifyPathValue(const std::string& path,
const std::string& value) {
std::string s;
EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
EXPECT_EQ(value, s);
}
// Test GetHints
TEST_F(HintManagerTest, GetHintsTest) {
HintManager hm(nm_, actions_);
EXPECT_TRUE(hm.Start());
std::vector<std::string> hints = hm.GetHints();
EXPECT_TRUE(hm.IsRunning());
EXPECT_EQ(2u, hints.size());
EXPECT_NE(std::find(hints.begin(), hints.end(), "INTERACTION"), hints.end());
EXPECT_NE(std::find(hints.begin(), hints.end(), "LAUNCH"), hints.end());
}
// Test initialization of default values
TEST_F(HintManagerTest, HintInitDefaultTest) {
HintManager hm(nm_, actions_);
EXPECT_TRUE(hm.Start());
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
EXPECT_TRUE(hm.IsRunning());
_VerifyPathValue(files_[0]->path, "");
_VerifyPathValue(files_[1]->path, "n1_value2");
_VerifyPropertyValue(prop_, "n2_value2");
}
// Test IsHintSupported
TEST_F(HintManagerTest, HintSupportedTest) {
HintManager hm(nm_, actions_);
EXPECT_TRUE(hm.IsHintSupported("INTERACTION"));
EXPECT_TRUE(hm.IsHintSupported("LAUNCH"));
EXPECT_FALSE(hm.IsHintSupported("NO_SUCH_HINT"));
}
// Test DumpToFd
TEST_F(HintManagerTest, DumpToFdTest) {
HintManager hm(nm_, actions_);
TemporaryFile dumptf;
hm.DumpToFd(dumptf.fd);
fsync(dumptf.fd);
std::ostringstream dump_buf;
dump_buf << "========== Begin perfmgr nodes ==========\nNode Name\tNode "
"Path\tCurrent Index\tCurrent Value\nn0\t"
<< files_[0]->path << "\t2\t\nn1\t" << files_[1]->path
<< "\t2\t\nn2\tvendor.pwhal.mode\t2\t\n========== End perfmgr "
"nodes ==========\n";
_VerifyPathValue(dumptf.path, dump_buf.str());
TemporaryFile dumptf_started;
EXPECT_TRUE(hm.Start());
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
EXPECT_TRUE(hm.IsRunning());
hm.DumpToFd(dumptf_started.fd);
fsync(dumptf_started.fd);
dump_buf.str("");
dump_buf.clear();
dump_buf << "========== Begin perfmgr nodes ==========\nNode Name\tNode "
"Path\tCurrent Index\tCurrent Value\nn0\t"
<< files_[0]->path << "\t2\t\nn1\t" << files_[1]->path
<< "\t2\tn1_value2\nn2\tvendor.pwhal.mode\t2\tn2_value2\n========="
"= End perfmgr nodes ==========\n";
_VerifyPathValue(dumptf_started.path, dump_buf.str());
}
// Test hint/cancel/expire with dummy actions
TEST_F(HintManagerTest, HintTest) {
HintManager hm(nm_, actions_);
EXPECT_TRUE(hm.Start());
EXPECT_TRUE(hm.IsRunning());
EXPECT_TRUE(hm.DoHint("INTERACTION"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value1");
_VerifyPathValue(files_[1]->path, "n1_value1");
_VerifyPropertyValue(prop_, "n2_value1");
// this won't change the expire time of INTERACTION hint
EXPECT_TRUE(hm.DoHint("INTERACTION", 200ms));
// now place new hint
EXPECT_TRUE(hm.DoHint("LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value0");
_VerifyPropertyValue(prop_, "n2_value0");
EXPECT_TRUE(hm.DoHint("LAUNCH", 500ms));
// "LAUNCH" node1 not expired
std::this_thread::sleep_for(400ms);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value0");
_VerifyPropertyValue(prop_, "n2_value0");
// "LAUNCH" node1 expired
std::this_thread::sleep_for(100ms + kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value1");
_VerifyPropertyValue(prop_, "n2_value1");
EXPECT_TRUE(hm.EndHint("LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
// "LAUNCH" canceled
_VerifyPathValue(files_[0]->path, "n0_value1");
_VerifyPathValue(files_[1]->path, "n1_value1");
_VerifyPropertyValue(prop_, "n2_value1");
std::this_thread::sleep_for(200ms);
// "INTERACTION" node0 expired
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value1");
_VerifyPropertyValue(prop_, "n2_value2");
EXPECT_TRUE(hm.EndHint("INTERACTION"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
// "INTERACTION" canceled
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value2");
_VerifyPropertyValue(prop_, "n2_value2");
}
// Test parsing nodes
TEST_F(HintManagerTest, ParseNodesTest) {
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(3u, nodes.size());
EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
EXPECT_TRUE(nodes[0]->GetResetOnInit());
EXPECT_FALSE(nodes[1]->GetResetOnInit());
// no dynamic_cast intentionally in Android
EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd());
EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd());
EXPECT_EQ("ModeProperty", nodes[2]->GetName());
EXPECT_EQ(prop_, nodes[2]->GetPath());
EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]);
EXPECT_EQ("LOW", nodes[2]->GetValues()[1]);
EXPECT_EQ("NONE", nodes[2]->GetValues()[2]);
EXPECT_EQ(2u, nodes[2]->GetDefaultIndex());
EXPECT_FALSE(nodes[2]->GetResetOnInit());
}
// Test parsing nodes with duplicate name
TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
std::string from = "CPUCluster0MinFreq";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
TEST_F(HintManagerTest, ParsePropertyNodesDuplicatNameTest) {
std::string from = "ModeProperty";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
// Test parsing nodes with duplicate path
TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) {
std::string from = files_[0 + 2]->path;
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
// Test parsing file node with duplicate value
TEST_F(HintManagerTest, ParseFileNodesDuplicateValueTest) {
std::string from = "1512000";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "1134000");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
// Test parsing property node with duplicate value
TEST_F(HintManagerTest, ParsePropertyNodesDuplicateValueTest) {
std::string from = "HIGH";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "LOW");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
// Test parsing file node with empty value
TEST_F(HintManagerTest, ParseFileNodesEmptyValueTest) {
std::string from = "384000";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
// Test parsing property node with empty value
TEST_F(HintManagerTest, ParsePropertyNodesEmptyValueTest) {
std::string from = "LOW";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(3u, nodes.size());
EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
EXPECT_TRUE(nodes[0]->GetResetOnInit());
EXPECT_FALSE(nodes[1]->GetResetOnInit());
// no dynamic_cast intentionally in Android
EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd());
EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd());
EXPECT_EQ("ModeProperty", nodes[2]->GetName());
EXPECT_EQ(prop_, nodes[2]->GetPath());
EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]);
EXPECT_EQ("", nodes[2]->GetValues()[1]);
EXPECT_EQ("NONE", nodes[2]->GetValues()[2]);
EXPECT_EQ(2u, nodes[2]->GetDefaultIndex());
EXPECT_FALSE(nodes[2]->GetResetOnInit());
}
// Test parsing invalid json for nodes
TEST_F(HintManagerTest, ParseBadFileNodesTest) {
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes("invalid json");
EXPECT_EQ(0u, nodes.size());
nodes = HintManager::ParseNodes(
"{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
"\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}");
EXPECT_EQ(0u, nodes.size());
}
// Test parsing actions
TEST_F(HintManagerTest, ParseActionsTest) {
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(2u, actions.size());
EXPECT_EQ(2u, actions["INTERACTION"].size());
EXPECT_EQ(1u, actions["INTERACTION"][0].node_index);
EXPECT_EQ(1u, actions["INTERACTION"][0].value_index);
EXPECT_EQ(std::chrono::milliseconds(800).count(),
actions["INTERACTION"][0].timeout_ms.count());
EXPECT_EQ(2u, actions["INTERACTION"][1].node_index);
EXPECT_EQ(1u, actions["INTERACTION"][1].value_index);
EXPECT_EQ(std::chrono::milliseconds(800).count(),
actions["INTERACTION"][1].timeout_ms.count());
EXPECT_EQ(3u, actions["LAUNCH"].size());
EXPECT_EQ(0u, actions["LAUNCH"][0].node_index);
EXPECT_EQ(1u, actions["LAUNCH"][0].value_index);
EXPECT_EQ(std::chrono::milliseconds(500).count(),
actions["LAUNCH"][0].timeout_ms.count());
EXPECT_EQ(2u, actions["LAUNCH"][1].node_index);
EXPECT_EQ(0u, actions["LAUNCH"][1].value_index);
EXPECT_EQ(std::chrono::milliseconds(500).count(),
actions["LAUNCH"][1].timeout_ms.count());
EXPECT_EQ(1u, actions["LAUNCH"][2].node_index);
EXPECT_EQ(0u, actions["LAUNCH"][2].value_index);
EXPECT_EQ(std::chrono::milliseconds(2000).count(),
actions["LAUNCH"][2].timeout_ms.count());
}
// Test parsing actions with duplicate File node
TEST_F(HintManagerTest, ParseActionDuplicateFileNodeTest) {
std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(),
"\"Node\":\"CPUCluster1MinFreq\"");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(3u, nodes.size());
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(0u, actions.size());
}
// Test parsing actions with duplicate Property node
TEST_F(HintManagerTest, ParseActionDuplicatePropertyNodeTest) {
std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "\"Node\":\"ModeProperty\"");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
EXPECT_EQ(3u, nodes.size());
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(0u, actions.size());
}
// Test parsing invalid json for actions
TEST_F(HintManagerTest, ParseBadActionsTest) {
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions("invalid json", nodes);
EXPECT_EQ(0u, actions.size());
actions = HintManager::ParseActions(
"{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
"\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}",
nodes);
EXPECT_EQ(0u, actions.size());
}
// Test hint/cancel/expire with json config
TEST_F(HintManagerTest, GetFromJSONTest) {
TemporaryFile json_file;
ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path))
<< strerror(errno);
std::unique_ptr<HintManager> hm =
HintManager::GetFromJSON(json_file.path, false);
EXPECT_NE(nullptr, hm.get());
EXPECT_FALSE(hm->IsRunning());
EXPECT_TRUE(hm->Start());
EXPECT_TRUE(hm->IsRunning());
hm = HintManager::GetFromJSON(json_file.path);
EXPECT_NE(nullptr, hm.get());
EXPECT_TRUE(hm->IsRunning());
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
EXPECT_TRUE(hm->IsRunning());
// Initial default value on Node0
_VerifyPathValue(files_[0 + 2]->path, "384000");
_VerifyPathValue(files_[1 + 2]->path, "");
_VerifyPropertyValue(prop_, "");
// Do INTERACTION
EXPECT_TRUE(hm->DoHint("INTERACTION"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0 + 2]->path, "384000");
_VerifyPathValue(files_[1 + 2]->path, "1134000");
_VerifyPropertyValue(prop_, "LOW");
// Do LAUNCH
EXPECT_TRUE(hm->DoHint("LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0 + 2]->path, "1134000");
_VerifyPathValue(files_[1 + 2]->path, "1512000");
_VerifyPropertyValue(prop_, "HIGH");
std::this_thread::sleep_for(500ms);
// "LAUNCH" node0 expired
_VerifyPathValue(files_[0 + 2]->path, "384000");
_VerifyPathValue(files_[1 + 2]->path, "1512000");
_VerifyPropertyValue(prop_, "LOW");
EXPECT_TRUE(hm->EndHint("LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
// "LAUNCH" canceled
_VerifyPathValue(files_[0 + 2]->path, "384000");
_VerifyPathValue(files_[1 + 2]->path, "1134000");
_VerifyPropertyValue(prop_, "LOW");
std::this_thread::sleep_for(300ms);
// "INTERACTION" node1 expired
_VerifyPathValue(files_[0 + 2]->path, "384000");
_VerifyPathValue(files_[1 + 2]->path, "384000");
_VerifyPropertyValue(prop_, "NONE");
}
} // namespace perfmgr
} // namespace android

View file

@ -1,195 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <thread>
#include "perfmgr/FileNode.h"
#include "perfmgr/NodeLooperThread.h"
namespace android {
namespace perfmgr {
using namespace std::chrono_literals;
constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
class NodeLooperThreadTest : public ::testing::Test {
protected:
virtual void SetUp() {
std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
nodes_.emplace_back(new FileNode(
"n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2,
false));
files_.emplace_back(std::move(tf));
tf = std::make_unique<TemporaryFile>();
nodes_.emplace_back(new FileNode(
"n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2,
true));
files_.emplace_back(std::move(tf));
}
virtual void TearDown() {
nodes_.clear();
files_.clear();
}
std::vector<std::unique_ptr<Node>> nodes_;
std::vector<std::unique_ptr<TemporaryFile>> files_;
};
static inline void _VerifyPathValue(const std::string& path,
const std::string& value) {
std::string s;
EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
EXPECT_EQ(value, s);
}
// Test default value init
TEST_F(NodeLooperThreadTest, InitRunTest) {
sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
EXPECT_TRUE(th->Start());
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
EXPECT_TRUE(th->isRunning());
_VerifyPathValue(files_[0]->path, "");
_VerifyPathValue(files_[1]->path, "n1_value2");
th->Stop();
EXPECT_FALSE(th->isRunning());
}
// Test add request
TEST_F(NodeLooperThreadTest, AddRequest) {
sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
EXPECT_TRUE(th->Start());
EXPECT_TRUE(th->isRunning());
// Dummy LAUNCH boost actions:
// Node0, value0, 200ms
// Node1, value1, 400ms
std::vector<NodeAction> actions{{0, 0, 200ms}, {1, 1, 400ms}};
EXPECT_TRUE(th->Request(actions, "LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value1");
std::this_thread::sleep_for(200ms);
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value1");
std::this_thread::sleep_for(200ms);
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value2");
th->Stop();
EXPECT_FALSE(th->isRunning());
}
// Test request to override expire time
TEST_F(NodeLooperThreadTest, AddRequestOverride) {
sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
EXPECT_TRUE(th->Start());
EXPECT_TRUE(th->isRunning());
// Dummy LAUNCH boost actions:
// Node0, value0, 200ms
// Node1, value1, 500ms
std::vector<NodeAction> actions{{0, 0, 200ms}, {1, 1, 500ms}};
EXPECT_TRUE(th->Request(actions, "LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value1");
// Dummy LAUNCH boost actions:
// Node0, value0, 300ms will extend
// Node1, value1, 100ms will not extend
actions = std::vector<NodeAction>{{0, 0, 300ms}, {1, 1, 100ms}};
EXPECT_TRUE(th->Request(actions, "LAUNCH"));
std::this_thread::sleep_for(200ms);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value1");
std::this_thread::sleep_for(150ms);
// Node0 value0 expired
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value1");
std::this_thread::sleep_for(150ms);
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value2");
th->Stop();
EXPECT_FALSE(th->isRunning());
}
// Test cancel request
TEST_F(NodeLooperThreadTest, CancelRequest) {
sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
EXPECT_TRUE(th->Start());
EXPECT_TRUE(th->isRunning());
// Dummy LAUNCH boost actions:
// Node0, value0, forever
// Node1, value1, forever
std::vector<NodeAction> actions{{0, 0, 0ms}, {1, 1, 0ms}};
EXPECT_TRUE(th->Request(actions, "LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value1");
EXPECT_TRUE(th->Cancel(actions, "LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value2");
th->Stop();
EXPECT_FALSE(th->isRunning());
}
// Test multiple request
TEST_F(NodeLooperThreadTest, MultipleRequest) {
sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
EXPECT_TRUE(th->Start());
EXPECT_TRUE(th->isRunning());
// Dummy LAUNCH boost actions:
// Node0, value1, 800ms
// Node1, value1, forever
std::vector<NodeAction> actions_interaction{{0, 1, 800ms}, {1, 1, 0ms}};
EXPECT_TRUE(th->Request(actions_interaction, "INTERACTION"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value1");
_VerifyPathValue(files_[1]->path, "n1_value1");
// Dummy LAUNCH boost actions:
// Node0, value0, forever
// Node1, value0, 400ms
std::vector<NodeAction> actions_launch{{0, 0, 0ms}, {1, 0, 400ms}};
EXPECT_TRUE(th->Request(actions_launch, "LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value0");
std::this_thread::sleep_for(400ms);
// "LAUNCH" node1 expired
_VerifyPathValue(files_[0]->path, "n0_value0");
_VerifyPathValue(files_[1]->path, "n1_value1");
EXPECT_TRUE(th->Cancel(actions_launch, "LAUNCH"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
// "LAUNCH" canceled
_VerifyPathValue(files_[0]->path, "n0_value1");
_VerifyPathValue(files_[1]->path, "n1_value1");
std::this_thread::sleep_for(400ms);
// "INTERACTION" node0 expired
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value1");
EXPECT_TRUE(th->Cancel(actions_interaction, "INTERACTION"));
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
// "INTERACTION" canceled
_VerifyPathValue(files_[0]->path, "n0_value2");
_VerifyPathValue(files_[1]->path, "n1_value2");
th->Stop();
EXPECT_FALSE(th->isRunning());
}
} // namespace perfmgr
} // namespace android

View file

@ -1,222 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <thread>
#include "perfmgr/PropertyNode.h"
namespace android {
namespace perfmgr {
using namespace std::chrono_literals;
constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
constexpr auto kSLEEP_TOLERANCE_MS = 2ms;
static inline void _VerifyPropertyValue(const std::string& path,
const std::string& value) {
std::string s = android::base::GetProperty(path, "");
EXPECT_EQ(value, s);
}
static inline const std::string _InitProperty(const std::string& path) {
EXPECT_TRUE(android::base::SetProperty(path, ""))
<< "failed to clear property";
return path;
}
// Test init with no default value
TEST(PropertyNodeTest, NoInitDefaultTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
t.Update(false);
_VerifyPropertyValue(key, "");
}
// Test init with default value
TEST(PropertyNodeTest, InitDefaultTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
t.Update(false);
_VerifyPropertyValue(key, "value1");
std::string key2 = _InitProperty("test.libperfmgr.key2");
PropertyNode t2("t2", key2, {{"value0"}, {"value1"}, {"value2"}}, 0, true);
t2.Update(false);
_VerifyPropertyValue(key2, "value0");
}
// Test DumpToFd
TEST(PropertyNodeTest, DumpToFdTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("test_dump", key, {{"value0"}, {"value1"}, {"value2"}}, 1,
true);
t.Update(false);
TemporaryFile dumptf;
t.DumpToFd(dumptf.fd);
fsync(dumptf.fd);
std::string buf(
android::base::StringPrintf("test_dump\t%s\t1\tvalue1\n", key.c_str()));
std::string s;
EXPECT_TRUE(android::base::ReadFileToString(dumptf.path, &s))
<< strerror(errno);
EXPECT_EQ(buf, s);
}
// Test GetValueIndex
TEST(PropertyNodeTest, GetValueIndexTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
std::size_t index = 0;
EXPECT_TRUE(t.GetValueIndex("value2", &index));
EXPECT_EQ(2u, index);
index = 1234;
EXPECT_FALSE(t.GetValueIndex("NON_EXIST", &index));
EXPECT_EQ(1234u, index);
}
// Test GetValues
TEST(PropertyNodeTest, GetValuesTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
std::vector values = t.GetValues();
EXPECT_EQ(3u, values.size());
EXPECT_EQ("value0", values[0]);
EXPECT_EQ("value1", values[1]);
EXPECT_EQ("value2", values[2]);
}
// Test get more properties
TEST(PropertyNodeTest, GetPropertiesTest) {
std::string test_name = "TESTREQ_1";
std::string test_path = "TEST_PATH";
PropertyNode t(test_name, test_path, {}, 0, false);
EXPECT_EQ(test_name, t.GetName());
EXPECT_EQ(test_path, t.GetPath());
EXPECT_EQ(0u, t.GetValues().size());
EXPECT_EQ(0u, t.GetDefaultIndex());
EXPECT_FALSE(t.GetResetOnInit());
}
// Test add request
TEST(PropertyNodeTest, AddRequestTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {""}}, 2, true);
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
_VerifyPropertyValue(key, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Let high prio request timeout, now only request @ value1 active
std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value1");
EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Let all requests timeout, now default value2
std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
expire_time = t.Update(true);
_VerifyPropertyValue(key, "");
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
}
// Test remove request
TEST(PropertyNodeTest, RemoveRequestTest) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
_VerifyPropertyValue(key, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove high prio request, now only request @ value1 active
t.RemoveRequest("LAUNCH");
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove request, now default value2
t.RemoveRequest("INTERACTION");
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value2");
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
}
// Test add request
TEST(PropertyNodeTest, AddRequestTestOverride) {
std::string key = _InitProperty("test.libperfmgr.key");
PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
auto start = std::chrono::steady_clock::now();
EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
std::chrono::milliseconds expire_time = t.Update(true);
// Add request @ value1
_VerifyPropertyValue(key, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 higher prio than value1
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 shorter
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 100ms));
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value0");
EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Add request @ value0 longer
EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 300ms));
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value0");
EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove high prio request, now only request @ value1 active
t.RemoveRequest("LAUNCH");
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value1");
EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
// Remove request, now default value2
t.RemoveRequest("INTERACTION");
expire_time = t.Update(true);
_VerifyPropertyValue(key, "value2");
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
}
} // namespace perfmgr
} // namespace android

View file

@ -1,164 +0,0 @@
/*
* Copyright (C) 2017 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 specic language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <algorithm>
#include <thread>
#include "perfmgr/RequestGroup.h"
namespace android {
namespace perfmgr {
using namespace std::chrono_literals;
constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
// Test GetRequestValue()
TEST(RequestGroupTest, GetRequestValueTest) {
std::string test_str = "TESTREQ_1";
RequestGroup req(test_str);
EXPECT_EQ(test_str, req.GetRequestValue());
}
// Test AddRequest()
TEST(RequestGroupTest, AddRequestTest) {
RequestGroup req("");
auto start = std::chrono::steady_clock::now();
auto duration = 500ms;
bool ret = req.AddRequest("INTERACTION", start + duration);
EXPECT_EQ(true, ret);
auto sleep_time = 200ms;
std::this_thread::sleep_for(sleep_time);
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
EXPECT_NEAR((duration - sleep_time).count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
EXPECT_EQ(true, active);
}
// Test AddRequest() with a huge expire time which could be done in some long
// persist power hint such as VR_MODE
TEST(RequestGroupTest, AddRequestNoExpireTest) {
RequestGroup req("");
bool ret = req.AddRequest("INTERACTION", ReqTime::max());
EXPECT_EQ(true, ret);
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
auto expect = std::chrono::duration_cast<std::chrono::milliseconds>(
ReqTime::max() - std::chrono::steady_clock::now());
EXPECT_NEAR(expect.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
// expire time is greater than 1 year
EXPECT_LE(365 * 24 * 60 * 60 * 1000, expire_time.count());
EXPECT_EQ(true, active);
}
// Test AddRequest() and expires
TEST(RequestGroupTest, AddRequestTestExpire) {
RequestGroup req("");
auto start = std::chrono::steady_clock::now();
auto duration = 5ms;
bool ret = req.AddRequest("INTERACTION", start + duration);
EXPECT_EQ(true, ret);
ret = req.AddRequest("INTERACTION", start + duration + 1ms);
EXPECT_EQ(false, ret);
std::this_thread::sleep_for(duration + 10ms);
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
EXPECT_EQ(false, active);
}
// Test AddRequest() with new value
TEST(RequestGroupTest, AddRequestNewValue) {
RequestGroup req("");
auto start = std::chrono::steady_clock::now();
auto duration = 5000ms;
bool ret = req.AddRequest("INTERACTION", start + duration);
EXPECT_EQ(true, ret);
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
EXPECT_NEAR(duration.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
EXPECT_EQ(true, active);
// Add a request shorter than the current outstanding one, expiration time
// not changed
auto shorter_duration = 100ms;
ret = req.AddRequest("INTERACTION", start + shorter_duration);
EXPECT_EQ(false, ret);
active = req.GetExpireTime(&expire_time);
EXPECT_NEAR(duration.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
EXPECT_EQ(true, active);
// Add a request longer than the current outstanding one, expiration time
// changed
duration = 10000ms;
ret = req.AddRequest("INTERACTION", start + duration);
EXPECT_EQ(false, ret);
active = req.GetExpireTime(&expire_time);
EXPECT_NEAR(duration.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
EXPECT_EQ(true, active);
}
// Test multiple AddRequest() with different hint_type
TEST(RequestGroupTest, AddRequestTestMultiple) {
RequestGroup req("");
auto start = std::chrono::steady_clock::now();
auto duration_interact = 500ms;
req.AddRequest("INTERACTION", start + duration_interact);
auto duration_launch = 5000ms;
req.AddRequest("LAUNCH", start + duration_launch);
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
EXPECT_NEAR(std::min(duration_interact, duration_launch).count(),
expire_time.count(), kTIMING_TOLERANCE_MS);
EXPECT_EQ(true, active);
}
// Test RemoveRequest()
TEST(RequestGroupTest, RemoveRequestTest) {
RequestGroup req("");
auto start = std::chrono::steady_clock::now();
auto duration_interact = 500ms;
req.AddRequest("INTERACTION", start + duration_interact);
bool ret = req.RemoveRequest("INTERACTION");
EXPECT_EQ(true, ret);
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
EXPECT_EQ(false, active);
// Test removing an already-removed request
ret = req.RemoveRequest("INTERACTION");
EXPECT_EQ(false, ret);
}
// Test multiple RemoveRequest() with different hint_type
TEST(RequestGroupTest, RemoveRequestTestMultiple) {
RequestGroup req("");
auto start = std::chrono::steady_clock::now();
auto duration_interact = 500ms;
req.AddRequest("INTERACTION", start + duration_interact);
auto duration_launch = 50000ms;
req.AddRequest("LAUNCH", start + duration_launch);
req.RemoveRequest("INTERACTION");
std::chrono::milliseconds expire_time;
bool active = req.GetExpireTime(&expire_time);
EXPECT_NEAR(duration_launch.count(), expire_time.count(),
kTIMING_TOLERANCE_MS);
EXPECT_EQ(true, active);
}
} // namespace perfmgr
} // namespace android

View file

@ -1,177 +0,0 @@
/*
* 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 specic language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/logging.h>
#include <getopt.h>
#include <sys/types.h>
#include <unistd.h>
#include <thread>
#include "perfmgr/HintManager.h"
namespace android {
namespace perfmgr {
class NodeVerifier : public HintManager {
public:
static bool VerifyNodes(const std::string& config_path) {
std::string json_doc;
if (!android::base::ReadFileToString(config_path, &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path;
return false;
}
std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
if (nodes.empty()) {
LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
return false;
}
return true;
}
private:
NodeVerifier(sp<NodeLooperThread> nm,
const std::map<std::string, std::vector<NodeAction>>& actions)
: HintManager(std::move(nm), actions) {}
};
} // namespace perfmgr
} // namespace android
static void printUsage(const char* exec_name) {
std::string usage = exec_name;
usage =
usage +
" is a command-line tool to verify Nodes in Json config are writable.\n"
"Usages:\n"
" [su system] " +
exec_name +
" [options]\n"
"\n"
"Options:\n"
" --config, -c [PATH]\n"
" path to Json config file\n\n"
" --exec_hint, -e\n"
" do hints in Json config\n\n"
" --hint_name, -i\n"
" do only the specific hint\n\n"
" --hint_duration, -d [duration]\n"
" duration in ms for each hint\n\n"
" --help, -h\n"
" print this message\n\n"
" --verbose, -v\n"
" print verbose log during execution\n\n";
LOG(INFO) << usage;
}
static void execConfig(const std::string& json_file,
const std::string& hint_name, uint64_t hint_duration) {
std::unique_ptr<android::perfmgr::HintManager> hm =
android::perfmgr::HintManager::GetFromJSON(json_file);
if (!hm.get() || !hm->IsRunning()) {
LOG(ERROR) << "Failed to Parse JSON config";
}
std::vector<std::string> hints = hm->GetHints();
for (const auto& hint : hints) {
if (!hint_name.empty() && hint_name != hint) continue;
LOG(INFO) << "Do hint: " << hint;
hm->DoHint(hint, std::chrono::milliseconds(hint_duration));
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(hint_duration));
LOG(INFO) << "End hint: " << hint;
hm->EndHint(hint);
std::this_thread::yield();
}
}
int main(int argc, char* argv[]) {
android::base::InitLogging(argv, android::base::StdioLogger);
if (getuid() == 0) {
LOG(WARNING) << "Running as root might mask node permission";
}
std::string config_path;
std::string hint_name;
bool exec_hint = false;
uint64_t hint_duration = 100;
while (true) {
static struct option opts[] = {
{"config", required_argument, nullptr, 'c'},
{"exec_hint", no_argument, nullptr, 'e'},
{"hint_name", required_argument, nullptr, 'i'},
{"hint_duration", required_argument, nullptr, 'd'},
{"help", no_argument, nullptr, 'h'},
{"verbose", no_argument, nullptr, 'v'},
{0, 0, 0, 0} // termination of the option list
};
int option_index = 0;
int c = getopt_long(argc, argv, "c:ei:d:hv", opts, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 'c':
config_path = optarg;
break;
case 'e':
exec_hint = true;
break;
case 'i':
hint_name = optarg;
break;
case 'd':
hint_duration = strtoul(optarg, NULL, 10);
break;
case 'v':
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
break;
case 'h':
printUsage(argv[0]);
return 0;
default:
// getopt already prints "invalid option -- %c" for us.
return 1;
}
}
if (config_path.empty()) {
LOG(ERROR) << "Need specify JSON config";
printUsage(argv[0]);
return 1;
}
if (exec_hint) {
execConfig(config_path, hint_name, hint_duration);
return 0;
}
if (android::perfmgr::NodeVerifier::VerifyNodes(config_path)) {
LOG(INFO) << "Verified writing to JSON config";
return 0;
} else {
LOG(ERROR) << "Failed to verify nodes in JSON config";
return 1;
}
}

View file

@ -282,7 +282,7 @@
"PowerHint": "EXPENSIVE_RENDERING",
"Node": "GPUMinFreq",
"Duration": 0,
"Value": "465000000"
"Value": "647000000"
},
{
"PowerHint": "EXPENSIVE_RENDERING",

View file

@ -384,15 +384,6 @@ DEVICE_PACKAGE_OVERLAYS += \
# RRO configuration
TARGET_USES_RRO := true
PRODUCT_ENFORCE_RRO_TARGETS := *
# Powerhint
ifeq ($(EAS_POWERHINT_VARIANT), sdm636)
PRODUCT_COPY_FILES += \
$(COMMON_PATH)/power-libperfmgr/sdm636_powerhint.json:$(TARGET_COPY_OUT_VENDOR)/etc/powerhint.json
else
PRODUCT_COPY_FILES += \
$(COMMON_PATH)/power-libperfmgr/sdm660_powerhint.json:$(TARGET_COPY_OUT_VENDOR)/etc/powerhint.json
endif
# Permissions
PRODUCT_COPY_FILES += \
@ -440,7 +431,16 @@ PRODUCT_COPY_FILES += \
# Power
PRODUCT_PACKAGES += \
android.hardware.power@1.3-service.xiaomi_sdm660-libperfmgr
android.hardware.power-service.xiaomi_sdm660-libperfmgr
# Powerhint
ifeq ($(EAS_POWERHINT_VARIANT), sdm636)
PRODUCT_COPY_FILES += \
$(COMMON_PATH)/power-libperfmgr/sdm636_powerhint.json:$(TARGET_COPY_OUT_VENDOR)/etc/powerhint.json
else
PRODUCT_COPY_FILES += \
$(COMMON_PATH)/power-libperfmgr/sdm660_powerhint.json:$(TARGET_COPY_OUT_VENDOR)/etc/powerhint.json
endif
# Preopt SystemUI
PRODUCT_DEXPREOPT_SPEED_APPS += SystemUI

View file

@ -1,4 +1,3 @@
type debugfs_rpm, debugfs_type, fs_type;
type fingerprint_data_file, file_type, data_file_type, core_data_file_type;
type fingerprint_sysfs, fs_type, sysfs_type;
type ir_dev_file, file_type;

View file

@ -18,9 +18,6 @@
/sys/devices/soc/soc:fpc1020(/.*)? u:object_r:fingerprint_sysfs:s0
/sys/bus/platform/devices/soc:fingerprint_fpc(/.*)? u:object_r:fingerprint_sysfs:s0
# Debugfs
/sys/kernel/debug/system_stats u:object_r:debugfs:s0
# Goodix Fingerprint
/data/misc/gf_data(/.*)? u:object_r:fingerprint_data_file:s0
/data/misc/goodix(/.*)? u:object_r:fingerprint_data_file:s0
@ -64,7 +61,7 @@
/persist u:object_r:mnt_vendor_file:s0
# Power
/(vendor|system/vendor)/bin/hw/android\.hardware\.power@1\.3-service\.xiaomi_sdm660-libperfmgr u:object_r:hal_power_default_exec:s0
/(vendor|system/vendor)/bin/hw/android\.hardware\.power-service\.xiaomi_sdm660-libperfmgr u:object_r:hal_power_default_exec:s0
# Shell Script
/(vendor|system/vendor)/bin/init\.goodix\.sh u:object_r:init_fingerprint_exec:s0

View file

@ -28,8 +28,6 @@ genfscon sysfs /devices/soc/800f000.qcom,spmi/spmi-0/spmi0-03/800f000.qcom,spmi:
genfscon sysfs /devices/soc/800f000.qcom,spmi/spmi-0/spmi0-03/800f000.qcom,spmi:qcom,pm660l@3:qcom,leds@d000/leds/white u:object_r:sysfs_graphics:s0
# Power
genfscon debugfs /rpm_stats u:object_r:debugfs_rpm:s0
genfscon debugfs /system_stats u:object_r:debugfs_rpm:s0
genfscon sysfs /devices/soc/soc:qcom,gpubw u:object_r:sysfs_devfreq:s0
genfscon sysfs /devices/soc/soc:qcom,cpubw u:object_r:sysfs_devfreq:s0
genfscon sysfs /devices/soc/soc:qcom,mincpubw u:object_r:sysfs_devfreq:s0

View file

@ -7,7 +7,6 @@ allow hal_power_default sysfs_touchpanel:file rw_file_perms;
allow hal_power_default sysfs_touchpanel:dir search;
allow hal_power_default sysfs_tap_to_wake:file rw_file_perms;
r_dir_file(hal_power_default, debugfs_rpm)
r_dir_file(hal_power_default, sysfs_graphics)
allow hal_power_default device_latency:chr_file rw_file_perms;

View file

@ -44,3 +44,6 @@ EXTRA_VENDOR_LIBRARIES_32 := \
EXTRA_VENDOR_LIBRARIES_64 := \
libstdc++ \
libpowermanager
EXTRA_SYSTEM_LIBRARIES_64 := \
libblasV8