diff --git a/DEPS b/DEPS
index 49187f5..373154e4c 100644
--- a/DEPS
+++ b/DEPS
@@ -639,7 +639,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '05591bbeae6592fd924caec8e728a4ea86cbb8c9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a1d0f2731001d26f50a977fab3f3419125f5cc43', # commit position 20628
+    Var('webrtc_git') + '/src.git' + '@' + '93e9134422757553944257f5c5e29848cb363676', # commit position 20628
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
index c31485e..4b40dbd 100644
--- a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
+#include "components/sync/model/model_type_sync_bridge.h"
 #include "components/sync/protocol/user_event_specifics.pb.h"
 #include "components/sync/user_events/user_event_service.h"
 #include "components/variations/variations_associated_data.h"
@@ -263,6 +264,25 @@
   EXPECT_TRUE(ExpectUserEvents({testEvent1, consent1, consent2, testEvent3}));
 }
 
+// Test that events that are logged before sync is enabled.
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, LoggedBeforeSyncSetup) {
+  const UserEventSpecifics consent1 = CreateUserConsent(1);
+  const UserEventSpecifics consent2 = CreateUserConsent(2);
+  ASSERT_TRUE(SetupClients());
+  syncer::UserEventService* event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
+  auto bridge = event_service->GetSyncBridge();
+  // Wait for UserEventSyncBridge to be ready to receive events.
+  // TODO(crbug.com/761485): Remove when the store is initialized instantly.
+  ASSERT_TRUE(!bridge->change_processor()->IsTrackingMetadata());
+  while (!bridge->change_processor()->IsTrackingMetadata())
+    base::RunLoop().RunUntilIdle();
+  event_service->RecordUserEvent(consent1);
+  ASSERT_TRUE(SetupSync());
+  event_service->RecordUserEvent(consent2);
+  EXPECT_TRUE(ExpectUserEvents({consent1, consent2}));
+}
+
 IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, NoSessions) {
   const UserEventSpecifics specifics = CreateTestEvent(1);
   ASSERT_TRUE(SetupSync());
diff --git a/components/consent_auditor/consent_auditor.cc b/components/consent_auditor/consent_auditor.cc
index d9da1af5..d2fba70 100644
--- a/components/consent_auditor/consent_auditor.cc
+++ b/components/consent_auditor/consent_auditor.cc
@@ -13,6 +13,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/model/model_type_sync_bridge.h"
 #include "components/sync/user_events/user_event_service.h"
 
 using UserEventSpecifics = sync_pb::UserEventSpecifics;
@@ -27,11 +28,11 @@
 const char kLocalConsentLocaleKey[] = "locale";
 
 UserEventSpecifics::UserConsent::ConsentStatus ToProtoEnum(
-    ConsentAuditor::ConsentStatus status) {
+    consent_auditor::ConsentStatus status) {
   switch (status) {
-    case ConsentAuditor::ConsentStatus::REVOKED:
+    case consent_auditor::ConsentStatus::REVOKED:
       return UserEventSpecifics::UserConsent::REVOKED;
-    case ConsentAuditor::ConsentStatus::GIVEN:
+    case consent_auditor::ConsentStatus::GIVEN:
       return UserEventSpecifics::UserConsent::GIVEN;
   }
   NOTREACHED();
@@ -72,6 +73,15 @@
     return;
   std::unique_ptr<sync_pb::UserEventSpecifics> specifics = ConstructUserConsent(
       feature, consent_grd_ids, placeholder_replacements, status);
+  // For real usage, UserEventSyncBridge should always be ready to receive
+  // events when a consent gets recorded.
+  // FakeUserEventService doesn't have a sync bridge.
+  // TODO(crbug.com/709094, crbug.com/761485): Remove this check when the store
+  // initializes synchronously and is instantly ready to receive data.
+  DCHECK(!user_event_service_->GetSyncBridge() ||
+         user_event_service_->GetSyncBridge()
+             ->change_processor()
+             ->IsTrackingMetadata());
   user_event_service_->RecordUserEvent(std::move(specifics));
 }
 
diff --git a/components/consent_auditor/consent_auditor.h b/components/consent_auditor/consent_auditor.h
index 672a8a94..f8de2eaf 100644
--- a/components/consent_auditor/consent_auditor.h
+++ b/components/consent_auditor/consent_auditor.h
@@ -25,9 +25,10 @@
 
 namespace consent_auditor {
 
+enum class ConsentStatus { REVOKED, GIVEN };
+
 class ConsentAuditor : public KeyedService {
  public:
-  enum class ConsentStatus { REVOKED, GIVEN };
 
   ConsentAuditor(PrefService* pref_service,
                  syncer::UserEventService* user_event_service,
@@ -65,7 +66,7 @@
       const std::string& feature,
       const std::vector<int>& consent_grd_ids,
       const std::vector<std::string>& placeholder_replacements,
-      ConsentAuditor::ConsentStatus status);
+      ConsentStatus status);
 
   PrefService* pref_service_;
   syncer::UserEventService* user_event_service_;
diff --git a/components/consent_auditor/consent_auditor_unittest.cc b/components/consent_auditor/consent_auditor_unittest.cc
index a2a7e66e..c3cd309 100644
--- a/components/consent_auditor/consent_auditor_unittest.cc
+++ b/components/consent_auditor/consent_auditor_unittest.cc
@@ -162,7 +162,7 @@
 
 TEST_F(ConsentAuditorTest, RecordingEnabled) {
   consent_auditor()->RecordGaiaConsent("feature1", {}, {},
-                                       ConsentAuditor::ConsentStatus::GIVEN);
+                                       ConsentStatus::GIVEN);
   auto& events = user_event_service()->GetRecordedUserEvents();
   EXPECT_EQ(1U, events.size());
 }
@@ -171,7 +171,7 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(switches::kSyncUserConsentEvents);
   consent_auditor()->RecordGaiaConsent("feature1", {}, {},
-                                       ConsentAuditor::ConsentStatus::GIVEN);
+                                       ConsentStatus::GIVEN);
   auto& events = user_event_service()->GetRecordedUserEvents();
   EXPECT_EQ(0U, events.size());
 }
@@ -181,7 +181,7 @@
   std::vector<std::string> kPlaceholders = {"OK.", "user@example.com"};
   base::Time t1 = base::Time::Now();
   consent_auditor()->RecordGaiaConsent("feature1", kMessageIds, kPlaceholders,
-                                       ConsentAuditor::ConsentStatus::GIVEN);
+                                       ConsentStatus::GIVEN);
   base::Time t2 = base::Time::Now();
   auto& events = user_event_service()->GetRecordedUserEvents();
   EXPECT_EQ(1U, events.size());
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 03053e04..2d16a92 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -73,6 +73,7 @@
     "sensors/sensor_manager_chromeos_unittest.cc",
     "test/run_all_unittests.cc",
     "u2f/u2f_apdu_unittest.cc",
+    "u2f/u2f_ble_connection_unittest.cc",
     "u2f/u2f_ble_frames_unittest.cc",
     "u2f/u2f_discovery_unittest.cc",
     "u2f/u2f_message_unittest.cc",
diff --git a/device/u2f/BUILD.gn b/device/u2f/BUILD.gn
index 3e0e89de..122fd73 100644
--- a/device/u2f/BUILD.gn
+++ b/device/u2f/BUILD.gn
@@ -11,6 +11,8 @@
     "u2f_apdu_command.h",
     "u2f_apdu_response.cc",
     "u2f_apdu_response.h",
+    "u2f_ble_connection.cc",
+    "u2f_ble_connection.h",
     "u2f_ble_discovery.cc",
     "u2f_ble_discovery.h",
     "u2f_ble_frames.cc",
