blob: 1f61159b7f11c873182fa0baa1be47b7fb4cef5f [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/full_restore/full_restore_service.h"
#include "ash/public/cpp/notification_utils.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/full_restore/full_restore_app_launch_handler.h"
#include "chrome/browser/chromeos/full_restore/full_restore_data_handler.h"
#include "chrome/browser/chromeos/full_restore/full_restore_prefs.h"
#include "chrome/browser/chromeos/full_restore/full_restore_service_factory.h"
#include "chrome/browser/chromeos/full_restore/new_user_restore_pref_handler.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/account_id/account_id.h"
#include "components/full_restore/full_restore_info.h"
#include "components/full_restore/full_restore_save_handler.h"
#include "components/prefs/pref_service.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/public/cpp/notification.h"
namespace chromeos {
namespace full_restore {
bool g_restore_for_testing = true;
const char kRestoreForCrashNotificationId[] = "restore_for_crash_notification";
const char kRestoreNotificationId[] = "restore_notification";
const char kSetRestorePrefNotificationId[] = "set_restore_pref_notification";
// If the user selected the 'Restore' button from the restore notification
// dialog for more than |kMaxConsecutiveRestoreSelectionCount| times, show the
// set restore pref notification.
const int kMaxConsecutiveRestoreSelectionCount = 3;
const char kRestoreNotificationHistogramName[] = "Apps.RestoreNotification";
const char kRestoreForCrashNotificationHistogramName[] =
"Apps.RestoreForCrashNotification";
const char kRestoreSettingHistogramName[] = "Apps.RestoreSetting";
const char kRestoreInitSettingHistogramName[] = "Apps.RestoreInitSetting";
// static
FullRestoreService* FullRestoreService::GetForProfile(Profile* profile) {
return static_cast<FullRestoreService*>(
FullRestoreServiceFactory::GetInstance()->GetForProfile(profile));
}
FullRestoreService::FullRestoreService(Profile* profile)
: profile_(profile),
app_launch_handler_(std::make_unique<FullRestoreAppLaunchHandler>(
profile_,
/*should_init_service=*/true)),
restore_data_handler_(
std::make_unique<FullRestoreDataHandler>(profile_)) {}
FullRestoreService::~FullRestoreService() = default;
void FullRestoreService::Init() {
PrefService* prefs = profile_->GetPrefs();
DCHECK(prefs);
pref_change_registrar_.Init(prefs);
pref_change_registrar_.Add(
kRestoreAppsAndPagesPrefName,
base::BindRepeating(&FullRestoreService::OnPreferenceChanged,
weak_ptr_factory_.GetWeakPtr()));
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
if (user) {
::full_restore::FullRestoreInfo::GetInstance()->SetRestorePref(
user->GetAccountId(), CanPerformRestore(prefs));
}
// If the system crashed before reboot, show the restore notification.
if (profile_->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
if (!HasRestorePref(prefs))
SetDefaultRestorePrefIfNecessary(prefs);
MaybeShowRestoreNotification(kRestoreForCrashNotificationId);
return;
}
// If either OS pref setting nor Chrome pref setting exist, that means we
// don't have restore data, so we don't need to consider restoration, and call
// NewUserRestorePrefHandler to set OS pref setting.
if (!HasRestorePref(prefs) && !HasSessionStartupPref(prefs)) {
new_user_pref_handler_ =
std::make_unique<NewUserRestorePrefHandler>(profile_);
return;
}
// If it is the first time to migrate to the full restore release, we don't
// have other restore data, so we don't need to consider restoration.
if (!HasRestorePref(prefs)) {
SetDefaultRestorePrefIfNecessary(prefs);
return;
}
RestoreOption restore_pref = static_cast<RestoreOption>(
prefs->GetInteger(kRestoreAppsAndPagesPrefName));
base::UmaHistogramEnumeration(kRestoreInitSettingHistogramName, restore_pref);
switch (restore_pref) {
case RestoreOption::kAlways:
Restore();
break;
case RestoreOption::kAskEveryTime:
MaybeShowRestoreNotification(kRestoreNotificationId);
break;
case RestoreOption::kDoNotRestore:
return;
}
}
void FullRestoreService::LaunchBrowserWhenReady() {
if (!g_restore_for_testing)
return;
app_launch_handler_->LaunchBrowserWhenReady();
}
void FullRestoreService::Close(bool by_user) {
if (!skip_notification_histogram_ &&
(notification_->id() == kRestoreNotificationId ||
notification_->id() == kRestoreForCrashNotificationId)) {
RecordRestoreAction(
notification_->id(),
by_user ? RestoreAction::kCloseByUser : RestoreAction::kCloseNotByUser);
}
}
void FullRestoreService::Click(const absl::optional<int>& button_index,
const absl::optional<std::u16string>& reply) {
skip_notification_histogram_ = true;
DCHECK(notification_);
if (!is_shut_down_) {
NotificationDisplayService::GetForProfile(profile_)->Close(
NotificationHandler::Type::TRANSIENT, notification_->id());
}
if (!button_index.has_value())
return;
if (notification_->id() == kRestoreNotificationId ||
notification_->id() == kRestoreForCrashNotificationId) {
RecordRestoreAction(
notification_->id(),
button_index.value() ==
static_cast<int>(RestoreNotificationButtonIndex::kRestore)
? RestoreAction::kRestore
: RestoreAction::kCancel);
}
if (button_index.value() !=
static_cast<int>(RestoreNotificationButtonIndex::kRestore)) {
return;
}
if (notification_->id() == kSetRestorePrefNotificationId) {
// Show the 'On Startup' OS setting page.
chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
profile_, chromeos::settings::mojom::kAppsSectionPath);
return;
}
// For the restore notification, check how many times the user selected the
// 'Restore' button. If the user selects the 'restore' button for more than 3
// times, show the set restore pref notification.
if (notification_->id() == kRestoreNotificationId) {
int count = GetRestoreSelectedCountPref(profile_->GetPrefs());
if (count < kMaxConsecutiveRestoreSelectionCount)
SetRestoreSelectedCountPref(profile_->GetPrefs(), ++count);
if (count >= kMaxConsecutiveRestoreSelectionCount)
MaybeShowRestoreNotification(kSetRestorePrefNotificationId);
}
Restore();
}
void FullRestoreService::Shutdown() {
is_shut_down_ = true;
}
void FullRestoreService::MaybeShowRestoreNotification(const std::string& id) {
if (!ShouldShowNotification())
return;
message_center::RichNotificationData notification_data;
message_center::ButtonInfo restore_button(l10n_util::GetStringUTF16(
base::ToUpperASCII(id == kSetRestorePrefNotificationId
? IDS_SET_RESTORE_NOTIFICATION_BUTTON
: IDS_RESTORE_NOTIFICATION_RESTORE_BUTTON)));
notification_data.buttons.push_back(restore_button);
message_center::ButtonInfo cancel_button(l10n_util::GetStringUTF16(
base::ToUpperASCII(IDS_RESTORE_NOTIFICATION_CANCEL_BUTTON)));
notification_data.buttons.push_back(cancel_button);
int title_id = id == kSetRestorePrefNotificationId
? IDS_SET_RESTORE_NOTIFICATION_TITLE
: IDS_RESTORE_NOTIFICATION_TITLE;
int message_id;
if (id == kRestoreForCrashNotificationId)
message_id = IDS_RESTORE_FOR_CRASH_NOTIFICATION_MESSAGE;
else if (id == kRestoreNotificationId)
message_id = IDS_RESTORE_NOTIFICATION_MESSAGE;
else
message_id = IDS_SET_RESTORE_NOTIFICATION_MESSAGE;
notification_ = ash::CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE, id,
l10n_util::GetStringUTF16(title_id),
l10n_util::GetStringUTF16(message_id),
l10n_util::GetStringUTF16(IDS_RESTORE_NOTIFICATION_DISPLAY_SOURCE),
GURL(),
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
id),
notification_data,
base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
weak_ptr_factory_.GetWeakPtr()),
kFullRestoreNotificationIcon,
message_center::SystemNotificationWarningLevel::NORMAL);
notification_->set_priority(message_center::SYSTEM_PRIORITY);
auto* notification_display_service =
NotificationDisplayService::GetForProfile(profile_);
DCHECK(notification_display_service);
notification_display_service->Display(NotificationHandler::Type::TRANSIENT,
*notification_,
/*metadata=*/nullptr);
}
void FullRestoreService::Restore() {
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
if (user) {
::full_restore::FullRestoreInfo::GetInstance()->SetRestoreFlag(
user->GetAccountId(), true);
}
app_launch_handler_->SetShouldRestore();
}
void FullRestoreService::RecordRestoreAction(const std::string& notification_id,
RestoreAction restore_action) {
base::UmaHistogramEnumeration(notification_id == kRestoreNotificationId
? kRestoreNotificationHistogramName
: kRestoreForCrashNotificationHistogramName,
restore_action);
}
void FullRestoreService::OnPreferenceChanged(const std::string& pref_name) {
DCHECK_EQ(pref_name, kRestoreAppsAndPagesPrefName);
RestoreOption restore_option = static_cast<RestoreOption>(
profile_->GetPrefs()->GetInteger(kRestoreAppsAndPagesPrefName));
base::UmaHistogramEnumeration(kRestoreSettingHistogramName, restore_option);
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
if (user) {
::full_restore::FullRestoreInfo::GetInstance()->SetRestorePref(
user->GetAccountId(), CanPerformRestore(profile_->GetPrefs()));
}
}
bool FullRestoreService::ShouldShowNotification() {
return app_launch_handler_->HasRestoreData() &&
!::first_run::IsChromeFirstRun();
}
ScopedRestoreForTesting::ScopedRestoreForTesting() {
g_restore_for_testing = false;
}
ScopedRestoreForTesting::~ScopedRestoreForTesting() {
g_restore_for_testing = true;
}
} // namespace full_restore
} // namespace chromeos