blob: 822a5d342f8cc1e8993fdccdb33661deedb386f5 [file] [log] [blame]
// Copyright 2022 The Cobalt Authors. All Rights Reserved.
//
// 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.
#include "cobalt/persistent_storage/persistent_settings.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/json_read_only_pref_store.h"
#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/configuration_constants.h"
namespace cobalt {
namespace persistent_storage {
namespace {
// Protected persistent settings key indicating whether or not the persistent
// settings file has been validated.
const char kValidated[] = "validated";
// Signals the given WaitableEvent.
void SignalWaitableEvent(base::WaitableEvent* event) { event->Signal(); }
} // namespace
void PersistentSettings::WillDestroyCurrentMessageLoop() {
// Clear all member variables allocated from the thread.
pref_store_.reset();
}
PersistentSettings::PersistentSettings(const std::string& file_name)
: thread_("PersistentSettings") {
if (!thread_.Start()) return;
DCHECK(message_loop());
std::vector<char> storage_dir(kSbFileMaxPath + 1, 0);
SbSystemGetPath(kSbSystemPathStorageDirectory, storage_dir.data(),
kSbFileMaxPath);
persistent_settings_file_ =
std::string(storage_dir.data()) + kSbFileSepString + file_name;
LOG(INFO) << "Persistent settings file path: " << persistent_settings_file_;
// Initialize pref_store_ with a JSONReadOnlyPrefStore, Used for
// synchronous PersistentSettings::Get calls made before the asynchronous
// InitializeWriteablePrefStore initializes pref_store_ with a writable
// instance.
{
base::AutoLock auto_lock(pref_store_lock_);
pref_store_ = base::MakeRefCounted<JsonReadOnlyPrefStore>(
base::FilePath(persistent_settings_file_));
pref_store_->ReadPrefs();
}
message_loop()->task_runner()->PostTask(
FROM_HERE, base::Bind(&PersistentSettings::InitializeWriteablePrefStore,
base::Unretained(this)));
// Register as a destruction observer to shut down the Web Agent once all
// pending tasks have been executed and the message loop is about to be
// destroyed. This allows us to safely stop the thread, drain the task queue,
// then destroy the internal components before the message loop is reset.
// No posted tasks will be executed once the thread is stopped.
message_loop()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&base::MessageLoop::AddDestructionObserver,
base::Unretained(message_loop()), base::Unretained(this)));
// This works almost like a PostBlockingTask, except that any blocking that
// may be necessary happens when Stop() is called instead of right now.
message_loop()->task_runner()->PostTask(
FROM_HERE, base::Bind(&SignalWaitableEvent,
base::Unretained(&destruction_observer_added_)));
}
PersistentSettings::~PersistentSettings() {
DCHECK(message_loop());
DCHECK(thread_.IsRunning());
// Ensure that the destruction observer got added and the pref store was
// initialized before stopping the thread. Stop the thread. This will cause
// the destruction observer to be notified.
writeable_pref_store_initialized_.Wait();
destruction_observer_added_.Wait();
thread_.Stop();
}
void PersistentSettings::InitializeWriteablePrefStore() {
DCHECK_EQ(base::MessageLoop::current(), message_loop());
{
base::AutoLock auto_lock(pref_store_lock_);
pref_store_ = base::MakeRefCounted<JsonPrefStore>(
base::FilePath(persistent_settings_file_));
pref_store_->ReadPrefs();
// PersistentSettings Set and Remove Helper methods will now be able to
// access the pref_store_ initialized from the dedicated thread_.
writeable_pref_store_initialized_.Signal();
}
validated_initial_settings_ = GetPersistentSettingAsBool(kValidated, false);
if (!validated_initial_settings_) {
starboard::SbFileDeleteRecursive(persistent_settings_file_.c_str(), true);
}
}
void PersistentSettings::ValidatePersistentSettings() {
message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&PersistentSettings::ValidatePersistentSettingsHelper,
base::Unretained(this)));
}
void PersistentSettings::ValidatePersistentSettingsHelper() {
DCHECK_EQ(base::MessageLoop::current(), message_loop());
if (!validated_initial_settings_) {
base::AutoLock auto_lock(pref_store_lock_);
writeable_pref_store()->SetValue(
kValidated, std::make_unique<base::Value>(true),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
writeable_pref_store()->CommitPendingWrite();
validated_initial_settings_ = true;
}
}
bool PersistentSettings::GetPersistentSettingAsBool(const std::string& key,
bool default_setting) {
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
const base::Value* result = persistent_settings->FindKey(key);
if (result && result->is_bool()) return result->GetBool();
LOG(INFO) << "Persistent setting does not exist: " << key;
return default_setting;
}
int PersistentSettings::GetPersistentSettingAsInt(const std::string& key,
int default_setting) {
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
const base::Value* result = persistent_settings->FindKey(key);
if (result && result->is_int()) return result->GetInt();
LOG(INFO) << "Persistent setting does not exist: " << key;
return default_setting;
}
double PersistentSettings::GetPersistentSettingAsDouble(
const std::string& key, double default_setting) {
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
const base::Value* result = persistent_settings->FindKey(key);
if (result && result->is_double()) return result->GetDouble();
LOG(INFO) << "Persistent setting does not exist: " << key;
return default_setting;
}
std::string PersistentSettings::GetPersistentSettingAsString(
const std::string& key, const std::string& default_setting) {
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
const base::Value* result = persistent_settings->FindKey(key);
if (result && result->is_string()) return result->GetString();
LOG(INFO) << "Persistent setting does not exist: " << key;
return default_setting;
}
std::vector<base::Value> PersistentSettings::GetPersistentSettingAsList(
const std::string& key) {
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
base::Value* result = persistent_settings->FindKey(key);
if (result && result->is_list()) {
return std::move(result->TakeList());
}
LOG(INFO) << "Persistent setting does not exist: " << key;
return std::vector<base::Value>();
}
base::flat_map<std::string, std::unique_ptr<base::Value>>
PersistentSettings::GetPersistentSettingAsDictionary(const std::string& key) {
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
base::Value* result = persistent_settings->FindKey(key);
base::flat_map<std::string, std::unique_ptr<base::Value>> dict;
if (result && result->is_dict()) {
for (const auto& key_value : result->DictItems()) {
dict.insert(std::make_pair(
key_value.first,
std::make_unique<base::Value>(std::move(key_value.second))));
}
return dict;
}
LOG(INFO) << "Persistent setting does not exist: " << key;
return dict;
}
void PersistentSettings::SetPersistentSetting(
const std::string& key, std::unique_ptr<base::Value> value,
base::OnceClosure closure) {
if (key == kValidated) {
LOG(ERROR) << "Cannot set protected persistent setting: " << key;
return;
}
message_loop()->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&PersistentSettings::SetPersistentSettingHelper,
base::Unretained(this), key, std::move(value),
std::move(closure)));
}
void PersistentSettings::SetPersistentSettingHelper(
const std::string& key, std::unique_ptr<base::Value> value,
base::OnceClosure closure) {
DCHECK_EQ(base::MessageLoop::current(), message_loop());
if (validated_initial_settings_) {
base::AutoLock auto_lock(pref_store_lock_);
writeable_pref_store()->SetValue(
kValidated, std::make_unique<base::Value>(false),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
writeable_pref_store()->SetValue(
key, std::move(value), WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
writeable_pref_store()->CommitPendingWrite();
} else {
LOG(ERROR) << "Cannot set persistent setting while unvalidated: " << key;
}
std::move(closure).Run();
}
void PersistentSettings::RemovePersistentSetting(const std::string& key,
base::OnceClosure closure) {
message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&PersistentSettings::RemovePersistentSettingHelper,
base::Unretained(this), key, std::move(closure)));
}
void PersistentSettings::RemovePersistentSettingHelper(
const std::string& key, base::OnceClosure closure) {
DCHECK_EQ(base::MessageLoop::current(), message_loop());
if (validated_initial_settings_) {
base::AutoLock auto_lock(pref_store_lock_);
writeable_pref_store()->SetValue(
kValidated, std::make_unique<base::Value>(false),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
writeable_pref_store()->RemoveValue(
key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
writeable_pref_store()->CommitPendingWrite();
} else {
LOG(ERROR) << "Cannot remove persistent setting while unvalidated: " << key;
}
std::move(closure).Run();
}
void PersistentSettings::DeletePersistentSettings(base::OnceClosure closure) {
message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&PersistentSettings::DeletePersistentSettingsHelper,
base::Unretained(this), std::move(closure)));
}
void PersistentSettings::DeletePersistentSettingsHelper(
base::OnceClosure closure) {
DCHECK_EQ(base::MessageLoop::current(), message_loop());
if (validated_initial_settings_) {
starboard::SbFileDeleteRecursive(persistent_settings_file_.c_str(), true);
base::AutoLock auto_lock(pref_store_lock_);
writeable_pref_store()->ReadPrefs();
} else {
LOG(ERROR) << "Cannot delete persistent setting while unvalidated.";
}
std::move(closure).Run();
}
} // namespace persistent_storage
} // namespace cobalt