blob: 26b126e5d5c3b320d5a607f50b532a245b98c192 [file] [log] [blame]
// Copyright 2020 The Chromium 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 "chrome/browser/ash/crosapi/browser_util.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_map.h"
#include "base/containers/flat_map.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/system/sys_info.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/crosapi/cpp/crosapi_constants.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "components/exo/shell_surface_util.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/version_info/channel.h"
#include "components/version_info/version_info.h"
#include "google_apis/gaia/gaia_auth_util.h"
using user_manager::User;
using version_info::Channel;
namespace crosapi {
namespace browser_util {
namespace {
bool g_lacros_enabled_for_test = false;
bool g_profile_migration_completed_for_test = false;
absl::optional<bool> g_lacros_primary_browser_for_test;
LacrosLaunchSwitchSource g_lacros_launch_switch_source =
LacrosLaunchSwitchSource::kUnknown;
// At session start the value for LacrosLaunchSwitch logic is applied and the
// result is stored in this value which is used after that as a cache.
absl::optional<LacrosLaunchSwitch> g_lacros_launch_switch_cache;
// The rootfs lacros-chrome metadata keys.
constexpr char kLacrosMetadataContentKey[] = "content";
constexpr char kLacrosMetadataVersionKey[] = "version";
// The conversion map for LacrosAvailability policy data. The values must match
// the ones from policy_templates.json.
const auto policy_value_to_enum =
base::MakeFixedFlatMap<std::string, LacrosLaunchSwitch>({
{"user_choice", LacrosLaunchSwitch::kUserChoice},
{"lacros_disallowed", LacrosLaunchSwitch::kLacrosDisallowed},
{"side_by_side", LacrosLaunchSwitch::kSideBySide},
{"lacros_primary", LacrosLaunchSwitch::kLacrosPrimary},
{"lacros_only", LacrosLaunchSwitch::kLacrosOnly},
});
// Some account types require features that aren't yet supported by lacros.
// See https://crbug.com/1080693
bool IsUserTypeAllowed(const User* user) {
switch (user->GetType()) {
case user_manager::USER_TYPE_REGULAR:
case user_manager::USER_TYPE_WEB_KIOSK_APP:
case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
return true;
case user_manager::USER_TYPE_GUEST:
case user_manager::USER_TYPE_KIOSK_APP:
case user_manager::USER_TYPE_CHILD:
case user_manager::USER_TYPE_ARC_KIOSK_APP:
case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
case user_manager::NUM_USER_TYPES:
return false;
}
}
// Returns true if the main profile is associated with a google internal
// account.
bool IsGoogleInternal() {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
const user_manager::User* user = user_manager->GetPrimaryUser();
if (!user)
return false;
return gaia::IsGoogleInternalAccountEmail(
user->GetAccountId().GetUserEmail());
}
// Returns the lacros integration suggested by the policy lacros-availability.
// There are several reasons why we might choose to ignore the
// lacros-availability policy.
// 1. The user has set a command line or chrome://flag for
// kLacrosAvailabilityIgnore.
// 2. The user is a Googler and they are not opted into the
// kLacrosGooglePolicyRollout trial and they did not have the
// kLacrosDisallowed policy.
LacrosLaunchSwitch GetLaunchSwitch() {
// TODO(crbug.com/1286340): add DCHECK for production use to avoid the
// same inconsistency for the future.
if (g_lacros_launch_switch_cache.has_value())
return g_lacros_launch_switch_cache.value();
// It could happen in some browser tests that value is not cached. Return
// default in that case.
return LacrosLaunchSwitch::kUserChoice;
}
// Given a raw policy value, decides what LacrosLaunchSwitch value should be
// used as a result of policy application.
std::pair<LacrosLaunchSwitch, LacrosLaunchSwitchSource>
DetermineLacrosLaunchSwitchFromPolicyValue(base::StringPiece policy_value) {
// Users can set this switch in chrome://flags to disable the effect of the
// lacros-availability policy.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(ash::switches::kLacrosAvailabilityIgnore)) {
return std::make_pair(LacrosLaunchSwitch::kUserChoice,
LacrosLaunchSwitchSource::kForcedByUser);
}
if (policy_value.empty()) {
// Some tests call IsLacrosAllowedToBeEnabled but don't have the value set.
return std::make_pair(LacrosLaunchSwitch::kUserChoice,
LacrosLaunchSwitchSource::kPossiblySetByUser);
}
auto* map_entry = policy_value_to_enum.find(policy_value);
if (map_entry == policy_value_to_enum.end()) {
LOG(ERROR) << "Invalid LacrosLaunchSwitch policy value: " << policy_value;
return std::make_pair(LacrosLaunchSwitch::kUserChoice,
LacrosLaunchSwitchSource::kPossiblySetByUser);
}
auto result = map_entry->second;
if (IsGoogleInternal() &&
!base::FeatureList::IsEnabled(kLacrosGooglePolicyRollout) &&
result != LacrosLaunchSwitch::kLacrosDisallowed) {
return std::make_pair(LacrosLaunchSwitch::kUserChoice,
LacrosLaunchSwitchSource::kPossiblySetByUser);
}
return std::make_pair(result,
result == LacrosLaunchSwitch::kUserChoice
? LacrosLaunchSwitchSource::kPossiblySetByUser
: LacrosLaunchSwitchSource::kForcedByPolicy);
}
// Gets called from IsLacrosAllowedToBeEnabled with primary user or from
// IsLacrosEnabledForMigration with the user that the
// IsLacrosEnabledForMigration was passed.
bool IsLacrosAllowedToBeEnabledWithUser(const User* user,
Channel channel,
LacrosLaunchSwitch launch_switch) {
if (g_lacros_enabled_for_test)
return true;
if (!IsUserTypeAllowed(user)) {
return false;
}
switch (launch_switch) {
case LacrosLaunchSwitch::kUserChoice:
break;
case LacrosLaunchSwitch::kLacrosDisallowed:
return false;
case LacrosLaunchSwitch::kSideBySide:
case LacrosLaunchSwitch::kLacrosPrimary:
case LacrosLaunchSwitch::kLacrosOnly:
return true;
}
switch (channel) {
case Channel::UNKNOWN:
case Channel::CANARY:
case Channel::DEV:
case Channel::BETA:
// Canary/dev/beta builds can use Lacros.
// Developer builds can use lacros.
return true;
case Channel::STABLE:
return base::FeatureList::IsEnabled(kLacrosAllowOnStableChannel);
}
}
// Called from `IsDataWipeRequired()` or `IsDataWipeRequiredForTesting()`.
// data_version` is the version of last data wipe. `current_version` is the
// version of ash-chrome. `required_version` is the version that introduces some
// breaking change. `data_version` needs to be greater or equal to
// `required_version`. If `required_version` is newer than `current_version`,
// data wipe is not required.
bool IsDataWipeRequiredInternal(base::Version data_version,
const base::Version& current_version,
const base::Version& required_version) {
// `data_version` is invalid if any wipe has not been recorded yet. In
// such a case, assume that the last data wipe happened significantly long
// time ago.
if (!data_version.IsValid())
data_version = base::Version("0");
if (current_version < required_version) {
// If `current_version` is smaller than the `required_version`, that means
// that the data wipe doesn't need to happen yet.
return false;
}
if (data_version >= required_version) {
// If `data_version` is greater or equal to `required_version`, this means
// data wipe has already happened and that user data is compatible with the
// current lacros.
return false;
}
return true;
}
// Returns the string value for the kLacrosStabilitySwitch if present.
absl::optional<std::string> GetLacrosStabilitySwitchValue() {
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
return cmdline->HasSwitch(browser_util::kLacrosStabilitySwitch)
? absl::optional<std::string>(cmdline->GetSwitchValueASCII(
browser_util::kLacrosStabilitySwitch))
: absl::nullopt;
}
// Resolves the Lacros stateful channel in the following order:
// 1. From the kLacrosStabilitySwitch command line flag if present.
// 2. From the current ash channel.
Channel GetStatefulLacrosChannel() {
static const auto kStabilitySwitchToChannelMap =
base::MakeFixedFlatMap<base::StringPiece, Channel>({
{browser_util::kLacrosStabilityChannelCanary, Channel::CANARY},
{browser_util::kLacrosStabilityChannelDev, Channel::DEV},
{browser_util::kLacrosStabilityChannelBeta, Channel::BETA},
{browser_util::kLacrosStabilityChannelStable, Channel::STABLE},
});
auto stability_switch_value = GetLacrosStabilitySwitchValue();
return stability_switch_value && base::Contains(kStabilitySwitchToChannelMap,
*stability_switch_value)
? kStabilitySwitchToChannelMap.at(*stability_switch_value)
: chrome::GetChannel();
}
static_assert(
crosapi::mojom::Crosapi::Version_ == 62,
"if you add a new crosapi, please add it to kInterfaceVersionEntries");
} // namespace
// NOTE: If you change the lacros component names, you must also update
// chrome/browser/component_updater/cros_component_installer_chromeos.cc
const ComponentInfo kLacrosDogfoodCanaryInfo = {
"lacros-dogfood-canary", "hkifppleldbgkdlijbdfkdpedggaopda"};
const ComponentInfo kLacrosDogfoodDevInfo = {
"lacros-dogfood-dev", "ldobopbhiamakmncndpkeelenhdmgfhk"};
const ComponentInfo kLacrosDogfoodBetaInfo = {
"lacros-dogfood-beta", "hnfmbeciphpghlfgpjfbcdifbknombnk"};
const ComponentInfo kLacrosDogfoodStableInfo = {
"lacros-dogfood-stable", "ehpjbaiafkpkmhjocnenjbbhmecnfcjb"};
// When this feature is enabled, Lacros will be available on stable channel.
const base::Feature kLacrosAllowOnStableChannel{
"LacrosAllowOnStableChannel", base::FEATURE_ENABLED_BY_DEFAULT};
// A kill switch for lacros chrome apps.
const base::Feature kLacrosDisableChromeApps{"LacrosDisableChromeApps",
base::FEATURE_DISABLED_BY_DEFAULT};
// When this feature is enabled, Lacros is allowed to roll out by policy to
// Googlers.
const base::Feature kLacrosGooglePolicyRollout{
"LacrosGooglePolicyRollout", base::FEATURE_DISABLED_BY_DEFAULT};
const Channel kLacrosDefaultChannel = Channel::DEV;
const char kLacrosStabilitySwitch[] = "lacros-stability";
const char kLacrosStabilityChannelCanary[] = "canary";
const char kLacrosStabilityChannelDev[] = "dev";
const char kLacrosStabilityChannelBeta[] = "beta";
const char kLacrosStabilityChannelStable[] = "stable";
const char kLacrosSelectionSwitch[] = "lacros-selection";
const char kLacrosSelectionRootfs[] = "rootfs";
const char kLacrosSelectionStateful[] = "stateful";
// The internal name in about_flags.cc for the lacros-availablility-policy
// config.
const char kLacrosAvailabilityPolicyInternalName[] =
"lacros-availability-policy";
// The commandline flag name of lacros-availability-policy.
// The value should be the policy value as defined just below.
// The values need to be consistent with policy_value_to_enum above.
const char kLacrosAvailabilityPolicySwitch[] = "lacros-availability-policy";
const char kLacrosAvailabilityPolicyUserChoice[] = "user_choice";
const char kLacrosAvailabilityPolicyLacrosDisabled[] = "lacros_disabled";
const char kLacrosAvailabilityPolicySideBySide[] = "side_by_side";
const char kLacrosAvailabilityPolicyLacrosPrimary[] = "lacros_primary";
const char kLacrosAvailabilityPolicyLacrosOnly[] = "lacros_only";
const char kLaunchOnLoginPref[] = "lacros.launch_on_login";
const char kClearUserDataDir1Pref[] = "lacros.clear_user_data_dir_1";
const char kDataVerPref[] = "lacros.data_version";
const char kRequiredDataVersion[] = "92.0.0.0";
const char kProfileMigrationCompletedForUserPref[] =
"lacros.profile_migration_completed_for_user";
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kLaunchOnLoginPref, /*default_value=*/false);
registry->RegisterBooleanPref(kClearUserDataDir1Pref,
/*default_value=*/false);
}
void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kDataVerPref);
registry->RegisterDictionaryPref(kProfileMigrationCompletedForUserPref,
base::DictionaryValue());
}
base::FilePath GetUserDataDir() {
if (base::SysInfo::IsRunningOnChromeOS()) {
// NOTE: On device this function is privacy/security sensitive. The
// directory must be inside the encrypted user partition.
return base::FilePath(crosapi::kLacrosUserDataPath);
}
// For developers on Linux desktop, put the directory under the developer's
// specified --user-data-dir.
base::FilePath base_path;
base::PathService::Get(chrome::DIR_USER_DATA, &base_path);
return base_path.Append("lacros");
}
bool IsLacrosAllowedToBeEnabled(Channel channel) {
// Allows tests to avoid enabling the flag, constructing a fake user manager,
// creating g_browser_process->local_state(), etc.
if (g_lacros_enabled_for_test)
return true;
// TODO(crbug.com/1185813): TaskManagerImplTest is not ready to run with
// Lacros enabled.
// UserManager is not initialized for unit tests by default, unless a fake
// user manager is constructed.
if (!user_manager::UserManager::IsInitialized())
return false;
// GetPrimaryUser works only after user session is started.
const User* user = user_manager::UserManager::Get()->GetPrimaryUser();
if (!user) {
return false;
}
return IsLacrosAllowedToBeEnabledWithUser(user, channel, GetLaunchSwitch());
}
bool IsLacrosEnabled() {
return IsLacrosEnabled(chrome::GetChannel());
}
bool IsLacrosEnabled(Channel channel) {
// Allows tests to avoid enabling the flag, constructing a fake user manager,
// creating g_browser_process->local_state(), etc.
if (g_lacros_enabled_for_test)
return true;
if (!IsLacrosAllowedToBeEnabled(channel))
return false;
// If profile migration is enabled for the user, then make profile migration a
// requirement to enable lacros.
if (IsProfileMigrationEnabled(
user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId())) {
PrefService* local_state = g_browser_process->local_state();
// Note that local_state can be nullptr in tests.
if (local_state && !IsProfileMigrationCompletedForUser(
local_state, user_manager::UserManager::Get()
->GetPrimaryUser()
->username_hash())) {
// If migration has not been completed, do not enable lacros.
return false;
}
}
switch (GetLaunchSwitch()) {
case LacrosLaunchSwitch::kUserChoice:
break;
case LacrosLaunchSwitch::kLacrosDisallowed:
DCHECK_EQ(channel, Channel::UNKNOWN);
return false;
case LacrosLaunchSwitch::kSideBySide:
case LacrosLaunchSwitch::kLacrosPrimary:
case LacrosLaunchSwitch::kLacrosOnly:
return true;
}
return base::FeatureList::IsEnabled(chromeos::features::kLacrosSupport);
}
bool IsProfileMigrationEnabled(const AccountId& account_id) {
// Emergency switch to turn off profile migration. Turn this on via Finch in
// case profile migration needs to be turned off after launch.
if (base::FeatureList::IsEnabled(
ash::features::kLacrosProfileMigrationForceOff)) {
return false;
}
// Currently we turn on profile migration only for Googlers.
// `kLacrosProfileMigrationForAnyUser` can be enabled to allow testing with
// non-googler accounts.
if (gaia::IsGoogleInternalAccountEmail(account_id.GetUserEmail()) ||
base::FeatureList::IsEnabled(
ash::features::kLacrosProfileMigrationForAnyUser))
return true;
return false;
}
bool IsLacrosEnabledForMigration(const User* user,
PolicyInitState policy_init_state) {
if (g_lacros_enabled_for_test)
return true;
LacrosLaunchSwitch launch_switch;
if (policy_init_state == PolicyInitState::kBeforeInit) {
// Before Policy is initialized, the value won't be available.
// So, we'll use the value preserved in the feature flags.
// See also LacrosAvailabilityPolicyObserver how it will be propergated.
launch_switch =
DetermineLacrosLaunchSwitchFromPolicyValue(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kLacrosAvailabilityPolicySwitch))
.first;
} else {
DCHECK_EQ(policy_init_state, PolicyInitState::kAfterInit);
launch_switch = GetLaunchSwitch();
}
if (!IsLacrosAllowedToBeEnabledWithUser(user, chrome::GetChannel(),
launch_switch)) {
return false;
}
switch (launch_switch) {
case LacrosLaunchSwitch::kUserChoice:
break;
case LacrosLaunchSwitch::kLacrosDisallowed:
return false;
case LacrosLaunchSwitch::kSideBySide:
case LacrosLaunchSwitch::kLacrosPrimary:
case LacrosLaunchSwitch::kLacrosOnly:
return true;
}
return base::FeatureList::IsEnabled(chromeos::features::kLacrosSupport);
}
bool IsLacrosSupportFlagAllowed(Channel channel) {
return IsLacrosAllowedToBeEnabled(channel) &&
(GetLaunchSwitch() == LacrosLaunchSwitch::kUserChoice);
}
void SetLacrosEnabledForTest(bool force_enabled) {
g_lacros_enabled_for_test = force_enabled;
}
bool IsAshWebBrowserEnabled() {
return IsAshWebBrowserEnabled(chrome::GetChannel());
}
bool IsAshWebBrowserEnabled(Channel channel) {
// If Lacros is not allowed or is not enabled, Ash browser is always enabled.
if (!IsLacrosEnabled(channel))
return true;
switch (GetLaunchSwitch()) {
case LacrosLaunchSwitch::kUserChoice:
break;
case LacrosLaunchSwitch::kLacrosDisallowed:
case LacrosLaunchSwitch::kSideBySide:
case LacrosLaunchSwitch::kLacrosPrimary:
return true;
case LacrosLaunchSwitch::kLacrosOnly:
return false;
}
return true;
}
bool IsLacrosPrimaryBrowser() {
return IsLacrosPrimaryBrowser(chrome::GetChannel());
}
bool IsLacrosPrimaryBrowser(Channel channel) {
if (g_lacros_primary_browser_for_test.has_value())
return g_lacros_primary_browser_for_test.value();
if (!IsLacrosEnabled(channel))
return false;
// Lacros-chrome will always be the primary browser if Lacros is enabled in
// web Kiosk session.
if (user_manager::UserManager::Get()->IsLoggedInAsWebKioskApp())
return true;
if (!IsLacrosPrimaryBrowserAllowed(channel))
return false;
switch (GetLaunchSwitch()) {
case LacrosLaunchSwitch::kUserChoice:
break;
case LacrosLaunchSwitch::kLacrosDisallowed:
NOTREACHED();
return false;
case LacrosLaunchSwitch::kSideBySide:
return false;
case LacrosLaunchSwitch::kLacrosPrimary:
case LacrosLaunchSwitch::kLacrosOnly:
return true;
}
return base::FeatureList::IsEnabled(chromeos::features::kLacrosPrimary);
}
void SetLacrosPrimaryBrowserForTest(absl::optional<bool> value) {
g_lacros_primary_browser_for_test = value;
}
bool IsLacrosPrimaryBrowserAllowed(Channel channel) {
if (!IsLacrosAllowedToBeEnabled(channel))
return false;
switch (GetLaunchSwitch()) {
case LacrosLaunchSwitch::kLacrosDisallowed:
DCHECK_EQ(channel, Channel::UNKNOWN);
return false;
case LacrosLaunchSwitch::kLacrosPrimary:
case LacrosLaunchSwitch::kLacrosOnly:
// Forcibly allow to use Lacros as a Primary respecting the policy.
return true;
default:
// Fallback others.
break;
}
return true;
}
bool IsLacrosPrimaryFlagAllowed(Channel channel) {
return IsLacrosPrimaryBrowserAllowed(channel) &&
(GetLaunchSwitch() == LacrosLaunchSwitch::kUserChoice);
}
bool IsLacrosAllowedToLaunch() {
return user_manager::UserManager::Get()->GetLoggedInUsers().size() <= 1;
}
bool IsLacrosChromeAppsEnabled() {
if (base::FeatureList::IsEnabled(kLacrosDisableChromeApps))
return false;
if (!IsLacrosPrimaryBrowser())
return false;
return true;
}
bool IsLacrosEnabledInWebKioskSession() {
return user_manager::UserManager::Get()->IsLoggedInAsWebKioskApp() &&
base::FeatureList::IsEnabled(features::kWebKioskEnableLacros) &&
IsLacrosEnabled();
}
bool IsLacrosWindow(const aura::Window* window) {
const std::string* app_id = exo::GetShellApplicationId(window);
if (!app_id)
return false;
return base::StartsWith(*app_id, kLacrosAppIdPrefix);
}
// Assuming the metadata exists, parse the version and check if it contains the
// non-backwards-compatible account_manager change.
// A typical format for metadata is:
// {
// "content": {
// "version": "91.0.4469.5"
// },
// "metadata_version": 1
// }
bool DoesMetadataSupportNewAccountManager(base::Value* metadata) {
if (!metadata)
return false;
base::Value* version = metadata->FindPath("content.version");
if (!version || !version->is_string())
return false;
std::string version_str = version->GetString();
std::vector<std::string> versions_str = base::SplitString(
version_str, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (versions_str.size() != 4)
return false;
int major_version = 0;
int minor_version = 0;
if (!base::StringToInt(versions_str[0], &major_version))
return false;
if (!base::StringToInt(versions_str[2], &minor_version))
return false;
// TODO(https://crbug.com/1197220): Come up with more appropriate major/minor
// version numbers.
return major_version >= 1000 && minor_version >= 0;
}
base::Version GetDataVer(PrefService* local_state,
const std::string& user_id_hash) {
const base::Value* data_versions = local_state->GetDictionary(kDataVerPref);
const std::string* data_version_str =
data_versions->FindStringKey(user_id_hash);
if (!data_version_str)
return base::Version();
return base::Version(*data_version_str);
}
void RecordDataVer(PrefService* local_state,
const std::string& user_id_hash,
const base::Version& version) {
DCHECK(version.IsValid());
DictionaryPrefUpdate update(local_state, kDataVerPref);
base::Value* dict = update.Get();
dict->SetStringKey(user_id_hash, version.GetString());
}
bool IsDataWipeRequired(const std::string& user_id_hash) {
base::Version data_version =
GetDataVer(g_browser_process->local_state(), user_id_hash);
base::Version current_version = version_info::GetVersion();
base::Version required_version =
base::Version(base::StringPiece(kRequiredDataVersion));
return IsDataWipeRequiredInternal(data_version, current_version,
required_version);
}
bool IsDataWipeRequiredForTesting(base::Version data_version,
const base::Version& current_version,
const base::Version& required_version) {
return IsDataWipeRequiredInternal(data_version, current_version,
required_version);
}
base::Version GetRootfsLacrosVersionMayBlock(
const base::FilePath& version_file_path) {
if (!base::PathExists(version_file_path)) {
LOG(WARNING) << "The rootfs lacros-chrome metadata is missing.";
return {};
}
std::string metadata;
if (!base::ReadFileToString(version_file_path, &metadata)) {
PLOG(WARNING) << "Failed to read rootfs lacros-chrome metadata.";
return {};
}
absl::optional<base::Value> v = base::JSONReader::Read(metadata);
if (!v || !v->is_dict()) {
LOG(WARNING) << "Failed to parse rootfs lacros-chrome metadata.";
return {};
}
const base::Value* content = v->FindKey(kLacrosMetadataContentKey);
if (!content || !content->is_dict()) {
LOG(WARNING)
<< "Failed to parse rootfs lacros-chrome metadata content key.";
return {};
}
const base::Value* version = content->FindKey(kLacrosMetadataVersionKey);
if (!version || !version->is_string()) {
LOG(WARNING)
<< "Failed to parse rootfs lacros-chrome metadata version key.";
return {};
}
return base::Version{version->GetString()};
}
void CacheLacrosLaunchSwitch(const policy::PolicyMap& map) {
if (g_lacros_launch_switch_cache.has_value()) {
// Some browser tests might call this multiple times.
LOG(ERROR) << "Trying to cache LacrosLaunchSwitch and the value was set";
return;
}
const base::Value* value = map.GetValue(policy::key::kLacrosAvailability);
std::tie(g_lacros_launch_switch_cache, g_lacros_launch_switch_source) =
DetermineLacrosLaunchSwitchFromPolicyValue(value ? value->GetString()
: base::StringPiece());
}
ComponentInfo GetLacrosComponentInfo() {
// We default to the Dev component for UNKNOWN channels.
static const auto kChannelToComponentInfoMap =
base::MakeFixedFlatMap<Channel, const ComponentInfo*>({
{Channel::UNKNOWN, &kLacrosDogfoodDevInfo},
{Channel::CANARY, &kLacrosDogfoodCanaryInfo},
{Channel::DEV, &kLacrosDogfoodDevInfo},
{Channel::BETA, &kLacrosDogfoodBetaInfo},
{Channel::STABLE, &kLacrosDogfoodStableInfo},
});
return *kChannelToComponentInfoMap.at(GetStatefulLacrosChannel());
}
Channel GetLacrosSelectionUpdateChannel(LacrosSelection selection) {
switch (selection) {
case LacrosSelection::kRootfs:
// For 'rootfs' Lacros use the same channel as ash/OS. Obtained from
// the LSB's release track property.
return chrome::GetChannel();
case LacrosSelection::kStateful:
// For 'stateful' Lacros directly check the channel of stateful-lacros
// that the user is on.
return GetStatefulLacrosChannel();
}
}
LacrosLaunchSwitch GetLaunchSwitchForTesting() {
return GetLaunchSwitch();
}
void ClearLacrosLaunchSwitchCacheForTest() {
g_lacros_launch_switch_cache.reset();
}
bool IsProfileMigrationCompletedForUser(PrefService* local_state,
const std::string& user_id_hash) {
// Allows tests to avoid marking profile migration as completed by getting
// user_id_hash of the logged in user and updating
// g_browser_process->local_state() etc.
if (g_profile_migration_completed_for_test)
return true;
if (base::FeatureList::IsEnabled(
ash::features::kForceProfileMigrationCompletion)) {
return true;
}
const auto* pref =
local_state->FindPreference(kProfileMigrationCompletedForUserPref);
// Return if the pref is not registered. This can happen in browsertests. In
// such a case, assume that migration was completed.
if (!pref)
return true;
const base::Value* value = pref->GetValue();
DCHECK(value->is_dict());
absl::optional<bool> is_completed = value->FindBoolKey(user_id_hash);
// If migration was skipped or failed, disable lacros.
return is_completed.value_or(false);
}
void SetProfileMigrationCompletedForUser(PrefService* local_state,
const std::string& user_id_hash) {
DictionaryPrefUpdate update(local_state,
kProfileMigrationCompletedForUserPref);
base::Value* dict = update.Get();
dict->SetBoolKey(user_id_hash, true);
}
void ClearProfileMigrationCompletedForUser(PrefService* local_state,
const std::string& user_id_hash) {
DictionaryPrefUpdate update(local_state,
kProfileMigrationCompletedForUserPref);
base::Value* dict = update.Get();
dict->RemoveKey(user_id_hash);
}
void SetProfileMigrationCompletedForTest(bool is_completed) {
g_profile_migration_completed_for_test = is_completed;
}
LacrosLaunchSwitchSource GetLacrosLaunchSwitchSource() {
return g_lacros_launch_switch_source;
}
base::StringPiece GetLacrosAvailabilityPolicyName(LacrosLaunchSwitch value) {
for (const auto& entry : policy_value_to_enum) {
if (entry.second == value)
return entry.first;
}
NOTREACHED();
return base::StringPiece();
}
} // namespace browser_util
} // namespace crosapi