blob: 3b4f80f3ae8ab0522b6d546e2de83e21d9533549 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/policy/dark_resume_policy.h"
#include <algorithm>
#include <base/file_util.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/time/time.h>
#include "power_manager/common/power_constants.h"
#include "power_manager/common/prefs.h"
#include "power_manager/common/util.h"
namespace power_manager {
namespace policy {
namespace {
const char kDarkResumeStatePath[] = "/sys/power/dark_resume_state";
// Within a device directory there is a directory named power/ which contain two
// files for every device, dark_resume_active and dark_resume_source. Given the
// path to the device, we can get to these files to enable dark resume
// functionality for the device by appending |kPowerDir| then |kDarkResume...|
// to the path.
const char kDarkResumeActive[] = "dark_resume_active";
const char kDarkResumeSource[] = "dark_resume_source";
const char kPowerDir[] = "power/";
// Strings to write to sysfs files to enable/disable dark resume functionality
// on the kernel level.
const char kEnabled[] = "enabled";
const char kDisabled[] = "disabled";
} // namespace
DarkResumePolicy::DarkResumePolicy()
: power_supply_(NULL),
prefs_(NULL),
battery_shutdown_threshold_(0.0),
battery_suspend_level_(0.0),
thresholds_set_(false) {
}
DarkResumePolicy::~DarkResumePolicy() {
SetStates(dark_resume_sources_, kDisabled);
SetStates(dark_resume_devices_, kDisabled);
}
void DarkResumePolicy::Init(system::PowerSupply* power_supply,
PrefsInterface* prefs) {
power_supply_ = power_supply;
prefs_ = prefs;
bool disable = false;
enabled_ = (!prefs_->GetBool(kDisableDarkResumePref, &disable) || !disable) &&
ReadSuspendDurationsPref() &&
ReadBatteryMarginsPref();
VLOG(1) << "Dark resume user space " << (enabled_ ? "enabled" : "disabled");
GetFiles(&dark_resume_sources_, kDarkResumeSourcesPref, kDarkResumeSource);
GetFiles(&dark_resume_devices_, kDarkResumeDevicesPref, kDarkResumeActive);
SetStates(dark_resume_sources_, (enabled_ ? kEnabled : kDisabled));
SetStates(dark_resume_devices_, (enabled_ ? kEnabled : kDisabled));
}
DarkResumePolicy::Action DarkResumePolicy::GetAction() {
if (!enabled_)
return SUSPEND_INDEFINITELY;
power_supply_->RefreshImmediately();
power_status_ = power_supply_->power_status();
LOG(INFO) << "Current battery is " << power_status_.battery_percentage
<< "% with line power "
<< (power_status_.line_power_on ? "on" : "off");
if (!thresholds_set_)
SetThresholds();
if (power_status_.battery_percentage < battery_shutdown_threshold_ &&
!power_status_.line_power_on)
return SHUT_DOWN;
if (power_status_.battery_percentage > battery_suspend_level_)
SetThresholds();
return SUSPEND_FOR_DURATION;
}
base::TimeDelta DarkResumePolicy::GetSuspendDuration() {
if (!enabled_)
return base::TimeDelta();
double battery = power_status_.battery_percentage;
SuspendMap::iterator upper = suspend_durations_.upper_bound(battery);
if (upper != suspend_durations_.begin())
upper--;
return upper->second;
}
bool DarkResumePolicy::CurrentlyInDarkResume() {
if (!enabled_)
return false;
std::string buf;
if (!base::ReadFileToString(base::FilePath(kDarkResumeStatePath), &buf)) {
PLOG(ERROR) << "Unable to read " << kDarkResumeStatePath;
return false;
}
base::TrimWhitespaceASCII(buf, base::TRIM_TRAILING, &buf);
uint64 value = 0;
return base::StringToUint64(buf, &value) && value;
}
void DarkResumePolicy::HandleResume() {
battery_suspend_level_ = 0.0;
battery_shutdown_threshold_ = 0.0;
thresholds_set_ = false;
}
bool DarkResumePolicy::ExtractLines(const std::string& pref_name,
std::vector<std::string>* lines) {
std::string input_str;
if (prefs_->GetString(pref_name, &input_str)) {
base::SplitString(input_str, '\n', lines);
return true;
}
return false;
}
bool DarkResumePolicy::ReadSuspendDurationsPref() {
suspend_durations_.clear();
std::vector<std::string> lines;
if (!ExtractLines(kDarkResumeSuspendDurationsPref, &lines))
return false;
for (std::vector<std::string>::iterator iter = lines.begin();
iter != lines.end(); ++iter) {
std::vector<std::string> segments;
base::SplitString(*iter, ' ', &segments);
if (segments.size() != 2) {
LOG(ERROR) << "Skipping line in dark resume suspend durations file:"
<< *iter;
return false;
}
double battery_level;
int suspend_duration;
if (!base::StringToDouble(segments[0], &battery_level) ||
!base::StringToInt(segments[1], &suspend_duration)) {
LOG(ERROR) << "Failure in parse string: " << *iter << " -> ("
<< segments[0] << ", "
<< segments[1] << ")";
return false;
}
if (suspend_duration % base::TimeDelta::FromDays(1).InSeconds() == 0) {
LOG(ERROR) << "Cannot have suspend duration of 0 or a multiple of 86400"
<< " (number of seconds in a day).";
return false;
}
suspend_durations_[battery_level] =
base::TimeDelta::FromSeconds(suspend_duration);
}
return !suspend_durations_.empty();
}
bool DarkResumePolicy::ReadBatteryMarginsPref() {
battery_margins_.clear();
std::vector<std::string> lines;
if (!ExtractLines(kDarkResumeBatteryMarginsPref, &lines))
return false;
for (std::vector<std::string>::iterator iter = lines.begin();
iter != lines.end(); ++iter) {
std::vector<std::string> segments;
base::SplitString(*iter, ' ', &segments);
if (segments.size() != 2) {
LOG(ERROR) << "Skipping line in dark resume battery margins file:"
<< *iter;
return false;
}
double battery_level;
double margin;
if (!base::StringToDouble(segments[0], &battery_level) ||
!base::StringToDouble(segments[1], &margin)) {
LOG(ERROR) << "Failure in parse string: " << *iter << " -> ("
<< segments[0] << ", "
<< segments[1] << ")";
return false;
}
battery_margins_[battery_level] = margin;
}
return !battery_margins_.empty();
}
void DarkResumePolicy::GetFiles(std::vector<base::FilePath>* files,
const std::string& pref_name,
const std::string& base_file) {
files->clear();
std::vector<std::string> lines;
if (!ExtractLines(pref_name, &lines))
return;
for (std::vector<std::string>::iterator iter = lines.begin();
iter != lines.end(); ++iter) {
base::FilePath path = base::FilePath(*iter);
path = path.AppendASCII(kPowerDir);
path = path.AppendASCII(base_file.c_str());
files->push_back(path);
}
}
void DarkResumePolicy::SetStates(const std::vector<base::FilePath>& files,
const std::string& state) {
for (std::vector<base::FilePath>::const_iterator iter = files.begin();
iter != files.end(); ++iter) {
base::WriteFile(*iter, state.c_str(), state.length());
}
}
void DarkResumePolicy::SetThresholds() {
double battery = power_status_.battery_percentage;
MarginMap::iterator margin = battery_margins_.upper_bound(battery);
if (margin != battery_margins_.begin())
margin--;
battery_shutdown_threshold_ = battery - margin->second;
battery_suspend_level_ = battery;
thresholds_set_ = true;
LOG(INFO) << "Current threshold is " << battery_shutdown_threshold_;
}
} // namespace policy
} // namespace power_manager