0

[CrOS Hotspot] Admin restricted and wifi turned off notifications

Changes to support notifications when Hotspot is turned off by
administrator and when hotspot is enabled while WiFi is active.

Screenshots:
https://screenshot.googleplex.com/6t98McbbL79gw6S
https://screenshot.googleplex.com/3icxA8KYmkvtHc6

Bug: b/269353987
Change-Id: I1dc7df0673a755d7f963e70db5ec6a960e24f97b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4312124
Reviewed-by: Jason Zhang <jiajunz@google.com>
Commit-Queue: Nikhil Nayunigari <nikhilcn@google.com>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Reviewed-by: Andre Le <leandre@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1116053}
This commit is contained in:
Nikhil Nayunigari
2023-03-11 07:35:44 +00:00
committed by Chromium LUCI CQ
parent 3984ce86e9
commit 49b9c3f5d7
13 changed files with 314 additions and 1 deletions

@ -1517,6 +1517,8 @@ component("ash") {
"system/network/fake_network_list_network_header_view_delegate.h",
"system/network/fake_network_list_wifi_header_view.cc",
"system/network/fake_network_list_wifi_header_view.h",
"system/network/hotspot_notifier.cc",
"system/network/hotspot_notifier.h",
"system/network/managed_sim_lock_notifier.cc",
"system/network/managed_sim_lock_notifier.h",
"system/network/network_detailed_network_view.cc",
@ -2481,6 +2483,7 @@ component("ash") {
"//ash/public/cpp/resources:ash_public_unscaled_resources",
"//ash/resources/vector_icons",
"//ash/strings",
"//chromeos/ash/services/hotspot_config/public/mojom",
"//chromeos/ash/services/multidevice_setup/public/mojom",
"//chromeos/crosapi/mojom",
"//chromeos/services/machine_learning/public/mojom",
@ -2582,6 +2585,7 @@ component("ash") {
"//chromeos/ash/services/bluetooth_config/public/mojom",
"//chromeos/ash/services/federated/public/cpp",
"//chromeos/ash/services/federated/public/mojom",
"//chromeos/ash/services/hotspot_config/public/cpp",
"//chromeos/ash/services/hotspot_config/public/mojom",
"//chromeos/ash/services/libassistant/public/cpp:structs",
"//chromeos/ash/services/libassistant/public/mojom",
@ -3175,6 +3179,7 @@ test("ash_unittests") {
"system/network/active_network_icon_unittest.cc",
"system/network/auto_connect_notifier_unittest.cc",
"system/network/cellular_setup_notifier_unittest.cc",
"system/network/hotspot_notifier_unittest.cc",
"system/network/managed_sim_lock_notifier_unittest.cc",
"system/network/network_detailed_network_view_unittest.cc",
"system/network/network_detailed_view_controller_unittest.cc",
@ -3498,6 +3503,7 @@ test("ash_unittests") {
"//chromeos/ash/services/bluetooth_config:test_support",
"//chromeos/ash/services/federated/public/cpp",
"//chromeos/ash/services/federated/public/cpp:test_support",
"//chromeos/ash/services/hotspot_config:hotspot_config",
"//chromeos/ash/services/multidevice_setup/public/cpp:test_support",
"//chromeos/ash/services/multidevice_setup/public/mojom",
"//chromeos/ash/services/nearby/public/cpp",

@ -92,6 +92,7 @@ include_rules = [
"+chromeos/ash/services/assistant/public/cpp" ,
"+chromeos/ash/services/assistant/test_support",
"+chromeos/ash/services/bluetooth_config",
"+chromeos/ash/services/hotspot_config",
"+chromeos/ash/services/federated/public",
"+chromeos/ash/services/libassistant/public",
"+chromeos/ash/services/nearby/public",

@ -2472,6 +2472,18 @@ Connect your device to power.
<message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE" desc="Toast shown when a Bluetooth adapter is discoverable.">
"<ph name="NAME">$1<ex>Chromebook</ex></ph>" visible to Bluetooth devices.
</message>
<message name="IDS_ASH_HOTSPOT_ON_TITLE" desc="Title used for the system notification shown when the hotspot is turned on.">
Hotspot is on
</message>
<message name="IDS_ASH_HOTSPOT_OFF_TITLE" desc="Title used for the system notification shown when the hotspot is turned off.">
Hotspot is off
</message>
<message name="IDS_ASH_HOTSPOT_WIFI_TURNED_OFF_MESSAGE" desc="Message displayed in the system notification shown when WiFi is turned off upon enabling hotspot.">
We've turned off the WiFi to start using Hotspot through Mobile data. This may incur data costs.
</message>
<message name="IDS_ASH_HOTSPOT_ADMIN_RESTRICTED_MESSAGE" desc="Message displayed in the system notification shown when the hotspot is turned off by the administrator.">
Your administrator has turned Hotspot off.
</message>
<message name="IDS_ASH_NETWORK_AUTOCONNECT" desc="Text used for the toast shown when a network has been auto-connected (e.g., when an enterprise policy is applied which initiates a connection to a corporate network like Google-A).">
We've switched you to a better network
</message>

@ -0,0 +1 @@
75fbfe3701728a1fce39c3ce3f064b258d456936

@ -0,0 +1 @@
75fbfe3701728a1fce39c3ce3f064b258d456936

@ -0,0 +1 @@
dad69cfe3fea482fe4f8016432a7f3f4abeccc50

@ -0,0 +1 @@
dad69cfe3fea482fe4f8016432a7f3f4abeccc50

@ -181,7 +181,8 @@ enum class NotificationCatalogName {
kLockScreen = 166,
kWebAppSettings = 167,
kEOLIncentive = 168,
kMaxValue = kEOLIncentive
kHotspot = 169,
kMaxValue = kHotspot
};
// A living catalog that registers system nudges.

@ -0,0 +1,89 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/network/hotspot_notifier.h"
#include "ash/public/cpp/hotspot_config_service.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/strings/grit/ash_strings.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 {
// static
const char HotspotNotifier::kWiFiTurnedOffNotificationId[] =
"cros_hotspot_notifier_ids.wifi_turned_off";
const char HotspotNotifier::kAdminRestrictedNotificationId[] =
"cros_hotspot_notifier_ids.admin_restricted";
const char kNotifierHotspot[] = "ash.hotspot";
HotspotNotifier::HotspotNotifier() {
GetHotspotConfigService(
remote_cros_hotspot_config_.BindNewPipeAndPassReceiver());
remote_cros_hotspot_config_->ObserveEnabledStateChanges(
hotspot_enabled_state_observer_receiver_.BindNewPipeAndPassRemote());
}
HotspotNotifier::~HotspotNotifier() = default;
void HotspotNotifier::OnHotspotTurnedOn(bool wifi_turned_off) {
if (wifi_turned_off) {
std::unique_ptr<message_center::Notification> notification =
CreateNotification(IDS_ASH_HOTSPOT_ON_TITLE,
IDS_ASH_HOTSPOT_WIFI_TURNED_OFF_MESSAGE,
kWiFiTurnedOffNotificationId,
/*delegate=*/nullptr);
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center->RemoveNotification(kWiFiTurnedOffNotificationId,
/*by_user=*/false);
message_center->AddNotification(std::move(notification));
}
}
void HotspotNotifier::OnHotspotTurnedOff(
hotspot_config::mojom::DisableReason disable_reason) {
if (disable_reason ==
hotspot_config::mojom::DisableReason::kProhibitedByPolicy) {
std::unique_ptr<message_center::Notification> notification =
CreateNotification(IDS_ASH_HOTSPOT_OFF_TITLE,
IDS_ASH_HOTSPOT_ADMIN_RESTRICTED_MESSAGE,
kAdminRestrictedNotificationId,
/*delegate=*/nullptr);
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center->RemoveNotification(kAdminRestrictedNotificationId,
/*by_user=*/false);
message_center->AddNotification(std::move(notification));
}
}
std::unique_ptr<message_center::Notification>
HotspotNotifier::CreateNotification(
const int title_id,
const int message_id,
const char* notification_id,
scoped_refptr<message_center::NotificationDelegate> delegate) {
std::unique_ptr<message_center::Notification> notification =
ash::CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
l10n_util::GetStringUTF16(title_id),
l10n_util::GetStringUTF16(message_id),
/*display_source=*/std::u16string(), GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT, kNotifierHotspot,
NotificationCatalogName::kHotspot),
message_center::RichNotificationData(), delegate,
/*small_image=*/gfx::VectorIcon(),
message_center::SystemNotificationWarningLevel::NORMAL);
return notification;
}
} // namespace ash

@ -0,0 +1,61 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_NETWORK_HOTSPOT_NOTIFIER_H_
#define ASH_SYSTEM_NETWORK_HOTSPOT_NOTIFIER_H_
#include "ash/ash_export.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom.h"
#include "chromeos/services/network_config/public/cpp/cros_network_config_observer.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/message_center/message_center.h"
namespace ash {
// Notifies the user about following hotspot events:
// - WiFi has been turned off to enable hotspot
// - Hotspot has been disabled due to following reasons -
// - 1. Internal error
// - 2. Admin policy
// - 3. WiFi has been enabled
// - 4. In activity
// - Hotspot is turned on and has 'n' active connections
class ASH_EXPORT HotspotNotifier
: public hotspot_config::mojom::HotspotEnabledStateObserver {
public:
HotspotNotifier();
HotspotNotifier(const HotspotNotifier&) = delete;
HotspotNotifier& operator=(const HotspotNotifier&) = delete;
~HotspotNotifier() override;
static const char kWiFiTurnedOffNotificationId[];
static const char kAdminRestrictedNotificationId[];
private:
friend class HotspotNotifierTest;
// HotspotEnabledStateObserver:
void OnHotspotTurnedOn(bool wifi_turned_off) override;
void OnHotspotTurnedOff(
hotspot_config::mojom::DisableReason disable_reason) override;
std::unique_ptr<message_center::Notification> CreateNotification(
const int title_id,
const int message_id,
const char* notification_id,
scoped_refptr<message_center::NotificationDelegate> delegate);
mojo::Remote<hotspot_config::mojom::CrosHotspotConfig>
remote_cros_hotspot_config_;
mojo::Receiver<hotspot_config::mojom::HotspotEnabledStateObserver>
hotspot_enabled_state_observer_receiver_{this};
base::WeakPtrFactory<HotspotNotifier> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_NETWORK_HOTSPOT_NOTIFIER_H_

@ -0,0 +1,133 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/network/hotspot_notifier.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/hotspot_config_service.h"
#include "ash/test/ash_test_base.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_state_test_helper.h"
#include "chromeos/ash/services/hotspot_config/cros_hotspot_config.h"
#include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom.h"
#include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
namespace ash {
namespace {
const char kCellularServicePath[] = "/service/cellular0";
const char kCellularServiceGuid[] = "cellular_guid0";
const char kCellularServiceName[] = "cellular_name0";
} // namespace
class HotspotNotifierTest : public NoSessionAshTestBase {
public:
HotspotNotifierTest() {
scoped_feature_list_.InitAndEnableFeature(features::kHotspot);
}
HotspotNotifierTest(const HotspotNotifierTest&) = delete;
HotspotNotifierTest& operator=(const HotspotNotifierTest&) = delete;
~HotspotNotifierTest() override = default;
void SetUp() override {
network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
network_handler_test_helper_->AddDefaultProfiles();
network_handler_test_helper_->ResetDevicesAndServices();
GetHotspotConfigService(cros_hotspot_config_.BindNewPipeAndPassReceiver());
hotspot_notifier_ = std::make_unique<ash::HotspotNotifier>();
NoSessionAshTestBase::SetUp();
LogIn();
}
void LogIn() { SimulateUserLogin("user1@test.com"); }
void TearDown() override {
NoSessionAshTestBase::TearDown();
network_config_helper_.reset();
network_handler_test_helper_.reset();
}
void SetValidHotspotCapabilities() {
base::Value::Dict capabilities_dict;
base::Value::List upstream_list;
upstream_list.Append(shill::kTypeCellular);
capabilities_dict.Set(shill::kTetheringCapUpstreamProperty,
std::move(upstream_list));
// Add WiFi to the downstream technology list in Shill
base::Value::List downstream_list;
downstream_list.Append(shill::kTypeWifi);
capabilities_dict.Set(shill::kTetheringCapDownstreamProperty,
std::move(downstream_list));
// Add allowed WiFi security mode in Shill
base::Value::List security_list;
security_list.Append(shill::kSecurityWpa2);
security_list.Append(shill::kSecurityWpa3);
capabilities_dict.Set(shill::kTetheringCapSecurityProperty,
std::move(security_list));
network_handler_test_helper_->manager_test()->SetManagerProperty(
shill::kTetheringCapabilitiesProperty,
base::Value(std::move(capabilities_dict)));
base::RunLoop().RunUntilIdle();
}
void SetReadinessCheckResultReady() {
network_handler_test_helper_->manager_test()
->SetSimulateCheckTetheringReadinessResult(
FakeShillSimulatedResult::kSuccess,
shill::kTetheringReadinessReady);
base::RunLoop().RunUntilIdle();
}
NetworkHandlerTestHelper* helper() {
return network_handler_test_helper_.get();
}
hotspot_config::mojom::HotspotControlResult EnableHotspot() {
base::RunLoop run_loop;
hotspot_config::mojom::HotspotControlResult out_result;
cros_hotspot_config_->EnableHotspot(base::BindLambdaForTesting(
[&](hotspot_config::mojom::HotspotControlResult result) {
out_result = result;
run_loop.Quit();
}));
run_loop.Run();
return out_result;
}
void AddActiveCellularService() {
network_handler_test_helper_->service_test()->AddService(
kCellularServicePath, kCellularServiceGuid, kCellularServiceName,
shill::kTypeCellular, shill::kStateOnline, /*visible=*/true);
base::RunLoop().RunUntilIdle();
}
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
std::unique_ptr<network_config::CrosNetworkConfigTestHelper>
network_config_helper_;
std::unique_ptr<HotspotNotifier> hotspot_notifier_;
mojo::Remote<hotspot_config::mojom::CrosHotspotConfig> cros_hotspot_config_;
};
TEST_F(HotspotNotifierTest, WiFiTurnedOff) {
SetValidHotspotCapabilities();
SetReadinessCheckResultReady();
AddActiveCellularService();
helper()->manager_test()->SetSimulateTetheringEnableResult(
FakeShillSimulatedResult::kSuccess, shill::kTetheringEnableResultSuccess);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(hotspot_config::mojom::HotspotControlResult::kSuccess,
EnableHotspot());
EXPECT_TRUE(message_center::MessageCenter::Get()->FindVisibleNotificationById(
HotspotNotifier::kWiFiTurnedOffNotificationId));
}
} // namespace ash

@ -12,6 +12,7 @@
#include "ash/system/lock_screen_notification_controller.h"
#include "ash/system/network/auto_connect_notifier.h"
#include "ash/system/network/cellular_setup_notifier.h"
#include "ash/system/network/hotspot_notifier.h"
#include "ash/system/network/managed_sim_lock_notifier.h"
#include "ash/system/network/wifi_toggle_notification_controller.h"
#include "ash/system/power/power_notification_controller.h"
@ -62,6 +63,9 @@ SystemNotificationController::SystemNotificationController()
managed_sim_lock_notifier_ =
std::make_unique<ash::ManagedSimLockNotifier>();
}
if (features::IsHotspotEnabled()) {
hotspot_notifier_ = std::make_unique<ash::HotspotNotifier>();
}
}
SystemNotificationController::~SystemNotificationController() = default;

@ -18,6 +18,7 @@ class CellularSetupNotifier;
class DoNotDisturbNotificationController;
class LockScreenNotificationController;
class ManagedSimLockNotifier;
class HotspotNotifier;
class PowerNotificationController;
class PowerSoundsController;
class PrivacyHubNotificationController;
@ -63,6 +64,7 @@ class SystemNotificationController {
const std::unique_ptr<LockScreenNotificationController> lock_screen_;
// TODO(b/228093904): Make |managed_sim_lock_notifier_| const during cleanup.
std::unique_ptr<ManagedSimLockNotifier> managed_sim_lock_notifier_;
std::unique_ptr<HotspotNotifier> hotspot_notifier_;
const std::unique_ptr<PowerNotificationController> power_;
const std::unique_ptr<PowerSoundsController> power_sounds_;
std::unique_ptr<PrivacyHubNotificationController> privacy_hub_;