blob: 99c2c130986cdcdb6d07cf92bfc84e80f9e1d918 [file] [log] [blame]
[email protected]b3142722014-03-04 06:59:501// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
James Cooka35a1e22017-04-08 02:33:085#include "ash/system/bluetooth/bluetooth_notification_controller.h"
[email protected]b3142722014-03-04 06:59:506
dchenga94547472016-04-08 08:41:117#include <memory>
dcheng22724952015-12-31 03:17:548#include <utility>
9
James Cook6def4d9d2017-03-05 22:13:4710#include "ash/common/system/system_notifier.h"
thakis9532e892017-02-22 18:46:2611#include "ash/resources/grit/ash_resources.h"
jamescooke45f8112017-03-02 16:45:4212#include "ash/strings/grit/ash_strings.h"
[email protected]b3142722014-03-04 06:59:5013#include "base/bind.h"
14#include "base/callback.h"
15#include "base/logging.h"
[email protected]b3142722014-03-04 06:59:5016#include "base/strings/stringprintf.h"
17#include "base/strings/utf_string_conversions.h"
18#include "device/bluetooth/bluetooth_adapter_factory.h"
19#include "device/bluetooth/bluetooth_device.h"
[email protected]b3142722014-03-04 06:59:5020#include "ui/base/l10n/l10n_util.h"
21#include "ui/base/resource/resource_bundle.h"
22#include "ui/message_center/message_center.h"
23#include "ui/message_center/notification.h"
24#include "ui/message_center/notification_delegate.h"
25#include "ui/message_center/notification_types.h"
26
27using device::BluetoothAdapter;
28using device::BluetoothAdapterFactory;
29using device::BluetoothDevice;
30using message_center::Notification;
31
32namespace {
33
[email protected]7abf2fc2014-03-05 00:08:2534// Identifier for the discoverable notification.
35const char kBluetoothDeviceDiscoverableNotificationId[] =
36 "chrome://settings/bluetooth/discoverable";
37
[email protected]b3142722014-03-04 06:59:5038// Identifier for the pairing notification; the Bluetooth code ensures we
39// only receive one pairing request at a time, so a single id is sufficient and
40// means we "update" one notification if not handled rather than continually
41// bugging the user.
42const char kBluetoothDevicePairingNotificationId[] =
43 "chrome://settings/bluetooth/pairing";
44
[email protected]98bd3f12014-03-04 21:59:1845// Identifier for the notification that a device has been paired with the
46// system.
47const char kBluetoothDevicePairedNotificationId[] =
48 "chrome://settings/bluetooth/paired";
49
[email protected]b3142722014-03-04 06:59:5050// The BluetoothPairingNotificationDelegate handles user interaction with the
51// pairing notification and sending the confirmation, rejection or cancellation
52// back to the underlying device.
53class BluetoothPairingNotificationDelegate
54 : public message_center::NotificationDelegate {
55 public:
56 BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter,
57 const std::string& address);
58
59 protected:
dcheng222b9c72015-01-16 00:48:0160 ~BluetoothPairingNotificationDelegate() override;
[email protected]b3142722014-03-04 06:59:5061
62 // message_center::NotificationDelegate overrides.
petercf29ede82014-10-30 23:19:1963 void Close(bool by_user) override;
64 void ButtonClick(int button_index) override;
[email protected]b3142722014-03-04 06:59:5065
66 private:
67 // Buttons that appear in notifications.
jamescookb8dcef522016-06-25 14:42:5568 enum Button { BUTTON_ACCEPT, BUTTON_REJECT };
[email protected]b3142722014-03-04 06:59:5069
70 // Reference to the underlying Bluetooth Adapter, holding onto this
71 // reference ensures the adapter object doesn't go out of scope while we have
72 // a pending request and user interaction.
73 scoped_refptr<BluetoothAdapter> adapter_;
74
75 // Address of the device being paired.
76 const std::string address_;
77
78 DISALLOW_COPY_AND_ASSIGN(BluetoothPairingNotificationDelegate);
79};
80
81BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate(
82 scoped_refptr<BluetoothAdapter> adapter,
83 const std::string& address)
jamescookb8dcef522016-06-25 14:42:5584 : adapter_(adapter), address_(address) {}
[email protected]b3142722014-03-04 06:59:5085
jamescookb8dcef522016-06-25 14:42:5586BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() {}
[email protected]b3142722014-03-04 06:59:5087
[email protected]b3142722014-03-04 06:59:5088void BluetoothPairingNotificationDelegate::Close(bool by_user) {
89 VLOG(1) << "Pairing notification closed. by_user = " << by_user;
90 // Ignore notification closes generated as a result of pairing completion.
91 if (!by_user)
92 return;
93
94 // Cancel the pairing of the device, if the object still exists.
95 BluetoothDevice* device = adapter_->GetDevice(address_);
96 if (device)
97 device->CancelPairing();
98}
99
[email protected]b3142722014-03-04 06:59:50100void BluetoothPairingNotificationDelegate::ButtonClick(int button_index) {
101 VLOG(1) << "Pairing notification, button click: " << button_index;
102 // If the device object still exists, send the appropriate response either
103 // confirming or rejecting the pairing.
104 BluetoothDevice* device = adapter_->GetDevice(address_);
105 if (device) {
106 switch (button_index) {
107 case BUTTON_ACCEPT:
108 device->ConfirmPairing();
109 break;
110 case BUTTON_REJECT:
111 device->RejectPairing();
112 break;
113 }
114 }
115
116 // In any case, remove this pairing notification.
117 message_center::MessageCenter::Get()->RemoveNotification(
118 kBluetoothDevicePairingNotificationId, false /* by_user */);
119}
120
121} // namespace
122
[email protected]b3142722014-03-04 06:59:50123namespace ash {
[email protected]b3142722014-03-04 06:59:50124
125BluetoothNotificationController::BluetoothNotificationController()
126 : weak_ptr_factory_(this) {
127 BluetoothAdapterFactory::GetAdapter(
128 base::Bind(&BluetoothNotificationController::OnGetAdapter,
129 weak_ptr_factory_.GetWeakPtr()));
130}
131
132BluetoothNotificationController::~BluetoothNotificationController() {
133 if (adapter_.get()) {
134 adapter_->RemoveObserver(this);
135 adapter_->RemovePairingDelegate(this);
136 adapter_ = NULL;
137 }
138}
139
[email protected]7abf2fc2014-03-05 00:08:25140void BluetoothNotificationController::AdapterDiscoverableChanged(
141 BluetoothAdapter* adapter,
142 bool discoverable) {
143 if (discoverable) {
144 NotifyAdapterDiscoverable();
145 } else {
146 // Clear any previous discoverable notification.
147 message_center::MessageCenter::Get()->RemoveNotification(
148 kBluetoothDeviceDiscoverableNotificationId, false /* by_user */);
149 }
150}
151
[email protected]b3142722014-03-04 06:59:50152void BluetoothNotificationController::DeviceAdded(BluetoothAdapter* adapter,
153 BluetoothDevice* device) {
[email protected]98bd3f12014-03-04 21:59:18154 // Add the new device to the list of currently paired devices; it doesn't
155 // receive a notification since it's assumed it was previously notified.
156 if (device->IsPaired())
[email protected]b3142722014-03-04 06:59:50157 paired_devices_.insert(device->GetAddress());
[email protected]b3142722014-03-04 06:59:50158}
159
160void BluetoothNotificationController::DeviceChanged(BluetoothAdapter* adapter,
161 BluetoothDevice* device) {
[email protected]98bd3f12014-03-04 21:59:18162 // If the device is already in the list of paired devices, then don't
163 // notify.
[email protected]b3142722014-03-04 06:59:50164 if (paired_devices_.find(device->GetAddress()) != paired_devices_.end())
165 return;
166
[email protected]98bd3f12014-03-04 21:59:18167 // Otherwise if it's marked as paired then it must be newly paired, so
168 // notify the user about that.
[email protected]b3142722014-03-04 06:59:50169 if (device->IsPaired()) {
170 paired_devices_.insert(device->GetAddress());
171 NotifyPairedDevice(device);
172 }
173}
174
175void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter,
176 BluetoothDevice* device) {
177 paired_devices_.erase(device->GetAddress());
178}
179
[email protected]b3142722014-03-04 06:59:50180void BluetoothNotificationController::RequestPinCode(BluetoothDevice* device) {
181 // Cannot provide keyboard entry in a notification; these devices (old car
182 // audio systems for the most part) will need pairing to be initiated from
183 // the Chromebook.
184 device->CancelPairing();
185}
186
187void BluetoothNotificationController::RequestPasskey(BluetoothDevice* device) {
188 // Cannot provide keyboard entry in a notification; fortunately the spec
189 // doesn't allow for this to be an option when we're receiving the pairing
190 // request anyway.
191 device->CancelPairing();
192}
193
194void BluetoothNotificationController::DisplayPinCode(
195 BluetoothDevice* device,
196 const std::string& pincode) {
197 base::string16 message = l10n_util::GetStringFUTF16(
scheibe5ccec5c2016-06-02 20:42:49198 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE,
199 device->GetNameForDisplay(), base::UTF8ToUTF16(pincode));
[email protected]b3142722014-03-04 06:59:50200
201 NotifyPairing(device, message, false);
202}
203
204void BluetoothNotificationController::DisplayPasskey(BluetoothDevice* device,
avidb567a8a2015-12-20 17:07:24205 uint32_t passkey) {
[email protected]b3142722014-03-04 06:59:50206 base::string16 message = l10n_util::GetStringFUTF16(
scheibe5ccec5c2016-06-02 20:42:49207 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY,
208 device->GetNameForDisplay(),
209 base::UTF8ToUTF16(base::StringPrintf("%06i", passkey)));
[email protected]b3142722014-03-04 06:59:50210
211 NotifyPairing(device, message, false);
212}
213
214void BluetoothNotificationController::KeysEntered(BluetoothDevice* device,
avidb567a8a2015-12-20 17:07:24215 uint32_t entered) {
[email protected]b3142722014-03-04 06:59:50216 // Ignored since we don't have CSS in the notification to update.
217}
218
219void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice* device,
avidb567a8a2015-12-20 17:07:24220 uint32_t passkey) {
[email protected]b3142722014-03-04 06:59:50221 base::string16 message = l10n_util::GetStringFUTF16(
scheibe5ccec5c2016-06-02 20:42:49222 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY,
223 device->GetNameForDisplay(),
224 base::UTF8ToUTF16(base::StringPrintf("%06i", passkey)));
[email protected]b3142722014-03-04 06:59:50225
226 NotifyPairing(device, message, true);
227}
228
229void BluetoothNotificationController::AuthorizePairing(
230 BluetoothDevice* device) {
231 base::string16 message = l10n_util::GetStringFUTF16(
scheibe5ccec5c2016-06-02 20:42:49232 IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING,
233 device->GetNameForDisplay());
[email protected]b3142722014-03-04 06:59:50234
235 NotifyPairing(device, message, true);
236}
237
[email protected]b3142722014-03-04 06:59:50238void BluetoothNotificationController::OnGetAdapter(
239 scoped_refptr<BluetoothAdapter> adapter) {
240 DCHECK(!adapter_.get());
241 adapter_ = adapter;
242 adapter_->AddObserver(this);
243 adapter_->AddPairingDelegate(this,
244 BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
245
[email protected]7abf2fc2014-03-05 00:08:25246 // Notify a user if the adapter is already in the discoverable state.
247 if (adapter_->IsDiscoverable())
248 NotifyAdapterDiscoverable();
249
[email protected]b3142722014-03-04 06:59:50250 // Build a list of the currently paired devices; these don't receive
251 // notifications since it's assumed they were previously notified.
252 BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
253 for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin();
254 iter != devices.end(); ++iter) {
255 const BluetoothDevice* device = *iter;
256 if (device->IsPaired())
257 paired_devices_.insert(device->GetAddress());
258 }
259}
260
[email protected]7abf2fc2014-03-05 00:08:25261void BluetoothNotificationController::NotifyAdapterDiscoverable() {
262 message_center::RichNotificationData optional;
263
264 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
265
dchenga94547472016-04-08 08:41:11266 std::unique_ptr<Notification> notification(new Notification(
[email protected]7abf2fc2014-03-05 00:08:25267 message_center::NOTIFICATION_TYPE_SIMPLE,
miguelg53a6dde52015-08-20 09:00:30268 kBluetoothDeviceDiscoverableNotificationId, base::string16() /* title */,
269 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE,
270 base::UTF8ToUTF16(adapter_->GetName()),
271 base::UTF8ToUTF16(adapter_->GetAddress())),
[email protected]21a838472014-06-11 13:40:37272 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
miguelg53a6dde52015-08-20 09:00:30273 base::string16() /* display source */, GURL(),
274 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
275 system_notifier::kNotifierBluetooth),
276 optional, NULL));
dcheng22724952015-12-31 03:17:54277 message_center::MessageCenter::Get()->AddNotification(
278 std::move(notification));
[email protected]7abf2fc2014-03-05 00:08:25279}
280
[email protected]b3142722014-03-04 06:59:50281void BluetoothNotificationController::NotifyPairing(
282 BluetoothDevice* device,
283 const base::string16& message,
284 bool with_buttons) {
285 message_center::RichNotificationData optional;
286 if (with_buttons) {
287 optional.buttons.push_back(message_center::ButtonInfo(
jamescookb8dcef522016-06-25 14:42:55288 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT)));
[email protected]b3142722014-03-04 06:59:50289 optional.buttons.push_back(message_center::ButtonInfo(
jamescookb8dcef522016-06-25 14:42:55290 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT)));
[email protected]b3142722014-03-04 06:59:50291 }
292
293 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
294
dchenga94547472016-04-08 08:41:11295 std::unique_ptr<Notification> notification(new Notification(
[email protected]b3142722014-03-04 06:59:50296 message_center::NOTIFICATION_TYPE_SIMPLE,
miguelg53a6dde52015-08-20 09:00:30297 kBluetoothDevicePairingNotificationId, base::string16() /* title */,
298 message, bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
299 base::string16() /* display source */, GURL(),
300 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
301 system_notifier::kNotifierBluetooth),
302 optional, new BluetoothPairingNotificationDelegate(
303 adapter_, device->GetAddress())));
dcheng22724952015-12-31 03:17:54304 message_center::MessageCenter::Get()->AddNotification(
305 std::move(notification));
[email protected]b3142722014-03-04 06:59:50306}
307
308void BluetoothNotificationController::NotifyPairedDevice(
309 BluetoothDevice* device) {
310 // Remove the currently presented pairing notification; since only one
311 // pairing request is queued at a time, this is guaranteed to be the device
312 // that just became paired.
313 message_center::MessageCenter::Get()->RemoveNotification(
314 kBluetoothDevicePairingNotificationId, false /* by_user */);
[email protected]98bd3f12014-03-04 21:59:18315
316 message_center::RichNotificationData optional;
317
318 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
319
dchenga94547472016-04-08 08:41:11320 std::unique_ptr<Notification> notification(new Notification(
[email protected]98bd3f12014-03-04 21:59:18321 message_center::NOTIFICATION_TYPE_SIMPLE,
miguelg53a6dde52015-08-20 09:00:30322 kBluetoothDevicePairedNotificationId, base::string16() /* title */,
323 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED,
scheibe5ccec5c2016-06-02 20:42:49324 device->GetNameForDisplay()),
[email protected]21a838472014-06-11 13:40:37325 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
miguelg53a6dde52015-08-20 09:00:30326 base::string16() /* display source */, GURL(),
327 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
328 system_notifier::kNotifierBluetooth),
329 optional, NULL));
dcheng22724952015-12-31 03:17:54330 message_center::MessageCenter::Get()->AddNotification(
331 std::move(notification));
[email protected]b3142722014-03-04 06:59:50332}
333
[email protected]b3142722014-03-04 06:59:50334} // namespace ash