File: data_migration.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 (177 lines) | stat: -rw-r--r-- 7,780 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
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/data_migration/data_migration.h"

#include <cstdint>
#include <optional>
#include <vector>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/time/time.h"
#include "third_party/nearby/src/internal/platform/byte_array.h"
#include "third_party/nearby/src/internal/platform/byte_utils.h"

namespace data_migration {
namespace {

std::vector<uint8_t> BuildEndpointInfo() {
  // Must be < 131:
  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/nearby/src/connections/implementation/bluetooth_device_name.h;l=70;drc=084f7ebd8847cfb68191f787dda192644377e6ad
  static constexpr size_t kEndpointInfoLength = 64;
  // TODO(esum): Fill with real content and comment. Currently, endpoint info
  // is still not used/required.
  return std::vector<uint8_t>(kEndpointInfoLength, 0);
}

}  // namespace

DataMigration::DataMigration(
    std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager)
    : nearby_connections_manager_(std::move(nearby_connections_manager)) {
  CHECK(nearby_connections_manager_);
}

// Do not use `nearby_connections_manager_` or anything that depends on it
// here. Any shutdown logic should go in `DataMigration::Shutdown()`.
DataMigration::~DataMigration() = default;

// Part of the KeyedService shutdown design. Must ensure all nearby connection
// activity is stopped here. Note `DataMigration` is expected to be destroyed
// shortly after this.
void DataMigration::Shutdown() {
  VLOG(4) << "__func__";
  // Ensure any pending callbacks do not run while tearing everything down.
  weak_factory_.InvalidateWeakPtrs();

  connected_device_.reset();
  nearby_connections_manager_->Shutdown();
}

void DataMigration::StartAdvertising() {
  VLOG(1) << "DataMigration advertising starting";
  CHECK(!connected_device_) << "Nearby connection already established";
  // `NearbyConnectionsManagerImpl` internally uses the arguments below to build
  // the following `AdvertisingOptions`:
  // * strategy - kP2pPointToPoint
  // * mediums
  //   * bluetooth - true
  //   * every other medium - false
  // * auto_upgrade_bandwidth - true
  // * enforce_topology_constraints - true
  // * enable_bluetooth_listening - false
  //   * From docs: "this allows listening on incoming Bluetooth Classic
  //     connections while BLE advertising". Should not be an issue because
  //     BLE is a disabled advertising medium to begin with.
  // * enable_webrtc_listening - false
  // * fast_advertisement_service_uuid - Some internal immutable value.
  nearby_connections_manager_->StartAdvertising(
      // `PowerLevel::kHighPower` matches what cros quick start uses and is
      // required by the `NearbyConnectionsManagerImpl` to use the bluetooth
      // classic medium.
      BuildEndpointInfo(), this,
      NearbyConnectionsManager::PowerLevel::kHighPower,
      // This causes `NearbyConnectionsManagerImpl` to disable all wifi-related
      // advertisement mechanisms (leaving only bluetooth classic). Note this
      // does not affect the medium for the main connection over which
      // payloads are transferred.
      nearby_share::mojom::DataUsage::kOffline,
      base::BindOnce(&DataMigration::OnStartAdvertising,
                     weak_factory_.GetWeakPtr()));
}

void DataMigration::OnStartAdvertising(
    NearbyConnectionsManager::ConnectionsStatus status) {
  if (status != NearbyConnectionsManager::ConnectionsStatus::kSuccess) {
    // See the `NearbyConnections::StartAdvertising()` API. None of the error
    // codes should apply here, but it's not worth crashing the browser process
    // in prod if this happens.
    // TODO(esum): Add metrics how often this is hit.
    LOG(DFATAL) << "DataMigration failed to start advertising with status="
                << status;
  }
}

void DataMigration::OnStopAdvertising(
    NearbyConnectionsManager::ConnectionsStatus status) {
  // Mojo docs claim this can never fail, but this is not worth crashing the
  // browser process even so. If advertising keeps running, it shouldn't cause
  // this class to fail; it's just less optimal.
  if (status != NearbyConnectionsManager::ConnectionsStatus::kSuccess) {
    LOG(DFATAL) << "DataMigration failed to stop advertising with status="
                << status;
  }
}

void DataMigration::OnIncomingConnectionInitiated(
    const std::string& endpoint_id,
    const std::vector<uint8_t>& endpoint_info) {
  // Note `NearbyConnectionsManagerImpl` automatically accepts this incoming
  // connection internally. This leaves the outcome in the hands of the remote
  // device, who must accept the connection as well before the 2 sides can start
  // exchanging payloads.
  //
  // This matches the DataMigration design because the user is expected to
  // verify that the 4 digit pin matches on both devices, and hit "accept" on
  // the remote device (the source of the data) for the connection to be
  // established.
  std::optional<std::vector<uint8_t>> auth_token =
      nearby_connections_manager_->GetRawAuthenticationToken(endpoint_id);
  CHECK(auth_token) << "Auth token missing. Should always be available because "
                       "connection was just initiated.";
  ::nearby::ByteArray auth_token_as_byte_array =
      ::nearby::ByteArray(std::string(auth_token->begin(), auth_token->end()));
  // TODO(esum):
  // * Check if `::nearby::ByteUtils::ToFourDigitString()` needs to run in a
  //   sandboxed process.
  // * Display the pin in an actual UI. Logs are used temporarily here for
  //   developers.
  // * Account for multiple incoming connection requests when the UI is built.
  VLOG(1) << "DataMigration connection requested with pin="
          << ::nearby::ByteUtils::ToFourDigitString(auth_token_as_byte_array);
}

void DataMigration::OnIncomingConnectionAccepted(
    const std::string& endpoint_id,
    const std::vector<uint8_t>& endpoint_info,
    NearbyConnection* connection) {
  if (connected_device_) {
    // Corner case should rarely happen, but only one data migration can be
    // active at a time.
    LOG(WARNING) << "DataMigration already active with another device. "
                    "Disconnecting from incoming endpoint.";
    connection->Close();
    return;
  }
  VLOG(1) << "DataMigration connection accepted";
  // Multiple parallel transfers is not supported, so there's no reason to
  // continue advertising at this point.
  nearby_connections_manager_->StopAdvertising(base::BindOnce(
      &DataMigration::OnStopAdvertising, weak_factory_.GetWeakPtr()));

  connected_device_.emplace(connection, nearby_connections_manager_.get());
  connection->SetDisconnectionListener(base::BindOnce(
      &DataMigration::OnDeviceDisconnected, weak_factory_.GetWeakPtr()));
}

void DataMigration::OnDeviceDisconnected() {
  CHECK(connected_device_);
  // Note this is not a transient disconnect. NC should handle transient network
  // errors internally. At this point, NC deems the connection unrecoverable
  // and its docs recommend starting the service discovery/advertising process
  // again.
  LOG(ERROR) << "DataMigration remote device has disconnected unexpectedly";
  connected_device_.reset();
  // Data Migration protocol does not persist state across connections. Once
  // the connection is dropped, the protocol resets. Clear any payloads in
  // memory that have not been processed yet since they are guaranteed to not be
  // used at this point. Any files that were completely transferred on disc will
  // be preserved though.
  nearby_connections_manager_->ClearIncomingPayloads();

  StartAdvertising();
}

}  // namespace data_migration