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 "extensions/browser/api/bluetooth/bluetooth_private_api.h"
6
7#include "base/callback.h"
8#include "base/lazy_instance.h"
9#include "base/strings/string_util.h"
10#include "device/bluetooth/bluetooth_adapter.h"
11#include "device/bluetooth/bluetooth_adapter_factory.h"
12#include "extensions/browser/api/bluetooth/bluetooth_api.h"
13#include "extensions/browser/api/bluetooth/bluetooth_event_router.h"
14#include "extensions/common/api/bluetooth_private.h"
15
16namespace bt_private = extensions::core_api::bluetooth_private;
17
18namespace extensions {
19
20static base::LazyInstance<BrowserContextKeyedAPIFactory<BluetoothPrivateAPI> >
21    g_factory = LAZY_INSTANCE_INITIALIZER;
22
23// static
24BrowserContextKeyedAPIFactory<BluetoothPrivateAPI>*
25BluetoothPrivateAPI::GetFactoryInstance() {
26  return g_factory.Pointer();
27}
28
29BluetoothPrivateAPI::BluetoothPrivateAPI(content::BrowserContext* context)
30    : browser_context_(context) {
31  EventRouter::Get(browser_context_)
32      ->RegisterObserver(this, bt_private::OnPairing::kEventName);
33}
34
35BluetoothPrivateAPI::~BluetoothPrivateAPI() {}
36
37void BluetoothPrivateAPI::Shutdown() {
38  EventRouter::Get(browser_context_)->UnregisterObserver(this);
39}
40
41void BluetoothPrivateAPI::OnListenerAdded(const EventListenerInfo& details) {
42  // This function can be called multiple times for the same JS listener, for
43  // example, once for the addListener call and again if it is a lazy listener.
44  if (!details.browser_context)
45    return;
46
47  BluetoothAPI::Get(browser_context_)->event_router()->AddPairingDelegate(
48      details.extension_id);
49}
50
51void BluetoothPrivateAPI::OnListenerRemoved(const EventListenerInfo& details) {
52  // This function can be called multiple times for the same JS listener, for
53  // example, once for the addListener call and again if it is a lazy listener.
54  if (!details.browser_context)
55    return;
56
57  BluetoothAPI::Get(browser_context_)->event_router()->RemovePairingDelegate(
58      details.extension_id);
59}
60
61namespace core_api {
62
63namespace {
64
65const char kNameProperty[] = "name";
66const char kPoweredProperty[] = "powered";
67const char kDiscoverableProperty[] = "discoverable";
68
69const char kSetAdapterPropertyError[] = "Error setting adapter properties: $1";
70
71const char kDeviceNotFoundError[] =
72    "Given address is not a valid Bluetooth device.";
73
74const char kPairingNotEnabled[] =
75    "Pairing must be enabled to set a pairing response.";
76
77const char kInvalidPairingResponseOptions[] =
78    "Invalid pairing response options";
79
80const char kAdapterNotPresent[] =
81    "Could not find a Bluetooth adapter.";
82
83// Returns true if the pairing response options passed into the
84// setPairingResponse function are valid.
85bool ValidatePairingResponseOptions(
86    const device::BluetoothDevice* device,
87    const bt_private::SetPairingResponseOptions& options) {
88  bool response = options.response != bt_private::PAIRING_RESPONSE_NONE;
89  bool pincode = options.pincode.get() != NULL;
90  bool passkey = options.passkey.get() != NULL;
91
92  if (!response && !pincode && !passkey)
93    return false;
94  if (pincode && passkey)
95    return false;
96  if (options.response != bt_private::PAIRING_RESPONSE_CONFIRM &&
97      (pincode || passkey))
98    return false;
99
100  // Check the BluetoothDevice is in expecting the correct response.
101  if (!device->ExpectingConfirmation() && !device->ExpectingPinCode() &&
102      !device->ExpectingPasskey())
103    return false;
104  if (pincode && !device->ExpectingPinCode())
105    return false;
106  if (passkey && !device->ExpectingPasskey())
107    return false;
108  if (options.response == bt_private::PAIRING_RESPONSE_CONFIRM && !pincode &&
109      !passkey && !device->ExpectingConfirmation())
110    return false;
111
112  return true;
113}
114
115}  // namespace
116
117BluetoothPrivateSetAdapterStateFunction::
118    BluetoothPrivateSetAdapterStateFunction() {}
119
120BluetoothPrivateSetAdapterStateFunction::
121    ~BluetoothPrivateSetAdapterStateFunction() {}
122
123bool BluetoothPrivateSetAdapterStateFunction::DoWork(
124    scoped_refptr<device::BluetoothAdapter> adapter) {
125  scoped_ptr<bt_private::SetAdapterState::Params> params(
126      bt_private::SetAdapterState::Params::Create(*args_));
127  EXTENSION_FUNCTION_VALIDATE(params.get());
128
129  if (!adapter->IsPresent()) {
130    SetError(kAdapterNotPresent);
131    SendResponse(false);
132    return true;
133  }
134
135  const bt_private::NewAdapterState& new_state = params->adapter_state;
136
137  // These properties are not owned.
138  std::string* name = new_state.name.get();
139  bool* powered = new_state.powered.get();
140  bool* discoverable = new_state.discoverable.get();
141
142  if (name && adapter->GetName() != *name) {
143    pending_properties_.insert(kNameProperty);
144    adapter->SetName(*name,
145                     CreatePropertySetCallback(kNameProperty),
146                     CreatePropertyErrorCallback(kNameProperty));
147  }
148
149  if (powered && adapter->IsPowered() != *powered) {
150    pending_properties_.insert(kPoweredProperty);
151    adapter->SetPowered(*powered,
152                        CreatePropertySetCallback(kPoweredProperty),
153                        CreatePropertyErrorCallback(kPoweredProperty));
154  }
155
156  if (discoverable && adapter->IsDiscoverable() != *discoverable) {
157    pending_properties_.insert(kDiscoverableProperty);
158    adapter->SetDiscoverable(
159        *discoverable,
160        CreatePropertySetCallback(kDiscoverableProperty),
161        CreatePropertyErrorCallback(kDiscoverableProperty));
162  }
163
164  if (pending_properties_.empty())
165    SendResponse(true);
166  return true;
167}
168
169base::Closure
170BluetoothPrivateSetAdapterStateFunction::CreatePropertySetCallback(
171    const std::string& property_name) {
172  return base::Bind(
173      &BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertySet,
174      this,
175      property_name);
176}
177
178base::Closure
179BluetoothPrivateSetAdapterStateFunction::CreatePropertyErrorCallback(
180    const std::string& property_name) {
181  return base::Bind(
182      &BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertyError,
183      this,
184      property_name);
185}
186
187void BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertySet(
188    const std::string& property) {
189  DCHECK(pending_properties_.find(property) != pending_properties_.end());
190  DCHECK(failed_properties_.find(property) == failed_properties_.end());
191
192  pending_properties_.erase(property);
193  if (pending_properties_.empty()) {
194    if (failed_properties_.empty())
195      SendResponse(true);
196    else
197      SendError();
198  }
199}
200
201void BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertyError(
202    const std::string& property) {
203  DCHECK(pending_properties_.find(property) != pending_properties_.end());
204  DCHECK(failed_properties_.find(property) == failed_properties_.end());
205
206  pending_properties_.erase(property);
207  failed_properties_.insert(property);
208  if (pending_properties_.empty())
209    SendError();
210}
211
212void BluetoothPrivateSetAdapterStateFunction::SendError() {
213  DCHECK(pending_properties_.empty());
214  DCHECK(!failed_properties_.empty());
215
216  std::vector<std::string> failed_vector;
217  std::copy(failed_properties_.begin(),
218            failed_properties_.end(),
219            std::back_inserter(failed_vector));
220
221  std::vector<std::string> replacements(1);
222  replacements[0] = JoinString(failed_vector, ", ");
223  std::string error =
224      ReplaceStringPlaceholders(kSetAdapterPropertyError, replacements, NULL);
225  SetError(error);
226  SendResponse(false);
227}
228
229BluetoothPrivateSetPairingResponseFunction::
230    BluetoothPrivateSetPairingResponseFunction() {}
231
232BluetoothPrivateSetPairingResponseFunction::
233    ~BluetoothPrivateSetPairingResponseFunction() {}
234
235bool BluetoothPrivateSetPairingResponseFunction::DoWork(
236    scoped_refptr<device::BluetoothAdapter> adapter) {
237  scoped_ptr<bt_private::SetPairingResponse::Params> params(
238      bt_private::SetPairingResponse::Params::Create(*args_));
239  EXTENSION_FUNCTION_VALIDATE(params.get());
240  const bt_private::SetPairingResponseOptions& options = params->options;
241
242  BluetoothEventRouter* router =
243      BluetoothAPI::Get(browser_context())->event_router();
244  if (!router->GetPairingDelegate(extension_id())) {
245    SetError(kPairingNotEnabled);
246    SendResponse(false);
247    return true;
248  }
249
250  const std::string& device_address = options.device.address;
251  device::BluetoothDevice* device = adapter->GetDevice(device_address);
252  if (!device) {
253    SetError(kDeviceNotFoundError);
254    SendResponse(false);
255    return true;
256  }
257
258  if (!ValidatePairingResponseOptions(device, options)) {
259    SetError(kInvalidPairingResponseOptions);
260    SendResponse(false);
261    return true;
262  }
263
264  if (options.pincode.get()) {
265    device->SetPinCode(*options.pincode.get());
266  } else if (options.passkey.get()) {
267    device->SetPasskey(*options.passkey.get());
268  } else {
269    switch (options.response) {
270      case bt_private::PAIRING_RESPONSE_CONFIRM:
271        device->ConfirmPairing();
272        break;
273      case bt_private::PAIRING_RESPONSE_REJECT:
274        device->RejectPairing();
275        break;
276      case bt_private::PAIRING_RESPONSE_CANCEL:
277        device->CancelPairing();
278        break;
279      default:
280        NOTREACHED();
281    }
282  }
283
284  SendResponse(true);
285  return true;
286}
287
288}  // namespace core_api
289
290}  // namespace extensions
291