blob: fb77e01cc40e5d5e1c315e2e606ccdebbbaa05c9 [file] [log] [blame]
// Copyright 2021 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 "ash/system/network/cellular_setup_notifier.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/public/cpp/network_config_service.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/public/cpp/system_tray_client.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/model/system_tray_model.h"
#include "base/bind.h"
#include "base/timer/timer.h"
#include "components/onc/onc_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
namespace ash {
namespace {
const char kNotifierCellularSetup[] = "ash.cellular-setup";
// Delay after OOBE until notification should be shown.
constexpr base::TimeDelta kNotificationDelay = base::TimeDelta::FromMinutes(15);
bool DoesCellularDeviceExist(
const std::vector<
chromeos::network_config::mojom::DeviceStatePropertiesPtr>& devices) {
for (const auto& device : devices) {
if (device->type ==
chromeos::network_config::mojom::NetworkType::kCellular) {
return true;
}
}
return false;
}
void OnCellularSetupNotificationClicked() {
Shell::Get()->system_tray_model()->client()->ShowNetworkCreate(
::onc::network_type::kCellular);
}
// Returns the value of kCanCellularSetupNotificationBeShown for the last active
// user. If the last active user's PrefService is null, returns false.
bool GetCanCellularSetupNotificationBeShown() {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
if (!prefs) {
// Return false because we don't want to show the notification if we're
// unsure if it can be shown or not.
return false;
}
return prefs->GetBoolean(prefs::kCanCellularSetupNotificationBeShown);
}
// Sets kCanCellularSetupNotificationBeShown to false for the last active user.
// Returns true if the flag was successfully set, and false if not.
bool SetCellularSetupNotificationCannotBeShown() {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
if (!prefs) {
return false;
}
prefs->SetBoolean(prefs::kCanCellularSetupNotificationBeShown, false);
return true;
}
} // namespace
// static
void CellularSetupNotifier::RegisterProfilePrefs(PrefRegistrySimple* registry) {
// Default value is true as we usually want to show the notification except
// for specific conditions (cellular-incapable device, already shown).
registry->RegisterBooleanPref(prefs::kCanCellularSetupNotificationBeShown,
true);
}
// static
const char CellularSetupNotifier::kCellularSetupNotificationId[] =
"cros_cellular_setup_notifier_ids.setup_network";
CellularSetupNotifier::CellularSetupNotifier()
: timer_(std::make_unique<base::OneShotTimer>()) {
GetNetworkConfigService(
remote_cros_network_config_.BindNewPipeAndPassReceiver());
remote_cros_network_config_->AddObserver(
cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
Shell::Get()->session_controller()->AddObserver(this);
}
CellularSetupNotifier::~CellularSetupNotifier() {
Shell::Get()->session_controller()->RemoveObserver(this);
}
void CellularSetupNotifier::OnSessionStateChanged(
session_manager::SessionState state) {
if (Shell::Get()->session_controller()->GetSessionState() !=
session_manager::SessionState::ACTIVE) {
timer_->Stop();
return;
}
if (!GetCanCellularSetupNotificationBeShown()) {
// The notification has already been shown or there is some condition that
// dictates that the notification shouldn't be shown.
return;
}
// Wait |kNotificationDelay| after the user logs in before attempting to show
// a notification. This allows the user time to set up a cellular network if
// they desire, and it also ensures we don't spam the user with an extra
// notification just after they log into their device for the first time.
timer_->Start(FROM_HERE, kNotificationDelay,
base::BindOnce(&CellularSetupNotifier::OnTimerFired,
base::Unretained(this)));
}
void CellularSetupNotifier::OnTimerFired() {
timer_fired_ = true;
MaybeShowCellularSetupNotification();
}
void CellularSetupNotifier::OnNetworkStateListChanged() {
MaybeShowCellularSetupNotification();
}
void CellularSetupNotifier::OnNetworkStateChanged(
chromeos::network_config::mojom::NetworkStatePropertiesPtr network) {
if (network->type !=
chromeos::network_config::mojom::NetworkType::kCellular ||
network->type_state->get_cellular()->activation_state !=
chromeos::network_config::mojom::ActivationStateType::kActivated) {
return;
}
SetCellularSetupNotificationCannotBeShown();
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center->RemoveNotification(kCellularSetupNotificationId, false);
}
void CellularSetupNotifier::MaybeShowCellularSetupNotification() {
remote_cros_network_config_->GetDeviceStateList(base::BindOnce(
&CellularSetupNotifier::OnGetDeviceStateList, base::Unretained(this)));
}
void CellularSetupNotifier::OnGetDeviceStateList(
std::vector<chromeos::network_config::mojom::DeviceStatePropertiesPtr>
devices) {
if (!DoesCellularDeviceExist(devices)) {
// Save to prefs not to show this notification again so we don't keep
// starting the timer for a cellular-incapable device.
SetCellularSetupNotificationCannotBeShown();
return;
}
remote_cros_network_config_->GetNetworkStateList(
chromeos::network_config::mojom::NetworkFilter::New(
chromeos::network_config::mojom::FilterType::kAll,
chromeos::network_config::mojom::NetworkType::kCellular,
chromeos::network_config::mojom::kNoLimit),
base::BindOnce(&CellularSetupNotifier::OnCellularNetworksList,
base::Unretained(this)));
}
void CellularSetupNotifier::OnCellularNetworksList(
std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>
networks) {
// Check if there are any activated pSIM or eSIM networks. The activation
// state property is set to activated for all eSIM services.
for (auto& network : networks) {
if (network->type_state->get_cellular()->activation_state ==
chromeos::network_config::mojom::ActivationStateType::kActivated) {
// Save to prefs not to try to show this notification again so we don't
// keep starting the timer for a user who already has an activated
// cellular network.
SetCellularSetupNotificationCannotBeShown();
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center->RemoveNotification(kCellularSetupNotificationId, false);
return;
}
}
// Do not show notification if it has already been shown, or the timer
// has not yet been fired.
if (!GetCanCellularSetupNotificationBeShown() || !timer_fired_) {
return;
}
ShowCellularSetupNotification();
}
// Shows the Cellular Setup notification except in cases where it is unable to
// save that it will have shown the notification.
void CellularSetupNotifier::ShowCellularSetupNotification() {
if (!SetCellularSetupNotificationCannotBeShown()) {
// If we didn't successfully set the flag, don't show the notification or
// else we may show the notification multiple times.
return;
}
std::unique_ptr<message_center::Notification> notification =
ash::CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE,
kCellularSetupNotificationId,
l10n_util::GetStringUTF16(
IDS_ASH_NETWORK_CELLULAR_SETUP_NOTIFICATION_TITLE),
l10n_util::GetStringUTF16(
IDS_ASH_NETWORK_CELLULAR_SETUP_NOTIFICATION_MESSAGE),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierCellularSetup),
message_center::RichNotificationData(),
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(&OnCellularSetupNotificationClicked)),
kAddCellularNetworkIcon,
message_center::SystemNotificationWarningLevel::NORMAL);
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
if (message_center->FindVisibleNotificationById(kCellularSetupNotificationId))
message_center->RemoveNotification(kCellularSetupNotificationId, false);
message_center->AddNotification(std::move(notification));
}
} // namespace ash