123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- // Copyright 2014 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/bluetooth/bluetooth_notification_controller.h"
- #include <memory>
- #include <utility>
- #include "ash/constants/notifier_catalogs.h"
- #include "ash/public/cpp/nearby_share_delegate.h"
- #include "ash/public/cpp/notification_utils.h"
- #include "ash/public/cpp/system/toast_data.h"
- #include "ash/public/cpp/system_tray_client.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/services/nearby/public/cpp/nearby_client_uuids.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 "ash/system/toast/toast_manager_impl.h"
- #include "ash/system/tray/tray_popup_utils.h"
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/logging.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/strings/utf_string_conversions.h"
- #include "device/bluetooth/bluetooth_adapter_factory.h"
- #include "device/bluetooth/bluetooth_device.h"
- #include "device/bluetooth/chromeos/bluetooth_utils.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/message_center/message_center.h"
- #include "ui/message_center/public/cpp/notification.h"
- #include "ui/message_center/public/cpp/notification_delegate.h"
- using device::BluetoothAdapter;
- using device::BluetoothAdapterFactory;
- using device::BluetoothDevice;
- using message_center::MessageCenter;
- using message_center::Notification;
- namespace ash {
- namespace {
- const char kNotifierBluetooth[] = "ash.bluetooth";
- const char kPairedNotificationPrefix[] =
- "cros_bluetooth_device_paired_notification_id-";
- // The BluetoothPairingNotificationDelegate handles user interaction with the
- // pairing notification and sending the confirmation, rejection or cancellation
- // back to the underlying device.
- class BluetoothPairingNotificationDelegate
- : public message_center::NotificationDelegate {
- public:
- BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter,
- const std::string& address,
- const std::string& notification_id);
- BluetoothPairingNotificationDelegate(
- const BluetoothPairingNotificationDelegate&) = delete;
- BluetoothPairingNotificationDelegate& operator=(
- const BluetoothPairingNotificationDelegate&) = delete;
- protected:
- ~BluetoothPairingNotificationDelegate() override;
- // message_center::NotificationDelegate overrides.
- void Close(bool by_user) override;
- void Click(const absl::optional<int>& button_index,
- const absl::optional<std::u16string>& reply) override;
- private:
- // Buttons that appear in notifications.
- enum Button { BUTTON_ACCEPT, BUTTON_REJECT };
- // Reference to the underlying Bluetooth Adapter, holding onto this
- // reference ensures the adapter object doesn't go out of scope while we have
- // a pending request and user interaction.
- scoped_refptr<BluetoothAdapter> adapter_;
- // Address of the device being paired.
- const std::string address_;
- const std::string notification_id_;
- };
- BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate(
- scoped_refptr<BluetoothAdapter> adapter,
- const std::string& address,
- const std::string& notification_id)
- : adapter_(adapter), address_(address), notification_id_(notification_id) {}
- BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() =
- default;
- void BluetoothPairingNotificationDelegate::Close(bool by_user) {
- VLOG(1) << "Pairing notification closed. by_user = " << by_user;
- // Ignore notification closes generated as a result of pairing completion.
- if (!by_user)
- return;
- // Cancel the pairing of the device, if the object still exists.
- BluetoothDevice* device = adapter_->GetDevice(address_);
- if (device)
- device->CancelPairing();
- }
- void BluetoothPairingNotificationDelegate::Click(
- const absl::optional<int>& button_index,
- const absl::optional<std::u16string>& reply) {
- if (!button_index)
- return;
- VLOG(1) << "Pairing notification, button click: " << *button_index;
- // If the device object still exists, send the appropriate response either
- // confirming or rejecting the pairing.
- BluetoothDevice* device = adapter_->GetDevice(address_);
- if (device) {
- switch (*button_index) {
- case BUTTON_ACCEPT:
- device->ConfirmPairing();
- break;
- case BUTTON_REJECT:
- device->RejectPairing();
- break;
- }
- }
- // In any case, remove this pairing notification.
- MessageCenter::Get()->RemoveNotification(notification_id_,
- false /* by_user */);
- }
- void ShowToast(const std::string& id,
- ToastCatalogName catalog_name,
- const std::u16string& text) {
- ash::ToastManager::Get()->Show(ash::ToastData(id, catalog_name, text));
- }
- } // namespace
- const char
- BluetoothNotificationController::kBluetoothDeviceDiscoverableToastId[] =
- "cros_bluetooth_device_discoverable_toast_id";
- const char
- BluetoothNotificationController::kBluetoothDevicePairingNotificationId[] =
- "cros_bluetooth_device_pairing_notification_id";
- // This class handles opening the Bluetooth Settings UI when the user clicks
- // on the Paired Notification.
- class BluetoothNotificationController::BluetoothPairedNotificationDelegate
- : public message_center::NotificationDelegate {
- public:
- BluetoothPairedNotificationDelegate() = default;
- BluetoothPairedNotificationDelegate(
- const BluetoothPairedNotificationDelegate&) = delete;
- BluetoothPairedNotificationDelegate& operator=(
- const BluetoothPairedNotificationDelegate&) = delete;
- protected:
- ~BluetoothPairedNotificationDelegate() override = default;
- // message_center::NotificationDelegate:
- void Click(const absl::optional<int>& button_index,
- const absl::optional<std::u16string>& reply) override {
- if (TrayPopupUtils::CanOpenWebUISettings())
- Shell::Get()->system_tray_model()->client()->ShowBluetoothSettings();
- }
- };
- BluetoothNotificationController::BluetoothNotificationController(
- message_center::MessageCenter* message_center)
- : message_center_(message_center) {
- BluetoothAdapterFactory::Get()->GetAdapter(
- base::BindOnce(&BluetoothNotificationController::OnGetAdapter,
- weak_ptr_factory_.GetWeakPtr()));
- }
- BluetoothNotificationController::~BluetoothNotificationController() {
- if (adapter_.get()) {
- adapter_->RemoveObserver(this);
- adapter_->RemovePairingDelegate(this);
- adapter_.reset();
- }
- }
- void BluetoothNotificationController::AdapterDiscoverableChanged(
- BluetoothAdapter* adapter,
- bool discoverable) {
- if (discoverable)
- NotifyAdapterDiscoverable();
- }
- void BluetoothNotificationController::DeviceAdded(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- // Add the new device to the list of currently paired devices; it doesn't
- // receive a notification since it's assumed it was previously notified.
- if (device->IsPaired())
- paired_devices_.insert(device->GetAddress());
- }
- void BluetoothNotificationController::DeviceChanged(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- // If the device is already in the list of paired devices, then don't
- // notify.
- if (paired_devices_.find(device->GetAddress()) != paired_devices_.end())
- return;
- // Otherwise if it's marked as paired then it must be newly paired, so
- // notify the user about that.
- if (device->IsPaired()) {
- paired_devices_.insert(device->GetAddress());
- NotifyPairedDevice(device);
- }
- }
- void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- paired_devices_.erase(device->GetAddress());
- }
- void BluetoothNotificationController::RequestPinCode(BluetoothDevice* device) {
- // Cannot provide keyboard entry in a notification; these devices (old car
- // audio systems for the most part) will need pairing to be initiated from
- // the Chromebook.
- device->CancelPairing();
- }
- void BluetoothNotificationController::RequestPasskey(BluetoothDevice* device) {
- // Cannot provide keyboard entry in a notification; fortunately the spec
- // doesn't allow for this to be an option when we're receiving the pairing
- // request anyway.
- device->CancelPairing();
- }
- void BluetoothNotificationController::DisplayPinCode(
- BluetoothDevice* device,
- const std::string& pincode) {
- std::u16string message = l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE,
- device->GetNameForDisplay(), base::UTF8ToUTF16(pincode));
- NotifyPairing(device, message, false);
- }
- void BluetoothNotificationController::DisplayPasskey(BluetoothDevice* device,
- uint32_t passkey) {
- std::u16string message = l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY,
- device->GetNameForDisplay(),
- base::UTF8ToUTF16(base::StringPrintf("%06i", passkey)));
- NotifyPairing(device, message, false);
- }
- void BluetoothNotificationController::KeysEntered(BluetoothDevice* device,
- uint32_t entered) {
- // Ignored since we don't have CSS in the notification to update.
- }
- void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice* device,
- uint32_t passkey) {
- std::u16string message = l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY,
- device->GetNameForDisplay(),
- base::UTF8ToUTF16(base::StringPrintf("%06i", passkey)));
- NotifyPairing(device, message, true);
- }
- void BluetoothNotificationController::AuthorizePairing(
- BluetoothDevice* device) {
- std::u16string message = l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING,
- device->GetNameForDisplay());
- NotifyPairing(device, message, true);
- }
- // static
- std::string BluetoothNotificationController::GetPairedNotificationId(
- const BluetoothDevice* device) {
- return kPairedNotificationPrefix + base::ToLowerASCII(device->GetAddress());
- }
- void BluetoothNotificationController::OnGetAdapter(
- scoped_refptr<BluetoothAdapter> adapter) {
- DCHECK(!adapter_.get());
- adapter_ = adapter;
- adapter_->AddObserver(this);
- adapter_->AddPairingDelegate(this,
- BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
- // Notify a user if the adapter is already in the discoverable state.
- if (adapter_->IsDiscoverable())
- NotifyAdapterDiscoverable();
- // Build a list of the currently paired devices; these don't receive
- // notifications since it's assumed they were previously notified.
- BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
- for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin();
- iter != devices.end(); ++iter) {
- const BluetoothDevice* device = *iter;
- if (device->IsPaired())
- paired_devices_.insert(device->GetAddress());
- }
- }
- void BluetoothNotificationController::NotifyAdapterDiscoverable() {
- // Do not show toast in kiosk app mode.
- if (Shell::Get()->session_controller()->IsRunningInAppMode())
- return;
- // If Nearby Share has made the local device discoverable, do not
- // unnecessarily display this toast.
- // TODO(crbug.com/1155669): Generalize this logic to prevent leaking Nearby
- // implementation details.
- auto* nearby_share_delegate = Shell::Get()->nearby_share_delegate();
- if (nearby_share_delegate &&
- (nearby_share_delegate->IsEnableHighVisibilityRequestActive() ||
- nearby_share_delegate->IsHighVisibilityOn())) {
- return;
- }
- ShowToast(
- kBluetoothDeviceDiscoverableToastId,
- ToastCatalogName::kBluetoothAdapterDiscoverable,
- l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE,
- base::UTF8ToUTF16(adapter_->GetName())));
- }
- void BluetoothNotificationController::NotifyPairing(
- BluetoothDevice* device,
- const std::u16string& message,
- bool with_buttons) {
- message_center::RichNotificationData optional;
- if (with_buttons) {
- optional.buttons.push_back(message_center::ButtonInfo(
- l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT)));
- optional.buttons.push_back(message_center::ButtonInfo(
- l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT)));
- }
- std::unique_ptr<Notification> notification = CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE,
- kBluetoothDevicePairingNotificationId, std::u16string() /* title */,
- message, std::u16string() /* display source */, GURL(),
- message_center::NotifierId(
- message_center::NotifierType::SYSTEM_COMPONENT, kNotifierBluetooth,
- NotificationCatalogName::kBluetoothPairingRequest),
- optional,
- base::MakeRefCounted<BluetoothPairingNotificationDelegate>(
- adapter_, device->GetAddress(),
- kBluetoothDevicePairingNotificationId),
- kNotificationBluetoothIcon,
- message_center::SystemNotificationWarningLevel::NORMAL);
- message_center_->AddNotification(std::move(notification));
- }
- void BluetoothNotificationController::NotifyPairedDevice(
- BluetoothDevice* device) {
- // Remove the currently presented pairing notification; since only one
- // pairing request is queued at a time, this is guaranteed to be the device
- // that just became paired.
- if (message_center_->FindVisibleNotificationById(
- kBluetoothDevicePairingNotificationId)) {
- message_center_->RemoveNotification(kBluetoothDevicePairingNotificationId,
- false /* by_user */);
- }
- // If the newly paired device is connected via a Nearby Connections client
- // (e.g., Nearby Share), do not display this notification.
- // TODO(crbug.com/1155669): Generalize this logic to prevent leaking Nearby
- // implementation details.
- for (const auto& uuid : device->GetUUIDs()) {
- if (nearby::IsNearbyClientUuid(uuid)) {
- return;
- }
- }
- // If bluetooth revamp flag is on, do not show a notification, this is
- // because notifications will be handled by BluetoothDeviceStatusUiHandler
- // when flag is on.
- if (ash::features::IsBluetoothRevampEnabled()) {
- return;
- }
- std::unique_ptr<Notification> notification = CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE, GetPairedNotificationId(device),
- std::u16string() /* title */,
- l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED,
- device->GetNameForDisplay()),
- std::u16string() /* display source */, GURL(),
- message_center::NotifierId(
- message_center::NotifierType::SYSTEM_COMPONENT, kNotifierBluetooth,
- NotificationCatalogName::kBluetoothPairedDevice),
- message_center::RichNotificationData(),
- base::MakeRefCounted<BluetoothPairedNotificationDelegate>(),
- kNotificationBluetoothIcon,
- message_center::SystemNotificationWarningLevel::NORMAL);
- message_center_->AddNotification(std::move(notification));
- device::RecordUiSurfaceDisplayed(
- device::BluetoothUiSurface::kPairedNotification);
- }
- } // namespace ash
|