Home | History | Annotate | Download | only in bluetooth
      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 
     86 BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate(
     87     scoped_refptr<BluetoothAdapter> adapter,
     88     const std::string& address)
     89     : adapter_(adapter),
     90       address_(address) {
     91 }
     92 
     93 BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() {
     94 }
     95 
     96 void BluetoothPairingNotificationDelegate::Display() {
     97 }
     98 
     99 void BluetoothPairingNotificationDelegate::Error() {
    100 }
    101 
    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 
    114 bool BluetoothPairingNotificationDelegate::HasClickedListener() {
    115   return false;
    116 }
    117 
    118 void BluetoothPairingNotificationDelegate::Click() {
    119 }
    120 
    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 
    147 BluetoothNotificationController::BluetoothNotificationController()
    148     : weak_ptr_factory_(this) {
    149   BluetoothAdapterFactory::GetAdapter(
    150       base::Bind(&BluetoothNotificationController::OnGetAdapter,
    151                  weak_ptr_factory_.GetWeakPtr()));
    152 }
    153 
    154 BluetoothNotificationController::~BluetoothNotificationController() {
    155   if (adapter_.get()) {
    156     adapter_->RemoveObserver(this);
    157     adapter_->RemovePairingDelegate(this);
    158     adapter_ = NULL;
    159   }
    160 }
    161 
    162 
    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 
    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 
    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 
    198 void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter,
    199                                                     BluetoothDevice* device) {
    200   paired_devices_.erase(device->GetAddress());
    201 }
    202 
    203 
    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 
    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 
    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 
    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 
    238 void BluetoothNotificationController::KeysEntered(BluetoothDevice* device,
    239                                                   uint32 entered) {
    240   // Ignored since we don't have CSS in the notification to update.
    241 }
    242 
    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 
    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 
    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 
    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 
    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 
    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