blob: b0efc48793e4f56770eff706029d4a9321ac43eb [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
5#include "ash/system/chromeos/bluetooth/bluetooth_notification_controller.h"
6
7#include "ash/system/system_notifier.h"
8#include "base/bind.h"
9#include "base/callback.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14#include "device/bluetooth/bluetooth_adapter_factory.h"
15#include "device/bluetooth/bluetooth_device.h"
16#include "grit/ash_resources.h"
17#include "grit/ash_strings.h"
18#include "ui/base/l10n/l10n_util.h"
19#include "ui/base/resource/resource_bundle.h"
20#include "ui/message_center/message_center.h"
21#include "ui/message_center/notification.h"
22#include "ui/message_center/notification_delegate.h"
23#include "ui/message_center/notification_types.h"
24
25using device::BluetoothAdapter;
26using device::BluetoothAdapterFactory;
27using device::BluetoothDevice;
28using message_center::Notification;
29
30namespace {
31
[email protected]7abf2fc2014-03-05 00:08:2532// Identifier for the discoverable notification.
33const char kBluetoothDeviceDiscoverableNotificationId[] =
34 "chrome://settings/bluetooth/discoverable";
35
[email protected]b3142722014-03-04 06:59:5036// Identifier for the pairing notification; the Bluetooth code ensures we
37// only receive one pairing request at a time, so a single id is sufficient and
38// means we "update" one notification if not handled rather than continually
39// bugging the user.
40const char kBluetoothDevicePairingNotificationId[] =
41 "chrome://settings/bluetooth/pairing";
42
[email protected]98bd3f12014-03-04 21:59:1843// Identifier for the notification that a device has been paired with the
44// system.
45const char kBluetoothDevicePairedNotificationId[] =
46 "chrome://settings/bluetooth/paired";
47
[email protected]b3142722014-03-04 06:59:5048// The BluetoothPairingNotificationDelegate handles user interaction with the
49// pairing notification and sending the confirmation, rejection or cancellation
50// back to the underlying device.
51class BluetoothPairingNotificationDelegate
52 : public message_center::NotificationDelegate {
53 public:
54 BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter,
55 const std::string& address);
56
57 protected:
dcheng222b9c72015-01-16 00:48:0158 ~BluetoothPairingNotificationDelegate() override;
[email protected]b3142722014-03-04 06:59:5059
60 // message_center::NotificationDelegate overrides.
petercf29ede82014-10-30 23:19:1961 void Close(bool by_user) override;
62 void ButtonClick(int button_index) override;
[email protected]b3142722014-03-04 06:59:5063
64 private:
65 // Buttons that appear in notifications.
66 enum Button {
67 BUTTON_ACCEPT,
68 BUTTON_REJECT
69 };
70
71 // Reference to the underlying Bluetooth Adapter, holding onto this
72 // reference ensures the adapter object doesn't go out of scope while we have
73 // a pending request and user interaction.
74 scoped_refptr<BluetoothAdapter> adapter_;
75
76 // Address of the device being paired.
77 const std::string address_;
78
79 DISALLOW_COPY_AND_ASSIGN(BluetoothPairingNotificationDelegate);
80};
81
82BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate(
83 scoped_refptr<BluetoothAdapter> adapter,
84 const std::string& address)
85 : adapter_(adapter),
86 address_(address) {
87}
88
89BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() {
90}
91
[email protected]b3142722014-03-04 06:59:5092void BluetoothPairingNotificationDelegate::Close(bool by_user) {
93 VLOG(1) << "Pairing notification closed. by_user = " << by_user;
94 // Ignore notification closes generated as a result of pairing completion.
95 if (!by_user)
96 return;
97
98 // Cancel the pairing of the device, if the object still exists.
99 BluetoothDevice* device = adapter_->GetDevice(address_);
100 if (device)
101 device->CancelPairing();
102}
103
[email protected]b3142722014-03-04 06:59:50104void BluetoothPairingNotificationDelegate::ButtonClick(int button_index) {
105 VLOG(1) << "Pairing notification, button click: " << button_index;
106 // If the device object still exists, send the appropriate response either
107 // confirming or rejecting the pairing.
108 BluetoothDevice* device = adapter_->GetDevice(address_);
109 if (device) {
110 switch (button_index) {
111 case BUTTON_ACCEPT:
112 device->ConfirmPairing();
113 break;
114 case BUTTON_REJECT:
115 device->RejectPairing();
116 break;
117 }
118 }
119
120 // In any case, remove this pairing notification.
121 message_center::MessageCenter::Get()->RemoveNotification(
122 kBluetoothDevicePairingNotificationId, false /* by_user */);
123}
124
125} // namespace
126
127
128namespace ash {
[email protected]b3142722014-03-04 06:59:50129
130BluetoothNotificationController::BluetoothNotificationController()
131 : weak_ptr_factory_(this) {
132 BluetoothAdapterFactory::GetAdapter(
133 base::Bind(&BluetoothNotificationController::OnGetAdapter,
134 weak_ptr_factory_.GetWeakPtr()));
135}
136
137BluetoothNotificationController::~BluetoothNotificationController() {
138 if (adapter_.get()) {
139 adapter_->RemoveObserver(this);
140 adapter_->RemovePairingDelegate(this);
141 adapter_ = NULL;
142 }
143}
144
145
[email protected]7abf2fc2014-03-05 00:08:25146void BluetoothNotificationController::AdapterDiscoverableChanged(
147 BluetoothAdapter* adapter,
148 bool discoverable) {
149 if (discoverable) {
150 NotifyAdapterDiscoverable();
151 } else {
152 // Clear any previous discoverable notification.
153 message_center::MessageCenter::Get()->RemoveNotification(
154 kBluetoothDeviceDiscoverableNotificationId, false /* by_user */);
155 }
156}
157
[email protected]b3142722014-03-04 06:59:50158void BluetoothNotificationController::DeviceAdded(BluetoothAdapter* adapter,
159 BluetoothDevice* device) {
[email protected]98bd3f12014-03-04 21:59:18160 // Add the new device to the list of currently paired devices; it doesn't
161 // receive a notification since it's assumed it was previously notified.
162 if (device->IsPaired())
[email protected]b3142722014-03-04 06:59:50163 paired_devices_.insert(device->GetAddress());
[email protected]b3142722014-03-04 06:59:50164}
165
166void BluetoothNotificationController::DeviceChanged(BluetoothAdapter* adapter,
167 BluetoothDevice* device) {
[email protected]98bd3f12014-03-04 21:59:18168 // If the device is already in the list of paired devices, then don't
169 // notify.
[email protected]b3142722014-03-04 06:59:50170 if (paired_devices_.find(device->GetAddress()) != paired_devices_.end())
171 return;
172
[email protected]98bd3f12014-03-04 21:59:18173 // Otherwise if it's marked as paired then it must be newly paired, so
174 // notify the user about that.
[email protected]b3142722014-03-04 06:59:50175 if (device->IsPaired()) {
176 paired_devices_.insert(device->GetAddress());
177 NotifyPairedDevice(device);
178 }
179}
180
181void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter,
182 BluetoothDevice* device) {
183 paired_devices_.erase(device->GetAddress());
184}
185
186
187void BluetoothNotificationController::RequestPinCode(BluetoothDevice* device) {
188 // Cannot provide keyboard entry in a notification; these devices (old car
189 // audio systems for the most part) will need pairing to be initiated from
190 // the Chromebook.
191 device->CancelPairing();
192}
193
194void BluetoothNotificationController::RequestPasskey(BluetoothDevice* device) {
195 // Cannot provide keyboard entry in a notification; fortunately the spec
196 // doesn't allow for this to be an option when we're receiving the pairing
197 // request anyway.
198 device->CancelPairing();
199}
200
201void BluetoothNotificationController::DisplayPinCode(
202 BluetoothDevice* device,
203 const std::string& pincode) {
204 base::string16 message = l10n_util::GetStringFUTF16(
205 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE,
206 device->GetName(), base::UTF8ToUTF16(pincode));
207
208 NotifyPairing(device, message, false);
209}
210
211void BluetoothNotificationController::DisplayPasskey(BluetoothDevice* device,
212 uint32 passkey) {
213 base::string16 message = l10n_util::GetStringFUTF16(
214 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY,
215 device->GetName(), base::UTF8ToUTF16(
216 base::StringPrintf("%06i", passkey)));
217
218 NotifyPairing(device, message, false);
219}
220
221void BluetoothNotificationController::KeysEntered(BluetoothDevice* device,
222 uint32 entered) {
223 // Ignored since we don't have CSS in the notification to update.
224}
225
226void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice* device,
227 uint32 passkey) {
228 base::string16 message = l10n_util::GetStringFUTF16(
229 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY,
230 device->GetName(), base::UTF8ToUTF16(
231 base::StringPrintf("%06i", passkey)));
232
233 NotifyPairing(device, message, true);
234}
235
236void BluetoothNotificationController::AuthorizePairing(
237 BluetoothDevice* device) {
238 base::string16 message = l10n_util::GetStringFUTF16(
239 IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING,
240 device->GetName());
241
242 NotifyPairing(device, message, true);
243}
244
245
246void BluetoothNotificationController::OnGetAdapter(
247 scoped_refptr<BluetoothAdapter> adapter) {
248 DCHECK(!adapter_.get());
249 adapter_ = adapter;
250 adapter_->AddObserver(this);
251 adapter_->AddPairingDelegate(this,
252 BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
253
[email protected]7abf2fc2014-03-05 00:08:25254 // Notify a user if the adapter is already in the discoverable state.
255 if (adapter_->IsDiscoverable())
256 NotifyAdapterDiscoverable();
257
[email protected]b3142722014-03-04 06:59:50258 // Build a list of the currently paired devices; these don't receive
259 // notifications since it's assumed they were previously notified.
260 BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
261 for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin();
262 iter != devices.end(); ++iter) {
263 const BluetoothDevice* device = *iter;
264 if (device->IsPaired())
265 paired_devices_.insert(device->GetAddress());
266 }
267}
268
269
[email protected]7abf2fc2014-03-05 00:08:25270void BluetoothNotificationController::NotifyAdapterDiscoverable() {
271 message_center::RichNotificationData optional;
272
273 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
274
275 scoped_ptr<Notification> notification(new Notification(
276 message_center::NOTIFICATION_TYPE_SIMPLE,
miguelg53a6dde52015-08-20 09:00:30277 kBluetoothDeviceDiscoverableNotificationId, base::string16() /* title */,
278 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE,
279 base::UTF8ToUTF16(adapter_->GetName()),
280 base::UTF8ToUTF16(adapter_->GetAddress())),
[email protected]21a838472014-06-11 13:40:37281 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
miguelg53a6dde52015-08-20 09:00:30282 base::string16() /* display source */, GURL(),
283 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
284 system_notifier::kNotifierBluetooth),
285 optional, NULL));
[email protected]7abf2fc2014-03-05 00:08:25286 message_center::MessageCenter::Get()->AddNotification(notification.Pass());
287}
288
[email protected]b3142722014-03-04 06:59:50289void BluetoothNotificationController::NotifyPairing(
290 BluetoothDevice* device,
291 const base::string16& message,
292 bool with_buttons) {
293 message_center::RichNotificationData optional;
294 if (with_buttons) {
295 optional.buttons.push_back(message_center::ButtonInfo(
296 l10n_util::GetStringUTF16(
297 IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT)));
298 optional.buttons.push_back(message_center::ButtonInfo(
299 l10n_util::GetStringUTF16(
300 IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT)));
301 }
302
303 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
304
305 scoped_ptr<Notification> notification(new Notification(
306 message_center::NOTIFICATION_TYPE_SIMPLE,
miguelg53a6dde52015-08-20 09:00:30307 kBluetoothDevicePairingNotificationId, base::string16() /* title */,
308 message, bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
309 base::string16() /* display source */, GURL(),
310 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
311 system_notifier::kNotifierBluetooth),
312 optional, new BluetoothPairingNotificationDelegate(
313 adapter_, device->GetAddress())));
[email protected]b3142722014-03-04 06:59:50314 message_center::MessageCenter::Get()->AddNotification(notification.Pass());
315}
316
317void BluetoothNotificationController::NotifyPairedDevice(
318 BluetoothDevice* device) {
319 // Remove the currently presented pairing notification; since only one
320 // pairing request is queued at a time, this is guaranteed to be the device
321 // that just became paired.
322 message_center::MessageCenter::Get()->RemoveNotification(
323 kBluetoothDevicePairingNotificationId, false /* by_user */);
[email protected]98bd3f12014-03-04 21:59:18324
325 message_center::RichNotificationData optional;
326
327 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
328
329 scoped_ptr<Notification> notification(new Notification(
330 message_center::NOTIFICATION_TYPE_SIMPLE,
miguelg53a6dde52015-08-20 09:00:30331 kBluetoothDevicePairedNotificationId, base::string16() /* title */,
332 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED,
333 device->GetName()),
[email protected]21a838472014-06-11 13:40:37334 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
miguelg53a6dde52015-08-20 09:00:30335 base::string16() /* display source */, GURL(),
336 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
337 system_notifier::kNotifierBluetooth),
338 optional, NULL));
[email protected]98bd3f12014-03-04 21:59:18339 message_center::MessageCenter::Get()->AddNotification(notification.Pass());
[email protected]b3142722014-03-04 06:59:50340}
341
[email protected]b3142722014-03-04 06:59:50342} // namespace ash