blob: c912c44f1a5f25a702c77df88cc82495d75b6a15 [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/chromeos/crosapi/browser_util.h"
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <utility>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/process/process_handle.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/crosapi/cpp/crosapi_constants.h"
#include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "components/exo/shell_surface_util.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
#include "components/version_info/channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
using user_manager::User;
using version_info::Channel;
namespace crosapi {
namespace browser_util {
namespace {
bool g_lacros_enabled_for_test = false;
// 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:
return true;
case user_manager::USER_TYPE_GUEST:
case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
case user_manager::USER_TYPE_SUPERVISED_DEPRECATED:
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::USER_TYPE_WEB_KIOSK_APP:
case user_manager::NUM_USER_TYPES:
return false;
}
}
// Returns the vector containing policy data of the device account. In case of
// an error, returns nullopt.
base::Optional<std::vector<uint8_t>> GetDeviceAccountPolicy(
EnvironmentProvider* environment_provider) {
if (!user_manager::UserManager::IsInitialized()) {
LOG(ERROR) << "User not initialized.";
return base::nullopt;
}
const auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
if (!primary_user) {
LOG(ERROR) << "No primary user.";
return base::nullopt;
}
std::string policy_data = environment_provider->GetDeviceAccountPolicy();
return std::vector<uint8_t>(policy_data.begin(), policy_data.end());
}
using InterfaceVersions = base::flat_map<base::Token, uint32_t>;
template <typename T>
void AddVersion(InterfaceVersions* map) {
(*map)[T::Uuid_] = T::Version_;
}
mojom::LacrosInitParamsPtr GetLacrosInitParams(
EnvironmentProvider* environment_provider) {
auto params = mojom::LacrosInitParams::New();
params->ash_chrome_service_version =
crosapi::mojom::AshChromeService::Version_;
params->deprecated_ash_metrics_enabled_has_value = true;
PrefService* local_state = g_browser_process->local_state();
params->ash_metrics_enabled =
local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled);
params->ash_metrics_managed =
local_state->IsManagedPreference(metrics::prefs::kMetricsReportingEnabled)
? mojom::MetricsReportingManaged::kManaged
: mojom::MetricsReportingManaged::kNotManaged;
params->session_type = environment_provider->GetSessionType();
params->device_mode = environment_provider->GetDeviceMode();
params->interface_versions = GetInterfaceVersions();
params->default_paths = environment_provider->GetDefaultPaths();
params->device_account_gaia_id =
environment_provider->GetDeviceAccountGaiaId();
// TODO(crbug.com/1093194): This should be updated to a new value when
// the long term fix is made in ash-chrome, atomically.
params->exo_ime_support =
crosapi::mojom::ExoImeSupport::kConsumedByImeWorkaround;
params->cros_user_id_hash = chromeos::ProfileHelper::GetUserIdHashFromProfile(
ProfileManager::GetPrimaryUserProfile());
params->device_account_policy = GetDeviceAccountPolicy(environment_provider);
return params;
}
} // namespace
// When this feature is enabled, Lacros will be available on stable channel.
const base::Feature kLacrosAllowOnStableChannel{
"LacrosAllowOnStableChannel", base::FEATURE_DISABLED_BY_DEFAULT};
const char kLacrosStabilitySwitch[] = "lacros-stability";
const char kLacrosStabilityLessStable[] = "less-stable";
const char kLacrosStabilityMoreStable[] = "more-stable";
const char kLaunchOnLoginPref[] = "lacros.launch_on_login";
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kLaunchOnLoginPref, /*default_value=*/false);
}
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 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 (!base::FeatureList::IsEnabled(chromeos::features::kLacrosSupport)) {
LOG(WARNING) << "Lacros-chrome is not supported";
return false;
}
const User* user = user_manager::UserManager::Get()->GetPrimaryUser();
if (!user) {
LOG(WARNING)
<< "Unable to get primary user. Lacros-chrome will be disabled";
return false;
}
if (!IsUserTypeAllowed(user)) {
LOG(WARNING) << "Current user type not allowed to launch lacros-chrome";
return false;
}
// TODO(https://crbug.com/1135494): Remove the free ticket for
// Channel::UNKNOWN after the policy is set on server side for developers.
if (channel == Channel::UNKNOWN)
return true;
if (!g_browser_process->local_state()->GetBoolean(prefs::kLacrosAllowed)) {
LOG(WARNING) << "Lacros-chrome is not allowed by policy";
return false;
}
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);
}
}
void SetLacrosEnabledForTest(bool force_enabled) {
g_lacros_enabled_for_test = force_enabled;
}
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);
}
base::flat_map<base::Token, uint32_t> GetInterfaceVersions() {
static_assert(
crosapi::mojom::AshChromeService::Version_ == 11,
"if you add a new crosapi, please add it to the version map here");
InterfaceVersions versions;
AddVersion<crosapi::mojom::AccountManager>(&versions);
AddVersion<crosapi::mojom::AshChromeService>(&versions);
AddVersion<crosapi::mojom::CertDatabase>(&versions);
AddVersion<crosapi::mojom::Clipboard>(&versions);
AddVersion<crosapi::mojom::Feedback>(&versions);
AddVersion<crosapi::mojom::FileManager>(&versions);
AddVersion<crosapi::mojom::KeystoreService>(&versions);
AddVersion<crosapi::mojom::MessageCenter>(&versions);
AddVersion<crosapi::mojom::MetricsReporting>(&versions);
AddVersion<crosapi::mojom::Prefs>(&versions);
AddVersion<crosapi::mojom::ScreenManager>(&versions);
AddVersion<crosapi::mojom::SnapshotCapturer>(&versions);
AddVersion<crosapi::mojom::TestController>(&versions);
AddVersion<device::mojom::HidConnection>(&versions);
AddVersion<device::mojom::HidManager>(&versions);
AddVersion<media_session::mojom::MediaControllerManager>(&versions);
AddVersion<media_session::mojom::AudioFocusManager>(&versions);
AddVersion<media_session::mojom::AudioFocusManagerDebug>(&versions);
return versions;
}
mojo::Remote<crosapi::mojom::LacrosChromeService>
SendMojoInvitationToLacrosChrome(
EnvironmentProvider* environment_provider,
mojo::PlatformChannelEndpoint local_endpoint,
base::OnceClosure mojo_disconnected_callback,
base::OnceCallback<
void(mojo::PendingReceiver<crosapi::mojom::AshChromeService>)>
ash_chrome_service_callback) {
mojo::OutgoingInvitation invitation;
mojo::Remote<crosapi::mojom::LacrosChromeService> lacros_chrome_service;
lacros_chrome_service.Bind(
mojo::PendingRemote<crosapi::mojom::LacrosChromeService>(
invitation.AttachMessagePipe(0 /* token */), /*version=*/0));
lacros_chrome_service.set_disconnect_handler(
std::move(mojo_disconnected_callback));
// This is for backward compatibility.
// TODO(crbug.com/1156033): Remove InitDeprecated() invocation when lacros
// becomes mature enough.
lacros_chrome_service->InitDeprecated(
GetLacrosInitParams(environment_provider));
lacros_chrome_service->RequestAshChromeServiceReceiver(
std::move(ash_chrome_service_callback));
mojo::OutgoingInvitation::Send(std::move(invitation),
base::kNullProcessHandle,
std::move(local_endpoint));
return lacros_chrome_service;
}
base::ScopedFD CreateStartupData(EnvironmentProvider* environment_provider) {
auto data = GetLacrosInitParams(environment_provider);
std::vector<uint8_t> serialized =
crosapi::mojom::LacrosInitParams::Serialize(&data);
base::ScopedFD fd(memfd_create("startup_data", 0));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to create a memory backed file";
return base::ScopedFD();
}
if (!base::WriteFileDescriptor(
fd.get(), reinterpret_cast<const char*>(serialized.data()),
serialized.size())) {
LOG(ERROR) << "Failed to dump the serialized startup data";
return base::ScopedFD();
}
if (lseek(fd.get(), 0, SEEK_SET) < 0) {
PLOG(ERROR) << "Failed to reset the FD position";
return base::ScopedFD();
}
return fd;
}
} // namespace browser_util
} // namespace crosapi