blob: 5e072addcfa811ef3ed57aa763d1905734380892 [file] [log] [blame]
// Copyright 2016 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/arc/arc_support_host.h"
#include <string>
#include <utility>
#include "ash/common/system/chromeos/devicetype_utils.h"
#include "base/i18n/timezone.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/grit/generated_resources.h"
#include "components/user_manager/known_user.h"
#include "extensions/browser/extension_registry.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/display/screen.h"
namespace {
constexpr char kAction[] = "action";
constexpr char kArcManaged[] = "arcManaged";
constexpr char kData[] = "data";
constexpr char kDeviceId[] = "deviceId";
constexpr char kActionInitialize[] = "initialize";
constexpr char kActionSetMetricsMode[] = "setMetricsMode";
constexpr char kActionBackupAndRestoreMode[] = "setBackupAndRestoreMode";
constexpr char kActionLocationServiceMode[] = "setLocationServiceMode";
constexpr char kActionSetWindowBounds[] = "setWindowBounds";
constexpr char kActionCloseWindow[] = "closeWindow";
// Action to show a page. The message should have "page" field, which is one of
// IDs for section div elements.
constexpr char kActionShowPage[] = "showPage";
constexpr char kPage[] = "page";
// Action to show the error page. The message should have "errorMessage",
// which is a localized error text, and "shouldShowSendFeedback" boolean value.
constexpr char kActionShowErrorPage[] = "showErrorPage";
constexpr char kErrorMessage[] = "errorMessage";
constexpr char kShouldShowSendFeedback[] = "shouldShowSendFeedback";
// The preference update should have those two fields.
constexpr char kEnabled[] = "enabled";
constexpr char kManaged[] = "managed";
// The JSON data sent from the extension should have at least "event" field.
// Each event data is defined below.
// The key of the event type.
constexpr char kEvent[] = "event";
// "onWindowClosed" is fired when the extension window is closed.
// No data will be provided.
constexpr char kEventOnWindowClosed[] = "onWindowClosed";
// "onAuthSucceeded" is fired when successfully done to LSO authorization in
// extension.
// The auth token is passed via "code" field.
constexpr char kEventOnAuthSucceeded[] = "onAuthSucceeded";
constexpr char kCode[] = "code";
// "onAgree" is fired when a user clicks "Agree" button.
// The message should have the following three fields:
// - isMetricsEnabled
// - isBackupRestoreEnabled
// - isLocationServiceEnabled
constexpr char kEventOnAgreed[] = "onAgreed";
constexpr char kIsMetricsEnabled[] = "isMetricsEnabled";
constexpr char kIsBackupRestoreEnabled[] = "isBackupRestoreEnabled";
constexpr char kIsLocationServiceEnabled[] = "isLocationServiceEnabled";
// "onRetryClicked" is fired when a user clicks "RETRY" button on the error
// page.
constexpr char kEventOnRetryClicked[] = "onRetryClicked";
// "onSendFeedbackClicked" is fired when a user clicks "Send Feedback" button.
constexpr char kEventOnSendFeedbackClicked[] = "onSendFeedbackClicked";
std::ostream& operator<<(std::ostream& os, ArcSupportHost::UIPage ui_page) {
switch (ui_page) {
case ArcSupportHost::UIPage::NO_PAGE:
return os << "NO_PAGE";
case ArcSupportHost::UIPage::TERMS:
return os << "TERMS";
case ArcSupportHost::UIPage::LSO:
return os << "LSO";
case ArcSupportHost::UIPage::ARC_LOADING:
return os << "ARC_LOADING";
case ArcSupportHost::UIPage::ERROR:
return os << "ERROR";
}
// Some compiler reports an error even if all values of an enum-class are
// covered indivisually in a switch statement.
NOTREACHED();
return os;
}
std::ostream& operator<<(std::ostream& os, ArcSupportHost::Error error) {
switch (error) {
case ArcSupportHost::Error::SIGN_IN_NETWORK_ERROR:
return os << "SIGN_IN_NETWORK_ERROR";
case ArcSupportHost::Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR:
return os << "SIGN_IN_SERVICE_UNAVAILABLE_ERROR";
case ArcSupportHost::Error::SIGN_IN_BAD_AUTHENTICATION_ERROR:
return os << "SIGN_IN_BAD_AUTHENTICATION_ERROR";
case ArcSupportHost::Error::SIGN_IN_GMS_NOT_AVAILABLE_ERROR:
return os << "SIGN_IN_GMS_NOT_AVAILABLE_ERROR";
case ArcSupportHost::Error::SIGN_IN_CLOUD_PROVISION_FLOW_FAIL_ERROR:
return os << "SIGN_IN_CLOUD_PROVISION_FLOW_FAIL_ERROR";
case ArcSupportHost::Error::SIGN_IN_UNKNOWN_ERROR:
return os << "SIGN_IN_UNKNOWN_ERROR";
case ArcSupportHost::Error::SERVER_COMMUNICATION_ERROR:
return os << "SERVER_COMMUNICATION_ERROR";
case ArcSupportHost::Error::ANDROID_MANAGEMENT_REQUIRED_ERROR:
return os << "ANDROID_MANAGEMENT_REQUIRED_ERROR";
}
// Some compiler reports an error even if all values of an enum-class are
// covered indivisually in a switch statement.
NOTREACHED();
return os;
}
} // namespace
// static
const char ArcSupportHost::kHostAppId[] = "cnbgggchhmkkdmeppjobngjoejnihlei";
// static
const char ArcSupportHost::kStorageId[] = "arc_support";
ArcSupportHost::ArcSupportHost(Profile* profile) : profile_(profile) {}
ArcSupportHost::~ArcSupportHost() {
if (message_host_)
DisconnectMessageHost();
}
void ArcSupportHost::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void ArcSupportHost::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
bool ArcSupportHost::HasObserver(Observer* observer) {
return observer_list_.HasObserver(observer);
}
void ArcSupportHost::SetArcManaged(bool is_arc_managed) {
DCHECK(!message_host_);
is_arc_managed_ = is_arc_managed;
}
void ArcSupportHost::Close() {
ui_page_ = UIPage::NO_PAGE;
if (!message_host_) {
VLOG(2) << "ArcSupportHost::Close() is called "
<< "but message_host_ is not available.";
return;
}
base::DictionaryValue message;
message.SetString(kAction, kActionCloseWindow);
message_host_->SendMessage(message);
// Disconnect immediately, so that onWindowClosed event will not be
// delivered to here.
DisconnectMessageHost();
}
void ArcSupportHost::ShowTermsOfService() {
ShowPage(UIPage::TERMS);
}
void ArcSupportHost::ShowLso() {
ShowPage(UIPage::LSO);
}
void ArcSupportHost::ShowArcLoading() {
ShowPage(UIPage::ARC_LOADING);
}
void ArcSupportHost::ShowPage(UIPage ui_page) {
ui_page_ = ui_page;
if (!message_host_) {
if (app_start_pending_) {
VLOG(2) << "ArcSupportHost::ShowPage(" << ui_page << ") is called "
<< "before connection to ARC support Chrome app has finished "
<< "establishing.";
return;
}
RequestAppStart();
return;
}
base::DictionaryValue message;
message.SetString(kAction, kActionShowPage);
switch (ui_page) {
case UIPage::TERMS:
message.SetString(kPage, "terms");
break;
case UIPage::LSO:
// TODO(hidehiko): Merge LSO and LSO_LOADING.
message.SetString(kPage, "lso-loading");
break;
case UIPage::ARC_LOADING:
message.SetString(kPage, "arc-loading");
break;
default:
NOTREACHED();
return;
}
message_host_->SendMessage(message);
}
void ArcSupportHost::ShowError(Error error, bool should_show_send_feedback) {
ui_page_ = UIPage::ERROR;
error_ = error;
should_show_send_feedback_ = should_show_send_feedback;
if (!message_host_) {
if (app_start_pending_) {
VLOG(2) << "ArcSupportHost::ShowError(" << error << ", "
<< should_show_send_feedback << ") is called before connection "
<< "to ARC support Chrome app is established.";
return;
}
RequestAppStart();
return;
}
base::DictionaryValue message;
message.SetString(kAction, kActionShowErrorPage);
int message_id;
switch (error) {
case Error::SIGN_IN_NETWORK_ERROR:
message_id = IDS_ARC_SIGN_IN_NETWORK_ERROR;
break;
case Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR:
message_id = IDS_ARC_SIGN_IN_SERVICE_UNAVAILABLE_ERROR;
break;
case Error::SIGN_IN_BAD_AUTHENTICATION_ERROR:
message_id = IDS_ARC_SIGN_IN_BAD_AUTHENTICATION_ERROR;
break;
case Error::SIGN_IN_GMS_NOT_AVAILABLE_ERROR:
message_id = IDS_ARC_SIGN_IN_GMS_NOT_AVAILABLE_ERROR;
break;
case Error::SIGN_IN_CLOUD_PROVISION_FLOW_FAIL_ERROR:
message_id = IDS_ARC_SIGN_IN_CLOUD_PROVISION_FLOW_FAIL_ERROR;
break;
case Error::SIGN_IN_UNKNOWN_ERROR:
message_id = IDS_ARC_SIGN_IN_UNKNOWN_ERROR;
break;
case Error::SERVER_COMMUNICATION_ERROR:
message_id = IDS_ARC_SERVER_COMMUNICATION_ERROR;
break;
case Error::ANDROID_MANAGEMENT_REQUIRED_ERROR:
message_id = IDS_ARC_ANDROID_MANAGEMENT_REQUIRED_ERROR;
break;
}
message.SetString(kErrorMessage, l10n_util::GetStringUTF16(message_id));
message.SetBoolean(kShouldShowSendFeedback, should_show_send_feedback);
message_host_->SendMessage(message);
}
void ArcSupportHost::SetMetricsPreferenceCheckbox(bool is_enabled,
bool is_managed) {
metrics_checkbox_ = PreferenceCheckboxData(is_enabled, is_managed);
SendPreferenceCheckboxUpdate(kActionSetMetricsMode, metrics_checkbox_);
}
void ArcSupportHost::SetBackupAndRestorePreferenceCheckbox(bool is_enabled,
bool is_managed) {
backup_and_restore_checkbox_ = PreferenceCheckboxData(is_enabled, is_managed);
SendPreferenceCheckboxUpdate(kActionBackupAndRestoreMode,
backup_and_restore_checkbox_);
}
void ArcSupportHost::SetLocationServicesPreferenceCheckbox(bool is_enabled,
bool is_managed) {
location_services_checkbox_ = PreferenceCheckboxData(is_enabled, is_managed);
SendPreferenceCheckboxUpdate(kActionLocationServiceMode,
location_services_checkbox_);
}
void ArcSupportHost::SendPreferenceCheckboxUpdate(
const std::string& action_name,
const PreferenceCheckboxData& data) {
if (!message_host_)
return;
base::DictionaryValue message;
message.SetString(kAction, action_name);
message.SetBoolean(kEnabled, data.is_enabled);
message.SetBoolean(kManaged, data.is_managed);
message_host_->SendMessage(message);
}
void ArcSupportHost::SetMessageHost(arc::ArcSupportMessageHost* message_host) {
if (message_host_ == message_host)
return;
app_start_pending_ = false;
if (message_host_)
DisconnectMessageHost();
message_host_ = message_host;
message_host_->SetObserver(this);
display::Screen::GetScreen()->AddObserver(this);
if (!Initialize()) {
Close();
return;
}
// Hereafter, install the requested ui state into the ARC support Chrome app.
// Set preference checkbox state.
SendPreferenceCheckboxUpdate(kActionSetMetricsMode, metrics_checkbox_);
SendPreferenceCheckboxUpdate(kActionBackupAndRestoreMode,
backup_and_restore_checkbox_);
SendPreferenceCheckboxUpdate(kActionLocationServiceMode,
location_services_checkbox_);
if (ui_page_ == UIPage::NO_PAGE) {
// Close() is called before opening the window.
Close();
} else if (ui_page_ == UIPage::ERROR) {
ShowError(error_, should_show_send_feedback_);
} else {
ShowPage(ui_page_);
}
}
void ArcSupportHost::UnsetMessageHost(
arc::ArcSupportMessageHost* message_host) {
if (message_host_ != message_host)
return;
DisconnectMessageHost();
}
void ArcSupportHost::DisconnectMessageHost() {
DCHECK(message_host_);
display::Screen::GetScreen()->RemoveObserver(this);
message_host_->SetObserver(nullptr);
message_host_ = nullptr;
}
void ArcSupportHost::RequestAppStart() {
DCHECK(!message_host_);
DCHECK(!app_start_pending_);
app_start_pending_ = true;
const extensions::Extension* extension =
extensions::ExtensionRegistry::Get(profile_)->GetInstalledExtension(
kHostAppId);
DCHECK(extension);
DCHECK(extensions::util::IsAppLaunchable(kHostAppId, profile_));
OpenApplication(CreateAppLaunchParamsUserContainer(
profile_, extension, WindowOpenDisposition::NEW_WINDOW,
extensions::SOURCE_CHROME_INTERNAL));
}
bool ArcSupportHost::Initialize() {
DCHECK(message_host_);
auto loadtime_data = base::MakeUnique<base::DictionaryValue>();
base::string16 device_name = ash::GetChromeOSDeviceName();
loadtime_data->SetString(
"greetingHeader",
l10n_util::GetStringFUTF16(IDS_ARC_OPT_IN_DIALOG_HEADER, device_name));
loadtime_data->SetString("greetingDescription",
l10n_util::GetStringFUTF16(
IDS_ARC_OPT_IN_DIALOG_DESCRIPTION, device_name));
loadtime_data->SetString(
"buttonAgree",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_BUTTON_AGREE));
loadtime_data->SetString(
"buttonCancel",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_BUTTON_CANCEL));
loadtime_data->SetString(
"buttonSendFeedback",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_BUTTON_SEND_FEEDBACK));
loadtime_data->SetString(
"buttonRetry",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_BUTTON_RETRY));
loadtime_data->SetString(
"progressLsoLoading",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_PROGRESS_LSO));
loadtime_data->SetString(
"progressAndroidLoading",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_PROGRESS_ANDROID));
loadtime_data->SetString(
"authorizationFailed",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_AUTHORIZATION_FAILED));
loadtime_data->SetString(
"termsOfService",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_TERMS_OF_SERVICE));
loadtime_data->SetString(
"textMetricsEnabled",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_METRICS_ENABLED));
loadtime_data->SetString(
"textMetricsDisabled",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_METRICS_DISABLED));
loadtime_data->SetString(
"textMetricsManagedEnabled",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_METRICS_MANAGED_ENABLED));
loadtime_data->SetString("textMetricsManagedDisabled",
l10n_util::GetStringUTF16(
IDS_ARC_OPT_IN_DIALOG_METRICS_MANAGED_DISABLED));
loadtime_data->SetString(
"textBackupRestore",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_DIALOG_BACKUP_RESTORE));
loadtime_data->SetString(
"textLocationService",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LOCATION_SETTING));
loadtime_data->SetString(
"serverError",
l10n_util::GetStringUTF16(IDS_ARC_SERVER_COMMUNICATION_ERROR));
loadtime_data->SetString(
"controlledByPolicy",
l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
loadtime_data->SetString(
"learnMoreStatistics",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LEARN_MORE_STATISTICS));
loadtime_data->SetString(
"learnMoreBackupAndRestore",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LEARN_MORE_BACKUP_AND_RESTORE));
loadtime_data->SetString(
"learnMoreLocationServices",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LEARN_MORE_LOCATION_SERVICES));
loadtime_data->SetString(
"overlayClose",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_LEARN_MORE_CLOSE));
loadtime_data->SetString(
"privacyPolicyLink",
l10n_util::GetStringUTF16(IDS_ARC_OPT_IN_PRIVACY_POLICY_LINK));
loadtime_data->SetBoolean(kArcManaged, is_arc_managed_);
loadtime_data->SetBoolean("isOwnerProfile",
chromeos::ProfileHelper::IsOwnerProfile(profile_));
const std::string& country_code = base::CountryCodeForCurrentTimezone();
loadtime_data->SetString("countryCode", country_code);
const std::string& app_locale = g_browser_process->GetApplicationLocale();
webui::SetLoadTimeDataDefaults(app_locale, loadtime_data.get());
base::DictionaryValue message;
message.SetString(kAction, kActionInitialize);
message.Set(kData, std::move(loadtime_data));
const std::string device_id = user_manager::known_user::GetDeviceId(
multi_user_util::GetAccountIdFromProfile(profile_));
DCHECK(!device_id.empty());
message.SetString(kDeviceId, device_id);
message_host_->SendMessage(message);
return true;
}
void ArcSupportHost::OnDisplayAdded(const display::Display& new_display) {}
void ArcSupportHost::OnDisplayRemoved(const display::Display& old_display) {}
void ArcSupportHost::OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) {
if (!message_host_)
return;
base::DictionaryValue message;
message.SetString(kAction, kActionSetWindowBounds);
message_host_->SendMessage(message);
}
void ArcSupportHost::OnMessage(const base::DictionaryValue& message) {
std::string event;
if (!message.GetString(kEvent, &event)) {
NOTREACHED();
return;
}
if (event == kEventOnWindowClosed) {
for (auto& observer : observer_list_)
observer.OnWindowClosed();
} else if (event == kEventOnAuthSucceeded) {
std::string code;
if (message.GetString(kCode, &code)) {
for (auto& observer : observer_list_)
observer.OnAuthSucceeded(code);
} else {
NOTREACHED();
}
} else if (event == kEventOnAgreed) {
bool is_metrics_enabled;
bool is_backup_restore_enabled;
bool is_location_service_enabled;
if (message.GetBoolean(kIsMetricsEnabled, &is_metrics_enabled) &&
message.GetBoolean(kIsBackupRestoreEnabled,
&is_backup_restore_enabled) &&
message.GetBoolean(kIsLocationServiceEnabled,
&is_location_service_enabled)) {
for (auto& observer : observer_list_) {
observer.OnTermsAgreed(is_metrics_enabled, is_backup_restore_enabled,
is_location_service_enabled);
}
} else {
NOTREACHED();
}
} else if (event == kEventOnRetryClicked) {
for (auto& observer : observer_list_)
observer.OnRetryClicked();
} else if (event == kEventOnSendFeedbackClicked) {
for (auto& observer : observer_list_)
observer.OnSendFeedbackClicked();
} else {
LOG(ERROR) << "Unknown message: " << event;
NOTREACHED();
}
}