blob: 79d78be0fa4c0d686ac7e25ce2663b93855edd81 [file] [log] [blame]
/*
* Copyright (c) 2021, 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 "carpowerpolicyd"
#include "SilentModeHandler.h"
#include "CarPowerPolicyServer.h"
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <unistd.h>
namespace android {
namespace frameworks {
namespace automotive {
namespace powerpolicy {
using ::android::Mutex;
using ::android::automotive::SysfsMonitor;
using ::android::base::Error;
using ::android::base::GetProperty;
using ::android::base::ReadFileToString;
using ::android::base::Result;
using ::android::base::SetProperty;
using ::android::base::StringPrintf;
using ::android::base::Trim;
using ::android::base::unique_fd;
using ::android::base::WriteStringToFd;
namespace {
constexpr const char kPropertySystemBootReason[] = "sys.boot.reason";
constexpr const char kSilentModeHwStateFilename[] = "/sys/power/pm_silentmode_hw_state";
constexpr const char kKernelSilentModeFilename[] = "/sys/power/pm_silentmode_kernel_state";
// To prevent boot animation from being started.
constexpr const char kPropertyNoBootAnimation[] = "debug.sf.nobootanimation";
// To stop boot animation while it is being played.
constexpr const char kPropertyBootAnimationExit[] = "service.bootanim.exit";
bool fileExists(const char* filename) {
struct stat buffer;
return stat(filename, &buffer) == 0;
}
} // namespace
SilentModeHandler::SilentModeHandler(ISilentModeChangeHandler* handler) :
mSilentModeByHwState(false),
mSilentModeHwStateFilename(kSilentModeHwStateFilename),
mKernelSilentModeFilename(kKernelSilentModeFilename),
mSilentModeChangeHandler(handler),
mSysfsMonitor(sp<SysfsMonitor>::make()) {
mBootReason = GetProperty(kPropertySystemBootReason, "");
}
void SilentModeHandler::init() {
if (mBootReason == kBootReasonForcedSilent) {
mForcedMode = true;
mSilentModeByHwState = true;
} else if (mBootReason == kBootReasonForcedNonSilent) {
mForcedMode = true;
mSilentModeByHwState = false;
}
if (mForcedMode) {
handleSilentModeChange(mSilentModeByHwState);
mSilentModeChangeHandler->notifySilentModeChange(mSilentModeByHwState);
ALOGI("Now in forced mode: monitoring %s is disabled", mSilentModeHwStateFilename.c_str());
} else {
startMonitoringSilentModeHwState();
}
}
void SilentModeHandler::release() {
stopMonitoringSilentModeHwState(/*shouldWaitThread=*/false);
}
bool SilentModeHandler::isSilentMode() {
Mutex::Autolock lock(mMutex);
return mSilentModeByHwState;
}
void SilentModeHandler::stopMonitoringSilentModeHwState(bool shouldWaitThread) {
if (mIsMonitoring) {
mIsMonitoring = false;
if (auto ret = mSysfsMonitor->unregisterFd(mFdSilentModeHwState.get()); !ret.ok()) {
ALOGW("Unregistering %s from SysfsMonitor failed", mSilentModeHwStateFilename.c_str());
}
if (shouldWaitThread && mSilentModeMonitoringThread.joinable()) {
mSilentModeMonitoringThread.join();
}
}
mFdSilentModeHwState.reset();
mSysfsMonitor->release();
}
Result<void> SilentModeHandler::dump(int fd, const Vector<String16>& /*args*/) {
const char* indent = " ";
WriteStringToFd(StringPrintf("%sMonitoring HW state: %s\n", indent,
mIsMonitoring ? "true" : "false"),
fd);
WriteStringToFd(StringPrintf("%sForced silent mode: %s\n", indent,
mForcedMode ? "true" : "false"),
fd);
if (mIsMonitoring) {
Mutex::Autolock lock(mMutex);
WriteStringToFd(StringPrintf("%sSilent mode by HW state: %s\n", indent,
mSilentModeByHwState ? "silent" : "non-silent"),
fd);
}
return {};
}
void SilentModeHandler::startMonitoringSilentModeHwState() {
if (mIsMonitoring) {
ALOGW("Silent Mode monitoring is already started");
return;
}
const char* filename = mSilentModeHwStateFilename.c_str();
if (!fileExists(filename)) {
ALOGW("Failed to start monitoring Silent Mode HW state: %s doesn't exist", filename);
return;
}
if (mFdSilentModeHwState == -1) {
if (mFdSilentModeHwState.reset(open(filename, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
mFdSilentModeHwState == -1) {
ALOGW("Failed to open %s for monitoring: errno = %d", filename, errno);
return;
}
}
auto ret = mSysfsMonitor->init([this](const std::vector<int32_t>& fileDescriptors) {
// Only one sysfs file is registered.
if (mFdSilentModeHwState != fileDescriptors[0]) {
return;
}
handleSilentModeHwStateChange();
});
if (!ret.ok()) {
ALOGW("Failed to initialize SysfsMonitor: %s", ret.error().message().c_str());
return;
}
if (auto ret = mSysfsMonitor->registerFd(mFdSilentModeHwState.get()); !ret.ok()) {
ALOGW("Failed to register %s to SysfsMonitor: %s", filename, ret.error().message().c_str());
return;
}
mIsMonitoring = true;
mSilentModeMonitoringThread = std::thread([this, filename]() {
if (auto ret = mSysfsMonitor->observe(); !ret.ok()) {
ALOGI("Failed to observe %s", filename);
return;
}
ALOGI("Monitoring %s ended", mSilentModeHwStateFilename.c_str());
});
// Read the current silent mode HW state.
handleSilentModeHwStateChange();
}
void SilentModeHandler::handleSilentModeHwStateChange() {
if (!mIsMonitoring) {
return;
}
std::string buf;
if (!ReadFileToString(mSilentModeHwStateFilename.c_str(), &buf)) {
ALOGW("Failed to read %s", mSilentModeHwStateFilename.c_str());
return;
}
bool newSilentMode;
bool oldSilentMode;
{
Mutex::Autolock lock(mMutex);
oldSilentMode = std::exchange(mSilentModeByHwState, Trim(buf) == kValueSilentMode);
newSilentMode = mSilentModeByHwState;
}
if (newSilentMode != oldSilentMode) {
ALOGI("%s is set to %s", mSilentModeHwStateFilename.c_str(),
newSilentMode ? "silent" : "non-silent");
handleSilentModeChange(newSilentMode);
mSilentModeChangeHandler->notifySilentModeChange(newSilentMode);
}
}
void SilentModeHandler::handleSilentModeChange(bool silent) {
if (auto ret = updateKernelSilentMode(silent); !ret.ok()) {
ALOGW("Failed to update kernel silent mode: %s", ret.error().message().c_str());
}
if (auto ret = enableBootAnimation(!silent); !ret.ok()) {
ALOGW("Failed to %s boot animation: %s", mSilentModeByHwState ? "disabling" : "enabling",
ret.error().message().c_str());
}
}
Result<void> SilentModeHandler::enableBootAnimation(bool enabled) {
const std::string value = enabled ? "0" : "1";
if (!SetProperty(kPropertyNoBootAnimation, value)) {
return Error() << "Failed to set " << kPropertyNoBootAnimation << " property to " << value;
}
if (!enabled) {
if (!SetProperty(kPropertyBootAnimationExit, value)) {
return Error() << "Failed to set " << kPropertyBootAnimationExit << " property to "
<< value;
}
}
return {};
}
Result<void> SilentModeHandler::updateKernelSilentMode(bool silent) {
int fd = open(mKernelSilentModeFilename.c_str(), O_WRONLY | O_NONBLOCK);
if (fd < 0) {
return Error() << "Failed to open " << mKernelSilentModeFilename;
}
Result<void> status = {};
if (const auto& value = silent ? kValueSilentMode : kValueNonSilentMode;
!WriteStringToFd(value, fd)) {
status = Error() << "Failed to write " << value << " to fd " << fd;
}
close(fd);
return status;
}
} // namespace powerpolicy
} // namespace automotive
} // namespace frameworks
} // namespace android