/* * 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 #include #include #include #include #include #include #include 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; using ::ndk::ScopedAStatus; namespace { constexpr const char kPropertySystemBootReason[] = "sys.boot.reason"; constexpr const char kHwStateFilename[] = "pm_silentmode_hw_state"; constexpr const char kKernelStateFilename[] = "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"; constexpr const char* kSysfsDirForSilentMode[] = {"/sys/kernel/silent_boot", "/sys/power"}; // Silent Mode types. Must be identical to definitions in SilentModeHandler.java. constexpr const char kSilentModeStringForcedSilent[] = "forced-silent"; constexpr const char kSilentModeStringForcedNonSilent[] = "forced-non-silent"; constexpr const char kSilentModeStringNonForced[] = "non-forced-silent-mode"; constexpr int32_t kSilentModeTypeForcedSilent = 1; constexpr int32_t kSilentModeTypeForcedNonSilent = 2; constexpr int32_t kSilentModeTypeNonForced = 3; const std::unordered_map kSilentModeToType = {{kSilentModeStringForcedSilent, kSilentModeTypeForcedSilent}, {kSilentModeStringForcedNonSilent, kSilentModeTypeForcedNonSilent}, {kSilentModeStringNonForced, kSilentModeTypeNonForced}}; bool fileExists(const char* filename) { struct stat buffer; return stat(filename, &buffer) == 0; } // We search for the folder where sysfs files for Silent Mode are located in the following order: // 1. /sys/kernel/silent_boot // 2. /sys/power (only for backward compatibility) std::string searchForSysfsDir() { std::error_code ec; for (const char* dir : kSysfsDirForSilentMode) { if (std::filesystem::is_directory(dir, ec)) { return std::string(dir) + "/"; } } return std::string(kSysfsDirForSilentMode[0]) + "/"; } } // namespace SilentModeHandler::SilentModeHandler(ISilentModeChangeHandler* handler) : mSilentModeByHwState(false), mSilentModeChangeHandler(handler), mSysfsMonitor(sp::make()) { mBootReason = GetProperty(kPropertySystemBootReason, ""); } void SilentModeHandler::init() { std::string sysfsDir = searchForSysfsDir(); mSilentModeHwStateFilename = sysfsDir + kHwStateFilename; mKernelSilentModeFilename = sysfsDir + kKernelStateFilename; bool forcedMode; { Mutex::Autolock lock(mMutex); mForcedMode = false; if (mBootReason == kBootReasonForcedSilent) { mForcedMode = true; mSilentModeByHwState = true; } else if (mBootReason == kBootReasonForcedNonSilent) { mForcedMode = true; mSilentModeByHwState = false; } forcedMode = mForcedMode; } if (forcedMode) { handleSilentModeChange(mSilentModeByHwState); mSilentModeChangeHandler->notifySilentModeChange(mSilentModeByHwState); ALOGI("Now in forced mode: monitoring %s is disabled", mSilentModeHwStateFilename.c_str()); } else { startMonitoringSilentModeHwState(); } } void SilentModeHandler::release() { stopMonitoringSilentModeHwState(); } bool SilentModeHandler::isSilentMode() { Mutex::Autolock lock(mMutex); return mSilentModeByHwState; } void SilentModeHandler::stopMonitoringSilentModeHwState() { if (mIsMonitoring.exchange(false)) { if (auto ret = mSysfsMonitor->unregisterFd(mFdSilentModeHwState.get()); !ret.ok()) { ALOGW("Unregistering %s from SysfsMonitor failed", mSilentModeHwStateFilename.c_str()); } } mFdSilentModeHwState.reset(); mSysfsMonitor->release(); } ScopedAStatus SilentModeHandler::setSilentMode(const std::string& silentMode) { if (kSilentModeToType.count(silentMode) == 0) { return ScopedAStatus:: fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, StringPrintf("Unsupported Silent Mode(%s)", silentMode.c_str()) .c_str()); } int32_t silentModeType = kSilentModeToType.at(silentMode); switch (silentModeType) { case kSilentModeTypeForcedSilent: switchToForcedMode(true); break; case kSilentModeTypeForcedNonSilent: switchToForcedMode(false); break; case kSilentModeTypeNonForced: switchToNonForcedMode(); break; } return ScopedAStatus::ok(); } Result SilentModeHandler::dump(int fd, const Vector& /*args*/) { const char* indent = " "; WriteStringToFd(StringPrintf("%sHW state filename: %s\n", indent, mSilentModeHwStateFilename.c_str()), fd); WriteStringToFd(StringPrintf("%sKernel state filename: %s\n", indent, mKernelSilentModeFilename.c_str()), fd); 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& 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; } if (mSysfsMonitor->observe(); !ret.ok()) { ALOGI("Failed to observe %s", filename); return; } mIsMonitoring = true; ALOGI("Started monitoring Silent Mode HW state"); // 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 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 {}; } void SilentModeHandler::switchToForcedMode(bool silent) { bool updated = false; { Mutex::Autolock lock(mMutex); if (!mForcedMode) { stopMonitoringSilentModeHwState(); mForcedMode = true; } if (mSilentModeByHwState != silent) { mSilentModeByHwState = silent; updated = true; } } if (updated) { updateKernelSilentMode(silent); mSilentModeChangeHandler->notifySilentModeChange(silent); } ALOGI("Now in forced %s mode: monitoring %s is disabled", silent ? "silent" : "non-silent", mSilentModeHwStateFilename.c_str()); } void SilentModeHandler::switchToNonForcedMode() { bool updated = false; { Mutex::Autolock lock(mMutex); if (mForcedMode) { ALOGI("Now in non forced mode: monitoring %s is started", mSilentModeHwStateFilename.c_str()); mForcedMode = false; updated = true; } } if (updated) { startMonitoringSilentModeHwState(); } } Result SilentModeHandler::updateKernelSilentMode(bool silent) { int fd = open(mKernelSilentModeFilename.c_str(), O_WRONLY | O_NONBLOCK); if (fd < 0) { return Error() << "Failed to open " << mKernelSilentModeFilename; } Result 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