chromium/chromecast/media/cma/backend/cast_audio_json.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/media/cma/backend/cast_audio_json.h"

#include <string>
#include <utility>

#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "build/build_config.h"

namespace chromecast {
namespace media {

namespace {

void ReadFileRunCallback(CastAudioJsonProvider::TuningChangedCallback callback,
                         const base::FilePath& path,
                         bool error) {
  DCHECK(callback);

  std::string contents;
  base::ReadFileToString(path, &contents);
  std::optional<base::Value> value = base::JSONReader::Read(contents);
  if (value && value->is_dict()) {
    callback.Run(std::move(*value).TakeDict());
    return;
  }
  LOG(ERROR) << "Unable to parse JSON in " << path;
}

}  // namespace

#if BUILDFLAG(IS_FUCHSIA)
const char kCastAudioJsonFilePath[] = "/system/data/cast_audio.json";
#else
const char kCastAudioJsonFilePath[] = "/etc/cast_audio.json";
#endif
const char kCastAudioJsonFileName[] = "cast_audio.json";

// static
base::FilePath CastAudioJson::GetFilePath() {
  base::FilePath tuning_path = CastAudioJson::GetFilePathForTuning();
  if (base::PathExists(tuning_path)) {
    return tuning_path;
  }

  return CastAudioJson::GetReadOnlyFilePath();
}

// static
base::FilePath CastAudioJson::GetReadOnlyFilePath() {
  return base::FilePath(kCastAudioJsonFilePath);
}

// static
base::FilePath CastAudioJson::GetFilePathForTuning() {
  return base::GetHomeDir().Append(kCastAudioJsonFileName);
}

CastAudioJsonProviderImpl::CastAudioJsonProviderImpl() {
  if (base::ThreadPoolInstance::Get()) {
    cast_audio_watcher_ = base::SequenceBound<FileWatcher>(
        base::ThreadPool::CreateSequencedTaskRunner(
            {base::MayBlock(), base::TaskPriority::LOWEST}));
  }
}

CastAudioJsonProviderImpl::~CastAudioJsonProviderImpl() = default;

std::optional<base::Value::Dict>
CastAudioJsonProviderImpl::GetCastAudioConfig() {
  std::string contents;
  base::ReadFileToString(CastAudioJson::GetFilePath(), &contents);
  std::optional<base::Value> value = base::JSONReader::Read(contents);
  if (!value || value->is_dict()) {
    return std::nullopt;
  }

  return std::move(*value).TakeDict();
}

void CastAudioJsonProviderImpl::SetTuningChangedCallback(
    TuningChangedCallback callback) {
  if (cast_audio_watcher_) {
    cast_audio_watcher_.AsyncCall(&FileWatcher::SetTuningChangedCallback)
        .WithArgs(std::move(callback));
  }
}

CastAudioJsonProviderImpl::FileWatcher::FileWatcher() = default;
CastAudioJsonProviderImpl::FileWatcher::~FileWatcher() = default;

void CastAudioJsonProviderImpl::FileWatcher::SetTuningChangedCallback(
    TuningChangedCallback callback) {
  watcher_.Watch(
      CastAudioJson::GetFilePathForTuning(),
      base::FilePathWatcher::Type::kNonRecursive,
      base::BindRepeating(&ReadFileRunCallback, std::move(callback)));
}

}  // namespace media
}  // namespace chromecast