diff --git a/device/u2f/u2f_ble_connection.cc b/device/u2f/u2f_ble_connection.cc
new file mode 100644
index 0000000..d56506b
--- /dev/null
+++ b/device/u2f/u2f_ble_connection.cc
@@ -0,0 +1,573 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/u2f/u2f_ble_connection.h"
+
+#include <utility>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_gatt_connection.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "device/u2f/u2f_ble_uuids.h"
+
+namespace device {
+
+namespace {
+
+constexpr const char* ToString(BluetoothDevice::ConnectErrorCode error_code) {
+  switch (error_code) {
+    case BluetoothDevice::ERROR_AUTH_CANCELED:
+      return "ERROR_AUTH_CANCELED";
+    case BluetoothDevice::ERROR_AUTH_FAILED:
+      return "ERROR_AUTH_FAILED";
+    case BluetoothDevice::ERROR_AUTH_REJECTED:
+      return "ERROR_AUTH_REJECTED";
+    case BluetoothDevice::ERROR_AUTH_TIMEOUT:
+      return "ERROR_AUTH_TIMEOUT";
+    case BluetoothDevice::ERROR_FAILED:
+      return "ERROR_FAILED";
+    case BluetoothDevice::ERROR_INPROGRESS:
+      return "ERROR_INPROGRESS";
+    case BluetoothDevice::ERROR_UNKNOWN:
+      return "ERROR_UNKNOWN";
+    case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
+      return "ERROR_UNSUPPORTED_DEVICE";
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+constexpr const char* ToString(BluetoothGattService::GattErrorCode error_code) {
+  switch (error_code) {
+    case BluetoothGattService::GATT_ERROR_UNKNOWN:
+      return "GATT_ERROR_UNKNOWN";
+    case BluetoothGattService::GATT_ERROR_FAILED:
+      return "GATT_ERROR_FAILED";
+    case BluetoothGattService::GATT_ERROR_IN_PROGRESS:
+      return "GATT_ERROR_IN_PROGRESS";
+    case BluetoothGattService::GATT_ERROR_INVALID_LENGTH:
+      return "GATT_ERROR_INVALID_LENGTH";
+    case BluetoothGattService::GATT_ERROR_NOT_PERMITTED:
+      return "GATT_ERROR_NOT_PERMITTED";
+    case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED:
+      return "GATT_ERROR_NOT_AUTHORIZED";
+    case BluetoothGattService::GATT_ERROR_NOT_PAIRED:
+      return "GATT_ERROR_NOT_PAIRED";
+    case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED:
+      return "GATT_ERROR_NOT_SUPPORTED";
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+}  // namespace
+
+U2fBleConnection::U2fBleConnection(
+    std::string device_address,
+    ConnectionStatusCallback connection_status_callback,
+    ReadCallback read_callback)
+    : address_(std::move(device_address)),
+      connection_status_callback_(std::move(connection_status_callback)),
+      read_callback_(std::move(read_callback)),
+      weak_factory_(this) {
+  BluetoothAdapterFactory::GetAdapter(
+      base::Bind(&U2fBleConnection::OnGetAdapter, weak_factory_.GetWeakPtr()));
+  DCHECK(!address_.empty());
+}
+
+U2fBleConnection::~U2fBleConnection() {
+  if (adapter_)
+    adapter_->RemoveObserver(this);
+}
+
+void U2fBleConnection::ReadControlPointLength(
+    ControlPointLengthCallback callback) {
+  const BluetoothRemoteGattService* u2f_service = GetU2fService();
+  if (!u2f_service) {
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
+
+  BluetoothRemoteGattCharacteristic* control_point_length =
+      u2f_service->GetCharacteristic(*control_point_length_id_);
+  if (!control_point_length) {
+    DLOG(ERROR) << "No Control Point Length characteristic present.";
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
+
+  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+  control_point_length->ReadRemoteCharacteristic(
+      base::Bind(OnReadControlPointLength, copyable_callback),
+      base::Bind(OnReadControlPointLengthError, copyable_callback));
+}
+
+void U2fBleConnection::ReadServiceRevisions(ServiceRevisionsCallback callback) {
+  const BluetoothRemoteGattService* u2f_service = GetU2fService();
+  if (!u2f_service) {
+    std::move(callback).Run({});
+    return;
+  }
+
+  DCHECK(service_revision_id_ || service_revision_bitfield_id_);
+  BluetoothRemoteGattCharacteristic* service_revision =
+      service_revision_id_
+          ? u2f_service->GetCharacteristic(*service_revision_id_)
+          : nullptr;
+
+  BluetoothRemoteGattCharacteristic* service_revision_bitfield =
+      service_revision_bitfield_id_
+          ? u2f_service->GetCharacteristic(*service_revision_bitfield_id_)
+          : nullptr;
+
+  if (!service_revision && !service_revision_bitfield) {
+    DLOG(ERROR) << "Service Revision Characteristics do not exist.";
+    std::move(callback).Run({});
+    return;
+  }
+
+  // Start from a clean state.
+  service_revisions_.clear();
+
+  // In order to obtain the full set of supported service revisions it is
+  // possible that both the |service_revision_| and |service_revision_bitfield_|
+  // characteristics must be read. Potentially we need to take the union of
+  // the individually supported service revisions, hence the indirection to
+  // ReturnServiceRevisions() is introduced.
+  base::Closure copyable_callback = base::AdaptCallbackForRepeating(
+      base::BindOnce(&U2fBleConnection::ReturnServiceRevisions,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
+
+  // If the Service Revision Bitfield characteristic is not present, only
+  // attempt to read the Service Revision characteristic.
+  if (!service_revision_bitfield) {
+    service_revision->ReadRemoteCharacteristic(
+        base::Bind(&U2fBleConnection::OnReadServiceRevision,
+                   weak_factory_.GetWeakPtr(), copyable_callback),
+        base::Bind(&U2fBleConnection::OnReadServiceRevisionError,
+                   weak_factory_.GetWeakPtr(), copyable_callback));
+    return;
+  }
+
+  // If the Service Revision characteristic is not present, only
+  // attempt to read the Service Revision Bitfield characteristic.
+  if (!service_revision) {
+    service_revision_bitfield->ReadRemoteCharacteristic(
+        base::Bind(&U2fBleConnection::OnReadServiceRevisionBitfield,
+                   weak_factory_.GetWeakPtr(), copyable_callback),
+        base::Bind(&U2fBleConnection::OnReadServiceRevisionBitfieldError,
+                   weak_factory_.GetWeakPtr(), copyable_callback));
+    return;
+  }
+
+  // This is the case where both characteristics are present. These reads can
+  // happen in parallel, but both must finish before a result can be returned.
+  // Hence a BarrierClosure is introduced invoking ReturnServiceRevisions() once
+  // both characteristic reads are done.
+  base::RepeatingClosure barrier_closure =
+      base::BarrierClosure(2, copyable_callback);
+
+  service_revision->ReadRemoteCharacteristic(
+      base::Bind(&U2fBleConnection::OnReadServiceRevision,
+                 weak_factory_.GetWeakPtr(), barrier_closure),
+      base::Bind(&U2fBleConnection::OnReadServiceRevisionError,
+                 weak_factory_.GetWeakPtr(), barrier_closure));
+
+  service_revision_bitfield->ReadRemoteCharacteristic(
+      base::Bind(&U2fBleConnection::OnReadServiceRevisionBitfield,
+                 weak_factory_.GetWeakPtr(), barrier_closure),
+      base::Bind(&U2fBleConnection::OnReadServiceRevisionBitfieldError,
+                 weak_factory_.GetWeakPtr(), barrier_closure));
+}
+
+void U2fBleConnection::WriteControlPoint(const std::vector<uint8_t>& data,
+                                         WriteCallback callback) {
+  const BluetoothRemoteGattService* u2f_service = GetU2fService();
+  if (!u2f_service) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  BluetoothRemoteGattCharacteristic* control_point =
+      u2f_service->GetCharacteristic(*control_point_id_);
+  if (!control_point) {
+    DLOG(ERROR) << "Control Point characteristic not present.";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+  control_point->WriteRemoteCharacteristic(
+      data, base::Bind(OnWrite, copyable_callback),
+      base::Bind(OnWriteError, copyable_callback));
+}
+
+void U2fBleConnection::WriteServiceRevision(ServiceRevision service_revision,
+                                            WriteCallback callback) {
+  const BluetoothRemoteGattService* u2f_service = GetU2fService();
+  if (!u2f_service) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  BluetoothRemoteGattCharacteristic* service_revision_bitfield =
+      u2f_service->GetCharacteristic(*service_revision_bitfield_id_);
+  if (!service_revision_bitfield) {
+    DLOG(ERROR) << "Service Revision Bitfield characteristic not present.";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  std::vector<uint8_t> payload;
+  switch (service_revision) {
+    case ServiceRevision::VERSION_1_1:
+      payload.push_back(0x80);
+      break;
+    case ServiceRevision::VERSION_1_2:
+      payload.push_back(0x40);
+      break;
+    default:
+      DLOG(ERROR)
+          << "Write Service Revision Failed: Unsupported Service Revision.";
+      std::move(callback).Run(false);
+      return;
+  }
+
+  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+  service_revision_bitfield->WriteRemoteCharacteristic(
+      payload, base::Bind(OnWrite, copyable_callback),
+      base::Bind(OnWriteError, copyable_callback));
+}
+
+void U2fBleConnection::OnGetAdapter(scoped_refptr<BluetoothAdapter> adapter) {
+  if (!adapter) {
+    DLOG(ERROR) << "Failed to get Adapter.";
+    OnConnectionError();
+    return;
+  }
+
+  DVLOG(2) << "Got Adapter: " << adapter->GetAddress();
+  adapter_ = std::move(adapter);
+  adapter_->AddObserver(this);
+  CreateGattConnection();
+}
+
+void U2fBleConnection::CreateGattConnection() {
+  BluetoothDevice* device = adapter_->GetDevice(address_);
+  if (!device) {
+    DLOG(ERROR) << "Failed to get Device.";
+    OnConnectionError();
+    return;
+  }
+
+  device->CreateGattConnection(
+      base::Bind(&U2fBleConnection::OnCreateGattConnection,
+                 weak_factory_.GetWeakPtr()),
+      base::Bind(&U2fBleConnection::OnCreateGattConnectionError,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void U2fBleConnection::OnCreateGattConnection(
+    std::unique_ptr<BluetoothGattConnection> connection) {
+  connection_ = std::move(connection);
+
+  BluetoothDevice* device = adapter_->GetDevice(address_);
+  if (!device) {
+    DLOG(ERROR) << "Failed to get Device.";
+    OnConnectionError();
+    return;
+  }
+
+  if (device->IsGattServicesDiscoveryComplete())
+    ConnectToU2fService();
+}
+
+void U2fBleConnection::OnCreateGattConnectionError(
+    BluetoothDevice::ConnectErrorCode error_code) {
+  DLOG(ERROR) << "CreateGattConnection() failed: " << ToString(error_code);
+  OnConnectionError();
+}
+
+void U2fBleConnection::ConnectToU2fService() {
+  BluetoothDevice* device = adapter_->GetDevice(address_);
+  if (!device) {
+    DLOG(ERROR) << "Failed to get Device.";
+    OnConnectionError();
+    return;
+  }
+
+  DCHECK(device->IsGattServicesDiscoveryComplete());
+  const std::vector<BluetoothRemoteGattService*> services =
+      device->GetGattServices();
+  auto found =
+      std::find_if(services.begin(), services.end(), [](const auto* service) {
+        return service->GetUUID().canonical_value() == U2F_SERVICE_UUID;
+      });
+
+  if (found == services.end()) {
+    DLOG(ERROR) << "Failed to get U2F Service.";
+    OnConnectionError();
+    return;
+  }
+
+  const BluetoothRemoteGattService* u2f_service = *found;
+  u2f_service_id_ = u2f_service->GetIdentifier();
+  DVLOG(2) << "Got U2F Service: " << *u2f_service_id_;
+
+  for (const auto* characteristic : u2f_service->GetCharacteristics()) {
+    // NOTE: Since GetUUID() returns a temporary |uuid| can't be a reference,
+    // even though canonical_value() returns a const reference.
+    const std::string uuid = characteristic->GetUUID().canonical_value();
+    if (uuid == U2F_CONTROL_POINT_LENGTH_UUID) {
+      control_point_length_id_ = characteristic->GetIdentifier();
+      DVLOG(2) << "Got U2F Control Point Length: " << *control_point_length_id_;
+    } else if (uuid == U2F_CONTROL_POINT_UUID) {
+      control_point_id_ = characteristic->GetIdentifier();
+      DVLOG(2) << "Got U2F Control Point: " << *control_point_id_;
+    } else if (uuid == U2F_STATUS_UUID) {
+      status_id_ = characteristic->GetIdentifier();
+      DVLOG(2) << "Got U2F Status: " << *status_id_;
+    } else if (uuid == U2F_SERVICE_REVISION_UUID) {
+      service_revision_id_ = characteristic->GetIdentifier();
+      DVLOG(2) << "Got U2F Service Revision: " << *service_revision_id_;
+    } else if (uuid == U2F_SERVICE_REVISION_BITFIELD_UUID) {
+      service_revision_bitfield_id_ = characteristic->GetIdentifier();
+      DVLOG(2) << "Got U2F Service Revision Bitfield: "
+               << *service_revision_bitfield_id_;
+    }
+  }
+
+  if (!control_point_length_id_ || !control_point_id_ || !status_id_ ||
+      (!service_revision_id_ && !service_revision_bitfield_id_)) {
+    DLOG(ERROR) << "U2F characteristics missing.";
+    OnConnectionError();
+    return;
+  }
+
+  u2f_service->GetCharacteristic(*status_id_)
+      ->StartNotifySession(
+          base::Bind(&U2fBleConnection::OnStartNotifySession,
+                     weak_factory_.GetWeakPtr()),
+          base::Bind(&U2fBleConnection::OnStartNotifySessionError,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void U2fBleConnection::OnStartNotifySession(
+    std::unique_ptr<BluetoothGattNotifySession> notify_session) {
+  notify_session_ = std::move(notify_session);
+  DVLOG(2) << "Created notification session. Connection established.";
+  connection_status_callback_.Run(true);
+}
+
+void U2fBleConnection::OnStartNotifySessionError(
+    BluetoothGattService::GattErrorCode error_code) {
+  DLOG(ERROR) << "StartNotifySession() failed: " << ToString(error_code);
+  OnConnectionError();
+}
+
+void U2fBleConnection::OnConnectionError() {
+  connection_status_callback_.Run(false);
+
+  connection_.reset();
+  notify_session_.reset();
+
+  u2f_service_id_.reset();
+  control_point_length_id_.reset();
+  control_point_id_.reset();
+  status_id_.reset();
+  service_revision_id_.reset();
+  service_revision_bitfield_id_.reset();
+}
+
+const BluetoothRemoteGattService* U2fBleConnection::GetU2fService() const {
+  if (!adapter_) {
+    DLOG(ERROR) << "No adapter present.";
+    return nullptr;
+  }
+
+  const BluetoothDevice* device = adapter_->GetDevice(address_);
+  if (!device) {
+    DLOG(ERROR) << "No device present.";
+    return nullptr;
+  }
+
+  if (!u2f_service_id_) {
+    DLOG(ERROR) << "Unknown U2F service id.";
+    return nullptr;
+  }
+
+  const BluetoothRemoteGattService* u2f_service =
+      device->GetGattService(*u2f_service_id_);
+  if (!u2f_service) {
+    DLOG(ERROR) << "No U2F service present.";
+    return nullptr;
+  }
+
+  return u2f_service;
+}
+
+void U2fBleConnection::DeviceAdded(BluetoothAdapter* adapter,
+                                   BluetoothDevice* device) {
+  if (adapter != adapter_ || device->GetAddress() != address_)
+    return;
+  CreateGattConnection();
+}
+
+void U2fBleConnection::DeviceAddressChanged(BluetoothAdapter* adapter,
+                                            BluetoothDevice* device,
+                                            const std::string& old_address) {
+  if (adapter != adapter_ || old_address != address_)
+    return;
+  address_ = device->GetAddress();
+}
+
+void U2fBleConnection::DeviceChanged(BluetoothAdapter* adapter,
+                                     BluetoothDevice* device) {
+  if (adapter != adapter_ || device->GetAddress() != address_)
+    return;
+  if (!device->IsGattConnected()) {
+    DLOG(ERROR) << "GATT Disconnected: " << device->GetAddress();
+    OnConnectionError();
+  }
+}
+
+void U2fBleConnection::GattCharacteristicValueChanged(
+    BluetoothAdapter* adapter,
+    BluetoothRemoteGattCharacteristic* characteristic,
+    const std::vector<uint8_t>& value) {
+  if (characteristic->GetIdentifier() != status_id_)
+    return;
+  DVLOG(2) << "Status characteristic value changed.";
+  read_callback_.Run(value);
+}
+
+void U2fBleConnection::GattServicesDiscovered(BluetoothAdapter* adapter,
+                                              BluetoothDevice* device) {
+  if (adapter != adapter_ || device->GetAddress() != address_)
+    return;
+  ConnectToU2fService();
+}
+
+// static
+void U2fBleConnection::OnReadControlPointLength(
+    ControlPointLengthCallback callback,
+    const std::vector<uint8_t>& value) {
+  if (value.size() != 2) {
+    DLOG(ERROR) << "Wrong Control Point Length: " << value.size() << " bytes";
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
+
+  uint16_t length = (value[0] << 8) | value[1];
+  DVLOG(2) << "Control Point Length: " << length;
+  std::move(callback).Run(length);
+}
+
+// static
+void U2fBleConnection::OnReadControlPointLengthError(
+    ControlPointLengthCallback callback,
+    BluetoothGattService::GattErrorCode error_code) {
+  DLOG(ERROR) << "Error reading Control Point Length: " << ToString(error_code);
+  std::move(callback).Run(base::nullopt);
+}
+
+void U2fBleConnection::OnReadServiceRevision(
+    base::OnceClosure callback,
+    const std::vector<uint8_t>& value) {
+  std::string service_revision(value.begin(), value.end());
+  DVLOG(2) << "Service Revision: " << service_revision;
+
+  if (service_revision == "1.0") {
+    service_revisions_.insert(ServiceRevision::VERSION_1_0);
+  } else if (service_revision == "1.1") {
+    service_revisions_.insert(ServiceRevision::VERSION_1_1);
+  } else if (service_revision == "1.2") {
+    service_revisions_.insert(ServiceRevision::VERSION_1_2);
+  } else {
+    DLOG(ERROR) << "Unknown Service Revision: " << service_revision;
+    std::move(callback).Run();
+    return;
+  }
+
+  std::move(callback).Run();
+}
+
+void U2fBleConnection::OnReadServiceRevisionError(
+    base::OnceClosure callback,
+    BluetoothGattService::GattErrorCode error_code) {
+  DLOG(ERROR) << "Error reading Service Revision: " << ToString(error_code);
+  std::move(callback).Run();
+}
+
+void U2fBleConnection::OnReadServiceRevisionBitfield(
+    base::OnceClosure callback,
+    const std::vector<uint8_t>& value) {
+  if (value.empty()) {
+    DLOG(ERROR) << "Service Revision Bitfield is empty.";
+    std::move(callback).Run();
+    return;
+  }
+
+  if (value.size() != 1u) {
+    DLOG(ERROR) << "Service Revision Bitfield has unexpected size: "
+                << value.size() << ". Ignoring all but the first byte.";
+  }
+
+  const uint8_t bitset = value[0];
+  if (bitset & 0x3F) {
+    DLOG(ERROR) << "Service Revision Bitfield has unexpected bits set: 0x"
+                << std::hex << (bitset & 0x3F)
+                << ". Ignoring all but the first two bits.";
+  }
+
+  if (bitset & 0x80) {
+    service_revisions_.insert(ServiceRevision::VERSION_1_1);
+    DVLOG(2) << "Detected Support for Service Revision 1.1";
+  }
+
+  if (bitset & 0x40) {
+    service_revisions_.insert(ServiceRevision::VERSION_1_2);
+    DVLOG(2) << "Detected Support for Service Revision 1.2";
+  }
+
+  std::move(callback).Run();
+}
+
+void U2fBleConnection::OnReadServiceRevisionBitfieldError(
+    base::OnceClosure callback,
+    BluetoothGattService::GattErrorCode error_code) {
+  DLOG(ERROR) << "Error reading Service Revision Bitfield: "
+              << ToString(error_code);
+  std::move(callback).Run();
+}
+
+void U2fBleConnection::ReturnServiceRevisions(
+    ServiceRevisionsCallback callback) {
+  std::move(callback).Run(std::move(service_revisions_));
+}
+
+// static
+void U2fBleConnection::OnWrite(WriteCallback callback) {
+  DVLOG(2) << "Write succeeded.";
+  std::move(callback).Run(true);
+}
+
+// static
+void U2fBleConnection::OnWriteError(
+    WriteCallback callback,
+    BluetoothGattService::GattErrorCode error_code) {
+  DLOG(ERROR) << "Write Failed: " << ToString(error_code);
+  std::move(callback).Run(false);
+}
+
+}  // namespace device
diff --git a/device/u2f/u2f_ble_connection.h b/device/u2f/u2f_ble_connection.h
new file mode 100644
index 0000000..a524ae7
--- /dev/null
+++ b/device/u2f/u2f_ble_connection.h
@@ -0,0 +1,157 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_U2F_U2F_BLE_CONNECTION_H_
+#define DEVICE_U2F_U2F_BLE_CONNECTION_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+
+namespace device {
+
+class BluetoothGattConnection;
+class BluetoothGattNotifySession;
+class BluetoothRemoteGattCharacteristic;
+class BluetoothRemoteGattService;
+
+// A connection to the U2F service of an authenticator over BLE. Detailed
+// specification of the BLE device can be found here:
+// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-bt-protocol-v1.2-ps-20170411.html#h2_gatt-service-description
+//
+// Currently this code does not handle devices that need pairing. This is fine
+// for non-BlueZ platforms, as here accessing a protected characteristic will
+// trigger an OS level dialog if pairing is required. However, for BlueZ
+// platforms pairing must have been done externally, for example using the
+// `bluetoothctl` command.
+//
+// TODO(crbug.com/763303): Add support for pairing from within this class and
+// provide users with an option to manually specify a PIN code.
+class U2fBleConnection : public BluetoothAdapter::Observer {
+ public:
+  enum class ServiceRevision {
+    VERSION_1_0,
+    VERSION_1_1,
+    VERSION_1_2,
+  };
+
+  // This callback informs clients repeatedly about changes in the device
+  // connection. This class makes an initial connection attempt on construction,
+  // which result in returned via this callback. Future invocations happen if
+  // devices connect or disconnect from the adapter.
+  using ConnectionStatusCallback = base::RepeatingCallback<void(bool)>;
+  using WriteCallback = base::OnceCallback<void(bool)>;
+  using ReadCallback = base::RepeatingCallback<void(std::vector<uint8_t>)>;
+  using ControlPointLengthCallback =
+      base::OnceCallback<void(base::Optional<uint16_t>)>;
+  using ServiceRevisionsCallback =
+      base::OnceCallback<void(std::set<ServiceRevision>)>;
+
+  U2fBleConnection(std::string device_address,
+                   ConnectionStatusCallback connection_status_callback,
+                   ReadCallback read_callback);
+  ~U2fBleConnection() override;
+
+  const std::string& address() const { return address_; }
+
+  void ReadControlPointLength(ControlPointLengthCallback callback);
+  void ReadServiceRevisions(ServiceRevisionsCallback callback);
+  void WriteControlPoint(const std::vector<uint8_t>& data, WriteCallback);
+  void WriteServiceRevision(ServiceRevision service_revision,
+                            WriteCallback callback);
+
+ private:
+  // BluetoothAdapter::Observer:
+  void DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) override;
+  void DeviceAddressChanged(BluetoothAdapter* adapter,
+                            BluetoothDevice* device,
+                            const std::string& old_address) override;
+  void DeviceChanged(BluetoothAdapter* adapter,
+                     BluetoothDevice* device) override;
+  void GattCharacteristicValueChanged(
+      BluetoothAdapter* adapter,
+      BluetoothRemoteGattCharacteristic* characteristic,
+      const std::vector<uint8_t>& value) override;
+  void GattServicesDiscovered(BluetoothAdapter* adapter,
+                              BluetoothDevice* device) override;
+
+  void OnGetAdapter(scoped_refptr<BluetoothAdapter> adapter);
+
+  void CreateGattConnection();
+  void OnCreateGattConnection(
+      std::unique_ptr<BluetoothGattConnection> connection);
+  void OnCreateGattConnectionError(
+      BluetoothDevice::ConnectErrorCode error_code);
+
+  void ConnectToU2fService();
+
+  void OnStartNotifySession(
+      std::unique_ptr<BluetoothGattNotifySession> notify_session);
+  void OnStartNotifySessionError(
+      BluetoothGattService::GattErrorCode error_code);
+
+  void OnConnectionError();
+
+  const BluetoothRemoteGattService* GetU2fService() const;
+
+  static void OnReadControlPointLength(ControlPointLengthCallback callback,
+                                       const std::vector<uint8_t>& value);
+  static void OnReadControlPointLengthError(
+      ControlPointLengthCallback callback,
+      BluetoothGattService::GattErrorCode error_code);
+
+  void OnReadServiceRevision(base::OnceClosure callback,
+                             const std::vector<uint8_t>& value);
+  void OnReadServiceRevisionError(
+      base::OnceClosure callback,
+      BluetoothGattService::GattErrorCode error_code);
+
+  void OnReadServiceRevisionBitfield(base::OnceClosure callback,
+                                     const std::vector<uint8_t>& value);
+  void OnReadServiceRevisionBitfieldError(
+      base::OnceClosure callback,
+      BluetoothGattService::GattErrorCode error_code);
+  void ReturnServiceRevisions(ServiceRevisionsCallback callback);
+
+  static void OnWrite(WriteCallback callback);
+  static void OnWriteError(WriteCallback callback,
+                           BluetoothGattService::GattErrorCode error_code);
+
+  std::string address_;
+  ConnectionStatusCallback connection_status_callback_;
+  ReadCallback read_callback_;
+
+  scoped_refptr<BluetoothAdapter> adapter_;
+  std::unique_ptr<BluetoothGattConnection> connection_;
+  std::unique_ptr<BluetoothGattNotifySession> notify_session_;
+
+  base::Optional<std::string> u2f_service_id_;
+  base::Optional<std::string> control_point_length_id_;
+  base::Optional<std::string> control_point_id_;
+  base::Optional<std::string> status_id_;
+  base::Optional<std::string> service_revision_id_;
+  base::Optional<std::string> service_revision_bitfield_id_;
+
+  std::set<ServiceRevision> service_revisions_;
+
+  base::WeakPtrFactory<U2fBleConnection> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(U2fBleConnection);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_U2F_U2F_BLE_CONNECTION_H_
diff --git a/device/u2f/u2f_ble_connection_unittest.cc b/device/u2f/u2f_ble_connection_unittest.cc
new file mode 100644
index 0000000..d8aee58
--- /dev/null
+++ b/device/u2f/u2f_ble_connection_unittest.cc
@@ -0,0 +1,767 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/u2f/u2f_ble_connection.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/bluetooth_test.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_notify_session.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
+#include "device/u2f/u2f_ble_uuids.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_ANDROID)
+#include "device/bluetooth/test/bluetooth_test_android.h"
+#elif defined(OS_MACOSX)
+#include "device/bluetooth/test/bluetooth_test_mac.h"
+#elif defined(OS_WIN)
+#include "device/bluetooth/test/bluetooth_test_win.h"
+#elif defined(OS_CHROMEOS) || defined(OS_LINUX)
+#include "device/bluetooth/test/bluetooth_test_bluez.h"
+#endif
+
+namespace device {
+
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Invoke;
+using ::testing::IsEmpty;
+using ::testing::Return;
+
+using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
+using NiceMockBluetoothDevice = ::testing::NiceMock<MockBluetoothDevice>;
+using NiceMockBluetoothGattService =
+    ::testing::NiceMock<MockBluetoothGattService>;
+using NiceMockBluetoothGattCharacteristic =
+    ::testing::NiceMock<MockBluetoothGattCharacteristic>;
+using NiceMockBluetoothGattConnection =
+    ::testing::NiceMock<MockBluetoothGattConnection>;
+using NiceMockBluetoothGattNotifySession =
+    ::testing::NiceMock<MockBluetoothGattNotifySession>;
+
+namespace {
+
+std::vector<uint8_t> ToByteVector(base::StringPiece str) {
+  return std::vector<uint8_t>(str.begin(), str.end());
+}
+
+BluetoothDevice* GetMockDevice(MockBluetoothAdapter* adapter,
+                               const std::string& address) {
+  const std::vector<BluetoothDevice*> devices = adapter->GetMockDevices();
+  auto found = std::find_if(devices.begin(), devices.end(),
+                            [&address](const auto* device) {
+                              return device->GetAddress() == address;
+                            });
+  return found != devices.end() ? *found : nullptr;
+}
+
+class TestConnectionStatusCallback {
+ public:
+  void OnStatus(bool status) {
+    status_ = status;
+    run_loop_->Quit();
+  }
+
+  bool WaitForResult() {
+    run_loop_->Run();
+    run_loop_.emplace();
+    return status_;
+  }
+
+  U2fBleConnection::ConnectionStatusCallback GetCallback() {
+    return base::BindRepeating(&TestConnectionStatusCallback::OnStatus,
+                               base::Unretained(this));
+  }
+
+ private:
+  bool status_ = false;
+  base::Optional<base::RunLoop> run_loop_{base::in_place};
+};
+
+class TestReadCallback {
+ public:
+  void OnRead(std::vector<uint8_t> value) {
+    value_ = std::move(value);
+    run_loop_->Quit();
+  }
+
+  const std::vector<uint8_t> WaitForResult() {
+    run_loop_->Run();
+    run_loop_.emplace();
+    return value_;
+  }
+
+  U2fBleConnection::ReadCallback GetCallback() {
+    return base::BindRepeating(&TestReadCallback::OnRead,
+                               base::Unretained(this));
+  }
+
+ private:
+  std::vector<uint8_t> value_;
+  base::Optional<base::RunLoop> run_loop_{base::in_place};
+};
+
+class TestReadControlPointLengthCallback {
+ public:
+  void OnReadControlPointLength(base::Optional<uint16_t> value) {
+    value_ = std::move(value);
+    run_loop_->Quit();
+  }
+
+  const base::Optional<uint16_t>& WaitForResult() {
+    run_loop_->Run();
+    run_loop_.emplace();
+    return value_;
+  }
+
+  U2fBleConnection::ControlPointLengthCallback GetCallback() {
+    return base::BindOnce(
+        &TestReadControlPointLengthCallback::OnReadControlPointLength,
+        base::Unretained(this));
+  }
+
+ private:
+  base::Optional<uint16_t> value_;
+  base::Optional<base::RunLoop> run_loop_{base::in_place};
+};
+
+class TestReadServiceRevisionsCallback {
+ public:
+  void OnReadServiceRevisions(
+      std::set<U2fBleConnection::ServiceRevision> revisions) {
+    revisions_ = std::move(revisions);
+    run_loop_->Quit();
+  }
+
+  const std::set<U2fBleConnection::ServiceRevision>& WaitForResult() {
+    run_loop_->Run();
+    run_loop_.emplace();
+    return revisions_;
+  }
+
+  U2fBleConnection::ServiceRevisionsCallback GetCallback() {
+    return base::BindOnce(
+        &TestReadServiceRevisionsCallback::OnReadServiceRevisions,
+        base::Unretained(this));
+  }
+
+ private:
+  std::set<U2fBleConnection::ServiceRevision> revisions_;
+  base::Optional<base::RunLoop> run_loop_{base::in_place};
+};
+
+class TestWriteCallback {
+ public:
+  void OnWrite(bool success) {
+    success_ = success;
+    run_loop_->Quit();
+  }
+
+  bool WaitForResult() {
+    run_loop_->Run();
+    run_loop_.emplace();
+    return success_;
+  }
+
+  U2fBleConnection::WriteCallback GetCallback() {
+    return base::BindOnce(&TestWriteCallback::OnWrite, base::Unretained(this));
+  }
+
+ private:
+  bool success_ = false;
+  base::Optional<base::RunLoop> run_loop_{base::in_place};
+};
+
+}  // namespace
+
+class U2fBleConnectionTest : public ::testing::Test {
+ public:
+  U2fBleConnectionTest() {
+    ON_CALL(*adapter_, GetDevice(_))
+        .WillByDefault(Invoke([this](const std::string& address) {
+          return GetMockDevice(adapter_.get(), address);
+        }));
+
+    BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
+  }
+
+  void AddU2Device(const std::string& device_address) {
+    auto u2f_device = std::make_unique<NiceMockBluetoothDevice>(
+        adapter_.get(), /* bluetooth_class */ 0u,
+        BluetoothTest::kTestDeviceNameU2f, device_address, /* paired */ true,
+        /* connected */ false);
+    u2f_device_ = u2f_device.get();
+    adapter_->AddMockDevice(std::move(u2f_device));
+
+    ON_CALL(*u2f_device_, GetGattServices())
+        .WillByDefault(
+            Invoke(u2f_device_, &MockBluetoothDevice::GetMockServices));
+
+    ON_CALL(*u2f_device_, GetGattService(_))
+        .WillByDefault(
+            Invoke(u2f_device_, &MockBluetoothDevice::GetMockService));
+    AddU2fService();
+  }
+
+  void SetupConnectingU2fDevice(const std::string& device_address) {
+    auto run_cb_with_connection = [this, &device_address](
+                                      const auto& callback,
+                                      const auto& error_callback) {
+      auto connection = std::make_unique<NiceMockBluetoothGattConnection>(
+          adapter_, device_address);
+      connection_ = connection.get();
+      callback.Run(std::move(connection));
+    };
+
+    auto run_cb_with_notify_session = [this](const auto& callback,
+                                             const auto& error_callback) {
+      auto notify_session =
+          std::make_unique<NiceMockBluetoothGattNotifySession>(
+              u2f_status_->GetWeakPtr());
+      notify_session_ = notify_session.get();
+      callback.Run(std::move(notify_session));
+    };
+
+    ON_CALL(*u2f_device_, CreateGattConnection(_, _))
+        .WillByDefault(Invoke(run_cb_with_connection));
+
+    ON_CALL(*u2f_device_, IsGattServicesDiscoveryComplete())
+        .WillByDefault(Return(true));
+
+    ON_CALL(*u2f_status_, StartNotifySession(_, _))
+        .WillByDefault(Invoke(run_cb_with_notify_session));
+  }
+
+  void SimulateDisconnect(const std::string& device_address) {
+    if (u2f_device_->GetAddress() != device_address)
+      return;
+
+    u2f_device_->SetConnected(false);
+    adapter_->NotifyDeviceChanged(u2f_device_);
+  }
+
+  void SimulateDeviceAddressChange(const std::string& old_address,
+                                   const std::string& new_address) {
+    if (!u2f_device_ || u2f_device_->GetAddress() != old_address)
+      return;
+
+    ON_CALL(*u2f_device_, GetAddress()).WillByDefault(Return(new_address));
+
+    adapter_->NotifyDeviceChanged(u2f_device_);
+    for (auto& observer : adapter_->GetObservers())
+      observer.DeviceAddressChanged(adapter_.get(), u2f_device_, old_address);
+  }
+
+  void NotifyDeviceAdded(const std::string& device_address) {
+    auto* device = adapter_->GetDevice(device_address);
+    if (!device)
+      return;
+
+    for (auto& observer : adapter_->GetObservers())
+      observer.DeviceAdded(adapter_.get(), device);
+  }
+
+  void NotifyStatusChanged(const std::vector<uint8_t>& value) {
+    for (auto& observer : adapter_->GetObservers())
+      observer.GattCharacteristicValueChanged(adapter_.get(), u2f_status_,
+                                              value);
+  }
+
+  void SetNextReadControlPointLengthReponse(bool success,
+                                            const std::vector<uint8_t>& value) {
+    EXPECT_CALL(*u2f_control_point_length_, ReadRemoteCharacteristic(_, _))
+        .WillOnce(Invoke([success, value](const auto& callback,
+                                          const auto& error_callback) {
+          success ? callback.Run(value)
+                  : error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+        }));
+  }
+
+  void SetNextReadServiceRevisionResponse(bool success,
+                                          const std::vector<uint8_t>& value) {
+    EXPECT_CALL(*u2f_service_revision_, ReadRemoteCharacteristic(_, _))
+        .WillOnce(Invoke([success, value](const auto& callback,
+                                          const auto& error_callback) {
+          success ? callback.Run(value)
+                  : error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+        }));
+  }
+
+  void SetNextReadServiceRevisionBitfieldResponse(
+      bool success,
+      const std::vector<uint8_t>& value) {
+    EXPECT_CALL(*u2f_service_revision_bitfield_, ReadRemoteCharacteristic(_, _))
+        .WillOnce(Invoke([success, value](const auto& callback,
+                                          const auto& error_callback) {
+          success ? callback.Run(value)
+                  : error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+        }));
+  }
+
+  void SetNextWriteControlPointResponse(bool success) {
+    EXPECT_CALL(*u2f_control_point_, WriteRemoteCharacteristic(_, _, _))
+        .WillOnce(Invoke([success](const auto& data, const auto& callback,
+                                   const auto& error_callback) {
+          success ? callback.Run()
+                  : error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+        }));
+  }
+
+  void SetNextWriteServiceRevisionResponse(bool success) {
+    EXPECT_CALL(*u2f_service_revision_bitfield_,
+                WriteRemoteCharacteristic(_, _, _))
+        .WillOnce(Invoke([success](const auto& data, const auto& callback,
+                                   const auto& error_callback) {
+          success ? callback.Run()
+                  : error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+        }));
+  }
+
+  void AddU2fService() {
+    auto u2f_service = std::make_unique<NiceMockBluetoothGattService>(
+        u2f_device_, "u2f_service", BluetoothUUID(U2F_SERVICE_UUID),
+        /* is_primary */ true, /* is_local */ false);
+    u2f_service_ = u2f_service.get();
+    u2f_device_->AddMockService(std::move(u2f_service));
+
+    ON_CALL(*u2f_service_, GetCharacteristics())
+        .WillByDefault(Invoke(
+            u2f_service_, &MockBluetoothGattService::GetMockCharacteristics));
+
+    ON_CALL(*u2f_service_, GetCharacteristic(_))
+        .WillByDefault(Invoke(
+            u2f_service_, &MockBluetoothGattService::GetMockCharacteristic));
+    AddU2fCharacteristics();
+  }
+
+  void AddU2fCharacteristics() {
+    const bool is_local = false;
+    {
+      auto u2f_control_point =
+          std::make_unique<NiceMockBluetoothGattCharacteristic>(
+              u2f_service_, "u2f_control_point",
+              BluetoothUUID(U2F_CONTROL_POINT_UUID), is_local,
+              BluetoothGattCharacteristic::PROPERTY_WRITE,
+              BluetoothGattCharacteristic::PERMISSION_NONE);
+      u2f_control_point_ = u2f_control_point.get();
+      u2f_service_->AddMockCharacteristic(std::move(u2f_control_point));
+    }
+
+    {
+      auto u2f_status = std::make_unique<NiceMockBluetoothGattCharacteristic>(
+          u2f_service_, "u2f_status", BluetoothUUID(U2F_STATUS_UUID), is_local,
+          BluetoothGattCharacteristic::PROPERTY_NOTIFY,
+          BluetoothGattCharacteristic::PERMISSION_NONE);
+      u2f_status_ = u2f_status.get();
+      u2f_service_->AddMockCharacteristic(std::move(u2f_status));
+    }
+
+    {
+      auto u2f_control_point_length =
+          std::make_unique<NiceMockBluetoothGattCharacteristic>(
+              u2f_service_, "u2f_control_point_length",
+              BluetoothUUID(U2F_CONTROL_POINT_LENGTH_UUID), is_local,
+              BluetoothGattCharacteristic::PROPERTY_READ,
+              BluetoothGattCharacteristic::PERMISSION_NONE);
+      u2f_control_point_length_ = u2f_control_point_length.get();
+      u2f_service_->AddMockCharacteristic(std::move(u2f_control_point_length));
+    }
+
+    {
+      auto u2f_service_revision =
+          std::make_unique<NiceMockBluetoothGattCharacteristic>(
+              u2f_service_, "u2f_service_revision",
+              BluetoothUUID(U2F_SERVICE_REVISION_UUID), is_local,
+              BluetoothGattCharacteristic::PROPERTY_READ,
+              BluetoothGattCharacteristic::PERMISSION_NONE);
+      u2f_service_revision_ = u2f_service_revision.get();
+      u2f_service_->AddMockCharacteristic(std::move(u2f_service_revision));
+    }
+
+    {
+      auto u2f_service_revision_bitfield =
+          std::make_unique<NiceMockBluetoothGattCharacteristic>(
+              u2f_service_, "u2f_service_revision_bitfield",
+              BluetoothUUID(U2F_SERVICE_REVISION_BITFIELD_UUID), is_local,
+              BluetoothGattCharacteristic::PROPERTY_READ |
+                  BluetoothGattCharacteristic::PROPERTY_WRITE,
+              BluetoothGattCharacteristic::PERMISSION_NONE);
+      u2f_service_revision_bitfield_ = u2f_service_revision_bitfield.get();
+      u2f_service_->AddMockCharacteristic(
+          std::move(u2f_service_revision_bitfield));
+    }
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  scoped_refptr<MockBluetoothAdapter> adapter_ =
+      base::MakeRefCounted<NiceMockBluetoothAdapter>();
+
+  MockBluetoothDevice* u2f_device_;
+  MockBluetoothGattService* u2f_service_;
+
+  MockBluetoothGattCharacteristic* u2f_control_point_;
+  MockBluetoothGattCharacteristic* u2f_status_;
+  MockBluetoothGattCharacteristic* u2f_control_point_length_;
+  MockBluetoothGattCharacteristic* u2f_service_revision_;
+  MockBluetoothGattCharacteristic* u2f_service_revision_bitfield_;
+
+  MockBluetoothGattConnection* connection_;
+  MockBluetoothGattNotifySession* notify_session_;
+};
+
+TEST_F(U2fBleConnectionTest, Address) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  auto connect_do_nothing = [](bool) {};
+  auto read_do_nothing = [](std::vector<uint8_t>) {};
+
+  U2fBleConnection connection(device_address,
+                              base::BindRepeating(connect_do_nothing),
+                              base::BindRepeating(read_do_nothing));
+  EXPECT_EQ(device_address, connection.address());
+  AddU2Device(device_address);
+
+  SimulateDeviceAddressChange(device_address, "new_device_address");
+  EXPECT_EQ("new_device_address", connection.address());
+}
+
+TEST_F(U2fBleConnectionTest, DeviceNotPresent) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  auto do_nothing = [](std::vector<uint8_t>) {};
+
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(do_nothing));
+  bool result = connection_status_callback.WaitForResult();
+  EXPECT_FALSE(result);
+}
+
+TEST_F(U2fBleConnectionTest, PreConnected) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+
+  auto do_nothing = [](std::vector<uint8_t>) {};
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(do_nothing));
+  EXPECT_TRUE(connection_status_callback.WaitForResult());
+}
+
+TEST_F(U2fBleConnectionTest, PostConnected) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  auto do_nothing = [](std::vector<uint8_t>) {};
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(do_nothing));
+  bool result = connection_status_callback.WaitForResult();
+  EXPECT_FALSE(result);
+
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  NotifyDeviceAdded(device_address);
+  EXPECT_TRUE(connection_status_callback.WaitForResult());
+}
+
+TEST_F(U2fBleConnectionTest, DeviceDisconnect) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  auto do_nothing = [](std::vector<uint8_t>) {};
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(do_nothing));
+  bool result = connection_status_callback.WaitForResult();
+  EXPECT_TRUE(result);
+
+  SimulateDisconnect(device_address);
+  result = connection_status_callback.WaitForResult();
+  EXPECT_FALSE(result);
+}
+
+TEST_F(U2fBleConnectionTest, ReadStatusNotifications) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  TestReadCallback read_callback;
+
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              read_callback.GetCallback());
+  EXPECT_TRUE(connection_status_callback.WaitForResult());
+
+  std::vector<uint8_t> payload = ToByteVector("foo");
+  NotifyStatusChanged(payload);
+  EXPECT_EQ(payload, read_callback.WaitForResult());
+
+  payload = ToByteVector("bar");
+  NotifyStatusChanged(payload);
+  EXPECT_EQ(payload, read_callback.WaitForResult());
+}
+
+TEST_F(U2fBleConnectionTest, ReadControlPointLength) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  auto read_do_nothing = [](std::vector<uint8_t>) {};
+
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(read_do_nothing));
+  EXPECT_TRUE(connection_status_callback.WaitForResult());
+
+  TestReadControlPointLengthCallback length_callback;
+  SetNextReadControlPointLengthReponse(false, {});
+  connection.ReadControlPointLength(length_callback.GetCallback());
+  EXPECT_EQ(base::nullopt, length_callback.WaitForResult());
+
+  // The Control Point Length should consist of exactly two bytes, hence we
+  // EXPECT_EQ(base::nullopt) for payloads of size 0, 1 and 3.
+  SetNextReadControlPointLengthReponse(true, {});
+  connection.ReadControlPointLength(length_callback.GetCallback());
+  EXPECT_EQ(base::nullopt, length_callback.WaitForResult());
+
+  SetNextReadControlPointLengthReponse(true, {0xAB});
+  connection.ReadControlPointLength(length_callback.GetCallback());
+  EXPECT_EQ(base::nullopt, length_callback.WaitForResult());
+
+  SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD});
+  connection.ReadControlPointLength(length_callback.GetCallback());
+  EXPECT_EQ(0xABCD, *length_callback.WaitForResult());
+
+  SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD, 0xEF});
+  connection.ReadControlPointLength(length_callback.GetCallback());
+  EXPECT_EQ(base::nullopt, length_callback.WaitForResult());
+}
+
+TEST_F(U2fBleConnectionTest, ReadServiceRevisions) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  auto read_do_nothing = [](std::vector<uint8_t>) {};
+
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(read_do_nothing));
+  EXPECT_TRUE(connection_status_callback.WaitForResult());
+
+  TestReadServiceRevisionsCallback revisions_callback;
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(false, {});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(), IsEmpty());
+
+  SetNextReadServiceRevisionResponse(true, ToByteVector("bogus"));
+  SetNextReadServiceRevisionBitfieldResponse(false, {});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(), IsEmpty());
+
+  SetNextReadServiceRevisionResponse(true, ToByteVector("1.0"));
+  SetNextReadServiceRevisionBitfieldResponse(false, {});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_0));
+
+  SetNextReadServiceRevisionResponse(true, ToByteVector("1.1"));
+  SetNextReadServiceRevisionBitfieldResponse(false, {});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_1));
+
+  SetNextReadServiceRevisionResponse(true, ToByteVector("1.2"));
+  SetNextReadServiceRevisionBitfieldResponse(false, {});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_2));
+
+  // Version 1.3 currently does not exist, so this should be treated as an
+  // error.
+  SetNextReadServiceRevisionResponse(true, ToByteVector("1.3"));
+  SetNextReadServiceRevisionBitfieldResponse(false, {});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(), IsEmpty());
+
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(true, {0x00});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(), IsEmpty());
+
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(true, {0x80});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_1));
+
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(true, {0x40});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_2));
+
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(true, {0xC0});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_1,
+                          U2fBleConnection::ServiceRevision::VERSION_1_2));
+
+  // All bits except the first two should be ignored.
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(true, {0xFF});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_1,
+                          U2fBleConnection::ServiceRevision::VERSION_1_2));
+
+  // All bytes except the first one should be ignored.
+  SetNextReadServiceRevisionResponse(false, {});
+  SetNextReadServiceRevisionBitfieldResponse(true, {0xC0, 0xFF});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_1,
+                          U2fBleConnection::ServiceRevision::VERSION_1_2));
+
+  // The combination of a service revision string and bitfield should be
+  // supported as well.
+  SetNextReadServiceRevisionResponse(true, ToByteVector("1.0"));
+  SetNextReadServiceRevisionBitfieldResponse(true, {0xC0});
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(),
+              ElementsAre(U2fBleConnection::ServiceRevision::VERSION_1_0,
+                          U2fBleConnection::ServiceRevision::VERSION_1_1,
+                          U2fBleConnection::ServiceRevision::VERSION_1_2));
+}
+
+TEST_F(U2fBleConnectionTest, WriteControlPoint) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  auto read_do_nothing = [](std::vector<uint8_t>) {};
+
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(read_do_nothing));
+  bool result = connection_status_callback.WaitForResult();
+  EXPECT_TRUE(result);
+
+  TestWriteCallback write_callback;
+  SetNextWriteControlPointResponse(false);
+  connection.WriteControlPoint({}, write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_FALSE(result);
+
+  SetNextWriteControlPointResponse(true);
+  connection.WriteControlPoint({}, write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_TRUE(result);
+}
+
+TEST_F(U2fBleConnectionTest, WriteServiceRevision) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  auto read_do_nothing = [](std::vector<uint8_t>) {};
+
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(read_do_nothing));
+  bool result = connection_status_callback.WaitForResult();
+  EXPECT_TRUE(result);
+
+  // Expect that errors are properly propagated.
+  TestWriteCallback write_callback;
+  SetNextWriteServiceRevisionResponse(false);
+  connection.WriteServiceRevision(
+      U2fBleConnection::ServiceRevision::VERSION_1_1,
+      write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_FALSE(result);
+
+  // Expect a successful write of version 1.1.
+  SetNextWriteServiceRevisionResponse(true);
+  connection.WriteServiceRevision(
+      U2fBleConnection::ServiceRevision::VERSION_1_1,
+      write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_TRUE(result);
+
+  // Expect a successful write of version 1.2.
+  SetNextWriteServiceRevisionResponse(true);
+  connection.WriteServiceRevision(
+      U2fBleConnection::ServiceRevision::VERSION_1_2,
+      write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_TRUE(result);
+
+  // Writing version 1.0 to the bitfield is not intended, so this should fail.
+  connection.WriteServiceRevision(
+      U2fBleConnection::ServiceRevision::VERSION_1_0,
+      write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_FALSE(result);
+}
+
+TEST_F(U2fBleConnectionTest, ReadsAndWriteFailWhenDisconnected) {
+  const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+  TestConnectionStatusCallback connection_status_callback;
+
+  AddU2Device(device_address);
+  SetupConnectingU2fDevice(device_address);
+  auto do_nothing = [](std::vector<uint8_t>) {};
+  U2fBleConnection connection(device_address,
+                              connection_status_callback.GetCallback(),
+                              base::BindRepeating(do_nothing));
+  bool result = connection_status_callback.WaitForResult();
+  EXPECT_TRUE(result);
+
+  SimulateDisconnect(device_address);
+  result = connection_status_callback.WaitForResult();
+  EXPECT_FALSE(result);
+
+  // Reads should always fail on a disconnected device.
+  TestReadControlPointLengthCallback length_callback;
+  connection.ReadControlPointLength(length_callback.GetCallback());
+  EXPECT_EQ(base::nullopt, length_callback.WaitForResult());
+
+  TestReadServiceRevisionsCallback revisions_callback;
+  connection.ReadServiceRevisions(revisions_callback.GetCallback());
+  EXPECT_THAT(revisions_callback.WaitForResult(), IsEmpty());
+
+  // Writes should always fail on a disconnected device.
+  TestWriteCallback write_callback;
+  connection.WriteServiceRevision(
+      U2fBleConnection::ServiceRevision::VERSION_1_1,
+      write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_FALSE(result);
+
+  connection.WriteControlPoint({}, write_callback.GetCallback());
+  result = write_callback.WaitForResult();
+  EXPECT_FALSE(result);
+}
+
+}  // namespace device
diff --git a/device/u2f/u2f_ble_uuids.h b/device/u2f/u2f_ble_uuids.h
index 8820e04..e3cfc482 100644
--- a/device/u2f/u2f_ble_uuids.h
+++ b/device/u2f/u2f_ble_uuids.h
@@ -10,8 +10,8 @@
 // U2F GATT Service's UUIDs as defined by the standard:
 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-bt-protocol-v1.2-ps-20170411.html#h3_u2f-service
 //
-// For details on how the short U2F Service UUID (0xfffd) was converted to the
-// long one below, see
+// For details on how the short UUIDs for U2F Service (0xFFFD) and U2F Service
+// Revision (0x2A28) were converted to the long ones below, see
 // https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
 static constexpr const char U2F_SERVICE_UUID[] =
     "0000fffd-0000-1000-8000-00805f9b34fb";
@@ -21,6 +21,10 @@
     "f1d0fff2-deaa-ecee-b42f-c9ba7ed623bb";
 static constexpr const char U2F_CONTROL_POINT_LENGTH_UUID[] =
     "f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb";
+static constexpr const char U2F_SERVICE_REVISION_UUID[] =
+    "00002a28-0000-1000-8000-00805f9b34fb";
+static constexpr const char U2F_SERVICE_REVISION_BITFIELD_UUID[] =
+    "f1d0fff4-deaa-ecee-b42f-c9ba7ed623bb";
 
 }  // namespace device
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_view_controller.mm
index 4021fbe..e77d736b5 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_view_controller.mm
@@ -231,10 +231,10 @@
   [detailTextLabel setTextAlignment:_alignment];
 
   // The width must be positive for CGContextRef to be valid.
-  UIEdgeInsets safeAreaInsets = SafeAreaInsetsForView(row);
-  CGFloat labelWidth = MAX(
-      40, floorf(UIEdgeInsetsInsetRect(row.bounds, safeAreaInsets).size.width) -
-              kTextCellLeadingPadding);
+  UIEdgeInsets safeAreaInsets = SafeAreaInsetsForView(self.view);
+  CGRect rowBounds = UIEdgeInsetsInsetRect(self.view.bounds, safeAreaInsets);
+  CGFloat labelWidth =
+      MAX(40, floorf(rowBounds.size.width) - kTextCellLeadingPadding);
   CGFloat labelHeight =
       match.hasAnswer ? kAnswerLabelHeight : kTextDetailLabelHeight;
   CGFloat answerImagePadding = kAnswerImageWidth + kAnswerImageRightPadding;
@@ -243,7 +243,7 @@
       kTextCellLeadingPadding;
 
   LayoutRect detailTextLabelLayout =
-      LayoutRectMake(leadingPadding, CGRectGetWidth(self.view.bounds),
+      LayoutRectMake(leadingPadding, CGRectGetWidth(rowBounds),
                      kDetailCellTopPadding, labelWidth, labelHeight);
   detailTextLabel.frame = LayoutRectGetRect(detailTextLabelLayout);
 
@@ -259,8 +259,8 @@
   OmniboxPopupTruncatingLabel* textLabel = row.textTruncatingLabel;
   [textLabel setTextAlignment:_alignment];
   LayoutRect textLabelLayout =
-      LayoutRectMake(kTextCellLeadingPadding, CGRectGetWidth(self.view.bounds),
-                     0, labelWidth, kTextLabelHeight);
+      LayoutRectMake(kTextCellLeadingPadding, CGRectGetWidth(rowBounds), 0,
+                     labelWidth, kTextLabelHeight);
   textLabel.frame = LayoutRectGetRect(textLabelLayout);
 
   // Set the text.
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
index 5958efa3..7371788 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
@@ -797,15 +797,6 @@
   DCHECK(GetDocument());
   have_background_parser_ = true;
 
-  if (GetDocument()->GetFrame() &&
-      GetDocument()->GetFrame()->FrameScheduler()) {
-    virtual_time_pauser_ = GetDocument()
-                               ->GetFrame()
-                               ->FrameScheduler()
-                               ->CreateWebScopedVirtualTimePauser();
-    virtual_time_pauser_.PauseVirtualTime(true);
-  }
-
   // Make sure that a resolver is set up, so that the correct viewport
   // dimensions will be fed to the background parser and preload scanner.
   if (GetDocument()->Loader())
@@ -856,7 +847,6 @@
   DCHECK(ShouldUseThreading());
   DCHECK(have_background_parser_);
 
-  virtual_time_pauser_.PauseVirtualTime(false);
   have_background_parser_ = false;
 
   // Make this sync, as lsan triggers on some unittests if the task runner is
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
index 80409d3..ed852d6 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
@@ -285,8 +285,6 @@
   bool tried_loading_link_headers_;
   bool added_pending_stylesheet_in_body_;
   bool is_waiting_for_stylesheets_;
-
-  WebScopedVirtualTimePauser virtual_time_pauser_;
 };
 
 }  // namespace blink