File: message_stream_lookup_impl.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (366 lines) | stat: -rw-r--r-- 14,566 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/quick_pair/message_stream/message_stream_lookup_impl.h"

#include "ash/quick_pair/common/constants.h"
#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
#include "base/containers/contains.h"
#include "components/cross_device/logging/logging.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_socket.h"

namespace {

const device::BluetoothUUID kMessageStreamUuid(
    "df21fe2c-2515-4fdb-8886-f12c4d67927c");
constexpr int kMaxCreateMessageStreamAttempts{6};

// Attempt retry `n` after cooldown period |message_retry_cooldowns[n-1]|.
// These cooldown periods replicate those that Android's Fast Pair service
// mandates.
const std::vector<base::TimeDelta> kCreateMessageStreamRetryCooldowns{
    base::Seconds(2), base::Seconds(4), base::Seconds(8), base::Seconds(16),
    base::Seconds(32)};

}  // namespace

namespace ash {
namespace quick_pair {

std::string MessageStreamLookupImpl::CreateMessageStreamAttemptTypeToString(
    const CreateMessageStreamAttemptType& type) {
  switch (type) {
    case CreateMessageStreamAttemptType::kDeviceConnectedStateChanged:
      return "[DeviceConnectedStateChanged]";
    case CreateMessageStreamAttemptType::kDeviceAdded:
      return "[DeviceAdded]";
    case CreateMessageStreamAttemptType::kDevicePairedChanged:
      return "[DevicePairedChanged]";
    case CreateMessageStreamAttemptType::kDeviceChanged:
      return "[DeviceChanged]";
  }

  NOTREACHED();
}

MessageStreamLookupImpl::MessageStreamLookupImpl() {
  device::BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce(
      &MessageStreamLookupImpl::OnGetAdapter, weak_ptr_factory_.GetWeakPtr()));
}

void MessageStreamLookupImpl::OnGetAdapter(
    scoped_refptr<device::BluetoothAdapter> adapter) {
  adapter_ = adapter;
  adapter_observation_.Observe(adapter_.get());
}

void MessageStreamLookupImpl::AddObserver(
    MessageStreamLookup::Observer* observer) {
  observers_.AddObserver(observer);
}

void MessageStreamLookupImpl::RemoveObserver(
    MessageStreamLookup::Observer* observer) {
  observers_.RemoveObserver(observer);
}

MessageStreamLookupImpl::~MessageStreamLookupImpl() = default;

MessageStream* MessageStreamLookupImpl::GetMessageStream(
    const std::string& device_address) {
  auto it = message_streams_.find(device_address);
  // If we don't have a MessageStream for the device at |device_address|, return
  // a nullptr.
  if (it == message_streams_.end())
    return nullptr;

  // Return the pointer underneath the unique_ptr to the MessageStream we are
  // owning for the device at |device_address|.
  return it->second.get();
}

void MessageStreamLookupImpl::DevicePairedChanged(
    device::BluetoothAdapter* adapter,
    device::BluetoothDevice* device,
    bool new_paired_status) {
  // This event is triggered for all paired devices when BT is toggled on, so it
  // is important to make sure the device is actively connected or a connection
  // attempt will be issued for the Message Stream service UUID which prevents
  // audio profiles from connecting.
  if (!device->IsConnected()) {
    return;
  }

  // Check to see if the device supports Message Streams.
  if (!device || !base::Contains(device->GetUUIDs(), kMessageStreamUuid)) {
    return;
  }

  // Remove and delete the memory stream for the device, if it exists.
  if (!new_paired_status) {
    AttemptRemoveMessageStream(device->GetAddress());
    return;
  }

  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": Attempting to create MessageStream for device = ["
      << device->GetAddress() << "] " << device->GetNameForDisplay();
  AttemptCreateMessageStream(
      device->GetAddress(),
      CreateMessageStreamAttemptType::kDevicePairedChanged);
}

void MessageStreamLookupImpl::DeviceConnectedStateChanged(
    device::BluetoothAdapter* adapter,
    device::BluetoothDevice* device,
    bool is_now_connected) {
  // Check to see if the device supports Message Streams.
  if (!device || !device->IsPaired() ||
      !base::Contains(device->GetUUIDs(), kMessageStreamUuid)) {
    return;
  }

  // Remove and delete the memory stream for the device, if it exists.
  if (!is_now_connected) {
    AttemptRemoveMessageStream(device->GetAddress());
    return;
  }

  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": Attempting to create MessageStream for device = ["
      << device->GetAddress() << "] " << device->GetNameForDisplay();
  AttemptCreateMessageStream(
      device->GetAddress(),
      CreateMessageStreamAttemptType::kDeviceConnectedStateChanged);
}

void MessageStreamLookupImpl::DeviceChanged(device::BluetoothAdapter* adapter,
                                            device::BluetoothDevice* device) {
  // Check to see if the device is connected and supports MessageStreams. We
  // need to check if the device is both connected and paired to the adapter
  // because it is possible for a device to be connected to the adapter but not
  // paired (example: a request for the adapter's SDP records).
  if (!device || !(device->IsConnected() && device->IsPaired()) ||
      !base::Contains(device->GetUUIDs(), kMessageStreamUuid)) {
    return;
  }

  CD_LOG(VERBOSE, Feature::FP)
      << __func__
      << ": found connected device. Attempting to create "
         "MessageStream for device = ["
      << device->GetAddress() << "] " << device->GetNameForDisplay();
  AttemptCreateMessageStream(device->GetAddress(),
                             CreateMessageStreamAttemptType::kDeviceChanged);
}

void MessageStreamLookupImpl::DeviceAdded(device::BluetoothAdapter* adapter,
                                          device::BluetoothDevice* device) {
  // Check to see if the device is connected and supports MessageStreams. We
  // need to check if the device is both connected and paired to the adapter
  // because it is possible for a device to be connected to the adapter but not
  // paired (example: a request for the adapter's SDP records).
  if (!device || !(device->IsConnected() && device->IsPaired()) ||
      !base::Contains(device->GetUUIDs(), kMessageStreamUuid)) {
    return;
  }

  CD_LOG(VERBOSE, Feature::FP)
      << __func__
      << ": found connected device. Attempting to create "
         "MessageStream for device = ["
      << device->GetAddress() << "] " << device->GetNameForDisplay();
  AttemptCreateMessageStream(device->GetAddress(),
                             CreateMessageStreamAttemptType::kDeviceAdded);
}

void MessageStreamLookupImpl::DeviceRemoved(device::BluetoothAdapter* adapter,
                                            device::BluetoothDevice* device) {
  if (!device)
    return;

  // Remove message stream if the device removed from the adapter has a
  // message stream and disconnect from socket if applicable. It isn't expected
  // to already have a MessageStream associated with it.
  AttemptEraseMessageStream(device->GetAddress());
}

void MessageStreamLookupImpl::AttemptRemoveMessageStream(
    const std::string& device_address) {
  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": device address = " << device_address;
  AttemptEraseMessageStream(device_address);
}

void MessageStreamLookupImpl::AttemptEraseMessageStream(
    const std::string& device_address) {
  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": device address = " << device_address;
  // Remove map entry if it exists. It may not exist if it was failed to be
  // created due to a |ConnectToService| error.
  if (!base::Contains(message_streams_, device_address))
    return;

  // If the MessageStream still exists, we can attempt to gracefully disconnect
  // the socket before erasing (and therefore destructing) the MessageStream
  // instance.
  message_streams_[device_address]->Disconnect(
      base::BindOnce(&MessageStreamLookupImpl::OnSocketDisconnected,
                     weak_ptr_factory_.GetWeakPtr(), device_address));
}

void MessageStreamLookupImpl::OnSocketDisconnected(
    const std::string& device_address) {
  message_streams_.erase(device_address);
}

void MessageStreamLookupImpl::AttemptCreateMessageStream(
    const std::string& device_address,
    const CreateMessageStreamAttemptType& type) {
  device::BluetoothDevice* device = adapter_->GetDevice(device_address);
  if (!device) {
    CD_LOG(INFO, Feature::FP)
        << __func__ << ": lost device for Message Stream creation";
    AttemptRemoveMessageStream(device_address);
    return;
  }

  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": device address = " << device_address
      << " type = " << CreateMessageStreamAttemptTypeToString(type);

  // Only open MessageStreams for new devices that don't already have a
  // MessageStream stored in the map. We can sometimes reach this point if
  // multiple BluetoothAdapter events fire for a device connected event, but
  // we need all of these BluetoothAdapter observation events to handle
  // different connection scenarios, and have coverage for different devices.
  if (base::Contains(message_streams_, device_address)) {
    CD_LOG(INFO, Feature::FP)
        << __func__ << ": Message Stream exists already for device";
    return;
  }

  if (base::Contains(pending_connect_requests_, device_address)) {
    CD_LOG(INFO, Feature::FP)
        << __func__ << ": Ignoring due to matching pending request";
    return;
  }

  pending_connect_requests_.insert(device_address);

  device->ConnectToService(
      /*uuid=*/kMessageStreamUuid, /*callback=*/
      base::BindOnce(&MessageStreamLookupImpl::OnConnected,
                     weak_ptr_factory_.GetWeakPtr(), device_address,
                     base::TimeTicks::Now(), type),
      /*error_callback=*/
      base::BindOnce(&MessageStreamLookupImpl::OnConnectError,
                     weak_ptr_factory_.GetWeakPtr(), device_address, type));
}

void MessageStreamLookupImpl::OnConnected(
    std::string device_address,
    base::TimeTicks connect_to_service_start_time,
    const CreateMessageStreamAttemptType& type,
    scoped_refptr<device::BluetoothSocket> socket) {
  if (create_message_stream_retry_timers_.contains(device_address)) {
    base::OneShotTimer* curr_create_message_stream_retry_timer =
        create_message_stream_retry_timers_[device_address].get();

    // This if branch should be unnecessary in theory, but it is included to
    // address the edge case that a success occurs after a failure.
    if (curr_create_message_stream_retry_timer->IsRunning())
      curr_create_message_stream_retry_timer->Stop();

    size_t timer_erased_ct =
        create_message_stream_retry_timers_.erase(device_address);
    DCHECK(timer_erased_ct == 1);
    size_t retry_ct_erased_ct =
        create_message_stream_attempts_.erase(device_address);
    DCHECK(retry_ct_erased_ct == 1);
  }

  // It is expected that at the point of a successful RFCOMM connection, the
  // device is known to the adapter.
  device::BluetoothDevice* bt_device = adapter_->GetDevice(device_address);
  DCHECK(bt_device);
  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": device = " << device_address
      << " device name = " << bt_device->GetNameForDisplay()
      << " Type = " << CreateMessageStreamAttemptTypeToString(type);

  RecordMessageStreamConnectToServiceResult(/*success=*/true);
  RecordMessageStreamConnectToServiceTime(base::TimeTicks::Now() -
                                          connect_to_service_start_time);

  std::unique_ptr<MessageStream> message_stream =
      std::make_unique<MessageStream>(device_address, std::move(socket));

  for (auto& observer : observers_)
    observer.OnMessageStreamConnected(device_address, message_stream.get());

  message_streams_[device_address] = std::move(message_stream);
  pending_connect_requests_.erase(device_address);
}

void MessageStreamLookupImpl::OnConnectError(
    std::string device_address,
    const CreateMessageStreamAttemptType& type,
    const std::string& error_message) {
  // Because we need to attempt to create MessageStreams at many different
  // iterations due to the variability of Bluetooth APIs, we can expect to
  // see errors here frequently, along with errors followed by a success.
  CD_LOG(INFO, Feature::FP)
      << __func__ << ": Error: [ " << error_message
      << "]. Type: " << CreateMessageStreamAttemptTypeToString(type) << ".";
  RecordMessageStreamConnectToServiceResult(/*success=*/false);
  RecordMessageStreamConnectToServiceError(error_message);
  pending_connect_requests_.erase(device_address);

  // A timer is started to retry AttemptCreateMessageStream if
  // the maximum number of attempts (6) to create the MessageStream has not been
  // reached. If this is the first retry, new entries in
  // |create_message_stream_attempts_| and
  // |create_message_stream_retry_timers_| are created.
  create_message_stream_attempts_.try_emplace(device_address, 1);

  int& create_message_stream_attempt_num =
      create_message_stream_attempts_[device_address];
  if (create_message_stream_attempt_num == kMaxCreateMessageStreamAttempts) {
    CD_LOG(INFO, Feature::FP)
        << __func__
        << ": 6 attempts to create a message stream have failed. "
           "There are no more retries.";
    return;
  }

  device::BluetoothDevice* device = adapter_->GetDevice(device_address);
  if (device) {
    create_message_stream_retry_timers_.try_emplace(
        device_address, std::make_unique<base::OneShotTimer>());

    base::OneShotTimer* curr_create_message_stream_retry_timer =
        create_message_stream_retry_timers_[device_address].get();
    curr_create_message_stream_retry_timer->Start(
        FROM_HERE,
        kCreateMessageStreamRetryCooldowns[create_message_stream_attempt_num++ -
                                           1],
        base::BindOnce(&MessageStreamLookupImpl::AttemptCreateMessageStream,
                       weak_ptr_factory_.GetWeakPtr(), device_address, type));
  } else {
    CD_LOG(INFO, Feature::FP)
        << __func__ << ": attempting to retry message stream creation with "
        << " a device no longer found by the adapter."
        << " device address: " << device_address;
    size_t retry_ct_erased_ct =
        create_message_stream_attempts_.erase(device_address);
    DCHECK(retry_ct_erased_ct == 1);
    create_message_stream_retry_timers_.erase(device_address);
  }
}

}  // namespace quick_pair
}  // namespace ash