• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
25 using device::BluetoothAdapter;
26 using device::BluetoothAdapterFactory;
27 using device::BluetoothDevice;
28 using message_center::Notification;
29 
30 namespace {
31 
32 // Identifier for the discoverable notification.
33 const char kBluetoothDeviceDiscoverableNotificationId[] =
34     "chrome://settings/bluetooth/discoverable";
35 
36 // 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.
40 const char kBluetoothDevicePairingNotificationId[] =
41     "chrome://settings/bluetooth/pairing";
42 
43 // Identifier for the notification that a device has been paired with the
44 // system.
45 const char kBluetoothDevicePairedNotificationId[] =
46     "chrome://settings/bluetooth/paired";
47 
48 // The BluetoothPairingNotificationDelegate handles user interaction with the
49 // pairing notification and sending the confirmation, rejection or cancellation
50 // back to the underlying device.
51 class BluetoothPairingNotificationDelegate
52     : public message_center::NotificationDelegate {
53  public:
54   BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter,
55                                        const std::string& address);
56 
57  protected:
58   virtual ~BluetoothPairingNotificationDelegate();
59 
60   // message_center::NotificationDelegate overrides.
61   virtual void Display() OVERRIDE;
62   virtual void Error() OVERRIDE;
63   virtual void Close(bool by_user) OVERRIDE;
64   virtual bool HasClickedListener() OVERRIDE;
65   virtual void Click() OVERRIDE;
66   virtual void ButtonClick(int button_index) OVERRIDE;
67 
68  private:
69   // Buttons that appear in notifications.
70   enum Button {
71     BUTTON_ACCEPT,
72     BUTTON_REJECT
73   };
74 
75   // Reference to the underlying Bluetooth Adapter, holding onto this
76   // reference ensures the adapter object doesn't go out of scope while we have
77   // a pending request and user interaction.
78   scoped_refptr<BluetoothAdapter> adapter_;
79 
80   // Address of the device being paired.
81   const std::string address_;
82 
83   DISALLOW_COPY_AND_ASSIGN(BluetoothPairingNotificationDelegate);
84 };
85 
BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter,const std::string & address)86 BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate(
87     scoped_refptr<BluetoothAdapter> adapter,
88     const std::string& address)
89     : adapter_(adapter),
90       address_(address) {
91 }
92 
~BluetoothPairingNotificationDelegate()93 BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() {
94 }
95 
Display()96 void BluetoothPairingNotificationDelegate::Display() {
97 }
98 
Error()99 void BluetoothPairingNotificationDelegate::Error() {
100 }
101 
Close(bool by_user)102 void BluetoothPairingNotificationDelegate::Close(bool by_user) {
103   VLOG(1) << "Pairing notification closed. by_user = " << by_user;
104   // Ignore notification closes generated as a result of pairing completion.
105   if (!by_user)
106     return;
107 
108   // Cancel the pairing of the device, if the object still exists.
109   BluetoothDevice* device = adapter_->GetDevice(address_);
110   if (device)
111     device->CancelPairing();
112 }
113 
HasClickedListener()114 bool BluetoothPairingNotificationDelegate::HasClickedListener() {
115   return false;
116 }
117 
Click()118 void BluetoothPairingNotificationDelegate::Click() {
119 }
120 
ButtonClick(int button_index)121 void BluetoothPairingNotificationDelegate::ButtonClick(int button_index) {
122   VLOG(1) << "Pairing notification, button click: " << button_index;
123   // If the device object still exists, send the appropriate response either
124   // confirming or rejecting the pairing.
125   BluetoothDevice* device = adapter_->GetDevice(address_);
126   if (device) {
127     switch (button_index) {
128       case BUTTON_ACCEPT:
129         device->ConfirmPairing();
130         break;
131       case BUTTON_REJECT:
132         device->RejectPairing();
133         break;
134     }
135   }
136 
137   // In any case, remove this pairing notification.
138   message_center::MessageCenter::Get()->RemoveNotification(
139       kBluetoothDevicePairingNotificationId, false /* by_user */);
140 }
141 
142 }  // namespace
143 
144 
145 namespace ash {
146 
BluetoothNotificationController()147 BluetoothNotificationController::BluetoothNotificationController()
148     : weak_ptr_factory_(this) {
149   BluetoothAdapterFactory::GetAdapter(
150       base::Bind(&BluetoothNotificationController::OnGetAdapter,
151                  weak_ptr_factory_.GetWeakPtr()));
152 }
153 
~BluetoothNotificationController()154 BluetoothNotificationController::~BluetoothNotificationController() {
155   if (adapter_.get()) {
156     adapter_->RemoveObserver(this);
157     adapter_->RemovePairingDelegate(this);
158     adapter_ = NULL;
159   }
160 }
161 
162 
AdapterDiscoverableChanged(BluetoothAdapter * adapter,bool discoverable)163 void BluetoothNotificationController::AdapterDiscoverableChanged(
164     BluetoothAdapter* adapter,
165     bool discoverable) {
166   if (discoverable) {
167     NotifyAdapterDiscoverable();
168   } else {
169     // Clear any previous discoverable notification.
170     message_center::MessageCenter::Get()->RemoveNotification(
171         kBluetoothDeviceDiscoverableNotificationId, false /* by_user */);
172   }
173 }
174 
DeviceAdded(BluetoothAdapter * adapter,BluetoothDevice * device)175 void BluetoothNotificationController::DeviceAdded(BluetoothAdapter* adapter,
176                                                   BluetoothDevice* device) {
177   // Add the new device to the list of currently paired devices; it doesn't
178   // receive a notification since it's assumed it was previously notified.
179   if (device->IsPaired())
180     paired_devices_.insert(device->GetAddress());
181 }
182 
DeviceChanged(BluetoothAdapter * adapter,BluetoothDevice * device)183 void BluetoothNotificationController::DeviceChanged(BluetoothAdapter* adapter,
184                                                     BluetoothDevice* device) {
185   // If the device is already in the list of paired devices, then don't
186   // notify.
187   if (paired_devices_.find(device->GetAddress()) != paired_devices_.end())
188     return;
189 
190   // Otherwise if it's marked as paired then it must be newly paired, so
191   // notify the user about that.
192   if (device->IsPaired()) {
193     paired_devices_.insert(device->GetAddress());
194     NotifyPairedDevice(device);
195   }
196 }
197 
DeviceRemoved(BluetoothAdapter * adapter,BluetoothDevice * device)198 void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter,
199                                                     BluetoothDevice* device) {
200   paired_devices_.erase(device->GetAddress());
201 }
202 
203 
RequestPinCode(BluetoothDevice * device)204 void BluetoothNotificationController::RequestPinCode(BluetoothDevice* device) {
205   // Cannot provide keyboard entry in a notification; these devices (old car
206   // audio systems for the most part) will need pairing to be initiated from
207   // the Chromebook.
208   device->CancelPairing();
209 }
210 
RequestPasskey(BluetoothDevice * device)211 void BluetoothNotificationController::RequestPasskey(BluetoothDevice* device) {
212   // Cannot provide keyboard entry in a notification; fortunately the spec
213   // doesn't allow for this to be an option when we're receiving the pairing
214   // request anyway.
215   device->CancelPairing();
216 }
217 
DisplayPinCode(BluetoothDevice * device,const std::string & pincode)218 void BluetoothNotificationController::DisplayPinCode(
219     BluetoothDevice* device,
220     const std::string& pincode) {
221   base::string16 message = l10n_util::GetStringFUTF16(
222           IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE,
223           device->GetName(), base::UTF8ToUTF16(pincode));
224 
225   NotifyPairing(device, message, false);
226 }
227 
DisplayPasskey(BluetoothDevice * device,uint32 passkey)228 void BluetoothNotificationController::DisplayPasskey(BluetoothDevice* device,
229                                                      uint32 passkey) {
230   base::string16 message = l10n_util::GetStringFUTF16(
231           IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY,
232           device->GetName(), base::UTF8ToUTF16(
233               base::StringPrintf("%06i", passkey)));
234 
235   NotifyPairing(device, message, false);
236 }
237 
KeysEntered(BluetoothDevice * device,uint32 entered)238 void BluetoothNotificationController::KeysEntered(BluetoothDevice* device,
239                                                   uint32 entered) {
240   // Ignored since we don't have CSS in the notification to update.
241 }
242 
ConfirmPasskey(BluetoothDevice * device,uint32 passkey)243 void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice* device,
244                                                      uint32 passkey) {
245   base::string16 message = l10n_util::GetStringFUTF16(
246           IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY,
247           device->GetName(), base::UTF8ToUTF16(
248               base::StringPrintf("%06i", passkey)));
249 
250   NotifyPairing(device, message, true);
251 }
252 
AuthorizePairing(BluetoothDevice * device)253 void BluetoothNotificationController::AuthorizePairing(
254     BluetoothDevice* device) {
255   base::string16 message = l10n_util::GetStringFUTF16(
256           IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING,
257           device->GetName());
258 
259   NotifyPairing(device, message, true);
260 }
261 
262 
OnGetAdapter(scoped_refptr<BluetoothAdapter> adapter)263 void BluetoothNotificationController::OnGetAdapter(
264     scoped_refptr<BluetoothAdapter> adapter) {
265   DCHECK(!adapter_.get());
266   adapter_ = adapter;
267   adapter_->AddObserver(this);
268   adapter_->AddPairingDelegate(this,
269                                BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
270 
271   // Notify a user if the adapter is already in the discoverable state.
272   if (adapter_->IsDiscoverable())
273     NotifyAdapterDiscoverable();
274 
275   // Build a list of the currently paired devices; these don't receive
276   // notifications since it's assumed they were previously notified.
277   BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
278   for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin();
279        iter != devices.end(); ++iter) {
280     const BluetoothDevice* device = *iter;
281     if (device->IsPaired())
282       paired_devices_.insert(device->GetAddress());
283   }
284 }
285 
286 
NotifyAdapterDiscoverable()287 void BluetoothNotificationController::NotifyAdapterDiscoverable() {
288   message_center::RichNotificationData optional;
289 
290   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
291 
292   scoped_ptr<Notification> notification(new Notification(
293       message_center::NOTIFICATION_TYPE_SIMPLE,
294       kBluetoothDeviceDiscoverableNotificationId,
295       base::string16() /* title */,
296       l10n_util::GetStringFUTF16(
297           IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE,
298           base::UTF8ToUTF16(adapter_->GetName()),
299           base::UTF8ToUTF16(adapter_->GetAddress())),
300       bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
301       base::string16() /* display source */,
302       message_center::NotifierId(
303           message_center::NotifierId::SYSTEM_COMPONENT,
304           system_notifier::kNotifierBluetooth),
305       optional,
306       NULL));
307   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
308 }
309 
NotifyPairing(BluetoothDevice * device,const base::string16 & message,bool with_buttons)310 void BluetoothNotificationController::NotifyPairing(
311     BluetoothDevice* device,
312     const base::string16& message,
313     bool with_buttons) {
314   message_center::RichNotificationData optional;
315   if (with_buttons) {
316     optional.buttons.push_back(message_center::ButtonInfo(
317         l10n_util::GetStringUTF16(
318             IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT)));
319     optional.buttons.push_back(message_center::ButtonInfo(
320         l10n_util::GetStringUTF16(
321             IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT)));
322   }
323 
324   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
325 
326   scoped_ptr<Notification> notification(new Notification(
327       message_center::NOTIFICATION_TYPE_SIMPLE,
328       kBluetoothDevicePairingNotificationId,
329       base::string16()  /* title */,
330       message,
331       bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
332       base::string16()  /* display source */,
333       message_center::NotifierId(
334           message_center::NotifierId::SYSTEM_COMPONENT,
335           system_notifier::kNotifierBluetooth),
336       optional,
337       new BluetoothPairingNotificationDelegate(adapter_,
338                                                device->GetAddress())));
339   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
340 }
341 
NotifyPairedDevice(BluetoothDevice * device)342 void BluetoothNotificationController::NotifyPairedDevice(
343     BluetoothDevice* device) {
344   // Remove the currently presented pairing notification; since only one
345   // pairing request is queued at a time, this is guaranteed to be the device
346   // that just became paired.
347   message_center::MessageCenter::Get()->RemoveNotification(
348       kBluetoothDevicePairingNotificationId, false /* by_user */);
349 
350   message_center::RichNotificationData optional;
351 
352   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
353 
354   scoped_ptr<Notification> notification(new Notification(
355       message_center::NOTIFICATION_TYPE_SIMPLE,
356       kBluetoothDevicePairedNotificationId,
357       base::string16() /* title */,
358       l10n_util::GetStringFUTF16(
359           IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED, device->GetName()),
360       bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH),
361       base::string16() /* display source */,
362       message_center::NotifierId(
363           message_center::NotifierId::SYSTEM_COMPONENT,
364           system_notifier::kNotifierBluetooth),
365       optional,
366       NULL));
367   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
368 }
369 
370 }  // namespace ash
371