blob: 4b796c5e6ec10c567933bf4f567897ce2cea3f0b [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media/router/media_router_feature.h"
#include <stdint.h>
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "components/media_router/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "crypto/random.h"
#include "extensions/buildflags/buildflags.h"
#include "ui/base/buildflags.h"
#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
#include "components/prefs/pref_registry_simple.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
#endif
// NOTE: Consider separating out UI-only features that are not consumed by the
// Media Router itself into their own file in chrome/browser/ui/media_router.
namespace media_router {
#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
BASE_FEATURE(kMediaRouter, base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kCastAllowAllIPsFeature,
"CastAllowAllIPs",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kAllowAllSitesToInitiateMirroring,
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kDialMediaRouteProvider, base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kDelayMediaSinkDiscovery, base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kShowCastPermissionRejectedError,
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kCastMessageLogging, base::FEATURE_DISABLED_BY_DEFAULT);
// TODO(crbug.com/1486680): Remove once stopping mirroring routes in the global
// media controls is implemented on ChromeOS.
BASE_FEATURE(kFallbackToAudioTabMirroring,
#if BUILDFLAG(IS_CHROMEOS)
base::FEATURE_DISABLED_BY_DEFAULT);
#else
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_CHROMEOS)
#endif // !BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
#if BUILDFLAG(IS_MAC)
BASE_FEATURE(kUseNetworkFrameworkForLocalDiscovery,
base::FEATURE_ENABLED_BY_DEFAULT);
#endif
namespace {
const PrefService::Preference* GetMediaRouterPref(
content::BrowserContext* context) {
return user_prefs::UserPrefs::Get(context)->FindPreference(
::prefs::kEnableMediaRouter);
}
base::flat_map<content::BrowserContext*, bool>& GetStoredPrefValues() {
static base::NoDestructor<base::flat_map<content::BrowserContext*, bool>>
stored_pref_values;
return *stored_pref_values;
}
#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
// TODO(mfoltz): Add full implementation for validating playout delay value.
bool IsValidMirroringPlayoutDelayMs(int delay_ms) {
return delay_ms <= 1000 && delay_ms >= 1;
}
#endif // !BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
} // namespace
void ClearMediaRouterStoredPrefsForTesting() {
GetStoredPrefValues().clear();
}
bool MediaRouterEnabled(content::BrowserContext* context) {
#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
if (!base::FeatureList::IsEnabled(kMediaRouter)) {
return false;
}
#endif // !BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
#if BUILDFLAG(IS_CHROMEOS)
// TODO(crbug.com/1380828): Make the Media Router feature configurable via a
// policy for non-user profiles, i.e. sign-in and lock screen profiles.
if (!ash::IsUserBrowserContext(context)) {
return false;
}
#endif // BUILDFLAG(IS_CHROMEOS)
// If the Media Router was already enabled or disabled for |context|, then it
// must remain so. The Media Router does not support dynamic
// enabling/disabling.
base::flat_map<content::BrowserContext*, bool>& pref_values =
GetStoredPrefValues();
auto const it = pref_values.find(context);
if (it != pref_values.end()) {
return it->second;
}
// Check the enterprise policy.
const PrefService::Preference* pref = GetMediaRouterPref(context);
if (pref->IsManaged() && !pref->IsDefaultValue()) {
CHECK(pref->GetValue()->is_bool());
bool allowed = pref->GetValue()->GetBool();
pref_values.insert(std::make_pair(context, allowed));
return allowed;
}
return true;
}
#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kMediaRouterCastAllowAllIPs, false,
PrefRegistry::PUBLIC);
registry->RegisterBooleanPref(prefs::kSuppressLocalDiscoveryPermissionError,
false);
}
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kMediaRouterReceiverIdHashToken, "",
PrefRegistry::PUBLIC);
registry->RegisterBooleanPref(
media_router::prefs::kMediaRouterMediaRemotingEnabled, true);
registry->RegisterBooleanPref(
media_router::prefs::kMediaRouterShowCastSessionsStartedByOtherDevices,
true);
}
bool GetCastAllowAllIPsPref(PrefService* pref_service) {
auto* pref = pref_service->FindPreference(prefs::kMediaRouterCastAllowAllIPs);
// Only use the pref value if it is set from a mandatory policy.
bool allow_all_ips = false;
if (pref->IsManaged() && !pref->IsDefaultValue()) {
CHECK(pref->GetValue()->is_bool());
allow_all_ips = pref->GetValue()->GetBool();
} else {
allow_all_ips = base::FeatureList::IsEnabled(kCastAllowAllIPsFeature);
}
return allow_all_ips;
}
std::string GetReceiverIdHashToken(PrefService* pref_service) {
static constexpr size_t kHashTokenSize = 64;
std::string token =
pref_service->GetString(prefs::kMediaRouterReceiverIdHashToken);
if (token.empty()) {
std::array<uint8_t, kHashTokenSize> rand_token;
crypto::RandBytes(rand_token);
token = base::Base64Encode(rand_token);
pref_service->SetString(prefs::kMediaRouterReceiverIdHashToken, token);
}
return token;
}
bool DialMediaRouteProviderEnabled() {
return base::FeatureList::IsEnabled(kDialMediaRouteProvider);
}
std::optional<base::TimeDelta> GetCastMirroringPlayoutDelay() {
std::optional<base::TimeDelta> target_playout_delay;
// The default playout delay can be overridden with the command line flag
// `cast-mirroring-target-playout-delay`.
const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
if (cl->HasSwitch(switches::kCastMirroringTargetPlayoutDelay)) {
int switch_playout_delay = 0;
if (base::StringToInt(
cl->GetSwitchValueASCII(switches::kCastMirroringTargetPlayoutDelay),
&switch_playout_delay) &&
IsValidMirroringPlayoutDelayMs(switch_playout_delay)) {
target_playout_delay = base::Milliseconds(switch_playout_delay);
}
}
return target_playout_delay;
}
bool IsCastMessageLoggingEnabled() {
return base::FeatureList::IsEnabled(kCastMessageLogging);
}
#endif // !BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
} // namespace media_router