blob: 9c4c2a37ffa663611d73c71a6b3a5fd4c8a57a60 [file] [log] [blame]
// Copyright (c) 2012 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/common/prefs.h"
#include <memory>
#include <set>
#include <utility>
#include <base/bind.h>
#include <base/check.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/location.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <cros_config/cros_config.h>
#include "power_manager/common/cros_config_prefs_source.h"
#include "power_manager/common/file_prefs_store.h"
#include "power_manager/common/prefs_observer.h"
#include "power_manager/common/util.h"
namespace power_manager {
namespace {
// Default directories where read/write and read-only preference files are
// stored.
constexpr char kReadWritePrefsDir[] = "/var/lib/power_manager";
constexpr char kReadOnlyPrefsDir[] = "/usr/share/power_manager";
// Subdirectory within the read-only prefs dir where board-specific prefs are
// stored.
constexpr char kBoardSpecificPrefsSubdir[] = "board_specific";
// Minimum time between batches of prefs being written to disk, in
// milliseconds.
const int kDefaultWriteIntervalMs = 1000;
} // namespace
Prefs::TestApi::TestApi(Prefs* prefs) : prefs_(prefs) {}
Prefs::TestApi::~TestApi() {}
bool Prefs::TestApi::TriggerWriteTimeout() {
if (!prefs_->write_prefs_timer_.IsRunning())
return false;
prefs_->write_prefs_timer_.Stop();
prefs_->WritePrefs();
return true;
}
Prefs::Prefs()
: write_interval_(
base::TimeDelta::FromMilliseconds(kDefaultWriteIntervalMs)) {}
Prefs::~Prefs() {
if (write_prefs_timer_.IsRunning())
WritePrefs();
}
// static
std::unique_ptr<PrefsStoreInterface> Prefs::GetDefaultStore() {
return std::make_unique<FilePrefsStore>(base::FilePath(kReadWritePrefsDir));
}
// static
PrefsSourceInterfaceVector Prefs::GetDefaultSources() {
PrefsSourceInterfaceVector sources;
const base::FilePath read_only_path(kReadOnlyPrefsDir);
auto config = std::make_unique<brillo::CrosConfig>();
if (config->Init()) {
sources.emplace_back(new CrosConfigPrefsSource(std::move(config)));
}
sources.emplace_back(
new FilePrefsStore(read_only_path.Append(kBoardSpecificPrefsSubdir)));
sources.emplace_back(new FilePrefsStore(read_only_path));
return sources;
}
bool Prefs::Init(std::unique_ptr<PrefsStoreInterface> pref_store,
PrefsSourceInterfaceVector pref_sources) {
pref_store_ = std::move(pref_store);
pref_sources_ = std::move(pref_sources);
return pref_store_->Watch(
base::Bind(&Prefs::HandlePrefChanged, base::Unretained(this)));
}
void Prefs::AddObserver(PrefsObserver* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void Prefs::RemoveObserver(PrefsObserver* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
void Prefs::HandlePrefChanged(const std::string& name) {
// Resist the temptation to erase |name| from |prefs_to_write_| here, as
// it would cause a race:
// 1. SetInt64() is called and pref is written to disk.
// 2. SetInt64() is called and and the new value is queued.
// 3. HandleFileChanged() is called regarding the initial write.
for (PrefsObserver& observer : observers_)
observer.OnPrefChanged(name);
}
void Prefs::GetPrefResults(const std::string& name,
bool read_all,
std::vector<PrefReadResult>* results) {
CHECK(results);
results->clear();
PrefReadResult result;
// If there's a queued value that'll be written to the store soon,
// use it instead of reading from disk.
bool in_store;
if (prefs_to_write_.count(name)) {
base::TrimWhitespaceASCII(prefs_to_write_[name], base::TRIM_TRAILING,
&result.value);
in_store = true;
} else {
in_store = pref_store_->ReadPrefString(name, &result.value);
}
if (in_store) {
result.source_desc = pref_store_->GetDescription();
results->push_back(result);
if (!read_all) {
return;
}
}
for (const auto& source : pref_sources_) {
if (source->ReadPrefString(name, &result.value)) {
result.source_desc = source->GetDescription();
results->push_back(result);
if (!read_all) {
return;
}
}
}
}
bool Prefs::GetString(const std::string& name, std::string* buf) {
DCHECK(buf);
std::vector<PrefReadResult> results;
GetPrefResults(name, false, &results);
if (results.empty())
return false;
*buf = results[0].value;
return true;
}
bool Prefs::GetInt64(const std::string& name, int64_t* value) {
DCHECK(value);
std::vector<PrefReadResult> results;
GetPrefResults(name, true, &results);
for (const auto& result : results) {
if (base::StringToInt64(result.value, value))
return true;
else
LOG(ERROR) << "Unable to parse int64_t from " << result.source_desc;
}
return false;
}
bool Prefs::GetDouble(const std::string& name, double* value) {
DCHECK(value);
std::vector<PrefReadResult> results;
GetPrefResults(name, true, &results);
for (const auto& result : results) {
if (base::StringToDouble(result.value, value))
return true;
else
LOG(ERROR) << "Unable to parse double from " << result.source_desc;
}
return false;
}
bool Prefs::GetBool(const std::string& name, bool* value) {
int64_t int_value = 0;
if (!GetInt64(name, &int_value))
return false;
*value = int_value != 0;
return true;
}
void Prefs::SetString(const std::string& name, const std::string& value) {
prefs_to_write_[name] = value;
ScheduleWrite();
}
void Prefs::SetInt64(const std::string& name, int64_t value) {
prefs_to_write_[name] = base::NumberToString(value);
ScheduleWrite();
}
void Prefs::SetDouble(const std::string& name, double value) {
prefs_to_write_[name] = base::NumberToString(value);
ScheduleWrite();
}
void Prefs::ScheduleWrite() {
base::TimeDelta time_since_last_write =
base::TimeTicks::Now() - last_write_time_;
if (last_write_time_.is_null() || time_since_last_write >= write_interval_) {
WritePrefs();
} else if (!write_prefs_timer_.IsRunning()) {
write_prefs_timer_.Start(FROM_HERE, write_interval_ - time_since_last_write,
this, &Prefs::WritePrefs);
}
}
void Prefs::WritePrefs() {
for (const auto& pref_pair : prefs_to_write_) {
if (!pref_store_->WritePrefString(pref_pair.first, pref_pair.second)) {
PLOG(ERROR) << "Failed to write to " << pref_store_->GetDescription();
}
}
prefs_to_write_.clear();
last_write_time_ = base::TimeTicks::Now();
}
} // namespace power_manager