summaryrefslogtreecommitdiff
path: root/chromium/device
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-10-24 11:30:15 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-10-30 12:56:19 +0000
commit6036726eb981b6c4b42047513b9d3f4ac865daac (patch)
tree673593e70678e7789766d1f732eb51f613a2703b /chromium/device
parent466052c4e7c052268fd931888cd58961da94c586 (diff)
downloadqtwebengine-chromium-6036726eb981b6c4b42047513b9d3f4ac865daac.tar.gz
BASELINE: Update Chromium to 70.0.3538.78
Change-Id: Ie634710bf039e26c1957f4ae45e101bd4c434ae7 Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/device')
-rw-r--r--chromium/device/BUILD.gn59
-rw-r--r--chromium/device/base/device_monitor_linux.h2
-rw-r--r--chromium/device/base/device_monitor_win.h2
-rw-r--r--chromium/device/base/features.cc2
-rw-r--r--chromium/device/bluetooth/BUILD.gn6
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter.cc14
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter.h19
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_android.cc2
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_factory.cc49
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_factory.h8
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_mac.mm5
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_unittest.cc416
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win.cc6
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win.h3
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_winrt.cc596
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_winrt.h57
-rw-r--r--chromium/device/bluetooth/bluetooth_advertisement.h2
-rw-r--r--chromium/device/bluetooth/bluetooth_advertisement_winrt.cc416
-rw-r--r--chromium/device/bluetooth/bluetooth_advertisement_winrt.h84
-rw-r--r--chromium/device/bluetooth/bluetooth_device.cc7
-rw-r--r--chromium/device/bluetooth/bluetooth_device.h9
-rw-r--r--chromium/device/bluetooth/bluetooth_device_unittest.cc309
-rw-r--r--chromium/device/bluetooth/bluetooth_device_winrt.cc208
-rw-r--r--chromium/device/bluetooth/bluetooth_device_winrt.h16
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc253
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h38
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm18
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm46
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm5
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm2
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm5
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm5
-rw-r--r--chromium/device/bluetooth/bluetooth_pairing_winrt.cc267
-rw-r--r--chromium/device/bluetooth/bluetooth_pairing_winrt.h99
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h7
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm10
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc656
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc585
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h97
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc97
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc339
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h107
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc65
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc77
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h79
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win.cc4
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win.h2
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid.h2
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid_unittest.cc2
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc32
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc121
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc20
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc2
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc2
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_device_client.cc2
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc3
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc3
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc3
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_service_client.cc3
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_input_client.cc2
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc3
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_media_client.cc2
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_media_transport_client.cc3
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h2
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc12
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h7
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h2
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h2
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h2
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_input_client.h2
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_media_client.h2
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_media_transport_client.h3
-rw-r--r--chromium/device/bluetooth/event_utils_winrt.h24
-rw-r--r--chromium/device/fido/BUILD.gn90
-rw-r--r--chromium/device/fido/DEPS1
-rw-r--r--chromium/device/fido/attestation_object.cc21
-rw-r--r--chromium/device/fido/attestation_object.h7
-rw-r--r--chromium/device/fido/attestation_statement.cc4
-rw-r--r--chromium/device/fido/attestation_statement.h8
-rw-r--r--chromium/device/fido/attestation_statement_formats.cc8
-rw-r--r--chromium/device/fido/attestation_statement_formats.h2
-rw-r--r--chromium/device/fido/attested_credential_data.cc5
-rw-r--r--chromium/device/fido/attested_credential_data.h3
-rw-r--r--chromium/device/fido/authenticator_make_credential_response.cc4
-rw-r--r--chromium/device/fido/authenticator_make_credential_response.h4
-rw-r--r--chromium/device/fido/ble/fido_ble_connection.cc518
-rw-r--r--chromium/device/fido/ble/fido_ble_connection.h (renamed from chromium/device/fido/fido_ble_connection.h)94
-rw-r--r--chromium/device/fido/ble/fido_ble_connection_unittest.cc738
-rw-r--r--chromium/device/fido/ble/fido_ble_device.cc (renamed from chromium/device/fido/fido_ble_device.cc)70
-rw-r--r--chromium/device/fido/ble/fido_ble_device.h (renamed from chromium/device/fido/fido_ble_device.h)22
-rw-r--r--chromium/device/fido/ble/fido_ble_device_unittest.cc (renamed from chromium/device/fido/fido_ble_device_unittest.cc)63
-rw-r--r--chromium/device/fido/ble/fido_ble_discovery.cc (renamed from chromium/device/fido/fido_ble_discovery.cc)57
-rw-r--r--chromium/device/fido/ble/fido_ble_discovery.h (renamed from chromium/device/fido/fido_ble_discovery.h)16
-rw-r--r--chromium/device/fido/ble/fido_ble_discovery_base.cc (renamed from chromium/device/fido/fido_ble_discovery_base.cc)41
-rw-r--r--chromium/device/fido/ble/fido_ble_discovery_base.h (renamed from chromium/device/fido/fido_ble_discovery_base.h)12
-rw-r--r--chromium/device/fido/ble/fido_ble_discovery_unittest.cc (renamed from chromium/device/fido/fido_ble_discovery_unittest.cc)70
-rw-r--r--chromium/device/fido/ble/fido_ble_frames.cc (renamed from chromium/device/fido/fido_ble_frames.cc)2
-rw-r--r--chromium/device/fido/ble/fido_ble_frames.h (renamed from chromium/device/fido/fido_ble_frames.h)6
-rw-r--r--chromium/device/fido/ble/fido_ble_frames_fuzzer.cc (renamed from chromium/device/fido/fido_ble_frames_fuzzer.cc)2
-rw-r--r--chromium/device/fido/ble/fido_ble_frames_unittest.cc (renamed from chromium/device/fido/fido_ble_frames_unittest.cc)2
-rw-r--r--chromium/device/fido/ble/fido_ble_transaction.cc (renamed from chromium/device/fido/fido_ble_transaction.cc)22
-rw-r--r--chromium/device/fido/ble/fido_ble_transaction.h (renamed from chromium/device/fido/fido_ble_transaction.h)10
-rw-r--r--chromium/device/fido/ble/fido_ble_uuids.cc (renamed from chromium/device/fido/fido_ble_uuids.cc)10
-rw-r--r--chromium/device/fido/ble/fido_ble_uuids.h (renamed from chromium/device/fido/fido_ble_uuids.h)9
-rw-r--r--chromium/device/fido/ble/mock_fido_ble_connection.cc (renamed from chromium/device/fido/mock_fido_ble_connection.cc)22
-rw-r--r--chromium/device/fido/ble/mock_fido_ble_connection.h (renamed from chromium/device/fido/mock_fido_ble_connection.h)25
-rw-r--r--chromium/device/fido/ble_adapter_power_manager.cc51
-rw-r--r--chromium/device/fido/ble_adapter_power_manager.h49
-rw-r--r--chromium/device/fido/ble_adapter_power_manager_unittest.cc143
-rw-r--r--chromium/device/fido/cable/cable_discovery_data.h45
-rw-r--r--chromium/device/fido/cable/fido_cable_device.cc (renamed from chromium/device/fido/fido_cable_device.cc)18
-rw-r--r--chromium/device/fido/cable/fido_cable_device.h (renamed from chromium/device/fido/fido_cable_device.h)14
-rw-r--r--chromium/device/fido/cable/fido_cable_device_unittest.cc (renamed from chromium/device/fido/fido_cable_device_unittest.cc)17
-rw-r--r--chromium/device/fido/cable/fido_cable_discovery.cc (renamed from chromium/device/fido/fido_cable_discovery.cc)256
-rw-r--r--chromium/device/fido/cable/fido_cable_discovery.h (renamed from chromium/device/fido/fido_cable_discovery.h)50
-rw-r--r--chromium/device/fido/cable/fido_cable_discovery_unittest.cc (renamed from chromium/device/fido/fido_cable_discovery_unittest.cc)265
-rw-r--r--chromium/device/fido/cable/fido_cable_handshake_handler.cc (renamed from chromium/device/fido/fido_cable_handshake_handler.cc)9
-rw-r--r--chromium/device/fido/cable/fido_cable_handshake_handler.h (renamed from chromium/device/fido/fido_cable_handshake_handler.h)6
-rw-r--r--chromium/device/fido/cable/fido_cable_handshake_handler_fuzzer.cc (renamed from chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc)12
-rw-r--r--chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc (renamed from chromium/device/fido/fido_cable_handshake_handler_unittest.cc)20
-rw-r--r--chromium/device/fido/ctap_get_assertion_request.cc2
-rw-r--r--chromium/device/fido/ctap_get_assertion_request.h11
-rw-r--r--chromium/device/fido/ctap_request_unittest.cc6
-rw-r--r--chromium/device/fido/ctap_response_unittest.cc7
-rw-r--r--chromium/device/fido/device_operation.h7
-rw-r--r--chromium/device/fido/device_response_converter.cc10
-rw-r--r--chromium/device/fido/fake_fido_discovery_unittest.cc6
-rw-r--r--chromium/device/fido/fido_authenticator.h8
-rw-r--r--chromium/device/fido/fido_ble_connection.cc593
-rw-r--r--chromium/device/fido/fido_ble_connection_unittest.cc708
-rw-r--r--chromium/device/fido/fido_constants.h2
-rw-r--r--chromium/device/fido/fido_device.cc4
-rw-r--r--chromium/device/fido/fido_device.h22
-rw-r--r--chromium/device/fido/fido_device_authenticator.cc26
-rw-r--r--chromium/device/fido/fido_device_authenticator.h6
-rw-r--r--chromium/device/fido/fido_discovery.cc49
-rw-r--r--chromium/device/fido/fido_discovery.h27
-rw-r--r--chromium/device/fido/fido_discovery_unittest.cc11
-rw-r--r--chromium/device/fido/fido_parsing_utils.cc24
-rw-r--r--chromium/device/fido/fido_parsing_utils.h6
-rw-r--r--chromium/device/fido/fido_request_handler.h41
-rw-r--r--chromium/device/fido/fido_request_handler_base.cc214
-rw-r--r--chromium/device/fido/fido_request_handler_base.h128
-rw-r--r--chromium/device/fido/fido_request_handler_unittest.cc297
-rw-r--r--chromium/device/fido/fido_strings.grd136
-rw-r--r--chromium/device/fido/fido_task.h8
-rw-r--r--chromium/device/fido/fido_test_data.h114
-rw-r--r--chromium/device/fido/fido_transport_protocol.cc57
-rw-r--r--chromium/device/fido/fido_transport_protocol.h26
-rw-r--r--chromium/device/fido/get_assertion_handler_unittest.cc564
-rw-r--r--chromium/device/fido/get_assertion_request_handler.cc188
-rw-r--r--chromium/device/fido/get_assertion_request_handler.h16
-rw-r--r--chromium/device/fido/get_assertion_task.cc134
-rw-r--r--chromium/device/fido/get_assertion_task.h36
-rw-r--r--chromium/device/fido/get_assertion_task_unittest.cc130
-rw-r--r--chromium/device/fido/hid/fake_hid_impl_for_testing.cc (renamed from chromium/device/fido/fake_hid_impl_for_testing.cc)2
-rw-r--r--chromium/device/fido/hid/fake_hid_impl_for_testing.h (renamed from chromium/device/fido/fake_hid_impl_for_testing.h)6
-rw-r--r--chromium/device/fido/hid/fido_hid_device.cc (renamed from chromium/device/fido/fido_hid_device.cc)37
-rw-r--r--chromium/device/fido/hid/fido_hid_device.h (renamed from chromium/device/fido/fido_hid_device.h)24
-rw-r--r--chromium/device/fido/hid/fido_hid_device_unittest.cc (renamed from chromium/device/fido/fido_hid_device_unittest.cc)242
-rw-r--r--chromium/device/fido/hid/fido_hid_discovery.cc (renamed from chromium/device/fido/fido_hid_discovery.cc)5
-rw-r--r--chromium/device/fido/hid/fido_hid_discovery.h (renamed from chromium/device/fido/fido_hid_discovery.h)6
-rw-r--r--chromium/device/fido/hid/fido_hid_discovery_unittest.cc (renamed from chromium/device/fido/fido_hid_discovery_unittest.cc)6
-rw-r--r--chromium/device/fido/hid/fido_hid_message.cc (renamed from chromium/device/fido/fido_hid_message.cc)2
-rw-r--r--chromium/device/fido/hid/fido_hid_message.h (renamed from chromium/device/fido/fido_hid_message.h)8
-rw-r--r--chromium/device/fido/hid/fido_hid_message_fuzzer.cc (renamed from chromium/device/fido/fido_hid_message_fuzzer.cc)2
-rw-r--r--chromium/device/fido/hid/fido_hid_message_unittest.cc (renamed from chromium/device/fido/fido_hid_message_unittest.cc)4
-rw-r--r--chromium/device/fido/hid/fido_hid_packet.cc (renamed from chromium/device/fido/fido_hid_packet.cc)2
-rw-r--r--chromium/device/fido/hid/fido_hid_packet.h (renamed from chromium/device/fido/fido_hid_packet.h)4
-rw-r--r--chromium/device/fido/mac/OWNERS5
-rw-r--r--chromium/device/fido/mac/authenticator.h23
-rw-r--r--chromium/device/fido/mac/authenticator.mm55
-rw-r--r--chromium/device/fido/mac/fake_keychain.h57
-rw-r--r--chromium/device/fido/mac/fake_keychain.mm46
-rw-r--r--chromium/device/fido/mac/fake_touch_id_context.h42
-rw-r--r--chromium/device/fido/mac/fake_touch_id_context.mm24
-rw-r--r--chromium/device/fido/mac/get_assertion_operation.h1
-rw-r--r--chromium/device/fido/mac/get_assertion_operation.mm94
-rw-r--r--chromium/device/fido/mac/keychain.h76
-rw-r--r--chromium/device/fido/mac/keychain.mm122
-rw-r--r--chromium/device/fido/mac/make_credential_operation.mm9
-rw-r--r--chromium/device/fido/mac/operation_base.h12
-rw-r--r--chromium/device/fido/mac/scoped_touch_id_test_environment.h70
-rw-r--r--chromium/device/fido/mac/scoped_touch_id_test_environment.mm82
-rw-r--r--chromium/device/fido/mac/touch_id_context.h40
-rw-r--r--chromium/device/fido/mac/touch_id_context.mm43
-rw-r--r--chromium/device/fido/mac/util.h2
-rw-r--r--chromium/device/fido/mac/util.mm13
-rw-r--r--chromium/device/fido/make_credential_handler_unittest.cc435
-rw-r--r--chromium/device/fido/make_credential_request_handler.cc133
-rw-r--r--chromium/device/fido/make_credential_request_handler.h27
-rw-r--r--chromium/device/fido/make_credential_task.cc35
-rw-r--r--chromium/device/fido/make_credential_task.h3
-rw-r--r--chromium/device/fido/make_credential_task_unittest.cc20
-rw-r--r--chromium/device/fido/mock_fido_device.cc53
-rw-r--r--chromium/device/fido/mock_fido_device.h37
-rw-r--r--chromium/device/fido/opaque_attestation_statement.cc18
-rw-r--r--chromium/device/fido/opaque_attestation_statement.h1
-rw-r--r--chromium/device/fido/public_key_credential_descriptor.cc17
-rw-r--r--chromium/device/fido/public_key_credential_descriptor.h10
-rw-r--r--chromium/device/fido/scoped_virtual_fido_device.cc2
-rw-r--r--chromium/device/fido/strings/BUILD.gn66
-rw-r--r--chromium/device/fido/strings/fido_strings_am.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ar.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_bg.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_bn.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ca.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_cs.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_da.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_de.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_el.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_en-GB.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_es-419.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_es.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_et.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_fa.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_fi.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_fil.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_fr.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_gu.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_hi.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_hr.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_hu.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_id.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_it.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_iw.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ja.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_kn.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ko.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_lt.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_lv.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ml.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_mr.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ms.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_nl.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_no.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_pl.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_pt-BR.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_pt-PT.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ro.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ru.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_sk.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_sl.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_sr.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_sv.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_sw.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_ta.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_te.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_th.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_tr.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_uk.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_vi.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_zh-CN.xtb5
-rw-r--r--chromium/device/fido/strings/fido_strings_zh-TW.xtb5
-rw-r--r--chromium/device/fido/test_callback_receiver.h14
-rw-r--r--chromium/device/fido/virtual_ctap2_device.cc38
-rw-r--r--chromium/device/fido/virtual_fido_device.cc5
-rw-r--r--chromium/device/fido/virtual_fido_device.h10
-rw-r--r--chromium/device/gamepad/gamepad_consumer.h6
-rw-r--r--chromium/device/gamepad/gamepad_haptics_manager.cc4
-rw-r--r--chromium/device/gamepad/gamepad_haptics_manager.h4
-rw-r--r--chromium/device/gamepad/gamepad_monitor.cc10
-rw-r--r--chromium/device/gamepad/gamepad_monitor.h6
-rw-r--r--chromium/device/gamepad/gamepad_pad_state_provider.cc6
-rw-r--r--chromium/device/gamepad/gamepad_pad_state_provider.h2
-rw-r--r--chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc6
-rw-r--r--chromium/device/gamepad/gamepad_provider.cc16
-rw-r--r--chromium/device/gamepad/gamepad_provider.h10
-rw-r--r--chromium/device/gamepad/gamepad_service.cc16
-rw-r--r--chromium/device/gamepad/gamepad_service.h10
-rw-r--r--chromium/device/gamepad/gamepad_service_unittest.cc6
-rw-r--r--chromium/device/gamepad/gamepad_test_helpers.cc2
-rw-r--r--chromium/device/gamepad/gamepad_user_gesture.cc7
-rw-r--r--chromium/device/gamepad/public/cpp/BUILD.gn1
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad.cc5
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_features.cc24
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_features.h2
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc (renamed from chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc)83
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_switches.cc5
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_switches.h1
-rw-r--r--chromium/device/gamepad/public/cpp/gamepads.cc11
-rw-r--r--chromium/device/gamepad/public/mojom/BUILD.gn10
-rw-r--r--chromium/device/gamepad/public/mojom/gamepad.mojom19
-rw-r--r--chromium/device/gamepad/public/mojom/gamepad_mojom_traits_test.mojom11
-rw-r--r--chromium/device/serial/BUILD.gn16
-rw-r--r--chromium/device/serial/serial_io_handler.cc4
-rw-r--r--chromium/device/serial/test_serial_io_handler.cc126
-rw-r--r--chromium/device/serial/test_serial_io_handler.h73
-rw-r--r--chromium/device/usb/BUILD.gn3
-rw-r--r--chromium/device/usb/mojo/BUILD.gn2
-rw-r--r--chromium/device/usb/mojo/device_impl.cc20
-rw-r--r--chromium/device/usb/mojo/device_impl.h11
-rw-r--r--chromium/device/usb/mojo/device_impl_unittest.cc47
-rw-r--r--chromium/device/usb/mojo/device_manager_impl.cc35
-rw-r--r--chromium/device/usb/mojo/device_manager_impl.h14
-rw-r--r--chromium/device/usb/mojo/device_manager_impl_unittest.cc11
-rw-r--r--chromium/device/usb/mojo/mock_permission_provider.cc29
-rw-r--r--chromium/device/usb/mojo/mock_permission_provider.h37
-rw-r--r--chromium/device/usb/mojo/permission_provider.cc15
-rw-r--r--chromium/device/usb/mojo/permission_provider.h32
-rw-r--r--chromium/device/usb/public/mojom/BUILD.gn15
-rw-r--r--chromium/device/usb/public/mojom/chooser_service.mojom19
-rw-r--r--chromium/device/usb/public/mojom/device.mojom16
-rw-r--r--chromium/device/usb/public/mojom/device_manager.mojom3
-rw-r--r--chromium/device/usb/scoped_libusb_device_handle.cc37
-rw-r--r--chromium/device/usb/scoped_libusb_device_handle.h41
-rw-r--r--chromium/device/usb/scoped_libusb_device_ref.cc39
-rw-r--r--chromium/device/usb/scoped_libusb_device_ref.h35
-rw-r--r--chromium/device/usb/usb_device.h2
-rw-r--r--chromium/device/usb/usb_device_handle_impl.cc42
-rw-r--r--chromium/device/usb/usb_device_handle_impl.h14
-rw-r--r--chromium/device/usb/usb_device_impl.cc19
-rw-r--r--chromium/device/usb/usb_device_impl.h13
-rw-r--r--chromium/device/usb/usb_service.cc2
-rw-r--r--chromium/device/usb/usb_service.h4
-rw-r--r--chromium/device/usb/usb_service_impl.cc22
-rw-r--r--chromium/device/usb/usb_service_impl.h4
-rw-r--r--chromium/device/vr/BUILD.gn12
-rw-r--r--chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc6
-rw-r--r--chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.h10
-rw-r--r--chromium/device/vr/android/gvr/gvr_delegate_provider.h4
-rw-r--r--chromium/device/vr/android/gvr/gvr_device.cc42
-rw-r--r--chromium/device/vr/android/gvr/gvr_device.h4
-rw-r--r--chromium/device/vr/android/gvr/gvr_device_provider.cc4
-rw-r--r--chromium/device/vr/android/gvr/gvr_device_provider.h4
-rw-r--r--chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc6
-rw-r--r--chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.h9
-rw-r--r--chromium/device/vr/buildflags/BUILD.gn5
-rw-r--r--chromium/device/vr/buildflags/buildflags.gni15
-rw-r--r--chromium/device/vr/isolated_gamepad_data_fetcher.cc225
-rw-r--r--chromium/device/vr/isolated_gamepad_data_fetcher.h61
-rw-r--r--chromium/device/vr/oculus/oculus_device.cc94
-rw-r--r--chromium/device/vr/oculus/oculus_device.h40
-rw-r--r--chromium/device/vr/oculus/oculus_device_provider.cc24
-rw-r--r--chromium/device/vr/oculus/oculus_device_provider.h7
-rw-r--r--chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc253
-rw-r--r--chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h72
-rw-r--r--chromium/device/vr/oculus/oculus_gamepad_helper.cc210
-rw-r--r--chromium/device/vr/oculus/oculus_gamepad_helper.h20
-rw-r--r--chromium/device/vr/oculus/oculus_render_loop.cc111
-rw-r--r--chromium/device/vr/oculus/oculus_render_loop.h44
-rw-r--r--chromium/device/vr/openvr/openvr_device.cc78
-rw-r--r--chromium/device/vr/openvr/openvr_device.h37
-rw-r--r--chromium/device/vr/openvr/openvr_device_provider.cc14
-rw-r--r--chromium/device/vr/openvr/openvr_device_provider.h5
-rw-r--r--chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc218
-rw-r--r--chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h72
-rw-r--r--chromium/device/vr/openvr/openvr_gamepad_helper.cc171
-rw-r--r--chromium/device/vr/openvr/openvr_gamepad_helper.h19
-rw-r--r--chromium/device/vr/openvr/openvr_render_loop.cc111
-rw-r--r--chromium/device/vr/openvr/openvr_render_loop.h33
-rw-r--r--chromium/device/vr/orientation/orientation_device.cc16
-rw-r--r--chromium/device/vr/orientation/orientation_device.h4
-rw-r--r--chromium/device/vr/orientation/orientation_device_provider.cc4
-rw-r--r--chromium/device/vr/orientation/orientation_device_provider.h6
-rw-r--r--chromium/device/vr/orientation/orientation_device_provider_unittest.cc26
-rw-r--r--chromium/device/vr/public/mojom/BUILD.gn1
-rw-r--r--chromium/device/vr/public/mojom/README.md55
-rw-r--r--chromium/device/vr/public/mojom/isolated_xr_service.mojom126
-rw-r--r--chromium/device/vr/public/mojom/vr_service.mojom240
-rw-r--r--chromium/device/vr/vr_device.h10
-rw-r--r--chromium/device/vr/vr_device_base.cc39
-rw-r--r--chromium/device/vr/vr_device_base.h23
-rw-r--r--chromium/device/vr/vr_device_base_unittest.cc21
-rw-r--r--chromium/device/vr/vr_device_provider.h5
-rw-r--r--chromium/device/vr/vr_display_impl.cc15
-rw-r--r--chromium/device/vr/vr_display_impl.h18
-rw-r--r--chromium/device/vr/vr_display_impl_unittest.cc51
368 files changed, 13199 insertions, 4906 deletions
diff --git a/chromium/device/BUILD.gn b/chromium/device/BUILD.gn
index 17eb38aef83..46218c909fc 100644
--- a/chromium/device/BUILD.gn
+++ b/chromium/device/BUILD.gn
@@ -62,25 +62,30 @@ test("device_unittests") {
"bluetooth/test/mock_bluetooth_central_manager_mac.mm",
"bluetooth/test/test_bluetooth_adapter_observer.cc",
"bluetooth/test/test_bluetooth_adapter_observer.h",
+ "bluetooth/test/test_bluetooth_advertisement_observer.cc",
+ "bluetooth/test/test_bluetooth_advertisement_observer.h",
"bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc",
"bluetooth/test/test_bluetooth_local_gatt_service_delegate.h",
+ "bluetooth/test/test_pairing_delegate.cc",
+ "bluetooth/test/test_pairing_delegate.h",
"bluetooth/uribeacon/uri_encoder_unittest.cc",
"fido/attestation_statement_formats_unittest.cc",
+ "fido/ble/fido_ble_connection_unittest.cc",
+ "fido/ble/fido_ble_device_unittest.cc",
+ "fido/ble/fido_ble_frames_unittest.cc",
+ "fido/ble_adapter_power_manager_unittest.cc",
+ "fido/cable/fido_cable_device_unittest.cc",
+ "fido/cable/fido_cable_discovery_unittest.cc",
+ "fido/cable/fido_cable_handshake_handler_unittest.cc",
"fido/ctap_request_unittest.cc",
"fido/ctap_response_unittest.cc",
"fido/fake_fido_discovery_unittest.cc",
- "fido/fido_ble_connection_unittest.cc",
- "fido/fido_ble_device_unittest.cc",
- "fido/fido_ble_frames_unittest.cc",
- "fido/fido_cable_device_unittest.cc",
- "fido/fido_cable_discovery_unittest.cc",
- "fido/fido_cable_handshake_handler_unittest.cc",
"fido/fido_discovery_unittest.cc",
- "fido/fido_hid_message_unittest.cc",
"fido/fido_parsing_utils_unittest.cc",
"fido/fido_request_handler_unittest.cc",
"fido/get_assertion_handler_unittest.cc",
"fido/get_assertion_task_unittest.cc",
+ "fido/hid/fido_hid_message_unittest.cc",
"fido/mac/browsing_data_deletion_unittest.mm",
"fido/mac/credential_metadata_unittest.cc",
"fido/mac/get_assertion_operation_unittest_mac.mm",
@@ -95,7 +100,7 @@ test("device_unittests") {
"gamepad/abstract_haptic_gamepad_unittest.cc",
"gamepad/gamepad_provider_unittest.cc",
"gamepad/gamepad_service_unittest.cc",
- "gamepad/public/mojom/gamepad_mojom_traits_unittest.cc",
+ "gamepad/public/cpp/gamepad_mojom_traits_unittest.cc",
"test/run_all_unittests.cc",
]
@@ -116,9 +121,9 @@ test("device_unittests") {
"//device/gamepad:test_helpers",
"//device/gamepad/public/cpp:shared_with_blink",
"//device/gamepad/public/mojom",
- "//device/gamepad/public/mojom:gamepad_mojom_traits_test",
"//mojo/core/embedder",
"//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/test_support:test_utils",
"//net",
"//testing/gmock",
"//testing/gtest",
@@ -132,8 +137,8 @@ test("device_unittests") {
# Linux, requires udev.
if (!is_linux_without_udev && !is_android) {
sources += [
- "fido/fido_hid_device_unittest.cc",
- "fido/fido_hid_discovery_unittest.cc",
+ "fido/hid/fido_hid_device_unittest.cc",
+ "fido/hid/fido_hid_discovery_unittest.cc",
]
deps += [
"//device/fido:test_support",
@@ -165,8 +170,6 @@ test("device_unittests") {
"test/test_device_client.h",
"usb/mojo/device_impl_unittest.cc",
"usb/mojo/device_manager_impl_unittest.cc",
- "usb/mojo/mock_permission_provider.cc",
- "usb/mojo/mock_permission_provider.h",
"usb/public/cpp/filter_utils_unittest.cc",
"usb/usb_descriptors_unittest.cc",
"usb/usb_device_handle_unittest.cc",
@@ -255,7 +258,7 @@ test("device_unittests") {
]
} else {
# BLE discovery: works on Linux.
- sources += [ "fido/fido_ble_discovery_unittest.cc" ]
+ sources += [ "fido/ble/fido_ble_discovery_unittest.cc" ]
}
}
@@ -287,6 +290,12 @@ test("device_unittests") {
"bluetooth/bluetooth_low_energy_win_fake.h",
"bluetooth/test/fake_bluetooth_adapter_winrt.cc",
"bluetooth/test/fake_bluetooth_adapter_winrt.h",
+ "bluetooth/test/fake_bluetooth_le_advertisement_data_section_winrt.cc",
+ "bluetooth/test/fake_bluetooth_le_advertisement_data_section_winrt.h",
+ "bluetooth/test/fake_bluetooth_le_advertisement_publisher_status_changed_event_args_winrt.cc",
+ "bluetooth/test/fake_bluetooth_le_advertisement_publisher_status_changed_event_args_winrt.h",
+ "bluetooth/test/fake_bluetooth_le_advertisement_publisher_winrt.cc",
+ "bluetooth/test/fake_bluetooth_le_advertisement_publisher_winrt.h",
"bluetooth/test/fake_bluetooth_le_advertisement_received_event_args_winrt.cc",
"bluetooth/test/fake_bluetooth_le_advertisement_received_event_args_winrt.h",
"bluetooth/test/fake_bluetooth_le_advertisement_watcher_winrt.cc",
@@ -295,16 +304,38 @@ test("device_unittests") {
"bluetooth/test/fake_bluetooth_le_advertisement_winrt.h",
"bluetooth/test/fake_bluetooth_le_device_winrt.cc",
"bluetooth/test/fake_bluetooth_le_device_winrt.h",
+ "bluetooth/test/fake_bluetooth_le_manufacturer_data_winrt.cc",
+ "bluetooth/test/fake_bluetooth_le_manufacturer_data_winrt.h",
+ "bluetooth/test/fake_device_information_custom_pairing_winrt.cc",
+ "bluetooth/test/fake_device_information_custom_pairing_winrt.h",
+ "bluetooth/test/fake_device_information_pairing_winrt.cc",
+ "bluetooth/test/fake_device_information_pairing_winrt.h",
"bluetooth/test/fake_device_information_winrt.cc",
"bluetooth/test/fake_device_information_winrt.h",
+ "bluetooth/test/fake_device_pairing_requested_event_args_winrt.cc",
+ "bluetooth/test/fake_device_pairing_requested_event_args_winrt.h",
+ "bluetooth/test/fake_device_pairing_result_winrt.cc",
+ "bluetooth/test/fake_device_pairing_result_winrt.h",
+ "bluetooth/test/fake_device_watcher_winrt.cc",
+ "bluetooth/test/fake_device_watcher_winrt.h",
"bluetooth/test/fake_gatt_characteristic_winrt.cc",
"bluetooth/test/fake_gatt_characteristic_winrt.h",
"bluetooth/test/fake_gatt_characteristics_result_winrt.cc",
"bluetooth/test/fake_gatt_characteristics_result_winrt.h",
+ "bluetooth/test/fake_gatt_descriptor_winrt.cc",
+ "bluetooth/test/fake_gatt_descriptor_winrt.h",
+ "bluetooth/test/fake_gatt_descriptors_result_winrt.cc",
+ "bluetooth/test/fake_gatt_descriptors_result_winrt.h",
"bluetooth/test/fake_gatt_device_service_winrt.cc",
"bluetooth/test/fake_gatt_device_service_winrt.h",
"bluetooth/test/fake_gatt_device_services_result_winrt.cc",
"bluetooth/test/fake_gatt_device_services_result_winrt.h",
+ "bluetooth/test/fake_gatt_read_result_winrt.cc",
+ "bluetooth/test/fake_gatt_read_result_winrt.h",
+ "bluetooth/test/fake_gatt_value_changed_event_args_winrt.cc",
+ "bluetooth/test/fake_gatt_value_changed_event_args_winrt.h",
+ "bluetooth/test/fake_gatt_write_result_winrt.cc",
+ "bluetooth/test/fake_gatt_write_result_winrt.h",
"bluetooth/test/fake_radio_winrt.cc",
"bluetooth/test/fake_radio_winrt.h",
]
diff --git a/chromium/device/base/device_monitor_linux.h b/chromium/device/base/device_monitor_linux.h
index 24b57ea26ab..319cefed861 100644
--- a/chromium/device/base/device_monitor_linux.h
+++ b/chromium/device/base/device_monitor_linux.h
@@ -54,7 +54,7 @@ class DEVICE_BASE_EXPORT DeviceMonitorLinux {
std::unique_ptr<base::FileDescriptorWatcher::Controller>
monitor_watch_controller_;
- base::ObserverList<Observer, true> observers_;
+ base::ObserverList<Observer, true>::Unchecked observers_;
base::ThreadChecker thread_checker_;
diff --git a/chromium/device/base/device_monitor_win.h b/chromium/device/base/device_monitor_win.h
index 0370c79281f..0f46eb86123 100644
--- a/chromium/device/base/device_monitor_win.h
+++ b/chromium/device/base/device_monitor_win.h
@@ -42,7 +42,7 @@ class DEVICE_BASE_EXPORT DeviceMonitorWin {
void NotifyDeviceRemoved(const GUID& class_guid,
const std::string& device_path);
- base::ObserverList<Observer> observer_list_;
+ base::ObserverList<Observer>::Unchecked observer_list_;
};
} // namespace device
diff --git a/chromium/device/base/features.cc b/chromium/device/base/features.cc
index eaf9cd93ffb..164fe58d6fd 100644
--- a/chromium/device/base/features.cc
+++ b/chromium/device/base/features.cc
@@ -12,7 +12,7 @@ namespace device {
const base::Feature kNewUsbBackend{"NewUsbBackend",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kNewBLEWinImplementation{"NewBLEWinImplementation",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
#endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
diff --git a/chromium/device/bluetooth/BUILD.gn b/chromium/device/bluetooth/BUILD.gn
index b18288d0070..46cb42afd91 100644
--- a/chromium/device/bluetooth/BUILD.gn
+++ b/chromium/device/bluetooth/BUILD.gn
@@ -246,12 +246,18 @@ component("bluetooth") {
sources += [
"bluetooth_adapter_winrt.cc",
"bluetooth_adapter_winrt.h",
+ "bluetooth_advertisement_winrt.cc",
+ "bluetooth_advertisement_winrt.h",
"bluetooth_device_winrt.cc",
"bluetooth_device_winrt.h",
"bluetooth_gatt_discoverer_winrt.cc",
"bluetooth_gatt_discoverer_winrt.h",
+ "bluetooth_pairing_winrt.cc",
+ "bluetooth_pairing_winrt.h",
"bluetooth_remote_gatt_characteristic_winrt.cc",
"bluetooth_remote_gatt_characteristic_winrt.h",
+ "bluetooth_remote_gatt_descriptor_winrt.cc",
+ "bluetooth_remote_gatt_descriptor_winrt.h",
"bluetooth_remote_gatt_service_winrt.cc",
"bluetooth_remote_gatt_service_winrt.h",
"event_utils_winrt.h",
diff --git a/chromium/device/bluetooth/bluetooth_adapter.cc b/chromium/device/bluetooth/bluetooth_adapter.cc
index 64fd5c8fe4b..54ec734b7be 100644
--- a/chromium/device/bluetooth/bluetooth_adapter.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter.cc
@@ -58,6 +58,10 @@ bool BluetoothAdapter::HasObserver(BluetoothAdapter::Observer* observer) {
return observers_.HasObserver(observer);
}
+bool BluetoothAdapter::CanPower() const {
+ return IsPresent();
+}
+
void BluetoothAdapter::SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) {
@@ -193,6 +197,11 @@ BluetoothDevice::PairingDelegate* BluetoothAdapter::DefaultPairingDelegate() {
return pairing_delegates_.front().first;
}
+std::vector<BluetoothAdvertisement*>
+BluetoothAdapter::GetPendingAdvertisementsForTesting() const {
+ return {};
+}
+
void BluetoothAdapter::NotifyAdapterPoweredChanged(bool powered) {
for (auto& observer : observers_)
observer.AdapterPoweredChanged(this, powered);
@@ -321,13 +330,12 @@ BluetoothAdapter::~BluetoothAdapter() {
}
}
-void BluetoothAdapter::DidChangePoweredState() {
+void BluetoothAdapter::RunPendingPowerCallbacks() {
if (set_powered_callbacks_) {
// Move into a local variable to clear out both callbacks at the end of the
// scope and to allow scheduling another SetPowered() call in either of the
// callbacks.
- std::unique_ptr<SetPoweredCallbacks> callbacks =
- std::move(set_powered_callbacks_);
+ auto callbacks = std::move(set_powered_callbacks_);
callbacks->powered == IsPowered() ? std::move(callbacks->callback).Run()
: callbacks->error_callback.Run();
}
diff --git a/chromium/device/bluetooth/bluetooth_adapter.h b/chromium/device/bluetooth/bluetooth_adapter.h
index b1399a64300..5129c45ff77 100644
--- a/chromium/device/bluetooth/bluetooth_adapter.h
+++ b/chromium/device/bluetooth/bluetooth_adapter.h
@@ -343,6 +343,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
// is only considered present if the address has been obtained.
virtual bool IsPresent() const = 0;
+ // Indicates whether the adapter radio can be powered. Defaults to
+ // IsPresent(). Currently only overridden on Windows, where the adapter can be
+ // present, but we might fail to get access to the underlying radio.
+ virtual bool CanPower() const;
+
// Indicates whether the adapter radio is powered.
virtual bool IsPowered() const = 0;
@@ -354,7 +359,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
// callback based API. It will store pending callbacks in
// |set_powered_callbacks_| and invoke SetPoweredImpl(bool) which these
// platforms need to implement. Pending callbacks are only run when
- // DidChangePoweredState() is invoked.
+ // RunPendingPowerCallbacks() is invoked.
//
// Platforms that natively support a callback based API (e.g. BlueZ and Win)
// should override this method and provide their own implementation instead.
@@ -522,6 +527,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
const AdvertisementErrorCallback& error_callback) = 0;
#endif
+ // Returns the list of pending advertisements that are not registered yet.
+ virtual std::vector<BluetoothAdvertisement*>
+ GetPendingAdvertisementsForTesting() const;
+
// Returns the local GATT services associated with this adapter with the
// given identifier. Returns NULL if the service doesn't exist.
virtual BluetoothLocalGattService* GetGattService(
@@ -590,9 +599,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
// pending SetPowered() callbacks need to be stored explicitly.
virtual bool SetPoweredImpl(bool powered) = 0;
- // Called by macOS and Android once the specific powered state events are
- // received. Clears out pending callbacks.
- void DidChangePoweredState();
+ // Called by macOS, Android and WinRT once the specific powered state events
+ // are received or an error occurred. Clears out pending callbacks.
+ void RunPendingPowerCallbacks();
// Internal methods for initiating and terminating device discovery sessions.
// An implementation of BluetoothAdapter keeps an internal reference count to
@@ -684,7 +693,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
// Observers of BluetoothAdapter, notified from implementation subclasses.
- base::ObserverList<device::BluetoothAdapter::Observer> observers_;
+ base::ObserverList<device::BluetoothAdapter::Observer>::Unchecked observers_;
// Devices paired with, connected to, discovered by, or visible to the
// adapter. The key is the Bluetooth address of the device and the value is
diff --git a/chromium/device/bluetooth/bluetooth_adapter_android.cc b/chromium/device/bluetooth/bluetooth_adapter_android.cc
index ad1446ad689..1b6b29cdcb9 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_android.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_android.cc
@@ -150,7 +150,7 @@ void BluetoothAdapterAndroid::OnAdapterStateChanged(
JNIEnv* env,
const JavaParamRef<jobject>& caller,
const bool powered) {
- DidChangePoweredState();
+ RunPendingPowerCallbacks();
NotifyAdapterPoweredChanged(powered);
}
diff --git a/chromium/device/bluetooth/bluetooth_adapter_factory.cc b/chromium/device/bluetooth/bluetooth_adapter_factory.cc
index 8ec94920945..ab82c1f6519 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_factory.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_factory.cc
@@ -19,6 +19,7 @@
#endif
#if defined(OS_WIN)
#include "base/win/windows_version.h"
+#include "device/bluetooth/bluetooth_adapter_win.h"
#endif
#if defined(ANDROID)
#include "base/android/build_info.h"
@@ -61,6 +62,26 @@ void RunAdapterCallbacks() {
}
#endif // defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_WIN)
+// Shared classic adapter instance. See above why this is a lazy instance.
+// Note: This is only applicable on Windows, as here the default adapter does
+// not provide Bluetooth Classic support yet.
+base::LazyInstance<base::WeakPtr<BluetoothAdapter>>::Leaky classic_adapter =
+ LAZY_INSTANCE_INITIALIZER;
+
+base::LazyInstance<AdapterCallbackList>::DestructorAtExit
+ classic_adapter_callbacks = LAZY_INSTANCE_INITIALIZER;
+
+void RunClassicAdapterCallbacks() {
+ DCHECK(classic_adapter.Get());
+ scoped_refptr<BluetoothAdapter> adapter(classic_adapter.Get().get());
+ for (auto& callback : classic_adapter_callbacks.Get())
+ callback.Run(adapter);
+
+ classic_adapter_callbacks.Get().clear();
+}
+#endif // defined(OS_WIN)
+
} // namespace
BluetoothAdapterFactory::~BluetoothAdapterFactory() = default;
@@ -132,6 +153,31 @@ void BluetoothAdapterFactory::GetAdapter(const AdapterCallback& callback) {
callback.Run(scoped_refptr<BluetoothAdapter>(default_adapter.Get().get()));
}
+// static
+void BluetoothAdapterFactory::GetClassicAdapter(
+ const AdapterCallback& callback) {
+#if defined(OS_WIN)
+ if (base::win::GetVersion() < base::win::VERSION_WIN10) {
+ // Prior to Win10, the default adapter will support Bluetooth classic.
+ GetAdapter(callback);
+ return;
+ }
+
+ if (!classic_adapter.Get()) {
+ classic_adapter.Get() = BluetoothAdapterWin::CreateClassicAdapter(
+ base::Bind(&RunClassicAdapterCallbacks));
+ DCHECK(!classic_adapter.Get()->IsInitialized());
+ }
+
+ if (!classic_adapter.Get()->IsInitialized())
+ classic_adapter_callbacks.Get().push_back(callback);
+ else
+ callback.Run(scoped_refptr<BluetoothAdapter>(classic_adapter.Get().get()));
+#else
+ GetAdapter(callback);
+#endif // defined(OS_WIN)
+}
+
#if defined(OS_LINUX)
// static
void BluetoothAdapterFactory::Shutdown() {
@@ -144,6 +190,9 @@ void BluetoothAdapterFactory::Shutdown() {
void BluetoothAdapterFactory::SetAdapterForTesting(
scoped_refptr<BluetoothAdapter> adapter) {
default_adapter.Get() = adapter->GetWeakPtrForTesting();
+#if defined(OS_WIN)
+ classic_adapter.Get() = adapter->GetWeakPtrForTesting();
+#endif
}
// static
diff --git a/chromium/device/bluetooth/bluetooth_adapter_factory.h b/chromium/device/bluetooth/bluetooth_adapter_factory.h
index 7217c2f7578..e82bad913f4 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_factory.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_factory.h
@@ -52,6 +52,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterFactory {
// use.
static void GetAdapter(const AdapterCallback& callback);
+ // Returns the shared instance of the classic adapter, creating and
+ // initializing it if necessary. |callback| is called with the adapter
+ // instance passed only once the adapter is fully initialized and ready to
+ // use.
+ // For all platforms except Windows this is equivalent to calling
+ // GetAdapter(), as the default adapter already supports Bluetooth classic.
+ static void GetClassicAdapter(const AdapterCallback& callback);
+
#if defined(OS_LINUX)
// Calls |BluetoothAdapter::Shutdown| on the adapter if
// present.
diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.mm b/chromium/device/bluetooth/bluetooth_adapter_mac.mm
index 8b2092ec78f..24a63be8a4b 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_adapter_mac.mm
@@ -157,6 +157,9 @@ BluetoothAdapterMac::~BluetoothAdapterMac() {
// disconnect the gatt connection. To make sure they don't use the mac
// adapter, they should be explicitly destroyed here.
devices_.clear();
+ // Explicitly clear out delegates, which might outlive the Adapter.
+ [low_energy_peripheral_manager_ setDelegate:nil];
+ [low_energy_central_manager_ setDelegate:nil];
// Set low_energy_central_manager_ to nil so no devices will try to use it
// while being destroyed after this method. |devices_| is owned by
// BluetoothAdapter.
@@ -515,7 +518,7 @@ void BluetoothAdapterMac::PollAdapter() {
if (classic_powered_ != state.classic_powered) {
classic_powered_ = state.classic_powered;
- DidChangePoweredState();
+ RunPendingPowerCallbacks();
NotifyAdapterPoweredChanged(classic_powered_);
}
diff --git a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
index fe16beb92ea..274ca34a628 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -25,6 +25,7 @@
#include "device/bluetooth/bluetooth_local_gatt_service.h"
#include "device/bluetooth/test/bluetooth_test.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
+#include "device/bluetooth/test/test_bluetooth_advertisement_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID)
@@ -44,6 +45,8 @@ using device::BluetoothDevice;
namespace device {
+namespace {
+
class TestBluetoothAdapter : public BluetoothAdapter {
public:
TestBluetoothAdapter() = default;
@@ -176,6 +179,8 @@ class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
void AuthorizePairing(BluetoothDevice* device) override {}
};
+} // namespace
+
TEST(BluetoothAdapterTest, NoDefaultPairingDelegate) {
scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
@@ -510,12 +515,31 @@ TEST_F(BluetoothTest, MAYBE_ConstructFakeAdapter) {
InitWithFakeAdapter();
EXPECT_EQ(adapter_->GetAddress(), kTestAdapterAddress);
EXPECT_EQ(adapter_->GetName(), kTestAdapterName);
+ EXPECT_TRUE(adapter_->CanPower());
EXPECT_TRUE(adapter_->IsPresent());
EXPECT_TRUE(adapter_->IsPowered());
EXPECT_FALSE(adapter_->IsDiscoverable());
EXPECT_FALSE(adapter_->IsDiscovering());
}
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, ConstructFakeAdapterWithoutRadio) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitFakeAdapterWithoutRadio();
+ EXPECT_EQ(adapter_->GetAddress(), kTestAdapterAddress);
+ EXPECT_EQ(adapter_->GetName(), kTestAdapterName);
+ EXPECT_TRUE(adapter_->IsPresent());
+ EXPECT_FALSE(adapter_->CanPower());
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsDiscoverable());
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+#endif // defined(OS_WIN)
+
// TODO(scheib): Enable BluetoothTest fixture tests on all platforms.
#if defined(OS_ANDROID)
#define MAYBE_DiscoverySession DiscoverySession
@@ -753,6 +777,88 @@ TEST_F(BluetoothTest, MAYBE_DiscoverMultipleLowEnergyDevices) {
EXPECT_EQ(2u, adapter_->GetDevices().size());
}
+#if defined(OS_WIN)
+// Tests that the adapter responds to external changes to the power state.
+TEST_P(BluetoothTestWinrtOnly, SimulateAdapterPoweredOffAndOn) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitWithFakeAdapter();
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ ASSERT_TRUE(adapter_->IsPresent());
+ ASSERT_TRUE(adapter_->IsPowered());
+ EXPECT_EQ(0, observer.powered_changed_count());
+
+ SimulateAdapterPoweredOff();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_EQ(1, observer.powered_changed_count());
+ EXPECT_FALSE(observer.last_powered());
+
+ SimulateAdapterPoweredOn();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_EQ(2, observer.powered_changed_count());
+ EXPECT_TRUE(observer.last_powered());
+}
+
+// Tests that the adapter responds to external changes to the power state, even
+// if it failed to obtain the underlying radio.
+TEST_P(BluetoothTestWinrtOnly, SimulateAdapterPoweredOnAndOffWithoutRadio) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitFakeAdapterWithoutRadio();
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ ASSERT_TRUE(adapter_->IsPresent());
+ ASSERT_FALSE(adapter_->IsPowered());
+ EXPECT_EQ(0, observer.powered_changed_count());
+
+ SimulateAdapterPoweredOn();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_EQ(1, observer.powered_changed_count());
+ EXPECT_TRUE(observer.last_powered());
+
+ SimulateAdapterPoweredOff();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_EQ(2, observer.powered_changed_count());
+ EXPECT_FALSE(observer.last_powered());
+}
+
+// Makes sure the error callback gets run when changing the adapter power state
+// fails.
+// TODO(https://crbug.com/878680): Implement SimulateAdapterPowerSuccess() and
+// enable on all platforms.
+TEST_P(BluetoothTestWinrtOnly, SimulateAdapterPowerFailure) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitWithFakeAdapter();
+ ASSERT_TRUE(adapter_->IsPresent());
+ ASSERT_TRUE(adapter_->IsPowered());
+
+ adapter_->SetPowered(false, GetCallback(Call::NOT_EXPECTED),
+ GetErrorCallback(Call::EXPECTED));
+ SimulateAdapterPowerFailure();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(adapter_->IsPowered());
+}
+#endif // defined(OS_WIN)
+
// TODO(https://crbug.com/804356): Enable this test on old Windows versions as
// well.
#if defined(OS_WIN)
@@ -1042,6 +1148,316 @@ TEST_F(BluetoothTest, MAYBE_TurnOffAdapterWithConnectedDevice) {
EXPECT_FALSE(device->IsGattConnected());
}
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, RegisterAdvertisement) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ InitWithFakeAdapter();
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_FALSE(pending_advertisements.empty());
+ SimulateAdvertisementStarted(pending_advertisements[0]);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, FailRegisterAdvertisement) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ InitWithFakeAdapter();
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::NOT_EXPECTED),
+ GetAdvertisementErrorCallback(Call::EXPECTED));
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_FALSE(pending_advertisements.empty());
+ SimulateAdvertisementError(pending_advertisements[0],
+ BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF,
+ last_advertisement_error_code_);
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, RegisterAndUnregisterAdvertisement) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ InitWithFakeAdapter();
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_FALSE(pending_advertisements.empty());
+ auto* advertisement = pending_advertisements[0];
+ SimulateAdvertisementStarted(advertisement);
+ base::RunLoop().RunUntilIdle();
+
+ TestBluetoothAdvertisementObserver observer(advertisement);
+ advertisement->Unregister(GetCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ SimulateAdvertisementStopped(advertisement);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(observer.released());
+ EXPECT_EQ(1u, observer.released_count());
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, FailUnregisterAdvertisement) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ InitWithFakeAdapter();
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_FALSE(pending_advertisements.empty());
+ auto* advertisement = pending_advertisements[0];
+ SimulateAdvertisementStarted(advertisement);
+ base::RunLoop().RunUntilIdle();
+
+ TestBluetoothAdvertisementObserver observer(advertisement);
+ advertisement->Unregister(GetCallback(Call::NOT_EXPECTED),
+ GetAdvertisementErrorCallback(Call::EXPECTED));
+ SimulateAdvertisementError(advertisement,
+ BluetoothAdvertisement::ERROR_RESET_ADVERTISING);
+ base::RunLoop().RunUntilIdle();
+
+ // Expect no change to the observer status.
+ EXPECT_FALSE(observer.released());
+ EXPECT_EQ(0u, observer.released_count());
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_RESET_ADVERTISING,
+ last_advertisement_error_code_);
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, RegisterAdvertisementWithInvalidData) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ // WinRT only accepts ManufacturerData in the payload, other data should be
+ // rejected.
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_service_data(
+ std::make_unique<BluetoothAdvertisement::ServiceData>());
+
+ InitWithFakeAdapter();
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::NOT_EXPECTED),
+ GetAdvertisementErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_STARTING_ADVERTISEMENT,
+ last_advertisement_error_code_);
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, RegisterMultipleAdvertisements) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitWithFakeAdapter();
+ constexpr size_t kNumAdvertisements = 10u;
+
+ for (size_t i = 0; i < kNumAdvertisements; ++i) {
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ }
+
+ base::RunLoop().RunUntilIdle();
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_EQ(kNumAdvertisements, pending_advertisements.size());
+ for (size_t i = 0; i < kNumAdvertisements; ++i)
+ SimulateAdvertisementStarted(pending_advertisements[i]);
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, UnregisterAdvertisementWhilePendingUnregister) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitWithFakeAdapter();
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+
+ base::RunLoop().RunUntilIdle();
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_EQ(1u, pending_advertisements.size());
+ auto* advertisement = pending_advertisements[0];
+ SimulateAdvertisementStarted(advertisement);
+ base::RunLoop().RunUntilIdle();
+
+ TestBluetoothAdvertisementObserver observer(advertisement);
+ advertisement->Unregister(GetCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+
+ // Schedule another Unregister, which is expected to fail.
+ advertisement->Unregister(GetCallback(Call::NOT_EXPECTED),
+ GetAdvertisementErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
+ // Expect no change to the observer status.
+ EXPECT_FALSE(observer.released());
+ EXPECT_EQ(0u, observer.released_count());
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_RESET_ADVERTISING,
+ last_advertisement_error_code_);
+
+ // Simulate success of the first unregistration.
+ SimulateAdvertisementStopped(advertisement);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(observer.released());
+ EXPECT_EQ(1u, observer.released_count());
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, DoubleUnregisterAdvertisement) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitWithFakeAdapter();
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+
+ base::RunLoop().RunUntilIdle();
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_EQ(1u, pending_advertisements.size());
+ auto* advertisement = pending_advertisements[0];
+ SimulateAdvertisementStarted(advertisement);
+ base::RunLoop().RunUntilIdle();
+
+ // Perform two unregistrations after each other. Both should succeed.
+ TestBluetoothAdvertisementObserver observer(advertisement);
+ advertisement->Unregister(GetCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ SimulateAdvertisementStopped(advertisement);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(observer.released());
+ EXPECT_EQ(1u, observer.released_count());
+
+ advertisement->Unregister(GetCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ SimulateAdvertisementStopped(advertisement);
+ base::RunLoop().RunUntilIdle();
+ // The second unregister is a no-op, and should not notify observers again.
+ EXPECT_TRUE(observer.released());
+ EXPECT_EQ(1u, observer.released_count());
+
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+}
+
+TEST_P(BluetoothTestWinrtOnly, SimulateAdvertisementStoppedByOS) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
+ InitWithFakeAdapter();
+ auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+ advertisement_data->set_manufacturer_data(
+ std::make_unique<BluetoothAdvertisement::ManufacturerData>());
+
+ adapter_->RegisterAdvertisement(
+ std::move(advertisement_data),
+ GetCreateAdvertisementCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+
+ base::RunLoop().RunUntilIdle();
+ auto pending_advertisements = adapter_->GetPendingAdvertisementsForTesting();
+ ASSERT_EQ(1u, pending_advertisements.size());
+ auto* advertisement = pending_advertisements[0];
+ SimulateAdvertisementStarted(advertisement);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(adapter_->GetPendingAdvertisementsForTesting().empty());
+
+ TestBluetoothAdvertisementObserver observer(advertisement);
+ // Simulate the OS stopping the advertisement. This should notify the
+ // |observer|.
+ SimulateAdvertisementStopped(advertisement);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(observer.released());
+ EXPECT_EQ(1u, observer.released_count());
+
+ // While Unregister() is a no-op now, we still expect an invocation of the
+ // success callback, but no change to the |observer| state.
+ advertisement->Unregister(GetCallback(Call::EXPECTED),
+ GetAdvertisementErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(observer.released());
+ EXPECT_EQ(1u, observer.released_count());
+}
+
+#endif // defined(OS_WIN)
+
#if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && \
!defined(USE_CAST_BLUETOOTH_ADAPTER)
#define MAYBE_RegisterLocalGattServices RegisterLocalGattServices
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.cc b/chromium/device/bluetooth/bluetooth_adapter_win.cc
index 213a76c28b3..22686c18fbf 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_win.cc
@@ -44,6 +44,12 @@ base::WeakPtr<BluetoothAdapter> BluetoothAdapterWin::CreateAdapter(
return adapter->weak_ptr_factory_.GetWeakPtr();
}
+ return BluetoothAdapterWin::CreateClassicAdapter(std::move(init_callback));
+}
+
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapterWin::CreateClassicAdapter(
+ InitCallback init_callback) {
auto* adapter = new BluetoothAdapterWin(std::move(init_callback));
adapter->Init();
return adapter->weak_ptr_factory_.GetWeakPtr();
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.h b/chromium/device/bluetooth/bluetooth_adapter_win.h
index dd996e8878e..c391eca773d 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_win.h
@@ -36,6 +36,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWin
static base::WeakPtr<BluetoothAdapter> CreateAdapter(
InitCallback init_callback);
+ static base::WeakPtr<BluetoothAdapter> CreateClassicAdapter(
+ InitCallback init_callback);
+
static bool UseNewBLEWinImplementation();
// BluetoothAdapter:
diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc
index 7660b563075..f53da79c97d 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc
@@ -6,23 +6,30 @@
#include <windows.foundation.collections.h>
#include <windows.foundation.h>
+#include <windows.storage.streams.h>
#include <wrl/event.h>
#include <memory>
#include <utility>
+#include <vector>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/win/core_winrt_util.h"
+#include "device/bluetooth/bluetooth_advertisement_winrt.h"
#include "device/bluetooth/bluetooth_device_winrt.h"
#include "device/bluetooth/bluetooth_discovery_filter.h"
#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
@@ -38,23 +45,37 @@ namespace uwp {
using ABI::Windows::Devices::Bluetooth::BluetoothAdapter;
} // namespace uwp
using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementDataSection;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementFlags;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStatus;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStatus_Aborted;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEManufacturerData;
using ABI::Windows::Devices::Bluetooth::Advertisement::BluetoothLEScanningMode;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEScanningMode_Active;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisement;
using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementDataSection;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisherFactory;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementReceivedEventArgs;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementWatcher;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEManufacturerData;
using ABI::Windows::Devices::Bluetooth::IBluetoothAdapter;
using ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics;
using ABI::Windows::Devices::Enumeration::DeviceInformation;
using ABI::Windows::Devices::Enumeration::IDeviceInformation;
using ABI::Windows::Devices::Enumeration::IDeviceInformationStatics;
+using ABI::Windows::Devices::Enumeration::IDeviceInformationUpdate;
+using ABI::Windows::Devices::Enumeration::IDeviceWatcher;
using ABI::Windows::Devices::Radios::IRadio;
using ABI::Windows::Devices::Radios::IRadioStatics;
using ABI::Windows::Devices::Radios::Radio;
@@ -67,7 +88,13 @@ using ABI::Windows::Devices::Radios::RadioState;
using ABI::Windows::Devices::Radios::RadioState_Off;
using ABI::Windows::Devices::Radios::RadioState_On;
using ABI::Windows::Foundation::Collections::IVector;
+using ABI::Windows::Foundation::Collections::IVectorView;
using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Foundation::IReference;
+using ABI::Windows::Storage::Streams::IBuffer;
+using ABI::Windows::Storage::Streams::IDataReader;
+using ABI::Windows::Storage::Streams::IDataReaderStatics;
+using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;
bool ResolveCoreWinRT() {
@@ -75,6 +102,15 @@ bool ResolveCoreWinRT() {
base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
}
+// Query string for powered Bluetooth radios. GUID Reference:
+// https://docs.microsoft.com/en-us/windows-hardware/drivers/install/guid-bthport-device-interface
+// TODO(https://crbug.com/821766): Consider adding WindowsCreateStringReference
+// to base::win::ScopedHString to avoid allocating memory for this string.
+constexpr wchar_t kPoweredRadiosAqsFilter[] =
+ L"System.Devices.InterfaceClassGuid:=\"{0850302A-B344-4fda-9BE9-"
+ L"90576B8D46F0}\" AND "
+ L"System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
+
// Utility functions to pretty print enum values.
constexpr const char* ToCString(RadioAccessStatus access_status) {
switch (access_status) {
@@ -92,37 +128,285 @@ constexpr const char* ToCString(RadioAccessStatus access_status) {
return "";
}
-base::Optional<BluetoothDevice::UUIDList> ExtractAdvertisedUUIDs(
+template <typename VectorView, typename T>
+bool ToStdVector(VectorView* view, std::vector<T>* vector) {
+ unsigned size;
+ HRESULT hr = view->get_Size(&size);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Size() failed: " << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ vector->resize(size);
+ for (size_t i = 0; i < size; ++i) {
+ hr = view->GetAt(i, &(*vector)[i]);
+ DCHECK(SUCCEEDED(hr)) << "GetAt(" << i << ") failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ return true;
+}
+
+base::Optional<std::vector<uint8_t>> ExtractVector(IBuffer* buffer) {
+ ComPtr<IDataReaderStatics> data_reader_statics;
+ HRESULT hr = base::win::GetActivationFactory<
+ IDataReaderStatics, RuntimeClass_Windows_Storage_Streams_DataReader>(
+ &data_reader_statics);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting DataReaderStatics Activation Factory failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ ComPtr<IDataReader> data_reader;
+ hr = data_reader_statics->FromBuffer(buffer, &data_reader);
+ if (FAILED(hr)) {
+ VLOG(2) << "FromBuffer() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ uint32_t buffer_length;
+ hr = buffer->get_Length(&buffer_length);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Length() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ std::vector<uint8_t> bytes(buffer_length);
+ hr = data_reader->ReadBytes(buffer_length, bytes.data());
+ if (FAILED(hr)) {
+ VLOG(2) << "ReadBytes() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ return bytes;
+}
+
+base::Optional<uint8_t> ExtractFlags(IBluetoothLEAdvertisement* advertisement) {
+ if (!advertisement)
+ return base::nullopt;
+
+ ComPtr<IReference<BluetoothLEAdvertisementFlags>> flags_ref;
+ HRESULT hr = advertisement->get_Flags(&flags_ref);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Flags() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ if (!flags_ref) {
+ VLOG(2) << "No advertisement flags found.";
+ return base::nullopt;
+ }
+
+ BluetoothLEAdvertisementFlags flags;
+ hr = flags_ref->get_Value(&flags);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Value() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ return flags;
+}
+
+BluetoothDevice::UUIDList ExtractAdvertisedUUIDs(
IBluetoothLEAdvertisement* advertisement) {
+ if (!advertisement)
+ return {};
+
ComPtr<IVector<GUID>> service_uuids;
HRESULT hr = advertisement->get_ServiceUuids(&service_uuids);
if (FAILED(hr)) {
VLOG(2) << "get_ServiceUuids() failed: "
<< logging::SystemErrorCodeToString(hr);
- return base::nullopt;
+ return {};
}
- unsigned num_service_uuids;
- hr = service_uuids->get_Size(&num_service_uuids);
+ std::vector<GUID> guids;
+ if (!ToStdVector(service_uuids.Get(), &guids))
+ return {};
+
+ BluetoothDevice::UUIDList advertised_uuids;
+ advertised_uuids.reserve(guids.size());
+ for (const auto& guid : guids)
+ advertised_uuids.emplace_back(guid);
+
+ return advertised_uuids;
+}
+
+// This method populates service data for a particular sized UUID. Given the
+// lack of tailored platform APIs, we need to parse the raw advertisement data
+// sections ourselves. These data sections are effectively a list of blobs,
+// where each blob starts with the corresponding UUID in little endian order,
+// followed by the corresponding service data.
+void PopulateServiceData(
+ BluetoothDevice::ServiceDataMap* service_data,
+ const std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>>&
+ data_sections,
+ size_t num_bytes_uuid) {
+ for (const auto& data_section : data_sections) {
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = data_section->get_Data(&buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Data() failed: " << logging::SystemErrorCodeToString(hr);
+ continue;
+ }
+
+ auto bytes = ExtractVector(buffer.Get());
+ if (!bytes)
+ continue;
+
+ auto bytes_span = base::make_span(*bytes);
+ if (bytes_span.size() < num_bytes_uuid) {
+ VLOG(2) << "Buffer Length is too small: " << bytes_span.size() << " vs. "
+ << num_bytes_uuid;
+ continue;
+ }
+
+ auto uuid_span = bytes_span.first(num_bytes_uuid);
+ // The UUID is specified in little endian format, thus we reverse the bytes
+ // here.
+ std::vector<uint8_t> uuid_bytes(uuid_span.rbegin(), uuid_span.rend());
+
+ // HexEncode the bytes and add dashes as required.
+ std::string uuid_str;
+ for (char c : base::HexEncode(uuid_bytes.data(), uuid_bytes.size())) {
+ const size_t size = uuid_str.size();
+ if (size == 8 || size == 13 || size == 18 || size == 23)
+ uuid_str.push_back('-');
+ uuid_str.push_back(c);
+ }
+
+ auto service_data_span = bytes_span.subspan(num_bytes_uuid);
+ auto result = service_data->emplace(
+ BluetoothUUID(uuid_str), std::vector<uint8_t>(service_data_span.begin(),
+ service_data_span.end()));
+ // Check that an insertion happened.
+ DCHECK(result.second);
+ // Check that the inserted UUID is valid.
+ DCHECK(result.first->first.IsValid());
+ }
+}
+
+BluetoothDevice::ServiceDataMap ExtractServiceData(
+ IBluetoothLEAdvertisement* advertisement) {
+ BluetoothDevice::ServiceDataMap service_data;
+ if (!advertisement)
+ return service_data;
+
+ static constexpr std::pair<uint8_t, size_t> kServiceDataTypesAndNumBits[] = {
+ {BluetoothDeviceWinrt::k16BitServiceDataSection, 16},
+ {BluetoothDeviceWinrt::k32BitServiceDataSection, 32},
+ {BluetoothDeviceWinrt::k128BitServiceDataSection, 128},
+ };
+
+ for (const auto& data_type_and_num_bits : kServiceDataTypesAndNumBits) {
+ ComPtr<IVectorView<BluetoothLEAdvertisementDataSection*>> data_sections;
+ HRESULT hr = advertisement->GetSectionsByType(data_type_and_num_bits.first,
+ &data_sections);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetSectionsByType() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ continue;
+ }
+
+ std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> vector;
+ if (!ToStdVector(data_sections.Get(), &vector))
+ continue;
+
+ PopulateServiceData(&service_data, vector,
+ data_type_and_num_bits.second / 8);
+ }
+
+ return service_data;
+}
+
+BluetoothDevice::ManufacturerDataMap ExtractManufacturerData(
+ IBluetoothLEAdvertisement* advertisement) {
+ if (!advertisement)
+ return {};
+
+ ComPtr<IVector<BluetoothLEManufacturerData*>> manufacturer_data_ptr;
+ HRESULT hr = advertisement->get_ManufacturerData(&manufacturer_data_ptr);
if (FAILED(hr)) {
- VLOG(2) << "get_Size() failed: " << logging::SystemErrorCodeToString(hr);
- return base::nullopt;
+ VLOG(2) << "GetManufacturerData() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return {};
}
- BluetoothDevice::UUIDList advertised_uuids;
- for (size_t i = 0; i < num_service_uuids; ++i) {
- GUID service_uuid;
- hr = service_uuids->GetAt(i, &service_uuid);
+ std::vector<ComPtr<IBluetoothLEManufacturerData>> manufacturer_data;
+ if (!ToStdVector(manufacturer_data_ptr.Get(), &manufacturer_data))
+ return {};
+
+ BluetoothDevice::ManufacturerDataMap manufacturer_data_map;
+ for (const auto& manufacturer_datum : manufacturer_data) {
+ uint16_t company_id;
+ hr = manufacturer_datum->get_CompanyId(&company_id);
if (FAILED(hr)) {
- VLOG(2) << "GetAt(" << i
- << ") failed: " << logging::SystemErrorCodeToString(hr);
- return base::nullopt;
+ VLOG(2) << "get_CompanyId() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ continue;
}
- advertised_uuids.emplace_back(service_uuid);
+ ComPtr<IBuffer> buffer;
+ hr = manufacturer_datum->get_Data(&buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Data() failed: " << logging::SystemErrorCodeToString(hr);
+ continue;
+ }
+
+ auto bytes = ExtractVector(buffer.Get());
+ if (!bytes)
+ continue;
+
+ manufacturer_data_map.emplace(company_id, std::move(*bytes));
}
- return advertised_uuids;
+ return manufacturer_data_map;
+}
+
+// Similarly to extracting the service data Windows does not provide a specific
+// API to extract the tx power. Thus we also parse the raw data sections here.
+// If present, we expect a single entry for tx power with a blob of size 1 byte.
+base::Optional<int8_t> ExtractTxPower(
+ IBluetoothLEAdvertisement* advertisement) {
+ if (!advertisement)
+ return base::nullopt;
+
+ ComPtr<IVectorView<BluetoothLEAdvertisementDataSection*>> data_sections;
+ HRESULT hr = advertisement->GetSectionsByType(
+ BluetoothDeviceWinrt::kTxPowerLevelDataSection, &data_sections);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetSectionsByType() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> vector;
+ if (!ToStdVector(data_sections.Get(), &vector) || vector.empty())
+ return base::nullopt;
+
+ if (vector.size() != 1u) {
+ VLOG(2) << "Unexpected number of data sections: " << vector.size();
+ return base::nullopt;
+ }
+
+ ComPtr<IBuffer> buffer;
+ hr = vector.front()->get_Data(&buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Data() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ auto bytes = ExtractVector(buffer.Get());
+ if (!bytes)
+ return base::nullopt;
+
+ if (bytes->size() != 1) {
+ VLOG(2) << "Unexpected number of bytes: " << bytes->size();
+ return base::nullopt;
+ }
+
+ return bytes->front();
}
ComPtr<IBluetoothLEAdvertisement> GetAdvertisement(
@@ -157,28 +441,19 @@ base::Optional<std::string> GetDeviceName(
void ExtractAndUpdateAdvertisementData(
IBluetoothLEAdvertisementReceivedEventArgs* received,
BluetoothDevice* device) {
- int16_t rssi;
+ int16_t rssi = 0;
HRESULT hr = received->get_RawSignalStrengthInDBm(&rssi);
if (FAILED(hr)) {
VLOG(2) << "get_RawSignalStrengthInDBm() failed: "
<< logging::SystemErrorCodeToString(hr);
- return;
}
ComPtr<IBluetoothLEAdvertisement> advertisement = GetAdvertisement(received);
- if (!advertisement)
- return;
-
- auto advertised_uuids = ExtractAdvertisedUUIDs(advertisement.Get());
- if (!advertised_uuids)
- return;
-
- // TODO(https://crbug.com/821766): Implement extraction of flags, tx power,
- // service data and manufacturer data.
- device->UpdateAdvertisementData(
- rssi, base::nullopt /* flags */, std::move(*advertised_uuids),
- base::nullopt /* tx_power */, BluetoothDevice::ServiceDataMap(),
- BluetoothDevice::ManufacturerDataMap());
+ device->UpdateAdvertisementData(rssi, ExtractFlags(advertisement.Get()),
+ ExtractAdvertisedUUIDs(advertisement.Get()),
+ ExtractTxPower(advertisement.Get()),
+ ExtractServiceData(advertisement.Get()),
+ ExtractManufacturerData(advertisement.Get()));
}
} // namespace
@@ -207,11 +482,15 @@ bool BluetoothAdapterWinrt::IsPresent() const {
return adapter_ != nullptr;
}
+bool BluetoothAdapterWinrt::CanPower() const {
+ return radio_ != nullptr;
+}
+
bool BluetoothAdapterWinrt::IsPowered() const {
// Due to an issue on WoW64 we might fail to obtain the radio in OnGetRadio().
// This is why it can be null here.
if (!radio_)
- return false;
+ return num_powered_radios_ != 0;
RadioState state;
HRESULT hr = radio_->get_State(&state);
@@ -237,8 +516,7 @@ void BluetoothAdapterWinrt::SetDiscoverable(
}
bool BluetoothAdapterWinrt::IsDiscovering() const {
- NOTIMPLEMENTED();
- return false;
+ return num_discovery_sessions_ != 0;
}
BluetoothAdapter::UUIDList BluetoothAdapterWinrt::GetUUIDs() const {
@@ -266,7 +544,37 @@ void BluetoothAdapterWinrt::RegisterAdvertisement(
std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const AdvertisementErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+ auto advertisement = CreateAdvertisement();
+ if (!advertisement->Initialize(std::move(advertisement_data))) {
+ VLOG(2) << "Failed to Initialize Advertisement.";
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothAdvertisement::ERROR_STARTING_ADVERTISEMENT));
+ return;
+ }
+
+ // In order to avoid |advertisement| holding a strong reference to itself, we
+ // pass only a weak reference to the callbacks, and store a strong reference
+ // in |pending_advertisements_|. When the callbacks are run, they will remove
+ // the corresponding advertisement from the list of pending advertisements.
+ advertisement->Register(
+ base::Bind(&BluetoothAdapterWinrt::OnRegisterAdvertisement,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(advertisement.get()), callback),
+ base::Bind(&BluetoothAdapterWinrt::OnRegisterAdvertisementError,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(advertisement.get()), error_callback));
+
+ pending_advertisements_.push_back(std::move(advertisement));
+}
+
+std::vector<BluetoothAdvertisement*>
+BluetoothAdapterWinrt::GetPendingAdvertisementsForTesting() const {
+ std::vector<BluetoothAdvertisement*> pending_advertisements;
+ for (const auto& pending_advertisement : pending_advertisements_)
+ pending_advertisements.push_back(pending_advertisement.get());
+ return pending_advertisements;
}
BluetoothLocalGattService* BluetoothAdapterWinrt::GetGattService(
@@ -275,11 +583,38 @@ BluetoothLocalGattService* BluetoothAdapterWinrt::GetGattService(
return nullptr;
}
+IRadio* BluetoothAdapterWinrt::GetRadioForTesting() {
+ return radio_.Get();
+}
+
+IDeviceWatcher* BluetoothAdapterWinrt::GetPoweredRadioWatcherForTesting() {
+ return powered_radio_watcher_.Get();
+}
+
BluetoothAdapterWinrt::BluetoothAdapterWinrt() : weak_ptr_factory_(this) {
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
-BluetoothAdapterWinrt::~BluetoothAdapterWinrt() = default;
+BluetoothAdapterWinrt::~BluetoothAdapterWinrt() {
+ // Explicitly move |pending_advertisements_| into a local variable and clear
+ // them out. Any remaining pending advertisement will attempt to remove itself
+ // from |pending_advertisements_|, which would result in a double-free
+ // otherwise.
+ auto pending_advertisements = std::move(pending_advertisements_);
+ pending_advertisements_.clear();
+
+ if (radio_)
+ TryRemoveRadioStateChangedHandler();
+
+ if (powered_radio_watcher_) {
+ TryRemovePoweredRadioEventHandlers();
+ HRESULT hr = powered_radio_watcher_->Stop();
+ if (FAILED(hr)) {
+ VLOG(2) << "Stopping powered radio watcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+ }
+}
void BluetoothAdapterWinrt::Init(InitCallback init_cb) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -341,7 +676,7 @@ bool BluetoothAdapterWinrt::SetPoweredImpl(bool powered) {
}
hr = PostAsyncResults(std::move(set_state_op),
- base::BindOnce(&BluetoothAdapterWinrt::OnSetState,
+ base::BindOnce(&BluetoothAdapterWinrt::OnSetRadioState,
weak_ptr_factory_.GetWeakPtr()));
if (FAILED(hr)) {
@@ -530,6 +865,11 @@ BluetoothAdapterWinrt::ActivateBluetoothAdvertisementLEWatcherInstance(
return watcher.CopyTo(instance);
}
+scoped_refptr<BluetoothAdvertisementWinrt>
+BluetoothAdapterWinrt::CreateAdvertisement() const {
+ return base::MakeRefCounted<BluetoothAdvertisementWinrt>();
+}
+
std::unique_ptr<BluetoothDeviceWinrt> BluetoothAdapterWinrt::CreateDevice(
uint64_t raw_address,
base::Optional<std::string> local_name) {
@@ -631,7 +971,7 @@ void BluetoothAdapterWinrt::OnCreateFromIdAsync(
hr = PostAsyncResults(
std::move(request_access_op),
- base::BindOnce(&BluetoothAdapterWinrt::OnRequestAccess,
+ base::BindOnce(&BluetoothAdapterWinrt::OnRequestRadioAccess,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
if (FAILED(hr)) {
@@ -640,8 +980,9 @@ void BluetoothAdapterWinrt::OnCreateFromIdAsync(
}
}
-void BluetoothAdapterWinrt::OnRequestAccess(base::ScopedClosureRunner on_init,
- RadioAccessStatus access_status) {
+void BluetoothAdapterWinrt::OnRequestRadioAccess(
+ base::ScopedClosureRunner on_init,
+ RadioAccessStatus access_status) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (access_status != RadioAccessStatus_Allowed) {
VLOG(2) << "Got unexpected Radio Access Status: "
@@ -670,25 +1011,117 @@ void BluetoothAdapterWinrt::OnRequestAccess(base::ScopedClosureRunner on_init,
void BluetoothAdapterWinrt::OnGetRadio(base::ScopedClosureRunner on_init,
ComPtr<IRadio> radio) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!radio) {
- // This happens within WoW64, due to an issue with non-native APIs.
- VLOG(2) << "Getting Radio failed.";
+ if (radio) {
+ radio_ = std::move(radio);
+ radio_state_changed_token_ = AddTypedEventHandler(
+ radio_.Get(), &IRadio::add_StateChanged,
+ base::BindRepeating(&BluetoothAdapterWinrt::OnRadioStateChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (!radio_state_changed_token_)
+ VLOG(2) << "Adding Radio State Changed Handler failed.";
+ return;
+ }
+
+ // This happens within WoW64, due to an issue with non-native APIs.
+ VLOG(2) << "Getting Radio failed. Chrome will be unable to change the power "
+ "state by itself.";
+
+ // Attempt to create a DeviceWatcher for powered radios, so that querying
+ // the power state is still possible.
+ ComPtr<IDeviceInformationStatics> device_information_statics;
+ HRESULT hr =
+ GetDeviceInformationStaticsActivationFactory(&device_information_statics);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetDeviceInformationStaticsActivationFactory failed: "
+ << logging::SystemErrorCodeToString(hr);
return;
}
- radio_ = std::move(radio);
+ auto aqs_filter = base::win::ScopedHString::Create(kPoweredRadiosAqsFilter);
+ hr = device_information_statics->CreateWatcherAqsFilter(
+ aqs_filter.get(), &powered_radio_watcher_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Creating Powered Radios Watcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ powered_radio_added_token_ = AddTypedEventHandler(
+ powered_radio_watcher_.Get(), &IDeviceWatcher::add_Added,
+ base::BindRepeating(&BluetoothAdapterWinrt::OnPoweredRadioAdded,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ powered_radio_removed_token_ = AddTypedEventHandler(
+ powered_radio_watcher_.Get(), &IDeviceWatcher::add_Removed,
+ base::BindRepeating(&BluetoothAdapterWinrt::OnPoweredRadioRemoved,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ powered_radios_enumerated_token_ = AddTypedEventHandler(
+ powered_radio_watcher_.Get(), &IDeviceWatcher::add_EnumerationCompleted,
+ base::BindRepeating(&BluetoothAdapterWinrt::OnPoweredRadiosEnumerated,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (!powered_radio_added_token_ || !powered_radio_removed_token_ ||
+ !powered_radios_enumerated_token_) {
+ VLOG(2) << "Failed to Register Powered Radio Event Handlers.";
+ TryRemovePoweredRadioEventHandlers();
+ return;
+ }
+
+ hr = powered_radio_watcher_->Start();
+ if (FAILED(hr)) {
+ VLOG(2) << "Starting the Powered Radio Watcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ TryRemovePoweredRadioEventHandlers();
+ return;
+ }
+
+ // Store the Closure Runner. It is expected that OnPoweredRadiosEnumerated()
+ // is invoked soon after.
+ on_init_ = std::make_unique<base::ScopedClosureRunner>(std::move(on_init));
}
-void BluetoothAdapterWinrt::OnSetState(RadioAccessStatus access_status) {
+void BluetoothAdapterWinrt::OnSetRadioState(RadioAccessStatus access_status) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (access_status != RadioAccessStatus_Allowed) {
VLOG(2) << "Got unexpected Radio Access Status: "
<< ToCString(access_status);
- } else {
- NotifyAdapterPoweredChanged(IsPowered());
+ RunPendingPowerCallbacks();
}
+}
- DidChangePoweredState();
+void BluetoothAdapterWinrt::OnRadioStateChanged(IRadio* radio,
+ IInspectable* object) {
+ RunPendingPowerCallbacks();
+ NotifyAdapterPoweredChanged(IsPowered());
+}
+
+void BluetoothAdapterWinrt::OnPoweredRadioAdded(IDeviceWatcher* watcher,
+ IDeviceInformation* info) {
+ if (++num_powered_radios_ == 1)
+ NotifyAdapterPoweredChanged(true);
+ VLOG(2) << "OnPoweredRadioAdded(), Number of Powered Radios: "
+ << num_powered_radios_;
+}
+
+void BluetoothAdapterWinrt::OnPoweredRadioRemoved(
+ IDeviceWatcher* watcher,
+ IDeviceInformationUpdate* update) {
+ if (--num_powered_radios_ == 0)
+ NotifyAdapterPoweredChanged(false);
+ VLOG(2) << "OnPoweredRadioRemoved(), Number of Powered Radios: "
+ << num_powered_radios_;
+}
+
+void BluetoothAdapterWinrt::OnPoweredRadiosEnumerated(IDeviceWatcher* watcher,
+ IInspectable* object) {
+ // Destroy the ScopedClosureRunner, triggering the contained Closure to be
+ // run.
+ DCHECK(on_init_);
+ on_init_.reset();
+ VLOG(2) << "OnPoweredRadiosEnumerated(), Number of Powered Radios: "
+ << num_powered_radios_;
}
void BluetoothAdapterWinrt::OnAdvertisementReceived(
@@ -725,6 +1158,75 @@ void BluetoothAdapterWinrt::OnAdvertisementReceived(
}
}
+void BluetoothAdapterWinrt::OnRegisterAdvertisement(
+ BluetoothAdvertisement* advertisement,
+ const CreateAdvertisementCallback& callback) {
+ DCHECK(base::ContainsValue(pending_advertisements_, advertisement));
+ auto wrapped_advertisement = base::WrapRefCounted(advertisement);
+ base::Erase(pending_advertisements_, advertisement);
+ callback.Run(std::move(wrapped_advertisement));
+}
+
+void BluetoothAdapterWinrt::OnRegisterAdvertisementError(
+ BluetoothAdvertisement* advertisement,
+ const AdvertisementErrorCallback& error_callback,
+ BluetoothAdvertisement::ErrorCode error_code) {
+ // Note: We are not DCHECKing that |pending_advertisements_| contains
+ // |advertisement|, as this method might be invoked during destruction.
+ base::Erase(pending_advertisements_, advertisement);
+ error_callback.Run(error_code);
+}
+
+void BluetoothAdapterWinrt::TryRemoveRadioStateChangedHandler() {
+ DCHECK(radio_);
+ if (!radio_state_changed_token_)
+ return;
+
+ HRESULT hr = radio_->remove_StateChanged(*radio_state_changed_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing Radio State Changed Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ radio_state_changed_token_.reset();
+}
+
+void BluetoothAdapterWinrt::TryRemovePoweredRadioEventHandlers() {
+ DCHECK(powered_radio_watcher_);
+ if (powered_radio_added_token_) {
+ HRESULT hr =
+ powered_radio_watcher_->remove_Added(*powered_radio_removed_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing the Powered Radio Added Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ powered_radio_added_token_.reset();
+ }
+
+ if (powered_radio_removed_token_) {
+ HRESULT hr =
+ powered_radio_watcher_->remove_Removed(*powered_radio_removed_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing the Powered Radio Removed Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ powered_radio_removed_token_.reset();
+ }
+
+ if (powered_radios_enumerated_token_) {
+ HRESULT hr = powered_radio_watcher_->remove_EnumerationCompleted(
+ *powered_radios_enumerated_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing the Powered Radios Enumerated Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ powered_radios_enumerated_token_.reset();
+ }
+}
+
void BluetoothAdapterWinrt::RemoveAdvertisementReceivedHandler() {
DCHECK(ble_advertisement_watcher_);
HRESULT hr = ble_advertisement_watcher_->remove_Received(
diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.h b/chromium/device/bluetooth/bluetooth_adapter_winrt.h
index cb60c4c84f1..25611d511da 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.h
@@ -12,9 +12,11 @@
#include <memory>
#include <string>
+#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_export.h"
@@ -25,6 +27,7 @@ class ScopedClosureRunner;
namespace device {
+class BluetoothAdvertisementWinrt;
class BluetoothDeviceWinrt;
class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
@@ -37,6 +40,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
const ErrorCallback& error_callback) override;
bool IsInitialized() const override;
bool IsPresent() const override;
+ bool CanPower() const override;
bool IsPowered() const override;
bool IsDiscoverable() const override;
void SetDiscoverable(bool discoverable,
@@ -58,9 +62,15 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const AdvertisementErrorCallback& error_callback) override;
+ std::vector<BluetoothAdvertisement*> GetPendingAdvertisementsForTesting()
+ const override;
BluetoothLocalGattService* GetGattService(
const std::string& identifier) const override;
+ ABI::Windows::Devices::Radios::IRadio* GetRadioForTesting();
+ ABI::Windows::Devices::Enumeration::IDeviceWatcher*
+ GetPoweredRadioWatcherForTesting();
+
protected:
friend class BluetoothAdapterWin;
friend class BluetoothTestWinrt;
@@ -102,6 +112,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
virtual HRESULT ActivateBluetoothAdvertisementLEWatcherInstance(
ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementWatcher** instance) const;
+
+ virtual scoped_refptr<BluetoothAdvertisementWinrt> CreateAdvertisement()
+ const;
+
virtual std::unique_ptr<BluetoothDeviceWinrt> CreateDevice(
uint64_t raw_address,
base::Optional<std::string> local_name);
@@ -118,7 +132,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
ABI::Windows::Devices::Enumeration::IDeviceInformation>
device_information);
- void OnRequestAccess(
+ void OnRequestRadioAccess(
base::ScopedClosureRunner on_init,
ABI::Windows::Devices::Radios::RadioAccessStatus access_status);
@@ -126,24 +140,63 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
base::ScopedClosureRunner on_init,
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadio> radio);
- void OnSetState(
+ void OnSetRadioState(
ABI::Windows::Devices::Radios::RadioAccessStatus access_status);
+ void OnRadioStateChanged(ABI::Windows::Devices::Radios::IRadio* radio,
+ IInspectable* object);
+
+ void OnPoweredRadioAdded(
+ ABI::Windows::Devices::Enumeration::IDeviceWatcher* watcher,
+ ABI::Windows::Devices::Enumeration::IDeviceInformation* info);
+
+ void OnPoweredRadioRemoved(
+ ABI::Windows::Devices::Enumeration::IDeviceWatcher* watcher,
+ ABI::Windows::Devices::Enumeration::IDeviceInformationUpdate* update);
+
+ void OnPoweredRadiosEnumerated(
+ ABI::Windows::Devices::Enumeration::IDeviceWatcher* watcher,
+ IInspectable* object);
+
void OnAdvertisementReceived(
ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementWatcher* watcher,
ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementReceivedEventArgs* received);
+ void OnRegisterAdvertisement(BluetoothAdvertisement* advertisement,
+ const CreateAdvertisementCallback& callback);
+
+ void OnRegisterAdvertisementError(
+ BluetoothAdvertisement* advertisement,
+ const AdvertisementErrorCallback& error_callback,
+ BluetoothAdvertisement::ErrorCode error_code);
+
+ void TryRemoveRadioStateChangedHandler();
+
+ void TryRemovePoweredRadioEventHandlers();
+
void RemoveAdvertisementReceivedHandler();
bool is_initialized_ = false;
std::string address_;
std::string name_;
+ std::unique_ptr<base::ScopedClosureRunner> on_init_;
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothAdapter>
adapter_;
+
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadio> radio_;
+ base::Optional<EventRegistrationToken> radio_state_changed_token_;
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Enumeration::IDeviceWatcher>
+ powered_radio_watcher_;
+ base::Optional<EventRegistrationToken> powered_radio_added_token_;
+ base::Optional<EventRegistrationToken> powered_radio_removed_token_;
+ base::Optional<EventRegistrationToken> powered_radios_enumerated_token_;
+ size_t num_powered_radios_ = 0;
+
+ std::vector<scoped_refptr<BluetoothAdvertisement>> pending_advertisements_;
size_t num_discovery_sessions_ = 0;
EventRegistrationToken advertisement_received_token_;
diff --git a/chromium/device/bluetooth/bluetooth_advertisement.h b/chromium/device/bluetooth/bluetooth_advertisement.h
index f17bb025bfd..041738f7ea8 100644
--- a/chromium/device/bluetooth/bluetooth_advertisement.h
+++ b/chromium/device/bluetooth/bluetooth_advertisement.h
@@ -146,7 +146,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdvertisement
// List of observers interested in event notifications from us. Objects in
// |observers_| are expected to outlive a BluetoothAdvertisement object.
- base::ObserverList<BluetoothAdvertisement::Observer> observers_;
+ base::ObserverList<BluetoothAdvertisement::Observer>::Unchecked observers_;
private:
DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisement);
diff --git a/chromium/device/bluetooth/bluetooth_advertisement_winrt.cc b/chromium/device/bluetooth/bluetooth_advertisement_winrt.cc
new file mode 100644
index 00000000000..71f603702b2
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_advertisement_winrt.cc
@@ -0,0 +1,416 @@
+// Copyright 2018 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/bluetooth/bluetooth_advertisement_winrt.h"
+
+#include <windows.foundation.collections.h>
+#include <windows.storage.streams.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/core_winrt_util.h"
+#include "base/win/scoped_hstring.h"
+#include "base/win/winrt_storage_util.h"
+#include "device/bluetooth/event_utils_winrt.h"
+
+namespace device {
+
+namespace {
+
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementPublisherStatus;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementPublisherStatus_Aborted;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementPublisherStatus_Started;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementPublisherStatus_Stopped;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEManufacturerData;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisement;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisher;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisherFactory;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisherStatusChangedEventArgs;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEManufacturerData;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEManufacturerDataFactory;
+using ABI::Windows::Devices::Bluetooth::BluetoothError;
+using ABI::Windows::Devices::Bluetooth::BluetoothError_NotSupported;
+using ABI::Windows::Devices::Bluetooth::BluetoothError_RadioNotAvailable;
+using ABI::Windows::Foundation::Collections::IVector;
+using ABI::Windows::Storage::Streams::IBuffer;
+using Microsoft::WRL::ComPtr;
+
+void RemoveStatusChangedHandler(IBluetoothLEAdvertisementPublisher* publisher,
+ EventRegistrationToken token) {
+ HRESULT hr = publisher->remove_StatusChanged(token);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing StatusChanged Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+} // namespace
+
+BluetoothAdvertisementWinrt::BluetoothAdvertisementWinrt()
+ : weak_ptr_factory_(this) {}
+
+bool BluetoothAdvertisementWinrt::Initialize(
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data) {
+ if (advertisement_data->service_uuids()) {
+ VLOG(2) << "Windows does not support advertising Service UUIDs.";
+ return false;
+ }
+
+ if (advertisement_data->solicit_uuids()) {
+ VLOG(2) << "Windows does not support advertising Solicit UUIDs.";
+ return false;
+ }
+
+ if (advertisement_data->service_data()) {
+ VLOG(2) << "Windows does not support advertising Service Data.";
+ return false;
+ }
+
+ auto manufacturer_data = advertisement_data->manufacturer_data();
+ if (!manufacturer_data) {
+ VLOG(2) << "No Manufacturer Data present.";
+ return false;
+ }
+
+ ComPtr<IBluetoothLEAdvertisement> advertisement;
+ HRESULT hr = ActivateBluetoothLEAdvertisementInstance(&advertisement);
+ if (FAILED(hr)) {
+ VLOG(2) << "ActivateBluetoothLEAdvertisementInstance failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ ComPtr<IVector<BluetoothLEManufacturerData*>> manufacturer_data_list;
+ hr = advertisement->get_ManufacturerData(&manufacturer_data_list);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting ManufacturerData failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ ComPtr<IBluetoothLEManufacturerDataFactory> manufacturer_data_factory;
+ hr = GetBluetoothLEManufacturerDataFactory(&manufacturer_data_factory);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetBluetoothLEManufacturerDataFactory failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ for (const auto& pair : *manufacturer_data) {
+ uint16_t manufacturer = pair.first;
+ const std::vector<uint8_t>& data = pair.second;
+
+ ComPtr<IBuffer> buffer;
+ hr = base::win::CreateIBufferFromData(data.data(), data.size(), &buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "CreateIBufferFromData() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ ComPtr<IBluetoothLEManufacturerData> manufacturer_data_entry;
+ hr = manufacturer_data_factory->Create(manufacturer, buffer.Get(),
+ &manufacturer_data_entry);
+ if (FAILED(hr)) {
+ VLOG(2) << "Creating BluetoothLEManufacturerData failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ hr = manufacturer_data_list->Append(manufacturer_data_entry.Get());
+ if (FAILED(hr)) {
+ VLOG(2) << "Appending BluetoothLEManufacturerData failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+ }
+
+ ComPtr<IBluetoothLEAdvertisementPublisherFactory> publisher_factory;
+ hr =
+ GetBluetoothLEAdvertisementPublisherActivationFactory(&publisher_factory);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetBluetoothLEAdvertisementPublisherActivationFactory "
+ "failed:"
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ hr = publisher_factory->Create(advertisement.Get(), &publisher_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Creating IBluetoothLEAdvertisementPublisher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ return true;
+}
+
+void BluetoothAdvertisementWinrt::Register(SuccessCallback callback,
+ ErrorCallback error_callback) {
+ // Register should only be called once during initialization.
+ DCHECK(!status_changed_token_);
+ DCHECK(!pending_register_callbacks_);
+ DCHECK(!pending_unregister_callbacks_);
+
+ // Register should only be called after successful initialization.
+ DCHECK(publisher_);
+
+ status_changed_token_ = AddTypedEventHandler(
+ publisher_.Get(), &IBluetoothLEAdvertisementPublisher::add_StatusChanged,
+ base::BindRepeating(&BluetoothAdvertisementWinrt::OnStatusChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (!status_changed_token_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(error_callback),
+ ERROR_STARTING_ADVERTISEMENT));
+ return;
+ }
+
+ HRESULT hr = publisher_->Start();
+ if (FAILED(hr)) {
+ VLOG(2) << "Starting IBluetoothLEAdvertisementPublisher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(error_callback),
+ ERROR_STARTING_ADVERTISEMENT));
+ RemoveStatusChangedHandler(publisher_.Get(), *status_changed_token_);
+ status_changed_token_.reset();
+ return;
+ }
+
+ pending_register_callbacks_ = std::make_unique<PendingCallbacks>(
+ std::move(callback), std::move(error_callback));
+}
+
+void BluetoothAdvertisementWinrt::Unregister(
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
+ // Unregister() should only be called when an advertisement is registered
+ // already, or during destruction. In both of these cases there should be no
+ // pending register callbacks and the publisher should be present.
+ DCHECK(!pending_register_callbacks_);
+ DCHECK(publisher_);
+
+ if (pending_unregister_callbacks_) {
+ VLOG(2) << "An Unregister Operation is already in progress.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING));
+ return;
+ }
+
+ BluetoothLEAdvertisementPublisherStatus status;
+ HRESULT hr = publisher_->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting the Publisher Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING));
+ return;
+ }
+
+ if (status == BluetoothLEAdvertisementPublisherStatus_Aborted) {
+ // Report an error if the publisher is in the aborted state.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING));
+ return;
+ }
+
+ if (status == BluetoothLEAdvertisementPublisherStatus_Stopped) {
+ // Report success if the publisher is already stopped.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, success_callback);
+ return;
+ }
+
+ hr = publisher_->Stop();
+ if (FAILED(hr)) {
+ VLOG(2) << "IBluetoothLEAdvertisementPublisher::Stop() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING));
+ return;
+ }
+
+ pending_unregister_callbacks_ =
+ std::make_unique<PendingCallbacks>(success_callback, error_callback);
+}
+
+IBluetoothLEAdvertisementPublisher*
+BluetoothAdvertisementWinrt::GetPublisherForTesting() {
+ return publisher_.Get();
+}
+
+BluetoothAdvertisementWinrt::~BluetoothAdvertisementWinrt() {
+ if (status_changed_token_) {
+ DCHECK(publisher_);
+ RemoveStatusChangedHandler(publisher_.Get(), *status_changed_token_);
+ }
+
+ // Stop any pending register operation.
+ if (pending_register_callbacks_) {
+ auto callbacks = std::move(pending_register_callbacks_);
+ std::move(callbacks->error_callback).Run(ERROR_STARTING_ADVERTISEMENT);
+ }
+
+ // Unregister the advertisement on a best effort basis if it's not already in
+ // process of doing so.
+ if (!pending_unregister_callbacks_ && publisher_)
+ Unregister(base::DoNothing(), base::DoNothing());
+}
+
+HRESULT
+BluetoothAdvertisementWinrt::
+ GetBluetoothLEAdvertisementPublisherActivationFactory(
+ IBluetoothLEAdvertisementPublisherFactory** factory) const {
+ return base::win::GetActivationFactory<
+ IBluetoothLEAdvertisementPublisherFactory,
+ RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementPublisher>(
+ factory);
+}
+
+HRESULT
+BluetoothAdvertisementWinrt::ActivateBluetoothLEAdvertisementInstance(
+ IBluetoothLEAdvertisement** instance) const {
+ auto advertisement_hstring = base::win::ScopedHString::Create(
+ RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisement);
+ if (!advertisement_hstring.is_valid())
+ return E_FAIL;
+
+ ComPtr<IInspectable> inspectable;
+ HRESULT hr =
+ base::win::RoActivateInstance(advertisement_hstring.get(), &inspectable);
+ if (FAILED(hr)) {
+ VLOG(2) << "RoActivateInstance failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return hr;
+ }
+
+ ComPtr<IBluetoothLEAdvertisement> advertisement;
+ hr = inspectable.As(&advertisement);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IBluetoothLEAdvertisementWatcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return hr;
+ }
+
+ return advertisement.CopyTo(instance);
+}
+
+HRESULT
+BluetoothAdvertisementWinrt::GetBluetoothLEManufacturerDataFactory(
+ IBluetoothLEManufacturerDataFactory** factory) const {
+ return base::win::GetActivationFactory<
+ IBluetoothLEManufacturerDataFactory,
+ RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEManufacturerData>(
+ factory);
+}
+
+BluetoothAdvertisementWinrt::PendingCallbacks::PendingCallbacks(
+ SuccessCallback callback,
+ ErrorCallback error_callback)
+ : callback(std::move(callback)),
+ error_callback(std::move(error_callback)) {}
+
+BluetoothAdvertisementWinrt::PendingCallbacks::~PendingCallbacks() = default;
+
+void BluetoothAdvertisementWinrt::OnStatusChanged(
+ IBluetoothLEAdvertisementPublisher* publisher,
+ IBluetoothLEAdvertisementPublisherStatusChangedEventArgs* changed) {
+ BluetoothLEAdvertisementPublisherStatus status;
+ HRESULT hr = changed->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting the Publisher Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ VLOG(2) << "Publisher Status: " << static_cast<int>(status);
+ if (status == BluetoothLEAdvertisementPublisherStatus_Stopped) {
+ // Notify Observers.
+ for (auto& observer : observers_)
+ observer.AdvertisementReleased(this);
+ }
+
+ // Return early if there is no pending action.
+ if (!pending_register_callbacks_ && !pending_unregister_callbacks_)
+ return;
+
+ // Register and Unregister should never be pending at the same time.
+ DCHECK(!pending_register_callbacks_ || !pending_unregister_callbacks_);
+
+ const bool is_starting = pending_register_callbacks_ != nullptr;
+ ErrorCode error_code =
+ is_starting ? ERROR_STARTING_ADVERTISEMENT : ERROR_RESET_ADVERTISING;
+
+ // Clears out pending callbacks by moving them into a local variable and runs
+ // the appropriate error callback with |error_code|.
+ auto run_error_cb = [&](ErrorCode error_code) {
+ auto callbacks = std::move(is_starting ? pending_register_callbacks_
+ : pending_unregister_callbacks_);
+ std::move(callbacks->error_callback).Run(error_code);
+ };
+
+ if (status == BluetoothLEAdvertisementPublisherStatus_Aborted) {
+ VLOG(2) << "The Publisher aborted.";
+ BluetoothError bluetooth_error;
+ hr = changed->get_Error(&bluetooth_error);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting the Publisher Error failed: "
+ << logging::SystemErrorCodeToString(hr);
+ run_error_cb(error_code);
+ return;
+ }
+
+ VLOG(2) << "Publisher Error: " << static_cast<int>(bluetooth_error);
+ switch (bluetooth_error) {
+ case BluetoothError_RadioNotAvailable:
+ error_code = ERROR_ADAPTER_POWERED_OFF;
+ break;
+ case BluetoothError_NotSupported:
+ error_code = ERROR_UNSUPPORTED_PLATFORM;
+ break;
+ default:
+ break;
+ }
+
+ run_error_cb(error_code);
+ return;
+ }
+
+ if (is_starting &&
+ status == BluetoothLEAdvertisementPublisherStatus_Started) {
+ VLOG(2) << "Starting the Publisher was successful.";
+ auto callbacks = std::move(pending_register_callbacks_);
+ std::move(callbacks->callback).Run();
+ return;
+ }
+
+ if (!is_starting &&
+ status == BluetoothLEAdvertisementPublisherStatus_Stopped) {
+ VLOG(2) << "Stopping the Publisher was successful.";
+ auto callbacks = std::move(pending_unregister_callbacks_);
+ std::move(callbacks->callback).Run();
+ return;
+ }
+
+ // The other states are temporary and we expect a future StatusChanged
+ // event.
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_advertisement_winrt.h b/chromium/device/bluetooth/bluetooth_advertisement_winrt.h
new file mode 100644
index 00000000000..7681e5f6d71
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_advertisement_winrt.h
@@ -0,0 +1,84 @@
+// Copyright 2018 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_BLUETOOTH_BLUETOOTH_ADVERTISEMENT_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_ADVERTISEMENT_WINRT_H_
+
+#include <windows.devices.bluetooth.advertisement.h>
+#include <wrl/client.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+#include "device/bluetooth/bluetooth_export.h"
+
+namespace device {
+
+class DEVICE_BLUETOOTH_EXPORT BluetoothAdvertisementWinrt
+ : public BluetoothAdvertisement {
+ public:
+ BluetoothAdvertisementWinrt();
+ bool Initialize(
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data);
+ void Register(SuccessCallback callback, ErrorCallback error_callback);
+
+ // BluetoothAdvertisement:
+ void Unregister(const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) override;
+
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisher*
+ GetPublisherForTesting();
+
+ protected:
+ ~BluetoothAdvertisementWinrt() override;
+
+ // These are declared virtual so that they can be overridden by tests.
+ virtual HRESULT GetBluetoothLEAdvertisementPublisherActivationFactory(
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisherFactory** factory) const;
+
+ virtual HRESULT ActivateBluetoothLEAdvertisementInstance(
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisement** instance) const;
+
+ virtual HRESULT GetBluetoothLEManufacturerDataFactory(
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEManufacturerDataFactory** factory) const;
+
+ private:
+ struct PendingCallbacks {
+ PendingCallbacks(SuccessCallback callback, ErrorCallback error_callback);
+ ~PendingCallbacks();
+
+ SuccessCallback callback;
+ ErrorCallback error_callback;
+ };
+
+ void OnStatusChanged(
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisher* publisher,
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisherStatusChangedEventArgs* changed);
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementPublisher>
+ publisher_;
+ base::Optional<EventRegistrationToken> status_changed_token_;
+ std::unique_ptr<PendingCallbacks> pending_register_callbacks_;
+ std::unique_ptr<PendingCallbacks> pending_unregister_callbacks_;
+
+ base::WeakPtrFactory<BluetoothAdvertisementWinrt> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisementWinrt);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADVERTISEMENT_WINRT_H_
diff --git a/chromium/device/bluetooth/bluetooth_device.cc b/chromium/device/bluetooth/bluetooth_device.cc
index 57134686533..dc280146a0c 100644
--- a/chromium/device/bluetooth/bluetooth_device.cc
+++ b/chromium/device/bluetooth/bluetooth_device.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -66,10 +67,8 @@ const BluetoothDevice::UUIDSet& BluetoothDevice::DeviceUUIDs::GetUUIDs() const {
}
void BluetoothDevice::DeviceUUIDs::UpdateDeviceUUIDs() {
- device_uuids_.clear();
- std::set_union(advertised_uuids_.begin(), advertised_uuids_.end(),
- service_uuids_.begin(), service_uuids_.end(),
- std::inserter(device_uuids_, device_uuids_.begin()));
+ device_uuids_ = base::STLSetUnion<BluetoothDevice::UUIDSet>(advertised_uuids_,
+ service_uuids_);
}
BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter)
diff --git a/chromium/device/bluetooth/bluetooth_device.h b/chromium/device/bluetooth/bluetooth_device.h
index e0044d638a4..87ec108a0be 100644
--- a/chromium/device/bluetooth/bluetooth_device.h
+++ b/chromium/device/bluetooth/bluetooth_device.h
@@ -16,6 +16,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_set.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -95,7 +96,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
};
typedef std::vector<BluetoothUUID> UUIDList;
- typedef std::unordered_set<BluetoothUUID, BluetoothUUIDHash> UUIDSet;
+ typedef base::flat_set<BluetoothUUID> UUIDSet;
typedef std::unordered_map<BluetoothUUID,
std::vector<uint8_t>,
BluetoothUUIDHash>
@@ -356,8 +357,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// Returns Advertising Data Flags.
// Returns cached value if the adapter is not discovering.
//
- // TODO(crbug.com/661814) Support this on platforms that don't use BlueZ.
- // Only Chrome OS supports this now. Upstream BlueZ has this feature
+ // Only Chrome OS and WinRT support this now. Upstream BlueZ has this feature
// as experimental. This method returns base::nullopt on platforms that don't
// support this feature.
base::Optional<uint8_t> GetAdvertisingDataFlags() const;
@@ -428,7 +428,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// ignores the |IsPaired()| value.
//
// In most cases |Connect()| should be preferred. This method is only
- // implemented on ChromeOS and Linux.
+ // implemented on ChromeOS, Linux and Windows 10. On Windows, only pairing
+ // with a pin code is currently supported.
virtual void Pair(PairingDelegate* pairing_delegate,
const base::Closure& callback,
const ConnectErrorCallback& error_callback);
diff --git a/chromium/device/bluetooth/bluetooth_device_unittest.cc b/chromium/device/bluetooth/bluetooth_device_unittest.cc
index 83924cb6713..a443aeffba4 100644
--- a/chromium/device/bluetooth/bluetooth_device_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_device_unittest.cc
@@ -13,6 +13,7 @@
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
+#include "device/bluetooth/test/test_pairing_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID)
@@ -94,6 +95,171 @@ TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_RejectsInvalidFormats) {
}
}
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, DeviceIsPaired) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(1);
+
+ // By default a device should not be paired.
+ EXPECT_FALSE(device->IsPaired());
+
+ // Connect to the device and simulate a paired state.
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ SimulateDevicePaired(device, true);
+ EXPECT_TRUE(device->IsPaired());
+
+ SimulateDevicePaired(device, false);
+ EXPECT_FALSE(device->IsPaired());
+}
+
+// Tests that providing a correct pin code results in a paired device.
+TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeCorrect) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(1);
+
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+
+ SimulatePairingPinCode(device, "123456");
+ TestPairingDelegate pairing_delegate;
+ device->Pair(&pairing_delegate, GetCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->ExpectingPinCode());
+
+ device->SetPinCode("123456");
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+}
+
+// Tests that providing a wrong pin code does not result in a paired device.
+TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeWrong) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(1);
+
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+
+ SimulatePairingPinCode(device, "123456");
+ TestPairingDelegate pairing_delegate;
+ device->Pair(&pairing_delegate, GetCallback(Call::NOT_EXPECTED),
+ GetConnectErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->ExpectingPinCode());
+
+ device->SetPinCode("000000");
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+ EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_code_);
+}
+
+// Tests that rejecting the pairing does not result in a paired device.
+TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeRejectPairing) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(1);
+
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+
+ SimulatePairingPinCode(device, "123456");
+ TestPairingDelegate pairing_delegate;
+ device->Pair(&pairing_delegate, GetCallback(Call::NOT_EXPECTED),
+ GetConnectErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->ExpectingPinCode());
+
+ device->RejectPairing();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_code_);
+}
+
+// Tests that cancelling the pairing does not result in a paired device.
+TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeCancelPairing) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(1);
+
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+
+ SimulatePairingPinCode(device, "123456");
+ TestPairingDelegate pairing_delegate;
+ device->Pair(&pairing_delegate, GetCallback(Call::NOT_EXPECTED),
+ GetConnectErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->ExpectingPinCode());
+
+ device->CancelPairing();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(device->IsPaired());
+ EXPECT_FALSE(device->ExpectingPinCode());
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_code_);
+}
+#endif
+
// Verifies basic device properties, e.g. GetAddress, GetName, ...
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, LowEnergyDeviceProperties) {
@@ -151,7 +317,11 @@ TEST_F(BluetoothTest, LowEnergyDeviceNoUUIDs) {
#define MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID \
DISABLED_GetServiceDataUUIDs_GetServiceDataForUUID
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GetServiceDataUUIDs_GetServiceDataForUUID) {
+#else
TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -166,6 +336,7 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
// Receive Advertisement with empty service data.
BluetoothDevice* device1 = SimulateLowEnergyDevice(4);
+ EXPECT_FALSE(device1->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device1->GetServiceData().empty());
EXPECT_TRUE(device1->GetServiceDataUUIDs().empty());
EXPECT_TRUE(device1->GetManufacturerData().empty());
@@ -173,6 +344,10 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
// Receive Advertisement with service data.
BluetoothDevice* device2 = SimulateLowEnergyDevice(1);
+#if defined(OS_WIN)
+ EXPECT_TRUE(device2->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x04, device2->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device2->GetServiceData());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate)}),
@@ -181,7 +356,7 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
*device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
EXPECT_EQ(std::vector<uint8_t>({1, 2, 3, 4}),
*device2->GetManufacturerDataForID(kTestManufacturerId));
- // Receive Advertisement with no service and manufacturer data.
+ // Receive Advertisement with no flags and no service and manufacturer data.
SimulateLowEnergyDevice(3);
// TODO(crbug.com/707039): Remove #if once the BlueZ caching behavior is
@@ -198,6 +373,7 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
EXPECT_EQ(std::vector<uint8_t>({1}),
*device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
#else
+ EXPECT_FALSE(device2->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device2->GetServiceData().empty());
EXPECT_TRUE(device2->GetServiceDataUUIDs().empty());
EXPECT_TRUE(device2->GetManufacturerData().empty());
@@ -208,6 +384,10 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
// Receive Advertisement with new service data and empty manufacturer data.
SimulateLowEnergyDevice(2);
+#if defined(OS_WIN)
+ EXPECT_TRUE(device2->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x05, device2->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
@@ -229,9 +409,11 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
// Stop discovery.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
+ EXPECT_FALSE(device2->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device2->GetServiceData().empty());
EXPECT_TRUE(device2->GetServiceDataUUIDs().empty());
EXPECT_EQ(nullptr,
@@ -248,7 +430,11 @@ TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
#endif
// Tests that the Advertisement Data fields are correctly updated during
// discovery.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, AdvertisementData_Discovery) {
+#else
TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -259,6 +445,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
// Start Discovery Session and receive Advertisement, should
// not notify of device changed because the device is new.
// - GetInquiryRSSI: Should return the packet's rssi.
+ // - GetAdvertisingDataFlags: Should return advertised flags.
// - GetUUIDs: Should return Advertised UUIDs.
// - GetServiceData: Should return advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
@@ -268,6 +455,10 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
EXPECT_EQ(0, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
@@ -277,9 +468,10 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
device->GetManufacturerData());
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
- // Receive Advertisement with no UUIDs, Service Data, or Tx Power, should
- // notify device changed.
+ // Receive Advertisement with no flags, no UUIDs, Service Data, or Tx Power,
+ // should notify device changed.
// - GetInquiryRSSI: Should return packet's rssi.
+ // - GetAdvertisingDataFlags: Should return nullopt because of no flags.
// - GetUUIDs: Should return no UUIDs.
// - GetServiceData: Should return empty map.
// - GetInquiryTxPower: Should return nullopt because of no Tx Power.
@@ -287,6 +479,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOW), device->GetInquiryRSSI().value());
+ EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device->GetUUIDs().empty());
EXPECT_TRUE(device->GetServiceData().empty());
EXPECT_TRUE(device->GetManufacturerData().empty());
@@ -295,6 +488,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
// Receive Advertisement with different UUIDs, Service Data, and Tx Power,
// should notify device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
+ // - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return latest Advertised UUIDs.
// - GetServiceData: Should return last advertised Service Data.
// - GetInquiryTxPower: Should return last advertised Tx Power.
@@ -302,6 +496,10 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDImmediateAlert),
BluetoothUUID(kTestUUIDLinkLoss)}),
device->GetUUIDs());
@@ -317,6 +515,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
// Stop discovery session, should notify of device changed.
// - GetInquiryRSSI: Should return nullopt because we are no longer
// discovering.
+ // - GetAdvertisingDataFlags: Should return no flags.
// - GetUUIDs: Should not return any UUIDs.
// - GetServiceData: Should return empty map.
// - GetMAnufacturerData: Should return empty map.
@@ -324,12 +523,14 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
// discovering.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
EXPECT_EQ(3, observer.device_changed_count());
EXPECT_FALSE(device->GetInquiryRSSI());
+ EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device->GetUUIDs().empty());
EXPECT_TRUE(device->GetServiceData().empty());
EXPECT_TRUE(device->GetManufacturerData().empty());
@@ -338,6 +539,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
// Discover the device again with different UUIDs, should notify of device
// changed.
// - GetInquiryRSSI: Should return last packet's rssi.
+ // - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return only the latest Advertised UUIDs.
// - GetServiceData: Should return last advertise Service Data.
// - GetInquiryTxPower: Should return last advertised Tx Power.
@@ -347,6 +549,10 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
EXPECT_EQ(4, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
@@ -558,7 +764,11 @@ TEST_F(BluetoothTest, ExtraDidDiscoverServicesCall) {
#endif
// Tests Advertisement Data is updated correctly when we start discovery
// during a connection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, AdvertisementData_DiscoveryDuringConnection) {
+#else
TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -575,6 +785,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
device->GetUUIDs());
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
ASSERT_EQ(0u, device->GetUUIDs().size());
@@ -592,6 +803,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
// Start Discovery and receive advertisement during connection,
// should notify of device changed.
// - GetInquiryRSSI: Should return the packet's rssi.
+ // - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return only Advertised UUIDs since services haven't
// been discovered yet.
// - GetServiceData: Should return last advertised Service Data.
@@ -607,10 +819,12 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
-#endif // defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Discover services, should notify of device changed.
@@ -629,6 +843,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
// Receive advertisement again, notify of device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
+ // - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return only new Advertised UUIDs and Service UUIDs.
// - GetServiceData: Should return last advertised Service Data.
// - GetInquiryTxPower: Should return the last packet's advertised Tx Power.
@@ -636,21 +851,25 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
EXPECT_EQ(3, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert),
BluetoothUUID(kTestUUIDHeartRate)}),
device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
-#endif // defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Stop discovery session, should notify of device changed.
// - GetInquiryRSSI: Should return nullopt because we are no longer
// discovering.
+ // - GetAdvertisingDataFlags: Should return no flags since we are no longer
+ // discovering.
// - GetUUIDs: Should only return Service UUIDs.
// - GetServiceData: Should return an empty map since we are no longer
// discovering.
@@ -658,15 +877,15 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
// discovering.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
EXPECT_EQ(4, observer.device_changed_count());
EXPECT_FALSE(device->GetInquiryRSSI());
+ EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate)}), device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ServiceDataMap(), device->GetServiceData());
-#endif // defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_FALSE(device->GetInquiryTxPower());
// Disconnect device, should notify of device changed.
@@ -688,7 +907,11 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
#define MAYBE_AdvertisementData_ConnectionDuringDiscovery \
DISABLED_AdvertisementData_ConnectionDuringDiscovery
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, AdvertisementData_ConnectionDuringDiscovery) {
+#else
TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
+#endif
// Tests that the Advertisement Data is correctly updated when
// the device connects during discovery.
if (!PlatformSupportsLowEnergy()) {
@@ -702,6 +925,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
// Start discovery session and receive and advertisement. No device changed
// notification because it's a new device.
// - GetInquiryRSSI: Should return the packet's rssi.
+ // - GetAdvertisingDataFlags: Should return advertised flags.
// - GetUUIDs: Should return Advertised UUIDs.
// - GetServiceData: Should return advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
@@ -712,13 +936,15 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
EXPECT_EQ(0, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
-#endif // defined(OS_MACOSX)
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Connect, should notify of device changed.
@@ -736,6 +962,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
// Receive Advertisement with new UUIDs, should notify of device changed.
// - GetInquiryRSSI: Should return the packet's rssi.
+ // - GetAdvertisingDataFlags: Should return advertised flags.
// - GetUUIDs: Should return new Advertised UUIDs.
// - GetServiceData: Should return new advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
@@ -743,15 +970,17 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert)}),
device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
-#endif // defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Discover Services, should notify of device changed.
@@ -770,6 +999,7 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
// Disconnect, should notify of device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
+ // - GetAdvertisingDataFlags: Should return same advertised flags.
// - GetUUIDs: Should return only Advertised UUIDs.
// - GetServiceData: Should still return same advertised Service Data.
// - GetInquiryTxPower: Should return the last packet's advertised Tx Power.
@@ -780,20 +1010,23 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
EXPECT_EQ(3, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert)}),
device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
-#endif // defined(OS_MACOSX)
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Receive Advertisement with new UUIDs, should notify of device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
+ // - GetAdvertisingDataFlags: Should return the new advertised flags.
// - GetUUIDs: Should return only new Advertised UUIDs.
// - GetServiceData: Should return only new advertised Service Data.
// - GetInquiryTxPower: Should return the last packet's advertised Tx Power.
@@ -801,18 +1034,22 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
EXPECT_EQ(4, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
+#if defined(OS_WIN)
+ EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
+ EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
+#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
-#endif // defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Stop discovery session, should notify of device changed.
// - GetInquiryRSSI: Should return nullopt because we are no longer
// discovering.
+ // - GetAdvertisingDataFlags: Should return no advertised flags since we are
+ // no longer discovering.
// - GetUUIDs: Should return no UUIDs.
// - GetServiceData: Should return no UUIDs since we are no longer
// discovering.
@@ -820,13 +1057,13 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
// discovering.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(5, observer.device_changed_count());
EXPECT_FALSE(device->GetInquiryRSSI());
+ EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device->GetUUIDs().empty());
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_TRUE(device->GetServiceData().empty());
-#endif // defined(OS_MACOSX) || defined(OS_ANDROID)
EXPECT_FALSE(device->GetInquiryTxPower());
}
@@ -922,7 +1159,7 @@ TEST_F(BluetoothTest, MAYBE_DisconnectionNotifiesDeviceChanged) {
EXPECT_TRUE(device->IsConnected());
EXPECT_TRUE(device->IsGattConnected());
- SimulateGattDisconnection(device);
+ SimulateDeviceBreaksConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_FALSE(device->IsConnected());
@@ -1622,7 +1859,7 @@ TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDisconnection) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
- SimulateGattDisconnection(device);
+ SimulateDeviceBreaksConnection(device);
base::RunLoop().RunUntilIdle();
SimulateGattServicesDiscovered(
@@ -1716,6 +1953,36 @@ TEST_F(BluetoothTest, MAYBE_GetGattServices_and_GetGattService) {
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
+#define MAYBE_GetGattServices_FindNone GetGattServices_FindNone
+#else
+#define MAYBE_GetGattServices_FindNone DISABLED_GetGattServices_FindNone
+#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GetGattServices_FindNone) {
+#else
+TEST_F(BluetoothTest, MAYBE_GetGattServices_FindNone) {
+#endif
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(3);
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ ResetEventCounts();
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, gatt_discovery_attempts_);
+
+ // Simulate an empty set of discovered services.
+ SimulateGattServicesDiscovered(device, {} /* uuids */);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0u, device->GetGattServices().size());
+}
+
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetGattServices_DiscoveryError GetGattServices_DiscoveryError
#else
#define MAYBE_GetGattServices_DiscoveryError \
diff --git a/chromium/device/bluetooth/bluetooth_device_winrt.cc b/chromium/device/bluetooth/bluetooth_device_winrt.cc
index bae8fb99a9a..0313a90e235 100644
--- a/chromium/device/bluetooth/bluetooth_device_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_device_winrt.cc
@@ -4,18 +4,21 @@
#include "device/bluetooth/bluetooth_device_winrt.h"
+#include <windows.devices.enumeration.h>
#include <windows.foundation.h>
#include <utility>
#include "base/bind_helpers.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_hstring.h"
#include "device/bluetooth/bluetooth_adapter_winrt.h"
#include "device/bluetooth/bluetooth_gatt_discoverer_winrt.h"
+#include "device/bluetooth/bluetooth_pairing_winrt.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h"
#include "device/bluetooth/event_utils_winrt.h"
@@ -34,13 +37,69 @@ using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceServicesResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceServicesResult;
-using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice;
+using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice2;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3;
+using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics;
+using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus;
+using ABI::Windows::Devices::Enumeration::IDeviceInformation2;
+using ABI::Windows::Devices::Enumeration::IDeviceInformation;
+using ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing;
+using ABI::Windows::Devices::Enumeration::IDeviceInformationPairing2;
+using ABI::Windows::Devices::Enumeration::IDeviceInformationPairing;
+using ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs;
using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Foundation::IClosable;
using Microsoft::WRL::ComPtr;
+void PostTask(BluetoothPairingWinrt::ErrorCallback error_callback,
+ BluetoothDevice::ConnectErrorCode error_code) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(error_callback), error_code));
+}
+
+ComPtr<IDeviceInformationPairing> GetDeviceInformationPairing(
+ ComPtr<IBluetoothLEDevice> ble_device) {
+ if (!ble_device) {
+ VLOG(2) << "No BLE device instance present.";
+ return nullptr;
+ }
+
+ ComPtr<IBluetoothLEDevice2> ble_device_2;
+ HRESULT hr = ble_device.As(&ble_device_2);
+ if (FAILED(hr)) {
+ VLOG(2) << "Obtaining IBluetoothLEDevice2 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ ComPtr<IDeviceInformation> device_information;
+ hr = ble_device_2->get_DeviceInformation(&device_information);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Device Information failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ ComPtr<IDeviceInformation2> device_information_2;
+ hr = device_information.As(&device_information_2);
+ if (FAILED(hr)) {
+ VLOG(2) << "Obtaining IDeviceInformation2 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ ComPtr<IDeviceInformationPairing> pairing;
+ hr = device_information_2->get_Pairing(&pairing);
+ if (FAILED(hr)) {
+ VLOG(2) << "DeviceInformation::get_Pairing() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ return pairing;
+}
+
void CloseDevice(ComPtr<IBluetoothLEDevice> ble_device) {
if (!ble_device)
return;
@@ -147,8 +206,24 @@ base::Optional<std::string> BluetoothDeviceWinrt::GetName() const {
}
bool BluetoothDeviceWinrt::IsPaired() const {
- NOTIMPLEMENTED();
- return false;
+ ComPtr<IDeviceInformationPairing> pairing =
+ GetDeviceInformationPairing(ble_device_);
+ if (!pairing) {
+ VLOG(2) << "Failed to get DeviceInformationPairing.";
+ return false;
+ }
+
+ boolean is_paired;
+ HRESULT hr = pairing->get_IsPaired(&is_paired);
+ if (FAILED(hr)) {
+ VLOG(2) << "DeviceInformationPairing::get_IsPaired() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ VLOG(2) << "BluetoothDeviceWinrt::IsPaired(): "
+ << (is_paired ? "True" : "False");
+ return is_paired;
}
bool BluetoothDeviceWinrt::IsConnected() const {
@@ -181,8 +256,7 @@ bool BluetoothDeviceWinrt::IsConnecting() const {
}
bool BluetoothDeviceWinrt::ExpectingPinCode() const {
- NOTIMPLEMENTED();
- return false;
+ return pairing_ && pairing_->ExpectingPinCode();
}
bool BluetoothDeviceWinrt::ExpectingPasskey() const {
@@ -213,8 +287,71 @@ void BluetoothDeviceWinrt::Connect(PairingDelegate* pairing_delegate,
NOTIMPLEMENTED();
}
+void BluetoothDeviceWinrt::Pair(PairingDelegate* pairing_delegate,
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) {
+ VLOG(2) << "BluetoothDeviceWinrt::Pair()";
+ if (pairing_) {
+ VLOG(2) << "Another Pair Operation is already in progress.";
+ PostTask(error_callback, ERROR_INPROGRESS);
+ return;
+ }
+
+ ComPtr<IDeviceInformationPairing> pairing =
+ GetDeviceInformationPairing(ble_device_);
+ if (!pairing) {
+ VLOG(2) << "Failed to get DeviceInformationPairing.";
+ PostTask(error_callback, ERROR_UNKNOWN);
+ return;
+ }
+
+ ComPtr<IDeviceInformationPairing2> pairing_2;
+ HRESULT hr = pairing.As(&pairing_2);
+ if (FAILED(hr)) {
+ VLOG(2) << "Obtaining IDeviceInformationPairing2 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ PostTask(error_callback, ERROR_UNKNOWN);
+ return;
+ }
+
+ ComPtr<IDeviceInformationCustomPairing> custom;
+ hr = pairing_2->get_Custom(&custom);
+ if (FAILED(hr)) {
+ VLOG(2) << "DeviceInformationPairing::get_Custom() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ PostTask(error_callback, ERROR_UNKNOWN);
+ return;
+ }
+
+ // Wrap success and error callback, so that they clean up the pairing object
+ // once they are run.
+ auto wrapped_callback = base::BindOnce(
+ [](base::WeakPtr<BluetoothDeviceWinrt> device,
+ base::OnceClosure callback) {
+ if (device)
+ device->pairing_.reset();
+ std::move(callback).Run();
+ },
+ weak_ptr_factory_.GetWeakPtr(), callback);
+
+ auto wrapped_error_callback = base::BindOnce(
+ [](base::WeakPtr<BluetoothDeviceWinrt> device,
+ ConnectErrorCallback error_callback, ConnectErrorCode error_code) {
+ if (device)
+ device->pairing_.reset();
+ std::move(error_callback).Run(error_code);
+ },
+ weak_ptr_factory_.GetWeakPtr(), error_callback);
+
+ pairing_ = std::make_unique<BluetoothPairingWinrt>(
+ this, pairing_delegate, std::move(custom), std::move(wrapped_callback),
+ std::move(wrapped_error_callback));
+ pairing_->StartPairing();
+}
+
void BluetoothDeviceWinrt::SetPinCode(const std::string& pincode) {
- NOTIMPLEMENTED();
+ if (pairing_)
+ pairing_->SetPinCode(pincode);
}
void BluetoothDeviceWinrt::SetPasskey(uint32_t passkey) {
@@ -226,11 +363,13 @@ void BluetoothDeviceWinrt::ConfirmPairing() {
}
void BluetoothDeviceWinrt::RejectPairing() {
- NOTIMPLEMENTED();
+ if (pairing_)
+ pairing_->RejectPairing();
}
void BluetoothDeviceWinrt::CancelPairing() {
- NOTIMPLEMENTED();
+ if (pairing_)
+ pairing_->CancelPairing();
}
void BluetoothDeviceWinrt::Disconnect(const base::Closure& callback,
@@ -311,7 +450,20 @@ void BluetoothDeviceWinrt::CreateGattConnectionImpl() {
}
void BluetoothDeviceWinrt::DisconnectGatt() {
+ // Closing the device and disposing of all references will trigger a Gatt
+ // Disconnection after a short timeout. Since the Gatt Services store a
+ // reference to |ble_device_| as well, we need to clear them to drop all
+ // remaining references, so that the OS disconnects.
+ // Reference:
+ // - https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client
CloseDevice(ble_device_);
+ ClearGattServices();
+
+ // Stop any pending Gatt Discovery sessions and report an error. This will
+ // destroy |gatt_discoverer_| and release remaining references the discoverer
+ // might have hold.
+ if (gatt_discoverer_)
+ OnGattDiscoveryComplete(false);
}
HRESULT BluetoothDeviceWinrt::GetBluetoothLEDeviceStaticsActivationFactory(
@@ -344,6 +496,13 @@ void BluetoothDeviceWinrt::OnFromBluetoothAddress(
base::BindRepeating(&BluetoothDeviceWinrt::OnConnectionStatusChanged,
weak_ptr_factory_.GetWeakPtr()));
+ // For paired devices the OS immediately establishes a GATT connection after
+ // the first advertisement. In this case our handler is registered too late to
+ // catch the initial connection changed event, and we need to perform an
+ // explicit check ourselves.
+ if (IsGattConnected())
+ DidConnectGatt();
+
if (gatt_services_changed_token_) {
RemoveGattServicesChangedHandler(ble_device_.Get(),
*gatt_services_changed_token_);
@@ -371,9 +530,7 @@ void BluetoothDeviceWinrt::OnConnectionStatusChanged(
DidConnectGatt();
} else {
gatt_discoverer_.reset();
- gatt_services_.clear();
- device_uuids_.ClearServiceUUIDs();
- SetGattServicesDiscoveryComplete(false);
+ ClearGattServices();
DidDisconnectGatt();
}
}
@@ -381,6 +538,8 @@ void BluetoothDeviceWinrt::OnConnectionStatusChanged(
void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device,
IInspectable* object) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // Note: We don't clear out |gatt_services_| here, as we don't want to break
+ // existing references to Gatt Services that did not change.
device_uuids_.ClearServiceUUIDs();
SetGattServicesDiscoveryComplete(false);
adapter_->NotifyDeviceChanged(this);
@@ -399,20 +558,35 @@ void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) {
if (!success) {
if (!IsGattConnected())
DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED);
+ gatt_discoverer_.reset();
return;
}
+ // Instead of clearing out |gatt_services_| and creating each service from
+ // scratch, we create a new map and move already existing services into it in
+ // order to preserve pointer stability.
+ GattServiceMap gatt_services;
for (const auto& gatt_service : gatt_discoverer_->GetGattServices()) {
auto gatt_service_winrt =
BluetoothRemoteGattServiceWinrt::Create(this, gatt_service);
if (!gatt_service_winrt)
continue;
- const auto& service = *gatt_service_winrt;
- gatt_services_.emplace(service.GetIdentifier(),
- std::move(gatt_service_winrt));
+ std::string identifier = gatt_service_winrt->GetIdentifier();
+ auto iter = gatt_services_.find(identifier);
+ if (iter != gatt_services_.end()) {
+ iter = gatt_services.emplace(std::move(*iter)).first;
+ } else {
+ iter = gatt_services
+ .emplace(std::move(identifier), std::move(gatt_service_winrt))
+ .first;
+ }
+
+ static_cast<BluetoothRemoteGattServiceWinrt*>(iter->second.get())
+ ->UpdateCharacteristics(gatt_discoverer_.get());
}
+ std::swap(gatt_services, gatt_services_);
device_uuids_.ReplaceServiceUUIDs(gatt_services_);
SetGattServicesDiscoveryComplete(true);
adapter_->NotifyGattServicesDiscovered(this);
@@ -420,4 +594,10 @@ void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) {
gatt_discoverer_.reset();
}
+void BluetoothDeviceWinrt::ClearGattServices() {
+ gatt_services_.clear();
+ device_uuids_.ClearServiceUUIDs();
+ SetGattServicesDiscoveryComplete(false);
+}
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_device_winrt.h b/chromium/device/bluetooth/bluetooth_device_winrt.h
index 2b7b4378909..93644ab66cd 100644
--- a/chromium/device/bluetooth/bluetooth_device_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_device_winrt.h
@@ -25,9 +25,18 @@ namespace device {
class BluetoothAdapterWinrt;
class BluetoothGattDiscovererWinrt;
+class BluetoothPairingWinrt;
class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
public:
+ // Constants required to extract the tx power level and service data from the
+ // raw advertisementment data. Reference:
+ // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+ static constexpr uint8_t kTxPowerLevelDataSection = 0x0A;
+ static constexpr uint8_t k16BitServiceDataSection = 0x16;
+ static constexpr uint8_t k32BitServiceDataSection = 0x20;
+ static constexpr uint8_t k128BitServiceDataSection = 0x21;
+
BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter,
uint64_t raw_address,
base::Optional<std::string> local_name);
@@ -57,6 +66,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
void Connect(PairingDelegate* pairing_delegate,
const base::Closure& callback,
const ConnectErrorCallback& error_callback) override;
+ void Pair(PairingDelegate* pairing_delegate,
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) override;
void SetPinCode(const std::string& pincode) override;
void SetPasskey(uint32_t passkey) override;
void ConfirmPairing() override;
@@ -107,10 +119,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
void OnGattDiscoveryComplete(bool success);
+ void ClearGattServices();
+
uint64_t raw_address_;
std::string address_;
base::Optional<std::string> local_name_;
+ std::unique_ptr<BluetoothPairingWinrt> pairing_;
+
std::unique_ptr<BluetoothGattDiscovererWinrt> gatt_discoverer_;
base::Optional<EventRegistrationToken> connection_changed_token_;
diff --git a/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
index aacaf680a62..d964e11ac43 100644
--- a/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
@@ -9,6 +9,7 @@
#include <utility>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h"
#include "device/bluetooth/event_utils_winrt.h"
@@ -17,23 +18,88 @@ namespace device {
namespace {
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCharacteristic;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCharacteristicsResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus_Success;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattDescriptorsResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceService;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceServicesResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
- IGattDeviceService;
+ IGattCharacteristic3;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattCharacteristicsResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDescriptorsResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDeviceService3;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceServicesResult;
-using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3;
-using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice;
using ABI::Windows::Foundation::Collections::IVectorView;
+using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Foundation::IReference;
using Microsoft::WRL::ComPtr;
+template <typename IGattResult>
+bool CheckCommunicationStatus(IGattResult* gatt_result) {
+ if (!gatt_result) {
+ VLOG(2) << "Getting GATT Results failed.";
+ return false;
+ }
+
+ GattCommunicationStatus status;
+ HRESULT hr = gatt_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting GATT Communication Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ if (status != GattCommunicationStatus_Success) {
+ VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
+ VLOG(2) << "GATT Error Code: "
+ << static_cast<int>(
+ BluetoothRemoteGattServiceWinrt::GetGattErrorCode(
+ gatt_result));
+ }
+
+ return status == GattCommunicationStatus_Success;
+}
+
+template <typename T, typename I>
+bool GetAsVector(IVectorView<T*>* view, std::vector<ComPtr<I>>* vector) {
+ unsigned size;
+ HRESULT hr = view->get_Size(&size);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Size failed: " << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ vector->reserve(size);
+ for (unsigned i = 0; i < size; ++i) {
+ ComPtr<I> entry;
+ hr = view->GetAt(i, &entry);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetAt(" << i
+ << ") failed: " << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ vector->push_back(std::move(entry));
+ }
+
+ return true;
+}
+
} // namespace
BluetoothGattDiscovererWinrt::BluetoothGattDiscovererWinrt(
@@ -80,64 +146,195 @@ BluetoothGattDiscovererWinrt::GetGattServices() const {
return gatt_services_;
}
+const BluetoothGattDiscovererWinrt::GattCharacteristicList*
+BluetoothGattDiscovererWinrt::GetCharacteristics(
+ uint16_t service_attribute_handle) const {
+ auto iter = service_to_characteristics_map_.find(service_attribute_handle);
+ return iter != service_to_characteristics_map_.end() ? &iter->second
+ : nullptr;
+}
+
+const BluetoothGattDiscovererWinrt::GattDescriptorList*
+BluetoothGattDiscovererWinrt::GetDescriptors(
+ uint16_t characteristic_attribute_handle) const {
+ auto iter =
+ characteristic_to_descriptors_map_.find(characteristic_attribute_handle);
+ return iter != characteristic_to_descriptors_map_.end() ? &iter->second
+ : nullptr;
+}
+
void BluetoothGattDiscovererWinrt::OnGetGattServices(
ComPtr<IGattDeviceServicesResult> services_result) {
- if (!services_result) {
- VLOG(2) << "Getting GATT Services failed.";
+ if (!CheckCommunicationStatus(services_result.Get())) {
std::move(callback_).Run(false);
return;
}
- GattCommunicationStatus status;
- HRESULT hr = services_result->get_Status(&status);
+ ComPtr<IVectorView<GattDeviceService*>> services;
+ HRESULT hr = services_result->get_Services(&services);
if (FAILED(hr)) {
- VLOG(2) << "Getting GATT Communication Status failed: "
+ VLOG(2) << "Getting GATT Services failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
- if (status != GattCommunicationStatus_Success) {
- VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
- // TODO(https://crbug.com/821766): Obtain and log the protocol error if
- // appropriate. Mention BT spec Version 5.0 Vol 3, Part F, 3.4.1.1 "Error
- // Response".
+ if (!GetAsVector(services.Get(), &gatt_services_)) {
std::move(callback_).Run(false);
return;
}
- ComPtr<IVectorView<GattDeviceService*>> services;
- hr = services_result->get_Services(&services);
- if (FAILED(hr)) {
- VLOG(2) << "Getting GATT Services failed: "
- << logging::SystemErrorCodeToString(hr);
+ num_services_ = gatt_services_.size();
+ for (const auto& gatt_service : gatt_services_) {
+ uint16_t service_attribute_handle;
+ hr = gatt_service->get_AttributeHandle(&service_attribute_handle);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting AttributeHandle failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ ComPtr<IGattDeviceService3> gatt_service_3;
+ hr = gatt_service.As(&gatt_service_3);
+ if (FAILED(hr)) {
+ VLOG(2) << "Obtaining IGattDeviceService3 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattCharacteristicsResult*>> get_characteristics_op;
+ hr = gatt_service_3->GetCharacteristicsAsync(&get_characteristics_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattDeviceService::GetCharacteristicsAsync() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(get_characteristics_op),
+ base::BindOnce(&BluetoothGattDiscovererWinrt::OnGetCharacteristics,
+ weak_ptr_factory_.GetWeakPtr(),
+ service_attribute_handle));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ }
+ }
+
+ RunCallbackIfDone();
+}
+
+void BluetoothGattDiscovererWinrt::OnGetCharacteristics(
+ uint16_t service_attribute_handle,
+ ComPtr<IGattCharacteristicsResult> characteristics_result) {
+ if (!CheckCommunicationStatus(characteristics_result.Get())) {
std::move(callback_).Run(false);
return;
}
- unsigned size;
- hr = services->get_Size(&size);
+ ComPtr<IVectorView<GattCharacteristic*>> characteristics;
+ HRESULT hr = characteristics_result->get_Characteristics(&characteristics);
if (FAILED(hr)) {
- VLOG(2) << "Getting Size of GATT Services failed: "
+ VLOG(2) << "Getting Characteristics failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
- for (unsigned i = 0; i < size; ++i) {
- ComPtr<IGattDeviceService> service;
- hr = services->GetAt(i, &service);
+ DCHECK(!base::ContainsKey(service_to_characteristics_map_,
+ service_attribute_handle));
+ auto& characteristics_list =
+ service_to_characteristics_map_[service_attribute_handle];
+ if (!GetAsVector(characteristics.Get(), &characteristics_list)) {
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ num_characteristics_ += characteristics_list.size();
+ for (const auto& gatt_characteristic : characteristics_list) {
+ uint16_t characteristic_attribute_handle;
+ hr = gatt_characteristic->get_AttributeHandle(
+ &characteristic_attribute_handle);
if (FAILED(hr)) {
- VLOG(2) << "GetAt(" << i
- << ") failed: " << logging::SystemErrorCodeToString(hr);
+ VLOG(2) << "Getting AttributeHandle failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ ComPtr<IGattCharacteristic3> gatt_characteristic_3;
+ hr = gatt_characteristic.As(&gatt_characteristic_3);
+ if (FAILED(hr)) {
+ VLOG(2) << "Obtaining IGattCharacteristic3 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattDescriptorsResult*>> get_descriptors_op;
+ hr = gatt_characteristic_3->GetDescriptorsAsync(&get_descriptors_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattCharacteristic::GetDescriptorsAsync() failed: "
+ << logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
- gatt_services_.push_back(std::move(service));
+ hr = PostAsyncResults(
+ std::move(get_descriptors_op),
+ base::BindOnce(&BluetoothGattDiscovererWinrt::OnGetDescriptors,
+ weak_ptr_factory_.GetWeakPtr(),
+ characteristic_attribute_handle));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ }
}
- std::move(callback_).Run(true);
+ RunCallbackIfDone();
+}
+
+void BluetoothGattDiscovererWinrt::OnGetDescriptors(
+ uint16_t characteristic_attribute_handle,
+ ComPtr<IGattDescriptorsResult> descriptors_result) {
+ if (!CheckCommunicationStatus(descriptors_result.Get())) {
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ ComPtr<IVectorView<GattDescriptor*>> descriptors;
+ HRESULT hr = descriptors_result->get_Descriptors(&descriptors);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Descriptors failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ DCHECK(!base::ContainsKey(characteristic_to_descriptors_map_,
+ characteristic_attribute_handle));
+ if (!GetAsVector(descriptors.Get(), &characteristic_to_descriptors_map_
+ [characteristic_attribute_handle])) {
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ RunCallbackIfDone();
+}
+
+void BluetoothGattDiscovererWinrt::RunCallbackIfDone() {
+ DCHECK(callback_);
+ if (service_to_characteristics_map_.size() == num_services_ &&
+ characteristic_to_descriptors_map_.size() == num_characteristics_) {
+ std::move(callback_).Run(true);
+ }
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
index 6f53c819a35..39392baaae7 100644
--- a/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
@@ -9,10 +9,13 @@
#include <windows.devices.bluetooth.h>
#include <wrl/client.h>
+#include <stdint.h>
+
#include <memory>
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
@@ -32,14 +35,27 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattDiscovererWinrt {
using GattServiceList = std::vector<
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
GenericAttributeProfile::IGattDeviceService>>;
+ using GattCharacteristicList = std::vector<
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattCharacteristic>>;
+ using GattDescriptorList = std::vector<
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDescriptor>>;
BluetoothGattDiscovererWinrt(
Microsoft::WRL::ComPtr<
ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> ble_device);
~BluetoothGattDiscovererWinrt();
+ // Note: In order to avoid running |callback| multiple times on errors,
+ // clients are expected to synchronously destroy the GattDiscoverer after
+ // |callback| has been invoked for the first time.
void StartGattDiscovery(GattDiscoveryCallback callback);
const GattServiceList& GetGattServices() const;
+ const GattCharacteristicList* GetCharacteristics(
+ uint16_t service_attribute_handle) const;
+ const GattDescriptorList* GetDescriptors(
+ uint16_t characteristic_attribute_handle) const;
private:
void OnGetGattServices(
@@ -47,10 +63,32 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattDiscovererWinrt {
ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceServicesResult> services_result);
+ void OnGetCharacteristics(
+ uint16_t service_attribute_handle,
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattCharacteristicsResult> characteristics_result);
+
+ void OnGetDescriptors(
+ uint16_t characteristic_attribute_handle,
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDescriptorsResult> descriptors_result);
+
+ void RunCallbackIfDone();
+
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice>
ble_device_;
+
GattDiscoveryCallback callback_;
GattServiceList gatt_services_;
+ base::flat_map<uint16_t, GattCharacteristicList>
+ service_to_characteristics_map_;
+ base::flat_map<uint16_t, GattDescriptorList>
+ characteristic_to_descriptors_map_;
+ size_t num_services_ = 0;
+ size_t num_characteristics_ = 0;
+
THREAD_CHECKER(thread_checker_);
// Note: This should remain the last member so it'll be destroyed and
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
index c8fe0db8e7b..a7b03c84975 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
@@ -32,8 +32,10 @@ void BluetoothLowEnergyAdvertisementManagerMac::
// powered on.
// 2. Start advertising a registered advertisement if the adapter is powered
// on.
- // Note that if the adapter is powered off while advertising, macOS will
- // automatically restart advertising when the adapter is powered back on.
+ // 3. Stop the advertisement when the adapter is powered off.
+ // Note that if the adapter is powered off while advertising, macOS will
+ // automatically restart advertising when the adapter is powered back on,
+ // so we need to explicitly stop advertising in this case.
if (!active_advertisement_) {
return;
@@ -61,12 +63,22 @@ void BluetoothLowEnergyAdvertisementManagerMac::
if (active_advertisement_->is_advertising() &&
adapter_state == CBPeripheralManagerStateResetting) {
- DVLOG(1) << "Adapter resetting. Invaldating advertisement.";
+ DVLOG(1) << "Adapter resetting. Invalidating advertisement.";
active_advertisement_->OnAdapterReset();
active_advertisement_ = nullptr;
return;
}
+ if (active_advertisement_->is_advertising() &&
+ adapter_state == CBPeripheralManagerStatePoweredOff) {
+ DVLOG(1) << "Adapter powered off. Stopping advertisement.";
+ // Note: we purposefully don't unregister the active advertisement for
+ // consistency with ChromeOS. The caller must manually unregister
+ // the advertisement themselves.
+ [peripheral_manager_ stopAdvertising];
+ return;
+ }
+
if (active_advertisement_->is_waiting_for_adapter()) {
StartAdvertising();
}
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
index 68ac8dc27de..0a85aad293f 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
+++ b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
@@ -159,7 +159,7 @@ TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
EXPECT_FALSE(advertisement_);
EXPECT_FALSE(registration_error_);
- // Change the adapter state to CBPeripheralManagerStateUnsupported, which
+ // Change the adapter state to CBPeripheralManagerStatePoweredOff, which
// causes the registration to fail.
peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff;
advertisement_manager_.OnPeripheralManagerStateChanged();
@@ -272,23 +272,6 @@ TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_Twice) {
}
TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
- AdapterPoweredOff_WhileAdvertising) {
- // Register advertisement.
- RegisterAdvertisement(CreateAdvertisementData());
- advertisement_manager_.DidStartAdvertising(nil);
- ui_task_runner_->RunPendingTasks();
- EXPECT_TRUE(advertisement_);
-
- // Power off the adapter. Advertisement should not be stopped.
- BluetoothAdvertisementMac* advertisement_mac =
- static_cast<BluetoothAdvertisementMac*>(advertisement_.get());
- EXPECT_TRUE(advertisement_mac->is_advertising());
- peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff;
- advertisement_manager_.OnPeripheralManagerStateChanged();
- EXPECT_TRUE(advertisement_mac->is_advertising());
-}
-
-TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
AdapterReset_RestartAdvertising) {
// Register advertisement.
RegisterAdvertisement(CreateAdvertisementData());
@@ -311,4 +294,31 @@ TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
[peripheral_manager_mock_ verifyAtLocation:nil];
}
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
+ Advertising_ThenAdapterPoweredOff_ThenReregister) {
+ // Register advertisement.
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_TRUE(advertisement_);
+
+ // Power off the adapter. Advertisement should be stopped.
+ OCMExpect([peripheral_manager_ stopAdvertising]);
+ peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff;
+ advertisement_manager_.OnPeripheralManagerStateChanged();
+ [peripheral_manager_mock_ verifyAtLocation:nil];
+
+ // Register a new advertisement after powering back on the adapter.
+ // This should fail as the caller needs to manually unregister the
+ // advertisement.
+ peripheral_manager_state_ = CBPeripheralManagerStatePoweredOn;
+ advertisement_ = nullptr;
+ RegisterAdvertisement(CreateAdvertisementData());
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_FALSE(advertisement_);
+ ASSERT_TRUE(registration_error_);
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS,
+ *registration_error_);
+}
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm b/chromium/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm
index c1306a96fbb..642767d381f 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm
+++ b/chromium/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm
@@ -68,11 +68,6 @@ class BluetoothLowEnergyCentralManagerBridge {
return self;
}
-- (void)dealloc {
- [bridge_->GetCentralManager() setDelegate:nil];
- [super dealloc];
-}
-
- (void)centralManager:(CBCentralManager*)central
didDiscoverPeripheral:(CBPeripheral*)peripheral
advertisementData:(NSDictionary*)advertisementData
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm b/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm
index 187f3c91c07..a65d9fe3932 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -56,6 +56,8 @@ BluetoothLowEnergyDeviceMac::~BluetoothLowEnergyDeviceMac() {
if (IsGattConnected()) {
GetMacAdapter()->DisconnectGatt(this);
}
+
+ [peripheral_ setDelegate:nil];
}
std::string BluetoothLowEnergyDeviceMac::GetIdentifier() const {
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm
index e0287fff8a6..f838aabe41e 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm
+++ b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_delegate.mm
@@ -75,11 +75,6 @@ class BluetoothLowEnergyPeripheralBridge {
return self;
}
-- (void)dealloc {
- [bridge_->GetPeripheral() setDelegate:nil];
- [super dealloc];
-}
-
- (void)peripheral:(CBPeripheral*)peripheral
didModifyServices:(NSArray*)invalidatedServices {
bridge_->DidModifyServices(invalidatedServices);
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm
index e1fe94fcc44..a951dfbf3b6 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm
+++ b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm
@@ -55,11 +55,6 @@ class BluetoothLowEnergyPeripheralManagerBridge {
return self;
}
-- (void)dealloc {
- [bridge_->GetPeripheralManager() setDelegate:nil];
- [super dealloc];
-}
-
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager*)peripheral {
bridge_->UpdatedState();
}
diff --git a/chromium/device/bluetooth/bluetooth_pairing_winrt.cc b/chromium/device/bluetooth/bluetooth_pairing_winrt.cc
new file mode 100644
index 00000000000..9f1ed08732d
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_pairing_winrt.cc
@@ -0,0 +1,267 @@
+// Copyright 2018 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/bluetooth/bluetooth_pairing_winrt.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/win/scoped_hstring.h"
+#include "device/bluetooth/bluetooth_device_winrt.h"
+#include "device/bluetooth/event_utils_winrt.h"
+
+namespace device {
+
+namespace {
+
+using ABI::Windows::Devices::Enumeration::DevicePairingKinds;
+using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ProvidePin;
+using ABI::Windows::Devices::Enumeration::DevicePairingResult;
+using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_AuthenticationFailure;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_AuthenticationTimeout;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_ConnectionRejected;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_AlreadyPaired;
+using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus_Failed;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_OperationAlreadyInProgress;
+using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus_Paired;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_PairingCanceled;
+using ABI::Windows::Devices::Enumeration::
+ DevicePairingResultStatus_RejectedByHandler;
+using ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing;
+using ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs;
+using ABI::Windows::Devices::Enumeration::IDevicePairingResult;
+using ABI::Windows::Foundation::IAsyncOperation;
+using Microsoft::WRL::ComPtr;
+
+void PostTask(BluetoothPairingWinrt::ErrorCallback error_callback,
+ BluetoothDevice::ConnectErrorCode error_code) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(error_callback), error_code));
+}
+
+} // namespace
+
+BluetoothPairingWinrt::BluetoothPairingWinrt(
+ BluetoothDeviceWinrt* device,
+ BluetoothDevice::PairingDelegate* pairing_delegate,
+ ComPtr<IDeviceInformationCustomPairing> custom_pairing,
+ Callback callback,
+ ErrorCallback error_callback)
+ : device_(device),
+ pairing_delegate_(pairing_delegate),
+ custom_pairing_(std::move(custom_pairing)),
+ callback_(std::move(callback)),
+ error_callback_(std::move(error_callback)),
+ weak_ptr_factory_(this) {
+ DCHECK(device_);
+ DCHECK(pairing_delegate_);
+ DCHECK(custom_pairing_);
+}
+
+BluetoothPairingWinrt::~BluetoothPairingWinrt() {
+ if (!pairing_requested_token_)
+ return;
+
+ HRESULT hr =
+ custom_pairing_->remove_PairingRequested(*pairing_requested_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing PairingRequested Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+void BluetoothPairingWinrt::StartPairing() {
+ pairing_requested_token_ = AddTypedEventHandler(
+ custom_pairing_.Get(),
+ &IDeviceInformationCustomPairing::add_PairingRequested,
+ base::BindRepeating(&BluetoothPairingWinrt::OnPairingRequested,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (!pairing_requested_token_) {
+ PostTask(std::move(error_callback_),
+ BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ ComPtr<IAsyncOperation<DevicePairingResult*>> pair_op;
+ HRESULT hr =
+ custom_pairing_->PairAsync(DevicePairingKinds_ProvidePin, &pair_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "DeviceInformationCustomPairing::PairAsync() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ PostTask(std::move(error_callback_),
+ BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ hr = PostAsyncResults(std::move(pair_op),
+ base::BindOnce(&BluetoothPairingWinrt::OnPair,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ PostTask(std::move(error_callback_),
+ BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+}
+
+bool BluetoothPairingWinrt::ExpectingPinCode() const {
+ return expecting_pin_code_;
+}
+
+void BluetoothPairingWinrt::SetPinCode(base::StringPiece pin_code) {
+ VLOG(2) << "BluetoothPairingWinrt::SetPinCode(" << pin_code << ")";
+ auto pin_hstring = base::win::ScopedHString::Create(pin_code);
+ DCHECK(expecting_pin_code_);
+ expecting_pin_code_ = false;
+ DCHECK(pairing_requested_);
+ HRESULT hr = pairing_requested_->AcceptWithPin(pin_hstring.get());
+ if (FAILED(hr)) {
+ VLOG(2) << "Accepting Pairing Request With Pin failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ DCHECK(pairing_deferral_);
+ hr = pairing_deferral_->Complete();
+ if (FAILED(hr)) {
+ VLOG(2) << "Completing Deferred Pairing Request failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ }
+}
+
+void BluetoothPairingWinrt::RejectPairing() {
+ VLOG(2) << "BluetoothPairingWinrt::RejectPairing()";
+ DCHECK(pairing_deferral_);
+ HRESULT hr = pairing_deferral_->Complete();
+ if (FAILED(hr)) {
+ VLOG(2) << "Completing Deferred Pairing Request failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED);
+}
+
+void BluetoothPairingWinrt::CancelPairing() {
+ VLOG(2) << "BluetoothPairingWinrt::CancelPairing()";
+ DCHECK(pairing_deferral_);
+ HRESULT hr = pairing_deferral_->Complete();
+ if (FAILED(hr)) {
+ VLOG(2) << "Completing Deferred Pairing Request failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_AUTH_CANCELED);
+}
+
+void BluetoothPairingWinrt::OnPairingRequested(
+ IDeviceInformationCustomPairing* custom_pairing,
+ IDevicePairingRequestedEventArgs* pairing_requested) {
+ VLOG(2) << "BluetoothPairingWinrt::OnPairingRequested()";
+
+ DevicePairingKinds pairing_kind;
+ HRESULT hr = pairing_requested->get_PairingKind(&pairing_kind);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Pairing Kind failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ VLOG(2) << "DevicePairingKind: " << static_cast<int>(pairing_kind);
+ if (pairing_kind != DevicePairingKinds_ProvidePin) {
+ VLOG(2) << "Unexpected DevicePairingKind.";
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ hr = pairing_requested->GetDeferral(&pairing_deferral_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Pairing Deferral failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ pairing_requested_ = pairing_requested;
+ expecting_pin_code_ = true;
+ pairing_delegate_->RequestPinCode(device_);
+}
+
+void BluetoothPairingWinrt::OnPair(
+ ComPtr<IDevicePairingResult> pairing_result) {
+ DevicePairingResultStatus status;
+ HRESULT hr = pairing_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Pairing Result Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ VLOG(2) << "Pairing Result Status: " << static_cast<int>(status);
+ switch (status) {
+ case DevicePairingResultStatus_AlreadyPaired:
+ case DevicePairingResultStatus_Paired:
+ std::move(callback_).Run();
+ return;
+ case DevicePairingResultStatus_PairingCanceled:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_AUTH_CANCELED);
+ return;
+ case DevicePairingResultStatus_AuthenticationFailure:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED);
+ return;
+ case DevicePairingResultStatus_ConnectionRejected:
+ case DevicePairingResultStatus_RejectedByHandler:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED);
+ return;
+ case DevicePairingResultStatus_AuthenticationTimeout:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_AUTH_TIMEOUT);
+ return;
+ case DevicePairingResultStatus_Failed:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ case DevicePairingResultStatus_OperationAlreadyInProgress:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS);
+ return;
+ default:
+ std::move(error_callback_)
+ .Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_pairing_winrt.h b/chromium/device/bluetooth/bluetooth_pairing_winrt.h
new file mode 100644
index 00000000000..88f57b6045f
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_pairing_winrt.h
@@ -0,0 +1,99 @@
+// Copyright 2018 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_BLUETOOTH_BLUETOOTH_PAIRING_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_WINRT_H_
+
+#include <windows.devices.enumeration.h>
+#include <windows.foundation.h>
+#include <wrl/client.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string_piece_forward.h"
+#include "device/bluetooth/bluetooth_device.h"
+
+namespace device {
+
+class BluetoothDeviceWinrt;
+
+// This class encapsulates logic required to perform a custom pairing on WinRT.
+// Currently only pairing with a pin code is supported.
+class BluetoothPairingWinrt {
+ public:
+ using Callback = base::OnceClosure;
+ using ErrorCallback =
+ base::OnceCallback<void(BluetoothDevice::ConnectErrorCode)>;
+
+ BluetoothPairingWinrt(
+ BluetoothDeviceWinrt* device,
+ BluetoothDevice::PairingDelegate* pairing_delegate,
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing>
+ custom_pairing,
+ Callback callback,
+ ErrorCallback error_callback);
+
+ ~BluetoothPairingWinrt();
+
+ // Initiates the pairing procedure.
+ void StartPairing();
+
+ // Indicates whether the device is currently pairing and expecting a
+ // PIN Code to be returned.
+ bool ExpectingPinCode() const;
+
+ // Sends the PIN code |pin_code| to the remote device during pairing.
+ void SetPinCode(base::StringPiece pin_code);
+
+ // Rejects a pairing or connection request from a remote device.
+ void RejectPairing();
+
+ // Cancels a pairing or connection attempt to a remote device.
+ void CancelPairing();
+
+ private:
+ void OnPairingRequested(
+ ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing*
+ custom_pairing,
+ ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs*
+ pairing_requested);
+
+ void OnPair(Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Enumeration::IDevicePairingResult>
+ pairing_result);
+
+ // Weak. This is the device object that owns this pairing instance.
+ BluetoothDeviceWinrt* device_;
+
+ // Weak. This is the pairing delegate provided to BluetoothDevice::Pair.
+ // Clients need to ensure the delegate stays alive during the pairing
+ // procedure.
+ BluetoothDevice::PairingDelegate* pairing_delegate_;
+
+ // Boolean indicating whether the device is currently pairing and expecting a
+ // PIN Code to be returned.
+ bool expecting_pin_code_ = false;
+
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing>
+ custom_pairing_;
+ Callback callback_;
+ ErrorCallback error_callback_;
+
+ base::Optional<EventRegistrationToken> pairing_requested_token_;
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IDeferral> pairing_deferral_;
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs>
+ pairing_requested_;
+
+ base::WeakPtrFactory<BluetoothPairingWinrt> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothPairingWinrt);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_WINRT_H_
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h
index c475063f5eb..c3c8df5d4f9 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h
@@ -169,6 +169,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
virtual bool WriteWithoutResponse(base::span<const uint8_t> value);
protected:
+ using DescriptorMap =
+ base::flat_map<std::string,
+ std::unique_ptr<BluetoothRemoteGattDescriptor>>;
+
BluetoothRemoteGattCharacteristic();
// Writes to the Client Characteristic Configuration descriptor to enable
@@ -206,8 +210,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
// Descriptors owned by the chracteristic. The descriptors' identifiers serve
// as keys.
- base::flat_map<std::string, std::unique_ptr<BluetoothRemoteGattDescriptor>>
- descriptors_;
+ DescriptorMap descriptors_;
private:
friend class BluetoothGattNotifySession;
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
index 2e91b58f8c6..086b01e6dba 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
@@ -84,14 +84,12 @@ BluetoothRemoteGattCharacteristicMac::BluetoothRemoteGattCharacteristicMac(
BluetoothRemoteGattCharacteristicMac::~BluetoothRemoteGattCharacteristicMac() {
if (HasPendingRead()) {
- std::pair<ValueCallback, ErrorCallback> callbacks;
- callbacks.swap(read_characteristic_value_callbacks_);
- callbacks.second.Run(BluetoothGattService::GATT_ERROR_FAILED);
+ read_characteristic_value_callbacks_.second.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
}
if (HasPendingWrite()) {
- std::pair<base::Closure, ErrorCallback> callbacks;
- callbacks.swap(write_characteristic_value_callbacks_);
- callbacks.second.Run(BluetoothGattService::GATT_ERROR_FAILED);
+ write_characteristic_value_callbacks_.second.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
}
}
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index c642513aa07..e6c286b2d5e 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -29,7 +29,12 @@
namespace device {
-class BluetoothRemoteGattCharacteristicTest : public BluetoothTest {
+class BluetoothRemoteGattCharacteristicTest :
+#if defined(OS_WIN)
+ public BluetoothTestWinrt {
+#else
+ public BluetoothTest {
+#endif
public:
// Creates adapter_, device_, service_, characteristic1_, & characteristic2_.
// |properties| will be used for each characteristic.
@@ -49,6 +54,7 @@ class BluetoothRemoteGattCharacteristicTest : public BluetoothTest {
service_ = device_->GetGattServices()[0];
SimulateGattCharacteristic(service_, kTestUUIDDeviceName, properties);
SimulateGattCharacteristic(service_, kTestUUIDDeviceName, properties);
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, service_->GetCharacteristics().size());
characteristic1_ = service_->GetCharacteristics()[0];
characteristic2_ = service_->GetCharacteristics()[1];
@@ -91,6 +97,7 @@ class BluetoothRemoteGattCharacteristicTest : public BluetoothTest {
.canonical_value());
expected_descriptors_count++;
}
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(expected_descriptors_count,
characteristic1_->GetDescriptors().size());
@@ -131,18 +138,42 @@ class BluetoothRemoteGattCharacteristicTest : public BluetoothTest {
ExpectedNotifyValue(notify_value_state);
}
+ // A few tests below don't behave correctly on Classic Windows, but do for
+ // WinRT. Since a #if defined(OS_WIN) guard is not sufficient to distinguish
+ // these two cases, this small utility function is added.
+ bool IsClassicWin() {
+#if defined(OS_WIN)
+ return !GetParam();
+#else
+ return false;
+#endif
+ }
+
BluetoothDevice* device_ = nullptr;
BluetoothRemoteGattService* service_ = nullptr;
BluetoothRemoteGattCharacteristic* characteristic1_ = nullptr;
BluetoothRemoteGattCharacteristic* characteristic2_ = nullptr;
};
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_WIN)
+using BluetoothRemoteGattCharacteristicTestWinrt =
+ BluetoothRemoteGattCharacteristicTest;
+using BluetoothRemoteGattCharacteristicTestWin32Only =
+ BluetoothRemoteGattCharacteristicTest;
+using BluetoothRemoteGattCharacteristicTestWinrtOnly =
+ BluetoothRemoteGattCharacteristicTest;
+#endif
+
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetIdentifier GetIdentifier
#else
#define MAYBE_GetIdentifier DISABLED_GetIdentifier
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GetIdentifier) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetIdentifier) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -181,6 +212,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetIdentifier) {
SimulateGattCharacteristic(service2, kTestUUIDDeviceName, /* properties */ 0);
SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
+ base::RunLoop().RunUntilIdle();
BluetoothRemoteGattCharacteristic* char1 = service1->GetCharacteristics()[0];
BluetoothRemoteGattCharacteristic* char2 = service1->GetCharacteristics()[1];
BluetoothRemoteGattCharacteristic* char3 = service2->GetCharacteristics()[0];
@@ -210,12 +242,16 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetIdentifier) {
EXPECT_NE(char5->GetIdentifier(), char6->GetIdentifier());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetUUID GetUUID
#else
#define MAYBE_GetUUID DISABLED_GetUUID
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GetUUID) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetUUID) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -238,6 +274,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetUUID) {
SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 0);
SimulateGattCharacteristic(service, kTestUUIDAppearance, /* properties */ 0);
SimulateGattCharacteristic(service, kTestUUIDAppearance, /* properties */ 0);
+ base::RunLoop().RunUntilIdle();
BluetoothRemoteGattCharacteristic* char1 = service->GetCharacteristics()[0];
BluetoothRemoteGattCharacteristic* char2 = service->GetCharacteristics()[1];
BluetoothRemoteGattCharacteristic* char3 = service->GetCharacteristics()[2];
@@ -254,12 +291,16 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetUUID) {
EXPECT_EQ(uuid2, char3->GetUUID());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetProperties GetProperties
#else
#define MAYBE_GetProperties DISABLED_GetProperties
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GetProperties) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetProperties) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -279,6 +320,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetProperties) {
// Create two characteristics with different properties:
SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 0);
SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 7);
+ base::RunLoop().RunUntilIdle();
// Read the properties. Because ordering is unknown swap as necessary.
int properties1 = service->GetCharacteristics()[0]->GetProperties();
@@ -289,13 +331,17 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetProperties) {
EXPECT_EQ(7, properties2);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetService GetService
#else
#define MAYBE_GetService DISABLED_GetService
#endif
// Tests GetService.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GetService) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetService) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -306,15 +352,20 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetService) {
EXPECT_EQ(service_, characteristic2_->GetService());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic_Empty ReadRemoteCharacteristic_Empty
#else
#define MAYBE_ReadRemoteCharacteristic_Empty \
DISABLED_ReadRemoteCharacteristic_Empty
#endif
// Tests ReadRemoteCharacteristic and GetValue with empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ ReadRemoteCharacteristic_Empty) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_Empty) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -338,15 +389,20 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(empty_vector, characteristic1_->GetValue());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteRemoteCharacteristic_Empty WriteRemoteCharacteristic_Empty
#else
#define MAYBE_WriteRemoteCharacteristic_Empty \
DISABLED_WriteRemoteCharacteristic_Empty
#endif
// Tests WriteRemoteCharacteristic with empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ WriteRemoteCharacteristic_Empty) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_Empty) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -369,7 +425,91 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(empty_vector, last_write_value_);
}
-#if defined(OS_ANDROID) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
+#define MAYBE_Retry_ReadRemoteCharacteristic_DuringDestruction_Fails \
+ Retry_ReadRemoteCharacteristic_DuringDestruction_Fails
+#else
+#define MAYBE_Retry_ReadRemoteCharacteristic_DuringDestruction_Fails \
+ DISABLED_Retry_ReadRemoteCharacteristic_DuringDestruction_Fails
+#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ Retry_ReadRemoteCharacteristic_DuringDestruction_Fails) {
+#else
+TEST_F(BluetoothRemoteGattCharacteristicTest,
+ MAYBE_Retry_ReadRemoteCharacteristic_DuringDestruction_Fails) {
+#endif
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+ BluetoothRemoteGattCharacteristic::PROPERTY_READ));
+
+ bool read_error_callback_called = false;
+ characteristic1_->ReadRemoteCharacteristic(
+ GetReadValueCallback(Call::NOT_EXPECTED),
+ base::BindLambdaForTesting(
+ [&](BluetoothRemoteGattService::GattErrorCode error_code) {
+ EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+ error_code);
+ read_error_callback_called = true;
+ // Retrying Read should fail:
+ characteristic1_->ReadRemoteCharacteristic(
+ GetReadValueCallback(Call::NOT_EXPECTED),
+ GetGattErrorCallback(Call::EXPECTED));
+ }));
+
+ DeleteDevice(device_); // TODO(576906) delete only the characteristic.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(read_error_callback_called);
+ EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+ last_gatt_error_code_);
+}
+
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
+#define MAYBE_Retry_WriteRemoteCharacteristic_DuringDestruction_Fails \
+ Retry_WriteRemoteCharacteristic_DuringDestruction_Fails
+#else
+#define MAYBE_Retry_WriteRemoteCharacteristic_DuringDestruction_Fails \
+ DISABLED_Retry_WriteRemoteCharacteristic_DuringDestruction_Fails
+#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ Retry_WriteRemoteCharacteristic_DuringDestruction_Fails) {
+#else
+TEST_F(BluetoothRemoteGattCharacteristicTest,
+ MAYBE_Retry_WriteRemoteCharacteristic_DuringDestruction_Fails) {
+#endif
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+ BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
+
+ bool write_error_callback_called = false;
+ characteristic1_->WriteRemoteCharacteristic(
+ {} /* value */, GetCallback(Call::NOT_EXPECTED),
+ base::BindLambdaForTesting(
+ [&](BluetoothRemoteGattService::GattErrorCode error_code) {
+ EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+ error_code);
+ write_error_callback_called = true;
+ // Retrying Write should fail:
+ characteristic1_->WriteRemoteCharacteristic(
+ {} /* value */, GetCallback(Call::NOT_EXPECTED),
+ GetGattErrorCallback(Call::EXPECTED));
+ }));
+
+ DeleteDevice(device_); // TODO(576906) delete only the characteristic.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(write_error_callback_called);
+ EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+ last_gatt_error_code_);
+}
+
+#if defined(OS_ANDROID)
#define MAYBE_ReadRemoteCharacteristic_AfterDeleted \
ReadRemoteCharacteristic_AfterDeleted
#else
@@ -379,8 +519,15 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// Tests ReadRemoteCharacteristic completing after Chrome objects are deleted.
// macOS: Not applicable: This can never happen if CBPeripheral delegate is set
// to nil.
+// WinRT: Not applicable: Pending callbacks won't fire once the underlying
+// object is destroyed.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWin32Only,
+ ReadRemoteCharacteristic_AfterDeleted) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_AfterDeleted) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -411,8 +558,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_ReadRemoteCharacteristic_Disconnected \
DISABLED_ReadRemoteCharacteristic_Disconnected
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ ReadRemoteCharacteristic_Disconnected) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_Disconnected) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -425,23 +577,23 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
GetGattErrorCallback(Call::EXPECTED));
// Set up for receiving a read response after disconnection.
-// On macOS no events arrive after disconnection so there is no point
+// On macOS or WinRT no events arrive after disconnection so there is no point
// in building the infrastructure to test this behavior. FYI
// the code CHECKs that responses arrive only when the device is connected.
-#if !defined(OS_MACOSX)
+#if defined(OS_ANDROID)
RememberCharacteristicForSubsequentAction(characteristic1_);
#endif
ASSERT_EQ(1u, adapter_->GetDevices().size());
- SimulateGattDisconnection(adapter_->GetDevices()[0]);
+ SimulateDeviceBreaksConnection(adapter_->GetDevices()[0]);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
last_gatt_error_code_);
// Dispatch read response after disconnection. See above explanation for why
-// we don't do this in macOS.
-#if !defined(OS_MACOSX)
+// we don't do this in macOS on WinRT.
+#if defined(OS_ANDROID)
std::vector<uint8_t> empty_vector;
SimulateGattCharacteristicRead(nullptr /* use remembered characteristic */,
empty_vector);
@@ -449,7 +601,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
}
-#if defined(OS_ANDROID) || defined(OS_WIN)
+#if defined(OS_ANDROID)
#define MAYBE_WriteRemoteCharacteristic_AfterDeleted \
WriteRemoteCharacteristic_AfterDeleted
#else
@@ -459,8 +611,15 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// Tests WriteRemoteCharacteristic completing after Chrome objects are deleted.
// macOS: Not applicable: This can never happen if CBPeripheral delegate is set
// to nil.
+// WinRT: Not applicable: Pending callbacks won't fire once the underlying
+// object is destroyed.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWin32Only,
+ WriteRemoteCharacteristic_AfterDeleted) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_AfterDeleted) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -490,8 +649,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_WriteRemoteCharacteristic_Disconnected \
DISABLED_WriteRemoteCharacteristic_Disconnected
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ WriteRemoteCharacteristic_Disconnected) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_Disconnected) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -505,35 +669,39 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
GetGattErrorCallback(Call::EXPECTED));
// Set up for receiving a write response after disconnection.
-// On macOS no events arrive after disconnection so there is no point
+// On macOS and WinRT no events arrive after disconnection so there is no point
// in building the infrastructure to test this behavior. FYI
// the code CHECKs that responses arrive only when the device is connected.
-#if !defined(OS_MACOSX)
+#if defined(OS_ANDROID)
RememberCharacteristicForSubsequentAction(characteristic1_);
-#endif // !defined(OS_MACOSX)
+#endif // defined(OS_ANDROID)
ASSERT_EQ(1u, adapter_->GetDevices().size());
- SimulateGattDisconnection(adapter_->GetDevices()[0]);
+ SimulateDeviceBreaksConnection(adapter_->GetDevices()[0]);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
last_gatt_error_code_);
// Dispatch write response after disconnection. See above explanation for why
-// we don't do this in macOS.
-#if !defined(OS_MACOSX)
+// we don't do this in macOS and WinRT.
+#if defined(OS_ANDROID)
SimulateGattCharacteristicWrite(/* use remembered characteristic */ nullptr);
base::RunLoop().RunUntilIdle();
-#endif // !defined(OS_MACOSX)
+#endif // defined(OS_ANDROID)
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic ReadRemoteCharacteristic
#else
#define MAYBE_ReadRemoteCharacteristic DISABLED_ReadRemoteCharacteristic
#endif
// Tests ReadRemoteCharacteristic and GetValue with non-empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, ReadRemoteCharacteristic) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_ReadRemoteCharacteristic) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -569,7 +737,7 @@ static void test_callback(
callback.Run(value);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic_GattCharacteristicValueChangedNotCalled \
ReadRemoteCharacteristic_GattCharacteristicValueChangedNotCalled
#else
@@ -578,8 +746,13 @@ static void test_callback(
#endif
// Tests that ReadRemoteCharacteristic doesn't result in a
// GattCharacteristicValueChanged call.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ ReadRemoteCharacteristic_GattCharacteristicValueChangedNotCalled) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_GattCharacteristicValueChangedNotCalled) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -601,23 +774,27 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// TODO(https://crbug.com/699694): Remove this #if once the bug on Windows is
// fixed.
-#if defined(OS_WIN)
- EXPECT_FALSE(observer.last_gatt_characteristic_id().empty());
- EXPECT_TRUE(observer.last_gatt_characteristic_uuid().IsValid());
-#else
- EXPECT_TRUE(observer.last_gatt_characteristic_id().empty());
- EXPECT_FALSE(observer.last_gatt_characteristic_uuid().IsValid());
-#endif // defined(OS_WIN)
+ if (IsClassicWin()) {
+ EXPECT_FALSE(observer.last_gatt_characteristic_id().empty());
+ EXPECT_TRUE(observer.last_gatt_characteristic_uuid().IsValid());
+ } else {
+ EXPECT_TRUE(observer.last_gatt_characteristic_id().empty());
+ EXPECT_FALSE(observer.last_gatt_characteristic_uuid().IsValid());
+ }
EXPECT_TRUE(observer.last_changed_characteristic_value().empty());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteRemoteCharacteristic WriteRemoteCharacteristic
#else
#define MAYBE_WriteRemoteCharacteristic DISABLED_WriteRemoteCharacteristic
#endif
// Tests WriteRemoteCharacteristic with non-empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, WriteRemoteCharacteristic) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteRemoteCharacteristic) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -637,22 +814,26 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteRemoteCharacteristic) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_write_characteristic_attempts_);
-#if !defined(OS_WIN)
- // TODO(crbug.com/653291): remove this #if once the bug on windows is fixed.
- EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
-#endif
+ // TODO(crbug.com/653291): remove this if once the bug on windows is fixed.
+ if (!IsClassicWin())
+ EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_EQ(test_vector, last_write_value_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic_Twice ReadRemoteCharacteristic_Twice
#else
#define MAYBE_ReadRemoteCharacteristic_Twice \
DISABLED_ReadRemoteCharacteristic_Twice
#endif
// Tests ReadRemoteCharacteristic and GetValue multiple times.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ ReadRemoteCharacteristic_Twice) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_Twice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -689,15 +870,20 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(empty_vector, characteristic1_->GetValue());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteRemoteCharacteristic_Twice WriteRemoteCharacteristic_Twice
#else
#define MAYBE_WriteRemoteCharacteristic_Twice \
DISABLEDWriteRemoteCharacteristic_Twice
#endif
// Tests WriteRemoteCharacteristic multiple times.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ WriteRemoteCharacteristic_Twice) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_Twice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -733,7 +919,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(empty_vector, last_write_value_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic_MultipleCharacteristics \
ReadRemoteCharacteristic_MultipleCharacteristics
#else
@@ -741,8 +927,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_ReadRemoteCharacteristic_MultipleCharacteristics
#endif
// Tests ReadRemoteCharacteristic on two characteristics.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ ReadRemoteCharacteristic_MultipleCharacteristics) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_MultipleCharacteristics) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -778,7 +969,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(test_vector2, characteristic2_->GetValue());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteRemoteCharacteristic_MultipleCharacteristics \
WriteRemoteCharacteristic_MultipleCharacteristics
#else
@@ -786,8 +977,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_WriteRemoteCharacteristic_MultipleCharacteristics
#endif
// Tests WriteRemoteCharacteristic on two characteristics.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ WriteRemoteCharacteristic_MultipleCharacteristics) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_MultipleCharacteristics) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -800,33 +996,29 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_->WriteRemoteCharacteristic(
test_vector1, GetCallback(Call::EXPECTED),
GetGattErrorCallback(Call::NOT_EXPECTED));
-#if defined(OS_ANDROID) || defined(OS_MACOSX)
- EXPECT_EQ(test_vector1, last_write_value_);
-#endif
+ if (!IsClassicWin())
+ EXPECT_EQ(test_vector1, last_write_value_);
std::vector<uint8_t> test_vector2;
test_vector2.push_back(222);
characteristic2_->WriteRemoteCharacteristic(
test_vector2, GetCallback(Call::EXPECTED),
GetGattErrorCallback(Call::NOT_EXPECTED));
-#if defined(OS_ANDROID) || defined(OS_MACOSX)
- EXPECT_EQ(test_vector2, last_write_value_);
-#endif
+ if (!IsClassicWin())
+ EXPECT_EQ(test_vector2, last_write_value_);
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(0, error_callback_count_);
SimulateGattCharacteristicWrite(characteristic1_);
base::RunLoop().RunUntilIdle();
-#if !(defined(OS_ANDROID) || defined(OS_MACOSX))
- EXPECT_EQ(test_vector1, last_write_value_);
-#endif
+ if (IsClassicWin())
+ EXPECT_EQ(test_vector1, last_write_value_);
SimulateGattCharacteristicWrite(characteristic2_);
base::RunLoop().RunUntilIdle();
-#if !(defined(OS_ANDROID) || defined(OS_MACOSX))
- EXPECT_EQ(test_vector2, last_write_value_);
-#endif
+ if (IsClassicWin())
+ EXPECT_EQ(test_vector2, last_write_value_);
EXPECT_EQ(2, gatt_write_characteristic_attempts_);
EXPECT_EQ(2, callback_count_);
@@ -835,7 +1027,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// TODO(591740): Remove if define for OS_ANDROID in this test.
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_RemoteCharacteristic_Nested_Read_Read \
RemoteCharacteristic_Nested_Read_Read
#else
@@ -844,8 +1036,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests a nested ReadRemoteCharacteristic from within another
// ReadRemoteCharacteristic.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ RemoteCharacteristic_Nested_Read_Read) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_RemoteCharacteristic_Nested_Read_Read) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -882,7 +1079,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(test_vector_2, characteristic1_->GetValue());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_RemoteCharacteristic_Nested_Write_Write \
RemoteCharacteristic_Nested_Write_Write
#else
@@ -891,8 +1088,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests a nested WriteRemoteCharacteristic from within another
// WriteRemoteCharacteristic.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ RemoteCharacteristic_Nested_Write_Write) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_RemoteCharacteristic_Nested_Write_Write) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -928,7 +1130,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(test_vector_2, last_write_value_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_RemoteCharacteristic_Nested_Read_Write \
RemoteCharacteristic_Nested_Read_Write
#else
@@ -937,8 +1139,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests a nested WriteRemoteCharacteristic from within a
// ReadRemoteCharacteristic.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ RemoteCharacteristic_Nested_Read_Write) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_RemoteCharacteristic_Nested_Read_Write) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -977,7 +1184,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(test_vector_2, last_write_value_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_RemoteCharacteristic_Nested_Write_Read \
RemoteCharacteristic_Nested_Write_Read
#else
@@ -986,8 +1193,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests a nested ReadRemoteCharacteristic from within a
// WriteRemoteCharacteristic.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ RemoteCharacteristic_Nested_Write_Read) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_RemoteCharacteristic_Nested_Write_Read) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1026,13 +1238,17 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(test_vector_2, characteristic1_->GetValue());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadError ReadError
#else
#define MAYBE_ReadError DISABLED_ReadError
#endif
// Tests ReadRemoteCharacteristic asynchronous error.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, ReadError) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_ReadError) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1055,13 +1271,17 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_ReadError) {
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteError WriteError
#else
#define MAYBE_WriteError DISABLED_WriteError
#endif
// Tests WriteRemoteCharacteristic asynchronous error.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, WriteError) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteError) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1089,8 +1309,8 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteError) {
#define MAYBE_ReadSynchronousError DISABLED_ReadSynchronousError
#endif
// Tests ReadRemoteCharacteristic synchronous error.
-// Test not relevant for macOS since characteristic read cannot generate
-// synchronous error.
+// Test not relevant for macOS and WinRT since characteristic read cannot
+// generate synchronous error.
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_ReadSynchronousError) {
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
@@ -1124,7 +1344,8 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_ReadSynchronousError) {
#define MAYBE_WriteSynchronousError DISABLED_WriteSynchronousError
#endif
// Tests WriteRemoteCharacteristic synchronous error.
-// This test doesn't apply to macOS synchronous API does exist.
+// This test doesn't apply to macOS and WinRT since a synchronous API does not
+// exist.
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteSynchronousError) {
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
@@ -1152,7 +1373,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteSynchronousError) {
EXPECT_EQ(0, error_callback_count_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic_ReadPending \
ReadRemoteCharacteristic_ReadPending
#else
@@ -1160,8 +1381,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteSynchronousError) {
DISABLED_ReadRemoteCharacteristic_ReadPending
#endif
// Tests ReadRemoteCharacteristic error with a pending read operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ ReadRemoteCharacteristic_ReadPending) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_ReadPending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1192,7 +1418,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(0, error_callback_count_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteRemoteCharacteristic_WritePending \
WriteRemoteCharacteristic_WritePending
#else
@@ -1200,8 +1426,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_WriteRemoteCharacteristic_WritePending
#endif
// Tests WriteRemoteCharacteristic error with a pending write operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ WriteRemoteCharacteristic_WritePending) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_WritePending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1232,7 +1463,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(0, error_callback_count_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ReadRemoteCharacteristic_WritePending \
ReadRemoteCharacteristic_WritePending
#else
@@ -1240,8 +1471,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_ReadRemoteCharacteristic_WritePending
#endif
// Tests ReadRemoteCharacteristic error with a pending write operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ ReadRemoteCharacteristic_WritePending) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_ReadRemoteCharacteristic_WritePending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1273,7 +1509,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(0, error_callback_count_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_WriteRemoteCharacteristic_ReadPending \
WriteRemoteCharacteristic_ReadPending
#else
@@ -1281,8 +1517,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_WriteRemoteCharacteristic_ReadPending
#endif
// Tests WriteRemoteCharacteristic error with a pending Read operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ WriteRemoteCharacteristic_ReadPending) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteRemoteCharacteristic_ReadPending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1324,8 +1565,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// how other platforms set global variables.
// Tests that a notification arriving during a pending read doesn't
// cause a crash.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ Notification_During_ReadRemoteCharacteristic) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_Notification_During_ReadRemoteCharacteristic) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1375,7 +1621,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif // defined(OS_MACOSX)
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_Notification_During_WriteRemoteCharacteristic \
Notification_During_WriteRemoteCharacteristic
#else
@@ -1384,8 +1630,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests that a notification arriving during a pending write doesn't
// cause a crash.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ Notification_During_WriteRemoteCharacteristic) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_Notification_During_WriteRemoteCharacteristic) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1416,7 +1667,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(write_value, last_write_value_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession_NoNotifyOrIndicate \
StartNotifySession_NoNotifyOrIndicate
#else
@@ -1425,8 +1676,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// StartNotifySession fails if characteristic doesn't have Notify or Indicate
// property.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_NoNotifyOrIndicate) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_NoNotifyOrIndicate) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1445,7 +1701,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
last_gatt_error_code_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession_NoConfigDescriptor \
StartNotifySession_NoConfigDescriptor
#else
@@ -1454,8 +1710,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// StartNotifySession fails if the characteristic is missing the Client
// Characteristic Configuration descriptor.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_NoConfigDescriptor) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_NoConfigDescriptor) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1474,7 +1735,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
last_gatt_error_code_);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession_MultipleConfigDescriptor \
StartNotifySession_MultipleConfigDescriptor
#else
@@ -1483,8 +1744,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// StartNotifySession fails if the characteristic has multiple Client
// Characteristic Configuration descriptors.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_MultipleConfigDescriptor) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_MultipleConfigDescriptor) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1560,13 +1826,17 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
ASSERT_EQ(0u, notify_sessions_.size());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession StartNotifySession
#else
#define MAYBE_StartNotifySession DISABLED_StartNotifySession
#endif
// Tests StartNotifySession success on a characteristic that enabled Notify.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, StartNotifySession) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_StartNotifySession) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1575,15 +1845,20 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_StartNotifySession) {
/* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession_OnIndicate StartNotifySession_OnIndicate
#else
#define MAYBE_StartNotifySession_OnIndicate \
DISABLED_StartNotifySession_OnIndicate
#endif
// Tests StartNotifySession success on a characteristic that enabled Indicate.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_OnIndicate) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_OnIndicate) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1592,7 +1867,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
/* properties: INDICATE */ 0x20, NotifyValueState::INDICATE));
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession_OnNotifyAndIndicate \
StartNotifySession_OnNotifyAndIndicate
#else
@@ -1601,8 +1876,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests StartNotifySession success on a characteristic that enabled Notify &
// Indicate.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_OnNotifyAndIndicate) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_OnNotifyAndIndicate) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1612,14 +1892,19 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
NotifyValueState::NOTIFY));
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StartNotifySession_Multiple StartNotifySession_Multiple
#else
#define MAYBE_StartNotifySession_Multiple DISABLED_StartNotifySession_Multiple
#endif
// Tests multiple StartNotifySession success.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_Multiple) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_Multiple) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1630,6 +1915,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
characteristic1_->StartNotifySession(
@@ -1663,8 +1949,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_StartNotifySessionError_Multiple
#endif
// Tests multiple StartNotifySessions pending and then an error.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StartNotifySessionError_Multiple) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySessionError_Multiple) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1675,6 +1966,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
characteristic1_->StartNotifySession(GetNotifyCallback(Call::NOT_EXPECTED),
@@ -1739,8 +2031,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_StartNotifySession_BeforeDeleted
#endif
// Tests StartNotifySession completing before chrome objects are deleted.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StartNotifySession_BeforeDeleted) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_BeforeDeleted) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1751,6 +2048,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
characteristic1_->StartNotifySession(
@@ -1781,7 +2079,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_FALSE(notify_sessions_[0]->IsActive());
}
-#if defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_MACOSX)
#define MAYBE_StartNotifySession_Reentrant_Success_Success \
StartNotifySession_Reentrant_Success_Success
#else
@@ -1790,8 +2088,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests StartNotifySession reentrant in start notify session success callback
// and the reentrant start notify session success.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StartNotifySession_Reentrant_Success_Success) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StartNotifySession_Reentrant_Success_Success) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1800,7 +2103,9 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
FakeCharacteristicBoilerplate(/* properties: NOTIFY */ 0x10));
SimulateGattDescriptor(
characteristic1_,
- BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid().value());
+ BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()
+ .canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
characteristic1_->StartNotifySession(
@@ -1831,16 +2136,10 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
}
#if defined(OS_WIN)
-#define MAYBE_StartNotifySession_Reentrant_Error_Error \
- StartNotifySession_Reentrant_Error_Error
-#else
-#define MAYBE_StartNotifySession_Reentrant_Error_Error \
- DISABLED_StartNotifySession_Reentrant_Error_Error
-#endif
// Tests StartNotifySession reentrant in start notify session error callback
// and the reentrant start notify session error.
-TEST_F(BluetoothRemoteGattCharacteristicTest,
- MAYBE_StartNotifySession_Reentrant_Error_Error) {
+TEST_P(BluetoothRemoteGattCharacteristicTestWin32Only,
+ StartNotifySession_Reentrant_Error_Error) {
ASSERT_NO_FATAL_FAILURE(
FakeCharacteristicBoilerplate(/* properties: NOTIFY */ 0x10));
SimulateGattDescriptor(
@@ -1870,6 +2169,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(2, error_callback_count_);
ASSERT_EQ(0u, notify_sessions_.size());
}
+#endif
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_StopNotifySession StopNotifySession
@@ -1877,7 +2177,11 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession DISABLED_StopNotifySession
#endif
// Tests StopNotifySession success on a characteristic that enabled Notify.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly, StopNotifySession) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_StopNotifySession) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1906,8 +2210,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_StopNotifySession) {
DISABLED_StopNotifySession_SessionDeleted
#endif
// Tests that deleted sessions are stopped.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_SessionDeleted) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_SessionDeleted) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1937,8 +2246,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests that deleting the sessions before the stop callbacks have been
// invoked does not cause problems.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_SessionDeleted2) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_SessionDeleted2) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1950,6 +2264,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
// Start notify sessions.
@@ -1998,10 +2313,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_Cancelled DISABLED_StopNotifySession_Cancelled
#endif
// Tests that cancelling StopNotifySession works.
-// TODO(crbug.com/636270): Enable on Windows when SubscribeToNotifications is
-// implemented.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StopNotifySession_Cancelled) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_Cancelled) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2030,8 +2348,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
DISABLED_StopNotifySession_AfterDeleted
#endif
// Tests that deleted sessions are stopped.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ StopNotifySession_AfterDeleted) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_AfterDeleted) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2071,8 +2394,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_OnIndicate DISABLED_StopNotifySession_OnIndicate
#endif
// Tests StopNotifySession success on a characteristic that enabled Indicate.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_OnIndicate) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_OnIndicate) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2103,8 +2431,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests StopNotifySession success on a characteristic that enabled Notify &
// Indicate.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_OnNotifyAndIndicate) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_OnNotifyAndIndicate) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2133,7 +2466,12 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_Error DISABLED_StopNotifySession_Error
#endif
// Tests StopNotifySession error
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_Error) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_StopNotifySession_Error) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2164,8 +2502,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_StopNotifySession_Error) {
#define MAYBE_StopNotifySession_Multiple1 DISABLED_StopNotifySession_Multiple1
#endif
// Tests multiple StopNotifySession calls for a single session.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_Multiple1) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_Multiple1) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2177,6 +2520,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
// Start notify session
@@ -2214,8 +2558,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_Multiple2 DISABLED_StopNotifySession_Multiple2
#endif
// Tests multiple StartNotifySession calls and multiple StopNotifySession calls.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_Multiple2) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_Multiple2) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2227,6 +2576,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
// Start notify sessions
@@ -2276,8 +2626,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests starting a new notify session before the previous stop request
// resolves.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_StopStart) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_StopStart) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2288,6 +2643,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
// Start notify session
@@ -2329,8 +2685,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_StartStopStart \
DISABLED_StopNotifySession_StartStopStart
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_StartStopStart) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_StartStopStart) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2390,8 +2751,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// Tests starting a new notify session before the previous stop requests
// resolve.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_StopStopStart) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_StopStopStart) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2402,6 +2768,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
// Start notify session
@@ -2458,8 +2825,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_Reentrant_Success_Stop \
DISABLED_StopNotifySession_Reentrant_Success_Stop
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_Reentrant_Success_Stop) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_Reentrant_Success_Stop) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2470,6 +2842,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
characteristic1_,
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
.canonical_value());
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, characteristic1_->GetDescriptors().size());
// Start notify session
@@ -2507,8 +2880,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_Reentrant_Stop_StartSuccess \
DISABLED_StopNotifySession_Reentrant_Stop_StartSuccess
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_Reentrant_Stop_StartSuccess) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_Reentrant_Stop_StartSuccess) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2557,8 +2935,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#define MAYBE_StopNotifySession_Reentrant_Stop_StartError \
DISABLED_StopNotifySession_Reentrant_Stop_StartError
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ StopNotifySession_Reentrant_Stop_StartError) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_StopNotifySession_Reentrant_Stop_StartError) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2605,7 +2988,11 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif
// TODO(786473) Android should report that services are discovered when a
// characteristic is added, but currently does not.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GattCharacteristicAdded) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GattCharacteristicAdded) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2618,15 +3005,20 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GattCharacteristicAdded) {
EXPECT_EQ(1, observer.gatt_services_discovered_count());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GattCharacteristicValueChanged GattCharacteristicValueChanged
#else
#define MAYBE_GattCharacteristicValueChanged \
DISABLED_GattCharacteristicValueChanged
#endif
// Tests Characteristic Value changes during a Notify Session.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ GattCharacteristicValueChanged) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_GattCharacteristicValueChanged) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2686,7 +3078,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
observer.previous_characteristic_value_changed_values());
}
-#if defined(OS_ANDROID) || defined(OS_WIN)
+#if defined(OS_ANDROID)
#define MAYBE_GattCharacteristicValueChanged_AfterDeleted \
GattCharacteristicValueChanged_AfterDeleted
#else
@@ -2697,8 +3089,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// destroyed.
// macOS: Not applicable: This can never happen if CBPeripheral delegate is set
// to nil.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWin32Only,
+ GattCharacteristicValueChanged_AfterDeleted) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_GattCharacteristicValueChanged_AfterDeleted) {
+#endif
ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
/* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
TestBluetoothAdapterObserver observer(adapter_);
@@ -2714,25 +3111,44 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
}
-#if defined(OS_ANDROID) || defined(OS_WIN)
+#if defined(OS_ANDROID)
#define MAYBE_GetDescriptors_FindNone GetDescriptors_FindNone
#else
#define MAYBE_GetDescriptors_FindNone DISABLED_GetDescriptors_FindNone
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GetDescriptors_FindNone) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetDescriptors_FindNone) {
+#endif
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
EXPECT_EQ(0u, characteristic1_->GetDescriptors().size());
}
-#if defined(OS_ANDROID) || defined(OS_WIN)
+#if defined(OS_ANDROID)
#define MAYBE_GetDescriptors_and_GetDescriptor GetDescriptors_and_GetDescriptor
#else
#define MAYBE_GetDescriptors_and_GetDescriptor \
DISABLED_GetDescriptors_and_GetDescriptor
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt,
+ GetDescriptors_and_GetDescriptor) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_GetDescriptors_and_GetDescriptor) {
+#endif
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
// Add several Descriptors:
@@ -2744,6 +3160,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
SimulateGattDescriptor(characteristic1_, uuid2.canonical_value());
SimulateGattDescriptor(characteristic2_, uuid3.canonical_value());
SimulateGattDescriptor(characteristic2_, uuid4.canonical_value());
+ base::RunLoop().RunUntilIdle();
// Verify that GetDescriptor can retrieve descriptors again by ID,
// and that the same Descriptor is returned when searched by ID.
@@ -2775,12 +3192,21 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_FALSE(c1_uuid1 == uuid3 || c1_uuid2 == uuid3);
}
-#if defined(OS_ANDROID) || defined(OS_WIN)
+#if defined(OS_ANDROID)
#define MAYBE_GetDescriptorsByUUID GetDescriptorsByUUID
#else
#define MAYBE_GetDescriptorsByUUID DISABLED_GetDescriptorsByUUID
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrt, GetDescriptorsByUUID) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetDescriptorsByUUID) {
+#endif
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
// Add several Descriptors:
@@ -2791,6 +3217,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_GetDescriptorsByUUID) {
SimulateGattDescriptor(characteristic1_, id2.canonical_value());
SimulateGattDescriptor(characteristic2_, id3.canonical_value());
SimulateGattDescriptor(characteristic2_, id3.canonical_value());
+ base::RunLoop().RunUntilIdle();
EXPECT_NE(characteristic2_->GetDescriptorsByUUID(id3).at(0)->GetIdentifier(),
characteristic2_->GetDescriptorsByUUID(id3).at(1)->GetIdentifier());
@@ -2872,12 +3299,18 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, MAYBE_WriteDuringDisconnect) {
DISABLED_WriteWithoutResponseOnlyCharacteristic_WriteRemoteCharacteristicDuringDisconnect
#endif
// Tests that writing without response during a disconnect results in an error.
-// Only applies to macOS whose events arrive all on the UI thread. See other
-// *DuringDisconnect tests for Android and Windows whose events arrive on a
-// different thread.
+// Only applies to macOS and WinRT whose events arrive all on the UI thread. See
+// other *DuringDisconnect tests for Android and Windows whose events arrive on
+// a different thread.
+#if defined(OS_WIN)
+TEST_P(
+ BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ WriteWithoutResponseOnlyCharacteristic_WriteRemoteCharacteristicDuringDisconnect) {
+#else
TEST_F(
BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteWithoutResponseOnlyCharacteristic_WriteRemoteCharacteristicDuringDisconnect) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2888,7 +3321,7 @@ TEST_F(
characteristic1_->WriteRemoteCharacteristic(
std::vector<uint8_t>(), GetCallback(Call::NOT_EXPECTED),
GetGattErrorCallback(Call::EXPECTED));
- SimulateGattDisconnection(device_);
+ SimulateDeviceBreaksConnection(adapter_->GetDevices()[0]);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
@@ -2973,8 +3406,13 @@ TEST_F(
// Tests that WriteWithoutResponse fails when a characteristic does not have the
// required property.
// TODO(https://crbug.com/831524): Enable for other platforms once supported.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ WriteWithoutResponse_PropertyNotPresent) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteWithoutResponse_PropertyNotPresent) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -2996,8 +3434,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// Tests that WriteWithoutResponse fails when a characteristic already has a
// pending write.
// TODO(https://crbug.com/831524): Enable for other platforms once supported.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ WriteWithoutResponse_PendingWrite) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteWithoutResponse_PendingWrite) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -3034,8 +3477,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// Tests that WriteWithoutResponse fails when a characteristic already has a
// pending read.
// TODO(https://crbug.com/831524): Enable for other platforms once supported.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ WriteWithoutResponse_PendingRead) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteWithoutResponse_PendingRead) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -3071,8 +3519,13 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
// Tests that WriteWithoutResponse indicates success if the proper conditions
// are met.
// TODO(https://crbug.com/831524): Enable for other platforms once supported.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ WriteWithoutResponse_Success) {
+#else
TEST_F(BluetoothRemoteGattCharacteristicTest,
MAYBE_WriteWithoutResponse_Success) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -3287,4 +3740,21 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ExtraDidDiscoverDescriptorsCall) {
}
#endif // defined(OS_MACOSX)
+#if defined(OS_WIN)
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ BluetoothRemoteGattCharacteristicTestWinrt,
+ ::testing::Bool());
+
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ BluetoothRemoteGattCharacteristicTestWin32Only,
+ ::testing::Values(false));
+
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ BluetoothRemoteGattCharacteristicTestWinrtOnly,
+ ::testing::Values(true));
+#endif // defined(OS_WIN)
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
index 93166b8b1fa..b18b7d5c166 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
@@ -4,31 +4,127 @@
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h"
+#include <utility>
+
+#include "base/bind_helpers.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/winrt_storage_util.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_discoverer_winrt.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h"
#include "device/bluetooth/bluetooth_uuid.h"
+#include "device/bluetooth/event_utils_winrt.h"
namespace device {
-BluetoothRemoteGattCharacteristicWinrt::
- BluetoothRemoteGattCharacteristicWinrt() = default;
+namespace {
+
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCharacteristicProperties;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattClientCharacteristicConfigurationDescriptorValue;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattClientCharacteristicConfigurationDescriptorValue_Indicate;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattClientCharacteristicConfigurationDescriptorValue_None;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattClientCharacteristicConfigurationDescriptorValue_Notify;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCommunicationStatus;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCommunicationStatus_Success;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattWriteOption_WriteWithResponse;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattWriteOption_WriteWithoutResponse;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattWriteResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattCharacteristic;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattCharacteristic3;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattReadResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattReadResult2;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattValueChangedEventArgs;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattWriteResult;
+using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Storage::Streams::IBuffer;
+using Microsoft::WRL::ComPtr;
+
+} // namespace
+
+// static
+std::unique_ptr<BluetoothRemoteGattCharacteristicWinrt>
+BluetoothRemoteGattCharacteristicWinrt::Create(
+ BluetoothRemoteGattService* service,
+ ComPtr<IGattCharacteristic> characteristic) {
+ DCHECK(characteristic);
+ GUID guid;
+ HRESULT hr = characteristic->get_Uuid(&guid);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting UUID failed: " << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ GattCharacteristicProperties properties;
+ hr = characteristic->get_CharacteristicProperties(&properties);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Properties failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ uint16_t attribute_handle;
+ hr = characteristic->get_AttributeHandle(&attribute_handle);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting AttributeHandle failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ return base::WrapUnique(new BluetoothRemoteGattCharacteristicWinrt(
+ service, std::move(characteristic), BluetoothUUID(guid), properties,
+ attribute_handle));
+}
BluetoothRemoteGattCharacteristicWinrt::
- ~BluetoothRemoteGattCharacteristicWinrt() = default;
+ ~BluetoothRemoteGattCharacteristicWinrt() {
+ if (pending_read_callbacks_) {
+ pending_read_callbacks_->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ }
+
+ if (pending_write_callbacks_) {
+ pending_write_callbacks_->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ }
+
+ if (value_changed_token_)
+ RemoveValueChangedHandler();
+}
std::string BluetoothRemoteGattCharacteristicWinrt::GetIdentifier() const {
- NOTIMPLEMENTED();
- return std::string();
+ return identifier_;
}
BluetoothUUID BluetoothRemoteGattCharacteristicWinrt::GetUUID() const {
- NOTIMPLEMENTED();
- return BluetoothUUID();
+ return uuid_;
}
BluetoothGattCharacteristic::Properties
BluetoothRemoteGattCharacteristicWinrt::GetProperties() const {
- NOTIMPLEMENTED();
- return Properties();
+ return properties_;
}
BluetoothGattCharacteristic::Permissions
@@ -44,35 +140,494 @@ const std::vector<uint8_t>& BluetoothRemoteGattCharacteristicWinrt::GetValue()
BluetoothRemoteGattService* BluetoothRemoteGattCharacteristicWinrt::GetService()
const {
- NOTIMPLEMENTED();
- return nullptr;
+ return service_;
}
void BluetoothRemoteGattCharacteristicWinrt::ReadRemoteCharacteristic(
const ValueCallback& callback,
const ErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+ if (!(GetProperties() & PROPERTY_READ)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED));
+ return;
+ }
+
+ if (pending_read_callbacks_ || pending_write_callbacks_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattReadResult*>> read_value_op;
+ HRESULT hr = characteristic_->ReadValueAsync(&read_value_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattCharacteristic::ReadValueAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(read_value_op),
+ base::BindOnce(&BluetoothRemoteGattCharacteristicWinrt::OnReadValue,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ pending_read_callbacks_ =
+ std::make_unique<PendingReadCallbacks>(callback, error_callback);
}
void BluetoothRemoteGattCharacteristicWinrt::WriteRemoteCharacteristic(
const std::vector<uint8_t>& value,
const base::Closure& callback,
const ErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+ if (!(GetProperties() & PROPERTY_WRITE) &&
+ !(GetProperties() & PROPERTY_WRITE_WITHOUT_RESPONSE)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED));
+ return;
+ }
+
+ if (pending_read_callbacks_ || pending_write_callbacks_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+ return;
+ }
+
+ ComPtr<IGattCharacteristic3> characteristic_3;
+ HRESULT hr = characteristic_.As(&characteristic_3);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IGattCharacteristic3 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ ComPtr<IBuffer> buffer;
+ hr = base::win::CreateIBufferFromData(value.data(), value.size(), &buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "base::win::CreateIBufferFromData failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattWriteResult*>> write_value_op;
+ hr = characteristic_3->WriteValueWithResultAndOptionAsync(
+ buffer.Get(),
+ (GetProperties() & PROPERTY_WRITE) ? GattWriteOption_WriteWithResponse
+ : GattWriteOption_WriteWithoutResponse,
+
+ &write_value_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattCharacteristic::WriteValueWithResultAndOptionAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ hr = PostAsyncResults(std::move(write_value_op),
+ base::BindOnce(&BluetoothRemoteGattCharacteristicWinrt::
+ OnWriteValueWithResultAndOption,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ pending_write_callbacks_ =
+ std::make_unique<PendingWriteCallbacks>(callback, error_callback);
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::UpdateDescriptors(
+ BluetoothGattDiscovererWinrt* gatt_discoverer) {
+ const auto* gatt_descriptors =
+ gatt_discoverer->GetDescriptors(attribute_handle_);
+ DCHECK(gatt_descriptors);
+
+ // Instead of clearing out |descriptors_| and creating each descriptor
+ // from scratch, we create a new map and move already existing descriptors
+ // into it in order to preserve pointer stability.
+ DescriptorMap descriptors;
+ for (const auto& gatt_descriptor : *gatt_descriptors) {
+ auto descriptor =
+ BluetoothRemoteGattDescriptorWinrt::Create(this, gatt_descriptor.Get());
+ if (!descriptor)
+ continue;
+
+ std::string identifier = descriptor->GetIdentifier();
+ auto iter = descriptors_.find(identifier);
+ if (iter != descriptors_.end())
+ descriptors.emplace(std::move(*iter));
+ else
+ descriptors.emplace(std::move(identifier), std::move(descriptor));
+ }
+
+ std::swap(descriptors, descriptors_);
+}
+
+bool BluetoothRemoteGattCharacteristicWinrt::WriteWithoutResponse(
+ base::span<const uint8_t> value) {
+ if (!(GetProperties() & PROPERTY_WRITE_WITHOUT_RESPONSE))
+ return false;
+
+ if (pending_read_callbacks_ || pending_write_callbacks_)
+ return false;
+
+ ComPtr<IGattCharacteristic3> characteristic_3;
+ HRESULT hr = characteristic_.As(&characteristic_3);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IGattCharacteristic3 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ ComPtr<IBuffer> buffer;
+ hr = base::win::CreateIBufferFromData(value.data(), value.size(), &buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "base::win::CreateIBufferFromData failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ ComPtr<IAsyncOperation<GattWriteResult*>> write_value_op;
+ // Note: As we are ignoring the result WriteValueWithOptionAsync() would work
+ // as well, but re-using WriteValueWithResultAndOptionAsync() does simplify
+ // the testing code and there is no difference in production.
+ hr = characteristic_3->WriteValueWithResultAndOptionAsync(
+ buffer.Get(), GattWriteOption_WriteWithoutResponse, &write_value_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattCharacteristic::WriteValueWithResultAndOptionAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ // While we are ignoring the response, we still post the async_op in order to
+ // extend its lifetime until the operation has completed.
+ hr = PostAsyncResults(std::move(write_value_op), base::DoNothing());
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ return true;
+}
+
+IGattCharacteristic*
+BluetoothRemoteGattCharacteristicWinrt::GetCharacteristicForTesting() {
+ return characteristic_.Get();
}
void BluetoothRemoteGattCharacteristicWinrt::SubscribeToNotifications(
BluetoothRemoteGattDescriptor* ccc_descriptor,
const base::Closure& callback,
const ErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+ value_changed_token_ = AddTypedEventHandler(
+ characteristic_.Get(), &IGattCharacteristic::add_ValueChanged,
+ base::BindRepeating(
+ &BluetoothRemoteGattCharacteristicWinrt::OnValueChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (!value_changed_token_) {
+ VLOG(2) << "Adding Value Changed Handler failed.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ WriteCccDescriptor(
+ (GetProperties() & PROPERTY_NOTIFY)
+ ? GattClientCharacteristicConfigurationDescriptorValue_Notify
+ : GattClientCharacteristicConfigurationDescriptorValue_Indicate,
+ callback, error_callback);
}
void BluetoothRemoteGattCharacteristicWinrt::UnsubscribeFromNotifications(
BluetoothRemoteGattDescriptor* ccc_descriptor,
const base::Closure& callback,
const ErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+ WriteCccDescriptor(
+ GattClientCharacteristicConfigurationDescriptorValue_None,
+ // Wrap the success and error callbacks in a lambda, so that we can notify
+ // callers whether removing the event handler succeeded after the
+ // descriptor has been written to.
+ base::BindOnce(
+ [](base::WeakPtr<BluetoothRemoteGattCharacteristicWinrt>
+ characteristic,
+ base::OnceClosure callback, ErrorCallback error_callback) {
+ if (characteristic &&
+ !characteristic->RemoveValueChangedHandler()) {
+ error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ std::move(callback).Run();
+ },
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback),
+ error_callback);
+}
+
+BluetoothRemoteGattCharacteristicWinrt::PendingReadCallbacks::
+ PendingReadCallbacks(ValueCallback callback, ErrorCallback error_callback)
+ : callback(std::move(callback)),
+ error_callback(std::move(error_callback)) {}
+BluetoothRemoteGattCharacteristicWinrt::PendingReadCallbacks::
+ ~PendingReadCallbacks() = default;
+
+BluetoothRemoteGattCharacteristicWinrt::PendingWriteCallbacks::
+ PendingWriteCallbacks(base::OnceClosure callback,
+ ErrorCallback error_callback)
+ : callback(std::move(callback)),
+ error_callback(std::move(error_callback)) {}
+BluetoothRemoteGattCharacteristicWinrt::PendingWriteCallbacks::
+ ~PendingWriteCallbacks() = default;
+
+BluetoothRemoteGattCharacteristicWinrt::BluetoothRemoteGattCharacteristicWinrt(
+ BluetoothRemoteGattService* service,
+ ComPtr<IGattCharacteristic> characteristic,
+ BluetoothUUID uuid,
+ Properties properties,
+ uint16_t attribute_handle)
+ : service_(service),
+ characteristic_(std::move(characteristic)),
+ uuid_(std::move(uuid)),
+ properties_(properties),
+ attribute_handle_(attribute_handle),
+ identifier_(base::StringPrintf("%s/%s_%04x",
+ service_->GetIdentifier().c_str(),
+ uuid_.value().c_str(),
+ attribute_handle_)),
+ weak_ptr_factory_(this) {}
+
+void BluetoothRemoteGattCharacteristicWinrt::WriteCccDescriptor(
+ GattClientCharacteristicConfigurationDescriptorValue value,
+ base::OnceClosure callback,
+ const ErrorCallback& error_callback) {
+ DCHECK(!pending_notification_callbacks_);
+ ComPtr<IGattCharacteristic3> characteristic_3;
+ HRESULT hr = characteristic_.As(&characteristic_3);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IGattCharacteristic3 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattWriteResult*>> write_ccc_descriptor_op;
+ hr = characteristic_3
+ ->WriteClientCharacteristicConfigurationDescriptorWithResultAsync(
+ value, &write_ccc_descriptor_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattCharacteristic::"
+ "WriteClientCharacteristicConfigurationDescriptorWithResultAsync"
+ " failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(write_ccc_descriptor_op),
+ base::BindOnce(
+ &BluetoothRemoteGattCharacteristicWinrt::OnWriteCccDescriptor,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ pending_notification_callbacks_ =
+ std::make_unique<PendingNotificationCallbacks>(std::move(callback),
+ error_callback);
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::OnReadValue(
+ ComPtr<IGattReadResult> read_result) {
+ DCHECK(pending_read_callbacks_);
+ auto pending_read_callbacks = std::move(pending_read_callbacks_);
+
+ if (!read_result) {
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ GattCommunicationStatus status;
+ HRESULT hr = read_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting GATT Communication Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ if (status != GattCommunicationStatus_Success) {
+ VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
+ ComPtr<IGattReadResult2> read_result_2;
+ hr = read_result.As(&read_result_2);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IGattReadResult2 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ pending_read_callbacks->error_callback.Run(
+ BluetoothRemoteGattServiceWinrt::GetGattErrorCode(read_result_2.Get()));
+ return;
+ }
+
+ ComPtr<IBuffer> value;
+ hr = read_result->get_Value(&value);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Characteristic Value failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ uint8_t* data = nullptr;
+ uint32_t length = 0;
+ hr = base::win::GetPointerToBufferData(value.Get(), &data, &length);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Pointer To Buffer Data failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ value_.assign(data, data + length);
+ pending_read_callbacks->callback.Run(value_);
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::OnWriteValueWithResultAndOption(
+ ComPtr<IGattWriteResult> write_result) {
+ DCHECK(pending_write_callbacks_);
+ OnWriteImpl(std::move(write_result), std::move(pending_write_callbacks_));
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::OnWriteCccDescriptor(
+ ComPtr<IGattWriteResult> write_result) {
+ OnWriteImpl(std::move(write_result),
+ std::move(pending_notification_callbacks_));
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::OnWriteImpl(
+ ComPtr<IGattWriteResult> write_result,
+ std::unique_ptr<PendingWriteCallbacks> callbacks) {
+ if (!write_result) {
+ callbacks->error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ GattCommunicationStatus status;
+ HRESULT hr = write_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting GATT Communication Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ callbacks->error_callback.Run(BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ if (status != GattCommunicationStatus_Success) {
+ VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
+ callbacks->error_callback.Run(
+ BluetoothRemoteGattServiceWinrt::GetGattErrorCode(write_result.Get()));
+ return;
+ }
+
+ std::move(callbacks->callback).Run();
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::OnValueChanged(
+ IGattCharacteristic* characteristic,
+ IGattValueChangedEventArgs* event_args) {
+ ComPtr<IBuffer> value;
+ HRESULT hr = event_args->get_CharacteristicValue(&value);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Characteristic Value failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ uint8_t* data = nullptr;
+ uint32_t length = 0;
+ hr = base::win::GetPointerToBufferData(value.Get(), &data, &length);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Pointer To Buffer Data failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ value_.assign(data, data + length);
+ service_->GetDevice()->GetAdapter()->NotifyGattCharacteristicValueChanged(
+ this, value_);
+}
+
+bool BluetoothRemoteGattCharacteristicWinrt::RemoveValueChangedHandler() {
+ DCHECK(value_changed_token_);
+ HRESULT hr = characteristic_->remove_ValueChanged(*value_changed_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing the Value Changed Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ value_changed_token_.reset();
+ return SUCCEEDED(hr);
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
index ca1b3c0255e..30e242a9a81 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
@@ -5,8 +5,12 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_WINRT_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_WINRT_H_
+#include <windows.devices.bluetooth.genericattributeprofile.h>
+#include <wrl/client.h>
+
#include <stdint.h>
+#include <memory>
#include <string>
#include <vector>
@@ -14,17 +18,22 @@
#include "base/macros.h"
#include "device/bluetooth/bluetooth_export.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace device {
class BluetoothRemoteGattDescriptor;
+class BluetoothGattDiscovererWinrt;
class BluetoothRemoteGattService;
-class BluetoothUUID;
class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWinrt
: public BluetoothRemoteGattCharacteristic {
public:
- BluetoothRemoteGattCharacteristicWinrt();
+ static std::unique_ptr<BluetoothRemoteGattCharacteristicWinrt> Create(
+ BluetoothRemoteGattService* service,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattCharacteristic>
+ characteristic);
~BluetoothRemoteGattCharacteristicWinrt() override;
// BluetoothGattCharacteristic:
@@ -41,6 +50,13 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWinrt
void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
+ bool WriteWithoutResponse(base::span<const uint8_t> value) override;
+
+ void UpdateDescriptors(BluetoothGattDiscovererWinrt* gatt_discoverer);
+
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattCharacteristic*
+ GetCharacteristicForTesting();
protected:
// BluetoothRemoteGattCharacteristic:
@@ -53,7 +69,84 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWinrt
const ErrorCallback& error_callback) override;
private:
+ struct PendingReadCallbacks {
+ PendingReadCallbacks(ValueCallback callback, ErrorCallback error_callback);
+ ~PendingReadCallbacks();
+
+ ValueCallback callback;
+ ErrorCallback error_callback;
+ };
+
+ struct PendingWriteCallbacks {
+ PendingWriteCallbacks(base::OnceClosure callback,
+ ErrorCallback error_callback);
+ ~PendingWriteCallbacks();
+
+ base::OnceClosure callback;
+ ErrorCallback error_callback;
+ };
+
+ using PendingNotificationCallbacks = PendingWriteCallbacks;
+
+ BluetoothRemoteGattCharacteristicWinrt(
+ BluetoothRemoteGattService* service,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattCharacteristic>
+ characteristic,
+ BluetoothUUID uuid,
+ Properties proporties,
+ uint16_t attribute_handle);
+
+ void WriteCccDescriptor(
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattClientCharacteristicConfigurationDescriptorValue value,
+ base::OnceClosure callback,
+ const ErrorCallback& error_callback);
+
+ void OnReadValue(Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattReadResult> read_result);
+
+ void OnWriteValueWithResultAndOption(
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattWriteResult>
+ write_result);
+
+ void OnWriteCccDescriptor(
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattWriteResult>
+ write_result);
+
+ void OnWriteImpl(
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattWriteResult>
+ write_result,
+ std::unique_ptr<PendingWriteCallbacks> callbacks);
+
+ void OnValueChanged(
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattCharacteristic* characteristic,
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattValueChangedEventArgs* event_args);
+
+ bool RemoveValueChangedHandler();
+
+ BluetoothRemoteGattService* service_;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattCharacteristic>
+ characteristic_;
+ BluetoothUUID uuid_;
+ Properties properties_;
+ uint16_t attribute_handle_;
+ std::string identifier_;
std::vector<uint8_t> value_;
+ std::unique_ptr<PendingReadCallbacks> pending_read_callbacks_;
+ std::unique_ptr<PendingWriteCallbacks> pending_write_callbacks_;
+ std::unique_ptr<PendingNotificationCallbacks> pending_notification_callbacks_;
+ base::Optional<EventRegistrationToken> value_changed_token_;
+
+ base::WeakPtrFactory<BluetoothRemoteGattCharacteristicWinrt>
+ weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicWinrt);
};
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
index 9ef4cfcfe1c..3c3e5d0726d 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
@@ -22,7 +22,12 @@
namespace device {
-class BluetoothRemoteGattDescriptorTest : public BluetoothTest {
+class BluetoothRemoteGattDescriptorTest :
+#if defined(OS_WIN)
+ public BluetoothTestWinrt {
+#else
+ public BluetoothTest {
+#endif
public:
// Creates adapter_, device_, service_, characteristic_,
// descriptor1_, & descriptor2_.
@@ -40,12 +45,14 @@ class BluetoothRemoteGattDescriptorTest : public BluetoothTest {
ASSERT_EQ(1u, device_->GetGattServices().size());
service_ = device_->GetGattServices()[0];
SimulateGattCharacteristic(service_, kTestUUIDDeviceName, 0);
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, service_->GetCharacteristics().size());
characteristic_ = service_->GetCharacteristics()[0];
SimulateGattDescriptor(characteristic_,
kTestUUIDCharacteristicUserDescription);
SimulateGattDescriptor(characteristic_,
kTestUUIDClientCharacteristicConfiguration);
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, characteristic_->GetDescriptors().size());
descriptor1_ = characteristic_->GetDescriptors()[0];
descriptor2_ = characteristic_->GetDescriptors()[1];
@@ -59,12 +66,21 @@ class BluetoothRemoteGattDescriptorTest : public BluetoothTest {
BluetoothRemoteGattDescriptor* descriptor2_ = nullptr;
};
+#if defined(OS_WIN)
+using BluetoothRemoteGattDescriptorTestWinrtOnly =
+ BluetoothRemoteGattDescriptorTest;
+#endif
+
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetIdentifier GetIdentifier
#else
#define MAYBE_GetIdentifier DISABLED_GetIdentifier
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, GetIdentifier) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetIdentifier) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -102,6 +118,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetIdentifier) {
SimulateGattCharacteristic(service2, kTestUUIDDeviceName, /* properties */ 0);
SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
+ base::RunLoop().RunUntilIdle();
BluetoothRemoteGattCharacteristic* char1 = service1->GetCharacteristics()[0];
BluetoothRemoteGattCharacteristic* char2 = service1->GetCharacteristics()[1];
BluetoothRemoteGattCharacteristic* char3 = service2->GetCharacteristics()[0];
@@ -117,6 +134,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetIdentifier) {
SimulateGattDescriptor(char4, kTestUUIDCharacteristicUserDescription);
SimulateGattDescriptor(char5, kTestUUIDCharacteristicUserDescription);
SimulateGattDescriptor(char6, kTestUUIDCharacteristicUserDescription);
+ base::RunLoop().RunUntilIdle();
BluetoothRemoteGattDescriptor* desc1 = char1->GetDescriptors()[0];
BluetoothRemoteGattDescriptor* desc2 = char2->GetDescriptors()[0];
BluetoothRemoteGattDescriptor* desc3 = char3->GetDescriptors()[0];
@@ -151,7 +169,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetIdentifier) {
#else
#define MAYBE_GetUUID DISABLED_GetUUID
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, GetUUID) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetUUID) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -170,6 +192,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetUUID) {
SimulateGattCharacteristic(service, kTestUUIDDeviceName,
/* properties */ 0);
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, service->GetCharacteristics().size());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristics()[0];
@@ -181,6 +204,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetUUID) {
kTestUUIDCharacteristicUserDescription);
SimulateGattDescriptor(characteristic,
kTestUUIDClientCharacteristicConfiguration);
+ base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, characteristic->GetDescriptors().size());
BluetoothRemoteGattDescriptor* descriptor1 =
characteristic->GetDescriptors()[0];
@@ -201,7 +225,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_GetUUID) {
#define MAYBE_ReadRemoteDescriptor_Empty DISABLED_ReadRemoteDescriptor_Empty
#endif
// Tests ReadRemoteDescriptor and GetValue with empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, ReadRemoteDescriptor_Empty) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadRemoteDescriptor_Empty) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -229,7 +257,12 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadRemoteDescriptor_Empty) {
#define MAYBE_WriteRemoteDescriptor_Empty DISABLED_WriteRemoteDescriptor_Empty
#endif
// Tests WriteRemoteDescriptor with empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ WriteRemoteDescriptor_Empty) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteRemoteDescriptor_Empty) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -318,7 +351,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest,
#define MAYBE_ReadRemoteDescriptor DISABLED_ReadRemoteDescriptor
#endif
// Tests ReadRemoteDescriptor and GetValue with non-empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, ReadRemoteDescriptor) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadRemoteDescriptor) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -349,7 +386,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadRemoteDescriptor) {
#define MAYBE_WriteRemoteDescriptor DISABLED_WriteRemoteDescriptor
#endif
// Tests WriteRemoteDescriptor with non-empty value buffer.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, WriteRemoteDescriptor) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteRemoteDescriptor) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -374,7 +415,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteRemoteDescriptor) {
#define MAYBE_ReadRemoteDescriptor_Twice DISABLED_ReadRemoteDescriptor_Twice
#endif
// Tests ReadRemoteDescriptor and GetValue multiple times.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, ReadRemoteDescriptor_Twice) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadRemoteDescriptor_Twice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -414,7 +459,12 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadRemoteDescriptor_Twice) {
#define MAYBE_WriteRemoteDescriptor_Twice DISABLED_WriteRemoteDescriptor_Twice
#endif
// Tests WriteRemoteDescriptor multiple times.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ WriteRemoteDescriptor_Twice) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteRemoteDescriptor_Twice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -454,8 +504,13 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteRemoteDescriptor_Twice) {
DISABLED_ReadRemoteDescriptor_MultipleDescriptors
#endif
// Tests ReadRemoteDescriptor on two descriptors.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ ReadRemoteDescriptor_MultipleDescriptors) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest,
MAYBE_ReadRemoteDescriptor_MultipleDescriptors) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -496,8 +551,13 @@ TEST_F(BluetoothRemoteGattDescriptorTest,
DISABLED_WriteRemoteDescriptor_MultipleDescriptors
#endif
// Tests WriteRemoteDescriptor on two descriptors.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ WriteRemoteDescriptor_MultipleDescriptors) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest,
MAYBE_WriteRemoteDescriptor_MultipleDescriptors) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -534,7 +594,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest,
#define MAYBE_ReadError DISABLED_ReadError
#endif
// Tests ReadRemoteDescriptor asynchronous error.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, ReadError) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadError) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -558,7 +622,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_ReadError) {
#define MAYBE_WriteError DISABLED_WriteError
#endif
// Tests WriteRemoteDescriptor asynchronous error.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly, WriteError) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteError) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -661,8 +729,13 @@ TEST_F(BluetoothRemoteGattDescriptorTest, MAYBE_WriteSynchronousError) {
DISABLED_ReadRemoteDescriptor_ReadPending
#endif
// Tests ReadRemoteDescriptor error with a pending read operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ ReadRemoteDescriptor_ReadPending) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest,
MAYBE_ReadRemoteDescriptor_ReadPending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -698,8 +771,13 @@ TEST_F(BluetoothRemoteGattDescriptorTest,
DISABLED_WriteRemoteDescriptor_WritePending
#endif
// Tests WriteRemoteDescriptor error with a pending write operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ WriteRemoteDescriptor_WritePending) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest,
MAYBE_WriteRemoteDescriptor_WritePending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -736,8 +814,13 @@ TEST_F(BluetoothRemoteGattDescriptorTest,
DISABLED_ReadRemoteDescriptor_WritePending
#endif
// Tests ReadRemoteDescriptor error with a pending write operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ ReadRemoteDescriptor_WritePending) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest,
MAYBE_ReadRemoteDescriptor_WritePending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -773,8 +856,13 @@ TEST_F(BluetoothRemoteGattDescriptorTest,
DISABLED_WriteRemoteDescriptor_ReadPending
#endif
// Tests WriteRemoteDescriptor error with a pending Read operation.
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattDescriptorTestWinrtOnly,
+ WriteRemoteDescriptor_ReadPending) {
+#else
TEST_F(BluetoothRemoteGattDescriptorTest,
MAYBE_WriteRemoteDescriptor_ReadPending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -904,4 +992,11 @@ TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSNumber) {
}
#endif // defined(OS_MACOSX)
+#if defined(OS_WIN)
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ BluetoothRemoteGattDescriptorTestWinrtOnly,
+ ::testing::Values(true));
+#endif // defined(OS_WIN)
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
new file mode 100644
index 00000000000..3c7b806fb77
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
@@ -0,0 +1,339 @@
+// Copyright 2018 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/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/winrt_storage_util.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h"
+#include "device/bluetooth/event_utils_winrt.h"
+
+namespace device {
+
+namespace {
+
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCommunicationStatus;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCommunicationStatus_Success;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattWriteResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDescriptor2;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDescriptor;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattReadResult2;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattReadResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattWriteResult;
+using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Storage::Streams::IBuffer;
+using Microsoft::WRL::ComPtr;
+
+} // namespace
+
+// static
+std::unique_ptr<BluetoothRemoteGattDescriptorWinrt>
+BluetoothRemoteGattDescriptorWinrt::Create(
+ BluetoothRemoteGattCharacteristic* characteristic,
+ ComPtr<IGattDescriptor> descriptor) {
+ DCHECK(descriptor);
+ GUID guid;
+ HRESULT hr = descriptor->get_Uuid(&guid);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting UUID failed: " << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ uint16_t attribute_handle;
+ hr = descriptor->get_AttributeHandle(&attribute_handle);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting AttributeHandle failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ return base::WrapUnique(new BluetoothRemoteGattDescriptorWinrt(
+ characteristic, std::move(descriptor), BluetoothUUID(guid),
+ attribute_handle));
+}
+
+BluetoothRemoteGattDescriptorWinrt::~BluetoothRemoteGattDescriptorWinrt() =
+ default;
+
+std::string BluetoothRemoteGattDescriptorWinrt::GetIdentifier() const {
+ return identifier_;
+}
+
+BluetoothUUID BluetoothRemoteGattDescriptorWinrt::GetUUID() const {
+ return uuid_;
+}
+
+BluetoothGattCharacteristic::Permissions
+BluetoothRemoteGattDescriptorWinrt::GetPermissions() const {
+ NOTIMPLEMENTED();
+ return BluetoothGattCharacteristic::Permissions();
+}
+
+const std::vector<uint8_t>& BluetoothRemoteGattDescriptorWinrt::GetValue()
+ const {
+ return value_;
+}
+
+BluetoothRemoteGattCharacteristic*
+BluetoothRemoteGattDescriptorWinrt::GetCharacteristic() const {
+ return characteristic_;
+}
+
+void BluetoothRemoteGattDescriptorWinrt::ReadRemoteDescriptor(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) {
+ if (pending_read_callbacks_ || pending_write_callbacks_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattReadResult*>> read_value_op;
+ HRESULT hr = descriptor_->ReadValueAsync(&read_value_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattDescriptor::ReadValueAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(read_value_op),
+ base::BindOnce(&BluetoothRemoteGattDescriptorWinrt::OnReadValue,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ pending_read_callbacks_ =
+ std::make_unique<PendingReadCallbacks>(callback, error_callback);
+}
+
+void BluetoothRemoteGattDescriptorWinrt::WriteRemoteDescriptor(
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (pending_read_callbacks_ || pending_write_callbacks_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+ return;
+ }
+
+ ComPtr<IGattDescriptor2> descriptor_2;
+ HRESULT hr = descriptor_.As(&descriptor_2);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IGattDescriptor2 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ ComPtr<IBuffer> buffer;
+ hr = base::win::CreateIBufferFromData(value.data(), value.size(), &buffer);
+ if (FAILED(hr)) {
+ VLOG(2) << "base::win::CreateIBufferFromData failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattWriteResult*>> write_value_op;
+ hr = descriptor_2->WriteValueWithResultAsync(buffer.Get(), &write_value_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GattDescriptor::WriteValueWithResultAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(write_value_op),
+ base::BindOnce(
+ &BluetoothRemoteGattDescriptorWinrt::OnWriteValueWithResult,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothRemoteGattService::GATT_ERROR_FAILED));
+ return;
+ }
+
+ pending_write_callbacks_ =
+ std::make_unique<PendingWriteCallbacks>(callback, error_callback);
+}
+
+IGattDescriptor* BluetoothRemoteGattDescriptorWinrt::GetDescriptorForTesting() {
+ return descriptor_.Get();
+}
+
+BluetoothRemoteGattDescriptorWinrt::PendingReadCallbacks::PendingReadCallbacks(
+ ValueCallback callback,
+ ErrorCallback error_callback)
+ : callback(std::move(callback)),
+ error_callback(std::move(error_callback)) {}
+
+BluetoothRemoteGattDescriptorWinrt::PendingReadCallbacks::
+ ~PendingReadCallbacks() = default;
+
+BluetoothRemoteGattDescriptorWinrt::PendingWriteCallbacks::
+ PendingWriteCallbacks(base::OnceClosure callback,
+ ErrorCallback error_callback)
+ : callback(std::move(callback)),
+ error_callback(std::move(error_callback)) {}
+
+BluetoothRemoteGattDescriptorWinrt::PendingWriteCallbacks::
+ ~PendingWriteCallbacks() = default;
+
+BluetoothRemoteGattDescriptorWinrt::BluetoothRemoteGattDescriptorWinrt(
+ BluetoothRemoteGattCharacteristic* characteristic,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDescriptor>
+ descriptor,
+ BluetoothUUID uuid,
+ uint16_t attribute_handle)
+ : characteristic_(characteristic),
+ descriptor_(std::move(descriptor)),
+ uuid_(std::move(uuid)),
+ identifier_(base::StringPrintf("%s/%s_%04x",
+ characteristic_->GetIdentifier().c_str(),
+ uuid_.value().c_str(),
+ attribute_handle)),
+ weak_ptr_factory_(this) {}
+
+void BluetoothRemoteGattDescriptorWinrt::OnReadValue(
+ ComPtr<IGattReadResult> read_result) {
+ DCHECK(pending_read_callbacks_);
+ auto pending_read_callbacks = std::move(pending_read_callbacks_);
+
+ if (!read_result) {
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ GattCommunicationStatus status;
+ HRESULT hr = read_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting GATT Communication Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ if (status != GattCommunicationStatus_Success) {
+ VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
+ ComPtr<IGattReadResult2> read_result_2;
+ hr = read_result.As(&read_result_2);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IGattReadResult2 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ pending_read_callbacks->error_callback.Run(
+ BluetoothRemoteGattServiceWinrt::GetGattErrorCode(read_result_2.Get()));
+ return;
+ }
+
+ ComPtr<IBuffer> value;
+ hr = read_result->get_Value(&value);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Descriptor Value failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ uint8_t* data = nullptr;
+ uint32_t length = 0;
+ hr = base::win::GetPointerToBufferData(value.Get(), &data, &length);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Pointer To Buffer Data failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_read_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ value_.assign(data, data + length);
+ pending_read_callbacks->callback.Run(value_);
+}
+
+void BluetoothRemoteGattDescriptorWinrt::OnWriteValueWithResult(
+ ComPtr<IGattWriteResult> write_result) {
+ DCHECK(pending_write_callbacks_);
+ auto pending_write_callbacks = std::move(pending_write_callbacks_);
+
+ if (!write_result) {
+ pending_write_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ GattCommunicationStatus status;
+ HRESULT hr = write_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting GATT Communication Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ pending_write_callbacks->error_callback.Run(
+ BluetoothGattService::GATT_ERROR_FAILED);
+ return;
+ }
+
+ if (status != GattCommunicationStatus_Success) {
+ VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
+ pending_write_callbacks->error_callback.Run(
+ BluetoothRemoteGattServiceWinrt::GetGattErrorCode(write_result.Get()));
+ return;
+ }
+
+ std::move(pending_write_callbacks->callback).Run();
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h
new file mode 100644
index 00000000000..1f3324b0935
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h
@@ -0,0 +1,107 @@
+// Copyright 2018 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_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_WINRT_H_
+
+#include <windows.devices.bluetooth.genericattributeprofile.h>
+#include <wrl/client.h>
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattDescriptorWinrt
+ : public BluetoothRemoteGattDescriptor {
+ public:
+ static std::unique_ptr<BluetoothRemoteGattDescriptorWinrt> Create(
+ BluetoothRemoteGattCharacteristic* characteristic,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDescriptor>
+ descriptor);
+ ~BluetoothRemoteGattDescriptorWinrt() override;
+
+ // BluetoothGattDescriptor:
+ std::string GetIdentifier() const override;
+ BluetoothUUID GetUUID() const override;
+ BluetoothGattCharacteristic::Permissions GetPermissions() const override;
+
+ // BluetoothRemoteGattDescriptor:
+ const std::vector<uint8_t>& GetValue() const override;
+ BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
+ void ReadRemoteDescriptor(const ValueCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void WriteRemoteDescriptor(const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDescriptor*
+ GetDescriptorForTesting();
+
+ private:
+ struct PendingReadCallbacks {
+ PendingReadCallbacks(ValueCallback callback, ErrorCallback error_callback);
+ ~PendingReadCallbacks();
+
+ ValueCallback callback;
+ ErrorCallback error_callback;
+ };
+
+ struct PendingWriteCallbacks {
+ PendingWriteCallbacks(base::OnceClosure callback,
+ ErrorCallback error_callback);
+ ~PendingWriteCallbacks();
+
+ base::OnceClosure callback;
+ ErrorCallback error_callback;
+ };
+
+ BluetoothRemoteGattDescriptorWinrt(
+ BluetoothRemoteGattCharacteristic* characteristic,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDescriptor>
+ descriptor,
+ BluetoothUUID uuid,
+ uint16_t attribute_handle);
+
+ void OnReadValue(Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattReadResult> read_result);
+
+ void OnWriteValueWithResult(
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattWriteResult>
+ write_result);
+
+ // Weak. This object is owned by |characteristic_|.
+ BluetoothRemoteGattCharacteristic* characteristic_;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDescriptor>
+ descriptor_;
+ BluetoothUUID uuid_;
+ std::string identifier_;
+ std::vector<uint8_t> value_;
+ std::unique_ptr<PendingReadCallbacks> pending_read_callbacks_;
+ std::unique_ptr<PendingWriteCallbacks> pending_write_callbacks_;
+
+ base::WeakPtrFactory<BluetoothRemoteGattDescriptorWinrt> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattDescriptorWinrt);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_WINRT_H_
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
index 02e770f0f9d..9574c4dd0cc 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
@@ -24,14 +24,21 @@
namespace device {
class BluetoothRemoteGattServiceTest : public BluetoothTest {};
+#if defined(OS_WIN)
+class BluetoothRemoteGattServiceTestWinrt : public BluetoothTestWinrt {};
+#endif
// Android is excluded because it fires a single discovery event per device.
-#if defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_MACOSX)
#define MAYBE_IsDiscoveryComplete IsDiscoveryComplete
#else
#define MAYBE_IsDiscoveryComplete DISABLED_IsDiscoveryComplete
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt, IsDiscoveryComplete) {
+#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_IsDiscoveryComplete) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -51,12 +58,16 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_IsDiscoveryComplete) {
EXPECT_TRUE(service->IsDiscoveryComplete());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetIdentifier GetIdentifier
#else
#define MAYBE_GetIdentifier DISABLED_GetIdentifier
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt, GetIdentifier) {
+#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetIdentifier) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -98,12 +109,16 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetIdentifier) {
EXPECT_NE(service3->GetIdentifier(), service4->GetIdentifier());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetUUID GetUUID
#else
#define MAYBE_GetUUID DISABLED_GetUUID
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt, GetUUID) {
+#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetUUID) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -129,12 +144,16 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetUUID) {
EXPECT_EQ(uuid, device->GetGattServices()[1]->GetUUID());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetCharacteristics_FindNone GetCharacteristics_FindNone
#else
#define MAYBE_GetCharacteristics_FindNone DISABLED_GetCharacteristics_FindNone
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt, GetCharacteristics_FindNone) {
+#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristics_FindNone) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -156,15 +175,20 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristics_FindNone) {
EXPECT_EQ(0u, service->GetCharacteristics().size());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetCharacteristics_and_GetCharacteristic \
GetCharacteristics_and_GetCharacteristic
#else
#define MAYBE_GetCharacteristics_and_GetCharacteristic \
DISABLED_GetCharacteristics_and_GetCharacteristic
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt,
+ GetCharacteristics_and_GetCharacteristic) {
+#else
TEST_F(BluetoothRemoteGattServiceTest,
MAYBE_GetCharacteristics_and_GetCharacteristic) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -191,6 +215,7 @@ TEST_F(BluetoothRemoteGattServiceTest,
SimulateGattCharacteristic(service, kTestUUIDReconnectionAddress,
/* properties */ 0);
+ base::RunLoop().RunUntilIdle();
// Verify that GetCharacteristic can retrieve characteristics again by ID,
// and that the same Characteristics come back.
EXPECT_EQ(4u, service->GetCharacteristics().size());
@@ -215,12 +240,16 @@ TEST_F(BluetoothRemoteGattServiceTest,
service->GetCharacteristic(char_id1));
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetCharacteristicsByUUID GetCharacteristicsByUUID
#else
#define MAYBE_GetCharacteristicsByUUID DISABLED_GetCharacteristicsByUUID
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt, GetCharacteristicsByUUID) {
+#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristicsByUUID) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -247,6 +276,7 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristicsByUUID) {
/* properties */ 0);
SimulateGattCharacteristic(service2, kTestUUIDHeartRateMeasurement,
/* properties */ 0);
+ base::RunLoop().RunUntilIdle();
{
std::vector<BluetoothRemoteGattCharacteristic*> characteristics =
@@ -286,6 +316,7 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristicsByUUID) {
#define MAYBE_GattCharacteristics_ObserversCalls \
DISABLED_GattCharacteristics_ObserversCalls
#endif
+// The GattServicesRemoved event is not implemented for WinRT.
TEST_F(BluetoothRemoteGattServiceTest,
MAYBE_GattCharacteristics_ObserversCalls) {
if (!PlatformSupportsLowEnergy()) {
@@ -344,12 +375,16 @@ TEST_F(BluetoothRemoteGattServiceTest,
EXPECT_EQ(0u, service->GetCharacteristics().size());
}
-#if defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_MACOSX)
#define MAYBE_SimulateGattServiceRemove SimulateGattServiceRemove
#else
#define MAYBE_SimulateGattServiceRemove DISABLED_SimulateGattServiceRemove
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothRemoteGattServiceTestWinrt, SimulateGattServiceRemove) {
+#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_SimulateGattServiceRemove) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -368,6 +403,7 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_SimulateGattServiceRemove) {
SimulateGattServicesDiscovered(
device,
std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, device->GetGattServices().size());
// Simulate remove of a primary service.
@@ -375,7 +411,13 @@ TEST_F(BluetoothRemoteGattServiceTest, MAYBE_SimulateGattServiceRemove) {
BluetoothRemoteGattService* service2 = device->GetGattServices()[1];
std::string removed_service = service1->GetIdentifier();
SimulateGattServiceRemoved(device->GetGattService(removed_service));
- EXPECT_EQ(1, observer.gatt_service_removed_count());
+ base::RunLoop().RunUntilIdle();
+#if defined(OS_WIN)
+ if (!GetParam()) {
+ // The GattServicesRemoved event is not implemented for WinRT.
+ EXPECT_EQ(1, observer.gatt_service_removed_count());
+ }
+#endif // defined(OS_WIN)
EXPECT_EQ(1u, device->GetGattServices().size());
EXPECT_FALSE(device->GetGattService(removed_service));
EXPECT_EQ(device->GetGattServices()[0], service2);
@@ -552,4 +594,11 @@ TEST_F(BluetoothRemoteGattServiceTest, ExtraDidDiscoverCharacteristicsCall) {
}
#endif // defined(OS_MACOSX)
+#if defined(OS_WIN)
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ BluetoothRemoteGattServiceTestWinrt,
+ ::testing::Bool());
+#endif // defined(OS_WIN)
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc
index b2de385570f..0ee0e47ea11 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc
@@ -11,18 +11,15 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
-#include "base/win/scoped_hstring.h"
#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_discoverer_winrt.h"
namespace device {
namespace {
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
- GattDeviceService;
-using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceService;
-using ABI::Windows::Foundation::Collections::IVectorView;
using Microsoft::WRL::ComPtr;
} // namespace
@@ -76,11 +73,74 @@ BluetoothRemoteGattServiceWinrt::GetIncludedServices() const {
return {};
}
+void BluetoothRemoteGattServiceWinrt::UpdateCharacteristics(
+ BluetoothGattDiscovererWinrt* gatt_discoverer) {
+ const auto* gatt_characteristics =
+ gatt_discoverer->GetCharacteristics(attribute_handle_);
+ DCHECK(gatt_characteristics);
+
+ // Instead of clearing out |characteristics_| and creating each characteristic
+ // from scratch, we create a new map and move already existing characteristics
+ // into it in order to preserve pointer stability.
+ CharacteristicMap characteristics;
+ for (const auto& gatt_characteristic : *gatt_characteristics) {
+ auto characteristic = BluetoothRemoteGattCharacteristicWinrt::Create(
+ this, gatt_characteristic.Get());
+ if (!characteristic)
+ continue;
+
+ std::string identifier = characteristic->GetIdentifier();
+ auto iter = characteristics_.find(identifier);
+ if (iter != characteristics_.end()) {
+ iter = characteristics.emplace(std::move(*iter)).first;
+ } else {
+ iter = characteristics
+ .emplace(std::move(identifier), std::move(characteristic))
+ .first;
+ }
+
+ static_cast<BluetoothRemoteGattCharacteristicWinrt*>(iter->second.get())
+ ->UpdateDescriptors(gatt_discoverer);
+ }
+
+ std::swap(characteristics, characteristics_);
+ SetDiscoveryComplete(true);
+}
+
+IGattDeviceService*
+BluetoothRemoteGattServiceWinrt::GetDeviceServiceForTesting() {
+ return gatt_service_.Get();
+}
+
+// static
+uint8_t BluetoothRemoteGattServiceWinrt::ToProtocolError(
+ GattErrorCode error_code) {
+ switch (error_code) {
+ case GATT_ERROR_UNKNOWN:
+ return 0xF0;
+ case GATT_ERROR_FAILED:
+ return 0x01;
+ case GATT_ERROR_IN_PROGRESS:
+ return 0x09;
+ case GATT_ERROR_INVALID_LENGTH:
+ return 0x0D;
+ case GATT_ERROR_NOT_PERMITTED:
+ return 0x02;
+ case GATT_ERROR_NOT_AUTHORIZED:
+ return 0x08;
+ case GATT_ERROR_NOT_PAIRED:
+ return 0x0F;
+ case GATT_ERROR_NOT_SUPPORTED:
+ return 0x06;
+ }
+
+ NOTREACHED();
+ return 0x00;
+}
+
BluetoothRemoteGattServiceWinrt::BluetoothRemoteGattServiceWinrt(
BluetoothDevice* device,
- Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
- GenericAttributeProfile::IGattDeviceService>
- gatt_service,
+ ComPtr<IGattDeviceService> gatt_service,
BluetoothUUID uuid,
uint16_t attribute_handle)
: device_(device),
@@ -90,6 +150,5 @@ BluetoothRemoteGattServiceWinrt::BluetoothRemoteGattServiceWinrt(
identifier_(base::StringPrintf("%s/%s_%04x",
device_->GetIdentifier().c_str(),
uuid_.value().c_str(),
- attribute_handle_)) {}
-
+ attribute_handle)) {}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h
index c04a73afef0..e8ea154df5e 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h
@@ -8,18 +8,22 @@
#include <windows.devices.bluetooth.genericattributeprofile.h>
#include <wrl/client.h>
+#include <stdint.h>
+
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace device {
class BluetoothDevice;
+class BluetoothGattDiscovererWinrt;
class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceWinrt
: public BluetoothRemoteGattService {
@@ -38,6 +42,81 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceWinrt
BluetoothDevice* GetDevice() const override;
std::vector<BluetoothRemoteGattService*> GetIncludedServices() const override;
+ void UpdateCharacteristics(BluetoothGattDiscovererWinrt* gatt_discoverer);
+
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService*
+ GetDeviceServiceForTesting();
+
+ template <typename Interface>
+ static GattErrorCode GetGattErrorCode(Interface* i) {
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IReference<uint8_t>>
+ protocol_error_ref;
+ HRESULT hr = i->get_ProtocolError(&protocol_error_ref);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Protocol Error Reference failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return GattErrorCode::GATT_ERROR_UNKNOWN;
+ }
+
+ if (!protocol_error_ref) {
+ VLOG(2) << "Got Null Protocol Error Reference.";
+ return GattErrorCode::GATT_ERROR_UNKNOWN;
+ }
+
+ uint8_t protocol_error;
+ hr = protocol_error_ref->get_Value(&protocol_error);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Protocol Error Value failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return GattErrorCode::GATT_ERROR_UNKNOWN;
+ }
+
+ VLOG(2) << "Got Protocol Error: " << static_cast<int>(protocol_error);
+
+ // GATT Protocol Errors are described in the Bluetooth Core Specification
+ // Version 5.0 Vol 3, Part F, 3.4.1.1.
+ switch (protocol_error) {
+ case 0x01: // Invalid Handle
+ return GATT_ERROR_FAILED;
+ case 0x02: // Read Not Permitted
+ return GATT_ERROR_NOT_PERMITTED;
+ case 0x03: // Write Not Permitted
+ return GATT_ERROR_NOT_PERMITTED;
+ case 0x04: // Invalid PDU
+ return GATT_ERROR_FAILED;
+ case 0x05: // Insufficient Authentication
+ return GATT_ERROR_NOT_AUTHORIZED;
+ case 0x06: // Request Not Supported
+ return GATT_ERROR_NOT_SUPPORTED;
+ case 0x07: // Invalid Offset
+ return GATT_ERROR_INVALID_LENGTH;
+ case 0x08: // Insufficient Authorization
+ return GATT_ERROR_NOT_AUTHORIZED;
+ case 0x09: // Prepare Queue Full
+ return GATT_ERROR_IN_PROGRESS;
+ case 0x0A: // Attribute Not Found
+ return GATT_ERROR_FAILED;
+ case 0x0B: // Attribute Not Long
+ return GATT_ERROR_FAILED;
+ case 0x0C: // Insufficient Encryption Key Size
+ return GATT_ERROR_FAILED;
+ case 0x0D: // Invalid Attribute Value Length
+ return GATT_ERROR_INVALID_LENGTH;
+ case 0x0E: // Unlikely Error
+ return GATT_ERROR_FAILED;
+ case 0x0F: // Insufficient Encryption
+ return GATT_ERROR_NOT_PAIRED;
+ case 0x10: // Unsupported Group Type
+ return GATT_ERROR_FAILED;
+ case 0x11: // Insufficient Resources
+ return GATT_ERROR_FAILED;
+ default:
+ return GATT_ERROR_UNKNOWN;
+ }
+ }
+
+ static uint8_t ToProtocolError(GattErrorCode error_code);
+
private:
BluetoothRemoteGattServiceWinrt(
BluetoothDevice* device,
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.cc b/chromium/device/bluetooth/bluetooth_task_manager_win.cc
index 2f9f7aeba3a..665c0637b5c 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win.cc
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win.cc
@@ -17,7 +17,7 @@
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
-#include "base/task_scheduler/post_task.h"
+#include "base/task/post_task.h"
#include "device/bluetooth/bluetooth_classic_win.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_init_win.h"
@@ -269,7 +269,7 @@ void BluetoothTaskManagerWin::RemoveObserver(Observer* observer) {
void BluetoothTaskManagerWin::Initialize() {
DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
InitializeWithBluetoothTaskRunner(base::CreateSequencedTaskRunnerWithTraits(
- {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
}
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.h b/chromium/device/bluetooth/bluetooth_task_manager_win.h
index 8a5e82f64d3..7dbbac12a63 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win.h
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win.h
@@ -333,7 +333,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothTaskManagerWin
scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner_;
// List of observers interested in event notifications.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
// indicates whether the adapter is in discovery mode or not.
bool discovering_ = false;
diff --git a/chromium/device/bluetooth/bluetooth_uuid.h b/chromium/device/bluetooth/bluetooth_uuid.h
index c2624148357..36e21c0c6f9 100644
--- a/chromium/device/bluetooth/bluetooth_uuid.h
+++ b/chromium/device/bluetooth/bluetooth_uuid.h
@@ -60,7 +60,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothUUID {
// value to them later. The default constructor will initialize an invalid
// UUID by definition and the string accessors will return an empty string.
BluetoothUUID();
- virtual ~BluetoothUUID();
+ ~BluetoothUUID();
#if defined(OS_WIN)
// The canonical UUID string format is device::BluetoothUUID.value().
diff --git a/chromium/device/bluetooth/bluetooth_uuid_unittest.cc b/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
index 04755198c66..647ff2846f9 100644
--- a/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
@@ -106,7 +106,7 @@ TEST(BluetoothUUIDTest, GetCanonicalValueAsGUID) {
const char kValid128Bit0[] = "12345678-1234-5678-9abc-def123456789";
GUID guid = BluetoothUUID::GetCanonicalValueAsGUID(kValid128Bit0);
- EXPECT_EQ(0x12345678, guid.Data1);
+ EXPECT_EQ(0x12345678u, guid.Data1);
EXPECT_EQ(0x1234, guid.Data2);
EXPECT_EQ(0x5678, guid.Data3);
EXPECT_EQ(0x9a, guid.Data4[0]);
diff --git a/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc
index 81518ba6020..f77cbb6419c 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc
@@ -20,40 +20,16 @@
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_le_advertisement_service_provider.h"
#include "device/bluetooth/dbus/fake_bluetooth_le_advertising_manager_client.h"
+#include "device/bluetooth/test/test_bluetooth_advertisement_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
using device::BluetoothAdapter;
using device::BluetoothAdapterFactory;
using device::BluetoothAdvertisement;
+using device::TestBluetoothAdvertisementObserver;
namespace bluez {
-class TestAdvertisementObserver : public BluetoothAdvertisement::Observer {
- public:
- explicit TestAdvertisementObserver(
- scoped_refptr<BluetoothAdvertisement> advertisement)
- : released_(false), advertisement_(advertisement) {
- advertisement_->AddObserver(this);
- }
-
- ~TestAdvertisementObserver() override {
- advertisement_->RemoveObserver(this);
- }
-
- // BluetoothAdvertisement::Observer overrides:
- void AdvertisementReleased(BluetoothAdvertisement* advertisement) override {
- released_ = true;
- }
-
- bool released() { return released_; }
-
- private:
- bool released_;
- scoped_refptr<BluetoothAdvertisement> advertisement_;
-
- DISALLOW_COPY_AND_ASSIGN(TestAdvertisementObserver);
-};
-
class BluetoothAdvertisementBlueZTest : public testing::Test {
public:
void SetUp() override {
@@ -193,7 +169,7 @@ class BluetoothAdvertisementBlueZTest : public testing::Test {
base::MessageLoopForIO message_loop_;
- std::unique_ptr<TestAdvertisementObserver> observer_;
+ std::unique_ptr<TestBluetoothAdvertisementObserver> observer_;
scoped_refptr<BluetoothAdapter> adapter_;
scoped_refptr<BluetoothAdvertisement> advertisement_;
};
@@ -257,7 +233,7 @@ TEST_F(BluetoothAdvertisementBlueZTest, UnregisterAfterReleasedFailed) {
ExpectSuccess();
EXPECT_TRUE(advertisement);
- observer_.reset(new TestAdvertisementObserver(advertisement));
+ observer_.reset(new TestBluetoothAdvertisementObserver(advertisement));
TriggerReleased(advertisement);
EXPECT_TRUE(observer_->released());
diff --git a/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
index 83ad64a235b..9962628a9fb 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
@@ -30,6 +30,7 @@
#include "device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_input_client.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
+#include "device/bluetooth/test/test_pairing_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
@@ -41,6 +42,7 @@ using device::BluetoothDiscoveryFilter;
using device::BluetoothDiscoverySession;
using device::BluetoothUUID;
using device::TestBluetoothAdapterObserver;
+using device::TestPairingDelegate;
namespace bluez {
@@ -87,88 +89,6 @@ class FakeBluetoothProfileServiceProviderDelegate
} // namespace
-class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
- public:
- TestPairingDelegate()
- : call_count_(0),
- request_pincode_count_(0),
- request_passkey_count_(0),
- display_pincode_count_(0),
- display_passkey_count_(0),
- keys_entered_count_(0),
- confirm_passkey_count_(0),
- authorize_pairing_count_(0),
- last_passkey_(9999999U),
- last_entered_(999U) {}
- ~TestPairingDelegate() override = default;
-
- void RequestPinCode(BluetoothDevice* device) override {
- ++call_count_;
- ++request_pincode_count_;
- QuitMessageLoop();
- }
-
- void RequestPasskey(BluetoothDevice* device) override {
- ++call_count_;
- ++request_passkey_count_;
- QuitMessageLoop();
- }
-
- void DisplayPinCode(BluetoothDevice* device,
- const std::string& pincode) override {
- ++call_count_;
- ++display_pincode_count_;
- last_pincode_ = pincode;
- QuitMessageLoop();
- }
-
- void DisplayPasskey(BluetoothDevice* device, uint32_t passkey) override {
- ++call_count_;
- ++display_passkey_count_;
- last_passkey_ = passkey;
- QuitMessageLoop();
- }
-
- void KeysEntered(BluetoothDevice* device, uint32_t entered) override {
- ++call_count_;
- ++keys_entered_count_;
- last_entered_ = entered;
- QuitMessageLoop();
- }
-
- void ConfirmPasskey(BluetoothDevice* device, uint32_t passkey) override {
- ++call_count_;
- ++confirm_passkey_count_;
- last_passkey_ = passkey;
- QuitMessageLoop();
- }
-
- void AuthorizePairing(BluetoothDevice* device) override {
- ++call_count_;
- ++authorize_pairing_count_;
- QuitMessageLoop();
- }
-
- int call_count_;
- int request_pincode_count_;
- int request_passkey_count_;
- int display_pincode_count_;
- int display_passkey_count_;
- int keys_entered_count_;
- int confirm_passkey_count_;
- int authorize_pairing_count_;
- uint32_t last_passkey_;
- uint32_t last_entered_;
- std::string last_pincode_;
-
- private:
- // Some tests use a message loop since background processing is simulated;
- // break out of those loops.
- void QuitMessageLoop() {
- if (base::RunLoop::IsRunningOnCurrentThread())
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
- }
-};
class BluetoothBlueZTest : public testing::Test {
public:
@@ -2839,6 +2759,43 @@ TEST_F(BluetoothBlueZTest, ConnectDeviceFails) {
EXPECT_EQ(1, fake_bluetooth_adapter_client_->GetUnpauseCount());
}
+// Tests that discovery is unpaused if the device gets removed during a
+// connection.
+TEST_F(BluetoothBlueZTest, RemoveDeviceDuringConnection) {
+ GetAdapter();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != nullptr);
+
+ fake_bluetooth_device_client_->LeaveConnectionsPending();
+ device->Connect(nullptr, GetCallback(),
+ base::Bind(&BluetoothBlueZTest::ConnectErrorCallback,
+ base::Unretained(this)));
+ // We pause discovery before connecting.
+ EXPECT_EQ(1, fake_bluetooth_adapter_client_->GetPauseCount());
+ EXPECT_EQ(0, fake_bluetooth_adapter_client_->GetUnpauseCount());
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Install an observer; expect the DeviceRemoved method to be called
+ // with the device we remove.
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ device->Forget(base::DoNothing(), GetErrorCallback());
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.device_removed_count());
+
+ // If the device gets removed, we should still unpause discovery.
+ EXPECT_EQ(1, fake_bluetooth_adapter_client_->GetPauseCount());
+ EXPECT_EQ(1, fake_bluetooth_adapter_client_->GetUnpauseCount());
+}
+
TEST_F(BluetoothBlueZTest, DisconnectDevice) {
GetAdapter();
diff --git a/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc
index 6f3612d1909..723bb6c4d7f 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc
@@ -200,6 +200,26 @@ BluetoothDeviceBlueZ::~BluetoothDeviceBlueZ() {
adapter()->NotifyGattServiceRemoved(
static_cast<BluetoothRemoteGattServiceBlueZ*>(iter.second.get()));
}
+
+ // We pause discovery when trying to connect. Ensure discovery is unpaused if
+ // we get destroyed during a pending connection.
+ if (IsConnecting()) {
+ BLUETOOTH_LOG(EVENT) << object_path_.value()
+ << ": Unpausing discovery. Device removed.";
+ // Temporarily unpause discovery manually instead of using
+ // UnpauseDiscovery() which was introduced after branch point.
+ // TODO(ortuno): Remove once this is merged to M70.
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->UnpauseDiscovery(
+ adapter()->object_path(), base::Bind([]() {
+ BLUETOOTH_LOG(EVENT) << "Successfully un-paused discovery";
+ }),
+ base::Bind([](const std::string& error_name,
+ const std::string& error_message) {
+ BLUETOOTH_LOG(EVENT) << "Failed to un-pause discovery";
+ }));
+ }
}
uint32_t BluetoothDeviceBlueZ::GetBluetoothClass() const {
diff --git a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc
index c95719c7a84..d0cb1d5a47f 100644
--- a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc
+++ b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/no_destructor.h"
-#include "base/task_scheduler/post_task.h"
+#include "base/task/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chromecast/device/bluetooth/bluetooth_util.h"
#include "chromecast/device/bluetooth/le/gatt_client_manager.h"
diff --git a/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc b/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc
index bf25dfa70a4..20f65b96f6c 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc
@@ -540,7 +540,7 @@ class BluetoothAdapterClientImpl : public BluetoothAdapterClient,
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothAdapterClient::Observer> observers_;
+ base::ObserverList<BluetoothAdapterClient::Observer>::Unchecked observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_device_client.cc b/chromium/device/bluetooth/dbus/bluetooth_device_client.cc
index 4ca96ede4dc..5167d69049d 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_device_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_device_client.cc
@@ -650,7 +650,7 @@ class BluetoothDeviceClientImpl : public BluetoothDeviceClient,
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothDeviceClient::Observer> observers_;
+ base::ObserverList<BluetoothDeviceClient::Observer>::Unchecked observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
index 22ab4de9416..fc3b0c9ab1c 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
@@ -321,7 +321,8 @@ class BluetoothGattCharacteristicClientImpl
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothGattCharacteristicClient::Observer> observers_;
+ base::ObserverList<BluetoothGattCharacteristicClient::Observer>::Unchecked
+ observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
index 813f11eea21..0df8b9c48b3 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
@@ -373,8 +373,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::PrepareWriteValue(
it = options.find(bluetooth_gatt_characteristic::kOptionOffset);
if (it != options.end())
it->second.PopUint16(&offset);
- // TODO(b/78650442): kOptionHasSubsequentWrite
- it = options.find("has-subsequent-write");
+ it = options.find(bluetooth_gatt_characteristic::kOptionHasSubsequentWrite);
if (it != options.end())
it->second.PopBool(&has_subsequent_write);
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
index 685f4535628..a203429d25f 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
@@ -242,7 +242,8 @@ class BluetoothGattDescriptorClientImpl
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothGattDescriptorClient::Observer> observers_;
+ base::ObserverList<BluetoothGattDescriptorClient::Observer>::Unchecked
+ observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_service_client.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_service_client.cc
index f4178a4fa71..232ac02aefb 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_service_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_service_client.cc
@@ -120,7 +120,8 @@ class BluetoothGattServiceClientImpl : public BluetoothGattServiceClient,
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothGattServiceClient::Observer> observers_;
+ base::ObserverList<BluetoothGattServiceClient::Observer>::Unchecked
+ observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_input_client.cc b/chromium/device/bluetooth/dbus/bluetooth_input_client.cc
index b909b0cb205..4b8d8f9f078 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_input_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_input_client.cc
@@ -108,7 +108,7 @@ class BluetoothInputClientImpl : public BluetoothInputClient,
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothInputClient::Observer> observers_;
+ base::ObserverList<BluetoothInputClient::Observer>::Unchecked observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc b/chromium/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc
index 58e9042996f..3af99b95ae7 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_le_advertising_manager_client.cc
@@ -202,7 +202,8 @@ class BluetoothAdvertisementManagerClientImpl
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothLEAdvertisingManagerClient::Observer> observers_;
+ base::ObserverList<BluetoothLEAdvertisingManagerClient::Observer>::Unchecked
+ observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/bluetooth_media_client.cc b/chromium/device/bluetooth/dbus/bluetooth_media_client.cc
index cfa2004f846..39f31d8bedb 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_media_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_media_client.cc
@@ -208,7 +208,7 @@ class BluetoothMediaClientImpl : public BluetoothMediaClient,
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothMediaClient::Observer> observers_;
+ base::ObserverList<BluetoothMediaClient::Observer>::Unchecked observers_;
base::WeakPtrFactory<BluetoothMediaClientImpl> weak_ptr_factory_;
diff --git a/chromium/device/bluetooth/dbus/bluetooth_media_transport_client.cc b/chromium/device/bluetooth/dbus/bluetooth_media_transport_client.cc
index 23eadd067d1..942b508ea81 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_media_transport_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_media_transport_client.cc
@@ -270,7 +270,8 @@ class BluetoothMediaTransportClientImpl
dbus::ObjectManager* object_manager_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothMediaTransportClient::Observer> observers_;
+ base::ObserverList<BluetoothMediaTransportClient::Observer>::Unchecked
+ observers_;
base::WeakPtrFactory<BluetoothMediaTransportClientImpl> weak_ptr_factory_;
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h
index 3c4257d3867..59adc334a6a 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h
@@ -115,7 +115,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothAdapterClient
void PostDelayedTask(base::OnceClosure callback);
// List of observers interested in event notifications from us.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
// Static properties we return.
std::unique_ptr<Properties> properties_;
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc
index 8dfcd20fd17..bfa2304ea18 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -25,7 +25,7 @@
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
-#include "base/task_scheduler/post_task.h"
+#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h"
@@ -318,7 +318,8 @@ FakeBluetoothDeviceClient::FakeBluetoothDeviceClient()
connection_rssi_(kUnkownPower),
transmit_power_(kUnkownPower),
max_transmit_power_(kUnkownPower),
- delay_start_discovery_(false) {
+ delay_start_discovery_(false),
+ should_leave_connections_pending_(false) {
std::unique_ptr<Properties> properties(new Properties(
base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged,
base::Unretained(this), dbus::ObjectPath(kPairedDevicePath))));
@@ -367,6 +368,10 @@ FakeBluetoothDeviceClient::FakeBluetoothDeviceClient()
FakeBluetoothDeviceClient::~FakeBluetoothDeviceClient() = default;
+void FakeBluetoothDeviceClient::LeaveConnectionsPending() {
+ should_leave_connections_pending_ = true;
+}
+
void FakeBluetoothDeviceClient::Init(
dbus::Bus* bus,
const std::string& bluetooth_service_name) {}
@@ -418,6 +423,9 @@ void FakeBluetoothDeviceClient::Connect(const dbus::ObjectPath& object_path,
return;
}
+ if (should_leave_connections_pending_)
+ return;
+
if (properties->paired.value() != true &&
object_path != dbus::ObjectPath(kConnectUnpairablePath) &&
object_path != dbus::ObjectPath(kLowEnergyPath)) {
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h
index 0b0d8e44f65..b483909a9e4 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h
@@ -72,6 +72,9 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothDeviceClient
FakeBluetoothDeviceClient();
~FakeBluetoothDeviceClient() override;
+ // Causes Connect() calls to never finish.
+ void LeaveConnectionsPending();
+
// BluetoothDeviceClient overrides
void Init(dbus::Bus* bus, const std::string& bluetooth_service_name) override;
void AddObserver(Observer* observer) override;
@@ -349,7 +352,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothDeviceClient
BluetoothProfileServiceProvider::Delegate::Status status);
// List of observers interested in event notifications from us.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
using PropertiesMap =
std::map<const dbus::ObjectPath, std::unique_ptr<Properties>>;
@@ -378,6 +381,8 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothDeviceClient
// Pending prepare write requests.
std::vector<std::pair<dbus::ObjectPath, std::vector<uint8_t>>>
prepare_write_requests_;
+
+ bool should_leave_connections_pending_;
};
} // namespace bluez
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
index dbc73e38e04..126bcb7b042 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
@@ -195,7 +195,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattCharacteristicClient
std::map<std::string, DelayedCallback*> action_extra_requests_;
// List of observers interested in event notifications from us.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h
index d67fec623c2..4f22a685cda 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h
@@ -91,7 +91,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattDescriptorClient
PropertiesMap properties_;
// List of observers interested in event notifications from us.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h
index 0969fe70250..cac2a56d2c4 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h
@@ -114,7 +114,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattServiceClient
std::string battery_service_path_;
// List of observers interested in event notifications from us.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_input_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_input_client.h
index a513db0670b..b4bf3db0165 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_input_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_input_client.h
@@ -57,7 +57,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothInputClient
std::map<const dbus::ObjectPath, std::unique_ptr<Properties>> properties_map_;
// List of observers interested in event notifications from us.
- base::ObserverList<Observer> observers_;
+ base::ObserverList<Observer>::Unchecked observers_;
DISALLOW_COPY_AND_ASSIGN(FakeBluetoothInputClient);
};
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_media_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_media_client.h
index 346a2f29c59..99a3e118fdb 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_media_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_media_client.h
@@ -70,7 +70,7 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothMediaClient
endpoints_;
// List of observers interested in event notifications from us.
- base::ObserverList<BluetoothMediaClient::Observer> observers_;
+ base::ObserverList<BluetoothMediaClient::Observer>::Unchecked observers_;
DISALLOW_COPY_AND_ASSIGN(FakeBluetoothMediaClient);
};
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_media_transport_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_media_transport_client.h
index 1b62f7a394c..31fe4f25227 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_media_transport_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_media_transport_client.h
@@ -142,7 +142,8 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothMediaTransportClient
// corresponding endpoint path when GetProperties() is called.
std::map<dbus::ObjectPath, dbus::ObjectPath> transport_to_endpoint_map_;
- base::ObserverList<BluetoothMediaTransportClient::Observer> observers_;
+ base::ObserverList<BluetoothMediaTransportClient::Observer>::Unchecked
+ observers_;
DISALLOW_COPY_AND_ASSIGN(FakeBluetoothMediaTransportClient);
};
diff --git a/chromium/device/bluetooth/event_utils_winrt.h b/chromium/device/bluetooth/event_utils_winrt.h
index ec032232a93..67c1ed213dc 100644
--- a/chromium/device/bluetooth/event_utils_winrt.h
+++ b/chromium/device/bluetooth/event_utils_winrt.h
@@ -123,7 +123,8 @@ HRESULT PostAsyncResults(
// Convenience template function to construct a TypedEventHandler from a
// base::RepeatingCallback of a matching signature. In case of success, the
// EventRegistrationToken is returned to the caller. A return value of
-// base::nullopt indicates a failure.
+// base::nullopt indicates a failure. Events are posted to the same thread the
+// event handler was created on.
template <typename Interface,
typename Sender,
typename Args,
@@ -133,17 +134,24 @@ base::Optional<EventRegistrationToken> AddTypedEventHandler(
Interface* i,
internal::IMemberFunction<
Interface,
- ABI::Windows::Foundation::ITypedEventHandler<Sender, Args>*,
+ ABI::Windows::Foundation::ITypedEventHandler<Sender*, Args*>*,
EventRegistrationToken*> function,
- base::RepeatingCallback<void(SenderAbi, ArgsAbi)> callback) {
+ base::RepeatingCallback<void(SenderAbi*, ArgsAbi*)> callback) {
EventRegistrationToken token;
HRESULT hr = ((*i).*function)(
Microsoft::WRL::Callback<
- ABI::Windows::Foundation::ITypedEventHandler<Sender, Args>>(
- [callback](SenderAbi sender, ArgsAbi args) {
- callback.Run(std::move(sender), std::move(args));
- return S_OK;
- })
+ ABI::Windows::Foundation::ITypedEventHandler<Sender*, Args*>>([
+ task_runner(base::ThreadTaskRunnerHandle::Get()),
+ callback(std::move(callback))
+ ](SenderAbi * sender, ArgsAbi * args) {
+ // Make sure we are still on the same thread.
+ DCHECK_EQ(base::ThreadTaskRunnerHandle::Get(), task_runner);
+ task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(callback, Microsoft::WRL::ComPtr<SenderAbi>(sender),
+ Microsoft::WRL::ComPtr<ArgsAbi>(args)));
+ return S_OK;
+ })
.Get(),
&token);
diff --git a/chromium/device/fido/BUILD.gn b/chromium/device/fido/BUILD.gn
index 369ac7d8522..c475300986d 100644
--- a/chromium/device/fido/BUILD.gn
+++ b/chromium/device/fido/BUILD.gn
@@ -27,6 +27,29 @@ component("fido") {
"authenticator_selection_criteria.h",
"authenticator_supported_options.cc",
"authenticator_supported_options.h",
+ "ble/fido_ble_connection.cc",
+ "ble/fido_ble_connection.h",
+ "ble/fido_ble_device.cc",
+ "ble/fido_ble_device.h",
+ "ble/fido_ble_discovery.cc",
+ "ble/fido_ble_discovery.h",
+ "ble/fido_ble_discovery_base.cc",
+ "ble/fido_ble_discovery_base.h",
+ "ble/fido_ble_frames.cc",
+ "ble/fido_ble_frames.h",
+ "ble/fido_ble_transaction.cc",
+ "ble/fido_ble_transaction.h",
+ "ble/fido_ble_uuids.cc",
+ "ble/fido_ble_uuids.h",
+ "ble_adapter_power_manager.cc",
+ "ble_adapter_power_manager.h",
+ "cable/cable_discovery_data.h",
+ "cable/fido_cable_device.cc",
+ "cable/fido_cable_device.h",
+ "cable/fido_cable_discovery.cc",
+ "cable/fido_cable_discovery.h",
+ "cable/fido_cable_handshake_handler.cc",
+ "cable/fido_cable_handshake_handler.h",
"ctap2_device_operation.h",
"ctap_empty_authenticator_request.cc",
"ctap_empty_authenticator_request.h",
@@ -40,26 +63,6 @@ component("fido") {
"ec_public_key.cc",
"ec_public_key.h",
"fido_authenticator.h",
- "fido_ble_connection.cc",
- "fido_ble_connection.h",
- "fido_ble_device.cc",
- "fido_ble_device.h",
- "fido_ble_discovery.cc",
- "fido_ble_discovery.h",
- "fido_ble_discovery_base.cc",
- "fido_ble_discovery_base.h",
- "fido_ble_frames.cc",
- "fido_ble_frames.h",
- "fido_ble_transaction.cc",
- "fido_ble_transaction.h",
- "fido_ble_uuids.cc",
- "fido_ble_uuids.h",
- "fido_cable_device.cc",
- "fido_cable_device.h",
- "fido_cable_discovery.cc",
- "fido_cable_discovery.h",
- "fido_cable_handshake_handler.cc",
- "fido_cable_handshake_handler.h",
"fido_constants.cc",
"fido_constants.h",
"fido_device.cc",
@@ -68,10 +71,6 @@ component("fido") {
"fido_device_authenticator.h",
"fido_discovery.cc",
"fido_discovery.h",
- "fido_hid_message.cc",
- "fido_hid_message.h",
- "fido_hid_packet.cc",
- "fido_hid_packet.h",
"fido_parsing_utils.cc",
"fido_parsing_utils.h",
"fido_request_handler.h",
@@ -79,11 +78,16 @@ component("fido") {
"fido_request_handler_base.h",
"fido_task.cc",
"fido_task.h",
+ "fido_transport_protocol.cc",
"fido_transport_protocol.h",
"get_assertion_request_handler.cc",
"get_assertion_request_handler.h",
"get_assertion_task.cc",
"get_assertion_task.h",
+ "hid/fido_hid_message.cc",
+ "hid/fido_hid_message.h",
+ "hid/fido_hid_packet.cc",
+ "hid/fido_hid_packet.h",
"make_credential_request_handler.cc",
"make_credential_request_handler.h",
"make_credential_task.cc",
@@ -125,9 +129,11 @@ component("fido") {
"//components/cbor",
"//crypto",
"//device/base",
+ "//device/fido/strings",
"//services/service_manager/public/cpp",
"//services/service_manager/public/mojom",
"//third_party/boringssl",
+ "//ui/base",
]
public_deps = [
@@ -141,10 +147,10 @@ component("fido") {
# HID is not supported on Android.
if (!is_android) {
sources += [
- "fido_hid_device.cc",
- "fido_hid_device.h",
- "fido_hid_discovery.cc",
- "fido_hid_discovery.h",
+ "hid/fido_hid_device.cc",
+ "hid/fido_hid_device.h",
+ "hid/fido_hid_discovery.cc",
+ "hid/fido_hid_discovery.h",
]
deps += [
@@ -187,8 +193,8 @@ source_set("mocks") {
testonly = true
sources = [
- "mock_fido_ble_connection.cc",
- "mock_fido_ble_connection.h",
+ "ble/mock_fido_ble_connection.cc",
+ "ble/mock_fido_ble_connection.h",
"mock_fido_device.cc",
"mock_fido_device.h",
"mock_fido_discovery_observer.cc",
@@ -205,7 +211,7 @@ source_set("mocks") {
fuzzer_test("fido_hid_message_fuzzer") {
sources = [
- "fido_hid_message_fuzzer.cc",
+ "hid/fido_hid_message_fuzzer.cc",
]
deps = [
":fido",
@@ -216,7 +222,7 @@ fuzzer_test("fido_hid_message_fuzzer") {
fuzzer_test("fido_ble_frames_fuzzer") {
sources = [
- "fido_ble_frames_fuzzer.cc",
+ "ble/fido_ble_frames_fuzzer.cc",
]
deps = [
":fido",
@@ -239,11 +245,14 @@ fuzzer_test("ctap_response_fuzzer") {
fuzzer_test("fido_cable_handshake_handler_fuzzer") {
sources = [
- "fido_cable_handshake_handler_fuzzer.cc",
+ "cable/fido_cable_handshake_handler_fuzzer.cc",
]
deps = [
":fido",
"//base",
+ "//device/bluetooth:mocks",
+ "//testing/gmock",
+ "//testing/gtest",
]
libfuzzer_options = [ "max_len=2048" ]
}
@@ -272,8 +281,19 @@ source_set("test_support") {
# Android doesn't compile. Linux requires udev.
if (!is_linux_without_udev && !is_android) {
sources += [
- "fake_hid_impl_for_testing.cc",
- "fake_hid_impl_for_testing.h",
+ "hid/fake_hid_impl_for_testing.cc",
+ "hid/fake_hid_impl_for_testing.h",
+ ]
+ }
+
+ if (is_mac) {
+ sources += [
+ "mac/fake_keychain.h",
+ "mac/fake_keychain.mm",
+ "mac/fake_touch_id_context.h",
+ "mac/fake_touch_id_context.mm",
+ "mac/scoped_touch_id_test_environment.h",
+ "mac/scoped_touch_id_test_environment.mm",
]
}
}
diff --git a/chromium/device/fido/DEPS b/chromium/device/fido/DEPS
index 37d845668ad..3d3a449eff9 100644
--- a/chromium/device/fido/DEPS
+++ b/chromium/device/fido/DEPS
@@ -4,5 +4,6 @@ include_rules = [
"+crypto",
"+net/base",
"+net/cert",
+ "+ui/base/l10n",
"+third_party/boringssl/src/include",
]
diff --git a/chromium/device/fido/attestation_object.cc b/chromium/device/fido/attestation_object.cc
index 1090e09e9e4..217ee4d6c33 100644
--- a/chromium/device/fido/attestation_object.cc
+++ b/chromium/device/fido/attestation_object.cc
@@ -39,19 +39,20 @@ void AttestationObject::EraseAttestationStatement() {
#if DCHECK_IS_ON()
if (!authenticator_data_.attested_data())
return;
-
- std::vector<uint8_t> auth_data = authenticator_data_.SerializeToByteArray();
- // See diagram at https://w3c.github.io/webauthn/#sctn-attestation
- constexpr size_t kAaguidOffset =
- 32 /* RP ID hash */ + 1 /* flags */ + 4 /* signature counter */;
- constexpr size_t kAaguidSize = 16;
- DCHECK_GE(auth_data.size(), kAaguidOffset + kAaguidSize);
- DCHECK(std::all_of(auth_data.data() + kAaguidOffset,
- auth_data.data() + kAaguidOffset + kAaguidSize,
- [](uint8_t v) { return v == 0; }));
+ DCHECK(authenticator_data_.attested_data()->IsAaguidZero());
#endif
}
+bool AttestationObject::IsSelfAttestation() {
+ if (!attestation_statement_->IsSelfAttestation()) {
+ return false;
+ }
+ // Self-attestation also requires that the AAGUID be zero. See
+ // https://www.w3.org/TR/webauthn/#createCredential.
+ return !authenticator_data_.attested_data() ||
+ authenticator_data_.attested_data()->IsAaguidZero();
+}
+
bool AttestationObject::IsAttestationCertificateInappropriatelyIdentifying() {
return attestation_statement_
->IsAttestationCertificateInappropriatelyIdentifying();
diff --git a/chromium/device/fido/attestation_object.h b/chromium/device/fido/attestation_object.h
index bde70e6d495..36f463d8493 100644
--- a/chromium/device/fido/attestation_object.h
+++ b/chromium/device/fido/attestation_object.h
@@ -41,6 +41,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationObject {
// https://w3c.github.io/webauthn/#createCredential.
void EraseAttestationStatement();
+ // Returns true if the attestation is a "self" attestation, i.e. is just the
+ // private key signing itself to show that it is fresh. See
+ // https://www.w3.org/TR/webauthn/#self-attestation. Note that self-
+ // attestation also requires at the AAGUID in the authenticator data be all
+ // zeros.
+ bool IsSelfAttestation();
+
// Returns true if the attestation certificate is known to be inappropriately
// identifying. Some tokens return unique attestation certificates even when
// the bit to request that is not set. (Normal attestation certificates are
diff --git a/chromium/device/fido/attestation_statement.cc b/chromium/device/fido/attestation_statement.cc
index e03c4997685..fea6e30d3c7 100644
--- a/chromium/device/fido/attestation_statement.cc
+++ b/chromium/device/fido/attestation_statement.cc
@@ -26,6 +26,10 @@ bool NoneAttestationStatement::
return false;
}
+bool NoneAttestationStatement::IsSelfAttestation() {
+ return false;
+}
+
cbor::CBORValue::MapValue NoneAttestationStatement::GetAsCBORMap() const {
return cbor::CBORValue::MapValue();
}
diff --git a/chromium/device/fido/attestation_statement.h b/chromium/device/fido/attestation_statement.h
index f7e0621a0c8..2be4288d516 100644
--- a/chromium/device/fido/attestation_statement.h
+++ b/chromium/device/fido/attestation_statement.h
@@ -31,6 +31,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationStatement {
// nested within another CBOR object and encoded then.
virtual cbor::CBORValue::MapValue GetAsCBORMap() const = 0;
+ // Returns true if the attestation is a "self" attestation, i.e. is just the
+ // private key signing itself to show that it is fresh.
+ virtual bool IsSelfAttestation() = 0;
+
// Returns true if the attestation is known to be inappropriately identifying.
// Some tokens return unique attestation certificates even when the bit to
// request that is not set. (Normal attestation certificates are not
@@ -41,10 +45,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationStatement {
protected:
explicit AttestationStatement(std::string format);
-
- private:
const std::string format_;
+ private:
DISALLOW_COPY_AND_ASSIGN(AttestationStatement);
};
@@ -57,6 +60,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) NoneAttestationStatement
NoneAttestationStatement();
~NoneAttestationStatement() override;
+ bool IsSelfAttestation() override;
bool IsAttestationCertificateInappropriatelyIdentifying() override;
cbor::CBORValue::MapValue GetAsCBORMap() const override;
diff --git a/chromium/device/fido/attestation_statement_formats.cc b/chromium/device/fido/attestation_statement_formats.cc
index e3f19693aa7..5b7cde84784 100644
--- a/chromium/device/fido/attestation_statement_formats.cc
+++ b/chromium/device/fido/attestation_statement_formats.cc
@@ -135,6 +135,10 @@ cbor::CBORValue::MapValue FidoAttestationStatement::GetAsCBORMap() const {
return attestation_statement_map;
}
+bool FidoAttestationStatement::IsSelfAttestation() {
+ return false;
+}
+
bool FidoAttestationStatement::
IsAttestationCertificateInappropriatelyIdentifying() {
// An attestation certificate is considered inappropriately identifying if it
@@ -182,6 +186,10 @@ cbor::CBORValue::MapValue PackedAttestationStatement::GetAsCBORMap() const {
return attestation_statement_map;
}
+bool PackedAttestationStatement::IsSelfAttestation() {
+ return x509_certificates_.empty();
+}
+
bool PackedAttestationStatement::
IsAttestationCertificateInappropriatelyIdentifying() {
for (const auto& der_bytes : x509_certificates_) {
diff --git a/chromium/device/fido/attestation_statement_formats.h b/chromium/device/fido/attestation_statement_formats.h
index fb16d88cd40..64de004d600 100644
--- a/chromium/device/fido/attestation_statement_formats.h
+++ b/chromium/device/fido/attestation_statement_formats.h
@@ -31,6 +31,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAttestationStatement
// AttestationStatement
cbor::CBORValue::MapValue GetAsCBORMap() const override;
+ bool IsSelfAttestation() override;
bool IsAttestationCertificateInappropriatelyIdentifying() override;
private:
@@ -56,6 +57,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PackedAttestationStatement
// AttestationStatement
cbor::CBORValue::MapValue GetAsCBORMap() const override;
+ bool IsSelfAttestation() override;
bool IsAttestationCertificateInappropriatelyIdentifying() override;
private:
diff --git a/chromium/device/fido/attested_credential_data.cc b/chromium/device/fido/attested_credential_data.cc
index 70614dfca93..5b93d8cd8d9 100644
--- a/chromium/device/fido/attested_credential_data.cc
+++ b/chromium/device/fido/attested_credential_data.cc
@@ -91,6 +91,11 @@ AttestedCredentialData& AttestedCredentialData::operator=(
AttestedCredentialData::~AttestedCredentialData() = default;
+bool AttestedCredentialData::IsAaguidZero() const {
+ return std::all_of(aaguid_.begin(), aaguid_.end(),
+ [](uint8_t v) { return v == 0; });
+}
+
void AttestedCredentialData::DeleteAaguid() {
std::fill(aaguid_.begin(), aaguid_.end(), 0);
}
diff --git a/chromium/device/fido/attested_credential_data.h b/chromium/device/fido/attested_credential_data.h
index 26048860c0d..73ed9f5e00a 100644
--- a/chromium/device/fido/attested_credential_data.h
+++ b/chromium/device/fido/attested_credential_data.h
@@ -38,6 +38,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestedCredentialData {
const std::vector<uint8_t>& credential_id() const { return credential_id_; }
+ // Returns true iff the AAGUID is all zero bytes.
+ bool IsAaguidZero() const;
+
// Invoked when sending "none" attestation statement to the relying party.
// Replaces AAGUID with zero bytes.
void DeleteAaguid();
diff --git a/chromium/device/fido/authenticator_make_credential_response.cc b/chromium/device/fido/authenticator_make_credential_response.cc
index fd6576e092e..bd79c114b20 100644
--- a/chromium/device/fido/authenticator_make_credential_response.cc
+++ b/chromium/device/fido/authenticator_make_credential_response.cc
@@ -78,6 +78,10 @@ void AuthenticatorMakeCredentialResponse::EraseAttestationStatement() {
attestation_object_.EraseAttestationStatement();
}
+bool AuthenticatorMakeCredentialResponse::IsSelfAttestation() {
+ return attestation_object_.IsSelfAttestation();
+}
+
bool AuthenticatorMakeCredentialResponse::
IsAttestationCertificateInappropriatelyIdentifying() {
return attestation_object_
diff --git a/chromium/device/fido/authenticator_make_credential_response.h b/chromium/device/fido/authenticator_make_credential_response.h
index dc9a6e7438a..27e1ed33d22 100644
--- a/chromium/device/fido/authenticator_make_credential_response.h
+++ b/chromium/device/fido/authenticator_make_credential_response.h
@@ -46,6 +46,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse
// https://w3c.github.io/webauthn/#createCredential
void EraseAttestationStatement();
+ // Returns true if the attestation is a "self" attestation, i.e. is just the
+ // private key signing itself to show that it is fresh and the AAGUID is zero.
+ bool IsSelfAttestation();
+
// Returns true if the attestation certificate is known to be inappropriately
// identifying. Some tokens return unique attestation certificates even when
// the bit to request that is not set. (Normal attestation certificates are
diff --git a/chromium/device/fido/ble/fido_ble_connection.cc b/chromium/device/fido/ble/fido_ble_connection.cc
new file mode 100644
index 00000000000..cb56324f4bc
--- /dev/null
+++ b/chromium/device/fido/ble/fido_ble_connection.cc
@@ -0,0 +1,518 @@
+// 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/fido/ble/fido_ble_connection.h"
+
+#include <algorithm>
+#include <ostream>
+#include <utility>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.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/fido/ble/fido_ble_uuids.h"
+
+namespace device {
+
+namespace {
+
+using ServiceRevisionsCallback =
+ base::OnceCallback<void(std::vector<FidoBleConnection::ServiceRevision>)>;
+
+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 "";
+ }
+}
+
+std::ostream& operator<<(std::ostream& os,
+ FidoBleConnection::ServiceRevision revision) {
+ switch (revision) {
+ case FidoBleConnection::ServiceRevision::kU2f11:
+ return os << "U2F 1.1";
+ case FidoBleConnection::ServiceRevision::kU2f12:
+ return os << "U2F 1.2";
+ case FidoBleConnection::ServiceRevision::kFido2:
+ return os << "FIDO2";
+ }
+
+ NOTREACHED();
+ return os;
+}
+
+const BluetoothRemoteGattService* GetFidoService(
+ const BluetoothDevice* device) {
+ if (!device) {
+ LOG(ERROR) << "No device present.";
+ return nullptr;
+ }
+
+ for (const auto* service : device->GetGattServices()) {
+ if (service->GetUUID() == BluetoothUUID(kFidoServiceUUID))
+ return service;
+ }
+
+ LOG(ERROR) << "No Fido service present.";
+ return nullptr;
+}
+
+void OnWriteRemoteCharacteristic(FidoBleConnection::WriteCallback callback) {
+ VLOG(2) << "Writing Remote Characteristic Succeeded.";
+ std::move(callback).Run(true);
+}
+
+void OnWriteRemoteCharacteristicError(
+ FidoBleConnection::WriteCallback callback,
+ BluetoothGattService::GattErrorCode error_code) {
+ LOG(ERROR) << "Writing Remote Characteristic Failed: "
+ << ToString(error_code);
+ std::move(callback).Run(false);
+}
+
+void OnReadServiceRevisionBitfield(ServiceRevisionsCallback callback,
+ const std::vector<uint8_t>& value) {
+ if (value.empty()) {
+ VLOG(2) << "Service Revision Bitfield is empty.";
+ std::move(callback).Run({});
+ return;
+ }
+
+ if (value.size() != 1u) {
+ VLOG(2) << "Service Revision Bitfield has unexpected size: " << value.size()
+ << ". Ignoring all but the first byte.";
+ }
+
+ const uint8_t bitset = value[0];
+ if (bitset & 0x1F) {
+ VLOG(2) << "Service Revision Bitfield has unexpected bits set: "
+ << base::StringPrintf("0x%02X", bitset)
+ << ". Ignoring all but the first three bits.";
+ }
+
+ std::vector<FidoBleConnection::ServiceRevision> service_revisions;
+ for (auto revision : {FidoBleConnection::ServiceRevision::kU2f11,
+ FidoBleConnection::ServiceRevision::kU2f12,
+ FidoBleConnection::ServiceRevision::kFido2}) {
+ if (bitset & static_cast<uint8_t>(revision)) {
+ VLOG(2) << "Detected Support for " << revision << ".";
+ service_revisions.push_back(revision);
+ }
+ }
+
+ std::move(callback).Run(std::move(service_revisions));
+}
+
+void OnReadServiceRevisionBitfieldError(
+ ServiceRevisionsCallback callback,
+ BluetoothGattService::GattErrorCode error_code) {
+ LOG(ERROR) << "Error while reading Service Revision Bitfield: "
+ << ToString(error_code);
+ std::move(callback).Run({});
+}
+
+} // namespace
+
+FidoBleConnection::FidoBleConnection(
+ BluetoothAdapter* adapter,
+ std::string device_address,
+ ReadCallback read_callback)
+ : adapter_(adapter),
+ address_(std::move(device_address)),
+ read_callback_(std::move(read_callback)),
+ weak_factory_(this) {
+ DCHECK(adapter_);
+ adapter_->AddObserver(this);
+ DCHECK(!address_.empty());
+}
+
+FidoBleConnection::~FidoBleConnection() {
+ adapter_->RemoveObserver(this);
+}
+
+BluetoothDevice* FidoBleConnection::GetBleDevice() {
+ return adapter_->GetDevice(address());
+}
+
+const BluetoothDevice* FidoBleConnection::GetBleDevice() const {
+ return adapter_->GetDevice(address());
+}
+
+FidoBleConnection::FidoBleConnection(BluetoothAdapter* adapter,
+ std::string device_address)
+ : adapter_(adapter),
+ address_(std::move(device_address)),
+ weak_factory_(this) {
+ adapter_->AddObserver(this);
+}
+
+void FidoBleConnection::Connect(ConnectionCallback callback) {
+ auto* device = GetBleDevice();
+ if (!device) {
+ LOG(ERROR) << "Failed to get Device.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), false));
+ return;
+ }
+
+ pending_connection_callback_ = std::move(callback);
+ device->CreateGattConnection(
+ base::Bind(&FidoBleConnection::OnCreateGattConnection,
+ weak_factory_.GetWeakPtr()),
+ base::Bind(&FidoBleConnection::OnCreateGattConnectionError,
+ weak_factory_.GetWeakPtr()));
+}
+
+void FidoBleConnection::ReadControlPointLength(
+ ControlPointLengthCallback callback) {
+ const auto* fido_service = GetFidoService(GetBleDevice());
+ if (!fido_service) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+ return;
+ }
+
+ if (!control_point_length_id_) {
+ LOG(ERROR) << "Failed to get Control Point Length.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+ return;
+ }
+
+ BluetoothRemoteGattCharacteristic* control_point_length =
+ fido_service->GetCharacteristic(*control_point_length_id_);
+ if (!control_point_length) {
+ LOG(ERROR) << "No Control Point Length characteristic present.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+ return;
+ }
+
+ // Work around legacy APIs. Only one of the callbacks to
+ // ReadRemoteCharacteristic() gets invoked, but we don't know which one.
+ auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+ control_point_length->ReadRemoteCharacteristic(
+ base::Bind(OnReadControlPointLength, copyable_callback),
+ base::Bind(OnReadControlPointLengthError, copyable_callback));
+}
+
+void FidoBleConnection::WriteControlPoint(const std::vector<uint8_t>& data,
+ WriteCallback callback) {
+ const auto* fido_service = GetFidoService(GetBleDevice());
+ if (!fido_service) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), false));
+ return;
+ }
+
+ if (!control_point_id_) {
+ LOG(ERROR) << "Failed to get Control Point.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), false));
+ return;
+ }
+
+ BluetoothRemoteGattCharacteristic* control_point =
+ fido_service->GetCharacteristic(*control_point_id_);
+ if (!control_point) {
+ LOG(ERROR) << "Control Point characteristic not present.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), false));
+ return;
+ }
+
+#if defined(OS_MACOSX)
+ // Attempt a write without response for performance reasons. Fall back to a
+ // confirmed write in case of failure, e.g. when the characteristic does not
+ // provide the required property.
+ if (control_point->WriteWithoutResponse(data)) {
+ VLOG(2) << "Write without response succeeded.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), true));
+ return;
+ }
+#endif // defined(OS_MACOSX)
+
+ auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+ control_point->WriteRemoteCharacteristic(
+ data, base::Bind(OnWriteRemoteCharacteristic, copyable_callback),
+ base::Bind(OnWriteRemoteCharacteristicError, copyable_callback));
+}
+
+void FidoBleConnection::OnCreateGattConnection(
+ std::unique_ptr<BluetoothGattConnection> connection) {
+ DCHECK(pending_connection_callback_);
+ connection_ = std::move(connection);
+
+ BluetoothDevice* device = adapter_->GetDevice(address_);
+ if (!device) {
+ LOG(ERROR) << "Failed to get Device.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(pending_connection_callback_), false));
+ return;
+ }
+
+ if (device->IsGattServicesDiscoveryComplete())
+ ConnectToFidoService();
+ else
+ waiting_for_gatt_discovery_ = true;
+}
+
+void FidoBleConnection::OnCreateGattConnectionError(
+ BluetoothDevice::ConnectErrorCode error_code) {
+ DCHECK(pending_connection_callback_);
+ LOG(ERROR) << "CreateGattConnection() failed: " << ToString(error_code);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(pending_connection_callback_), false));
+}
+
+void FidoBleConnection::ConnectToFidoService() {
+ DCHECK(pending_connection_callback_);
+ const auto* fido_service = GetFidoService(GetBleDevice());
+ if (!fido_service) {
+ LOG(ERROR) << "Failed to get Fido Service.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(pending_connection_callback_), false));
+ return;
+ }
+
+ for (const auto* characteristic : fido_service->GetCharacteristics()) {
+ std::string uuid = characteristic->GetUUID().canonical_value();
+ if (uuid == kFidoControlPointLengthUUID) {
+ control_point_length_id_ = characteristic->GetIdentifier();
+ VLOG(2) << "Got Fido Control Point Length: " << *control_point_length_id_;
+ continue;
+ }
+
+ if (uuid == kFidoControlPointUUID) {
+ control_point_id_ = characteristic->GetIdentifier();
+ VLOG(2) << "Got Fido Control Point: " << *control_point_id_;
+ continue;
+ }
+
+ if (uuid == kFidoStatusUUID) {
+ status_id_ = characteristic->GetIdentifier();
+ VLOG(2) << "Got Fido Status: " << *status_id_;
+ continue;
+ }
+
+ if (uuid == kFidoServiceRevisionUUID) {
+ service_revision_id_ = characteristic->GetIdentifier();
+ VLOG(2) << "Got Fido Service Revision: " << *service_revision_id_;
+ continue;
+ }
+
+ if (uuid == kFidoServiceRevisionBitfieldUUID) {
+ service_revision_bitfield_id_ = characteristic->GetIdentifier();
+ VLOG(2) << "Got Fido Service Revision Bitfield: "
+ << *service_revision_bitfield_id_;
+ }
+ }
+
+ if (!control_point_length_id_ || !control_point_id_ || !status_id_ ||
+ (!service_revision_id_ && !service_revision_bitfield_id_)) {
+ LOG(ERROR) << "Fido Characteristics missing.";
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(pending_connection_callback_), false));
+ return;
+ }
+
+ // In case the bitfield characteristic is present, the client has to select a
+ // supported bersion by writing the corresponding bit. Reference:
+ // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#ble-protocol-overview
+ if (service_revision_bitfield_id_) {
+ auto callback = base::Bind(&FidoBleConnection::OnReadServiceRevisions,
+ weak_factory_.GetWeakPtr());
+ fido_service->GetCharacteristic(*service_revision_bitfield_id_)
+ ->ReadRemoteCharacteristic(
+ base::Bind(OnReadServiceRevisionBitfield, callback),
+ base::Bind(OnReadServiceRevisionBitfieldError, callback));
+ return;
+ }
+
+ StartNotifySession();
+}
+
+void FidoBleConnection::OnReadServiceRevisions(
+ std::vector<ServiceRevision> service_revisions) {
+ DCHECK(pending_connection_callback_);
+ if (service_revisions.empty()) {
+ LOG(ERROR) << "Could not obtain Service Revisions.";
+ std::move(pending_connection_callback_).Run(false);
+ return;
+ }
+
+ // Write the most recent supported service revision back to the
+ // characteristic. Note that this information is currently not used in another
+ // way, as we will still attempt a CTAP GetInfo() command, even if only U2F is
+ // supported.
+ // TODO(https://crbug.com/780078): Consider short circuiting to the
+ // U2F logic if FIDO2 is not supported.
+ DCHECK_EQ(
+ *std::min_element(service_revisions.begin(), service_revisions.end()),
+ service_revisions.back());
+ WriteServiceRevision(service_revisions.back());
+}
+
+void FidoBleConnection::WriteServiceRevision(ServiceRevision service_revision) {
+ auto callback = base::BindOnce(&FidoBleConnection::OnServiceRevisionWritten,
+ weak_factory_.GetWeakPtr());
+
+ const auto* fido_service = GetFidoService(GetBleDevice());
+ if (!fido_service) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), false));
+ return;
+ }
+
+ auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+ DCHECK(service_revision_bitfield_id_);
+ fido_service->GetCharacteristic(*service_revision_bitfield_id_)
+ ->WriteRemoteCharacteristic(
+ {static_cast<uint8_t>(service_revision)},
+ base::Bind(OnWriteRemoteCharacteristic, copyable_callback),
+ base::Bind(OnWriteRemoteCharacteristicError, copyable_callback));
+}
+
+void FidoBleConnection::OnServiceRevisionWritten(bool success) {
+ DCHECK(pending_connection_callback_);
+ if (success) {
+ StartNotifySession();
+ return;
+ }
+
+ std::move(pending_connection_callback_).Run(false);
+}
+
+void FidoBleConnection::StartNotifySession() {
+ DCHECK(pending_connection_callback_);
+ const auto* fido_service = GetFidoService(GetBleDevice());
+ if (!fido_service) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(pending_connection_callback_), false));
+ return;
+ }
+
+ DCHECK(status_id_);
+ fido_service->GetCharacteristic(*status_id_)
+ ->StartNotifySession(
+ base::Bind(&FidoBleConnection::OnStartNotifySession,
+ weak_factory_.GetWeakPtr()),
+ base::Bind(&FidoBleConnection::OnStartNotifySessionError,
+ weak_factory_.GetWeakPtr()));
+}
+
+void FidoBleConnection::OnStartNotifySession(
+ std::unique_ptr<BluetoothGattNotifySession> notify_session) {
+ notify_session_ = std::move(notify_session);
+ VLOG(2) << "Created notification session. Connection established.";
+ std::move(pending_connection_callback_).Run(true);
+}
+
+void FidoBleConnection::OnStartNotifySessionError(
+ BluetoothGattService::GattErrorCode error_code) {
+ LOG(ERROR) << "StartNotifySession() failed: " << ToString(error_code);
+ std::move(pending_connection_callback_).Run(false);
+}
+
+void FidoBleConnection::GattCharacteristicValueChanged(
+ BluetoothAdapter* adapter,
+ BluetoothRemoteGattCharacteristic* characteristic,
+ const std::vector<uint8_t>& value) {
+ if (characteristic->GetIdentifier() != status_id_)
+ return;
+ VLOG(2) << "Status characteristic value changed.";
+ read_callback_.Run(value);
+}
+
+void FidoBleConnection::GattServicesDiscovered(BluetoothAdapter* adapter,
+ BluetoothDevice* device) {
+ if (adapter != adapter_ || device->GetAddress() != address_)
+ return;
+
+ if (waiting_for_gatt_discovery_) {
+ waiting_for_gatt_discovery_ = false;
+ ConnectToFidoService();
+ }
+}
+
+// static
+void FidoBleConnection::OnReadControlPointLength(
+ ControlPointLengthCallback callback,
+ const std::vector<uint8_t>& value) {
+ if (value.size() != 2) {
+ LOG(ERROR) << "Wrong Control Point Length: " << value.size() << " bytes";
+ std::move(callback).Run(base::nullopt);
+ return;
+ }
+
+ uint16_t length = (value[0] << 8) | value[1];
+ VLOG(2) << "Control Point Length: " << length;
+ std::move(callback).Run(length);
+}
+
+// static
+void FidoBleConnection::OnReadControlPointLengthError(
+ ControlPointLengthCallback callback,
+ BluetoothGattService::GattErrorCode error_code) {
+ LOG(ERROR) << "Error reading Control Point Length: " << ToString(error_code);
+ std::move(callback).Run(base::nullopt);
+}
+
+} // namespace device
diff --git a/chromium/device/fido/fido_ble_connection.h b/chromium/device/fido/ble/fido_ble_connection.h
index f7efb7044af..34174629662 100644
--- a/chromium/device/fido/fido_ble_connection.h
+++ b/chromium/device/fido/ble/fido_ble_connection.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_CONNECTION_H_
-#define DEVICE_FIDO_FIDO_BLE_CONNECTION_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_CONNECTION_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_CONNECTION_H_
#include <stdint.h>
@@ -27,72 +27,56 @@ namespace device {
class BluetoothGattConnection;
class BluetoothGattNotifySession;
class BluetoothRemoteGattCharacteristic;
-class BluetoothRemoteGattService;
-// A connection to the U2F service of an authenticator over BLE. Detailed
+// A connection to the Fido 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.
+// https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#ble
class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
: public BluetoothAdapter::Observer {
public:
- enum class ServiceRevision {
- VERSION_1_0,
- VERSION_1_1,
- VERSION_1_2,
+ // Valid Service Revisions. Reference:
+ // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#ble-fido-service
+ enum class ServiceRevision : uint8_t {
+ kU2f11 = 1 << 7,
+ kU2f12 = 1 << 6,
+ kFido2 = 1 << 5,
};
// 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 ConnectionCallback = base::OnceCallback<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>)>;
- FidoBleConnection(std::string device_address,
- ConnectionStatusCallback connection_status_callback,
+ FidoBleConnection(BluetoothAdapter* adapter,
+ std::string device_address,
ReadCallback read_callback);
~FidoBleConnection() override;
const std::string& address() const { return address_; }
+
+ BluetoothDevice* GetBleDevice();
const BluetoothDevice* GetBleDevice() const;
- virtual void Connect();
+ virtual void Connect(ConnectionCallback callback);
virtual void ReadControlPointLength(ControlPointLengthCallback callback);
- virtual void ReadServiceRevisions(ServiceRevisionsCallback callback);
virtual void WriteControlPoint(const std::vector<uint8_t>& data,
WriteCallback callback);
- virtual void WriteServiceRevision(ServiceRevision service_revision,
- WriteCallback callback);
protected:
- explicit FidoBleConnection(std::string device_address);
+ // Used for testing.
+ FidoBleConnection(BluetoothAdapter* adapter, std::string device_address);
+ scoped_refptr<BluetoothAdapter> adapter_;
std::string address_;
- ConnectionStatusCallback connection_status_callback_;
ReadCallback read_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,
@@ -100,61 +84,41 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
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 ConnectToFidoService();
+ void OnReadServiceRevisions(std::vector<ServiceRevision> service_revisions);
+
+ void WriteServiceRevision(ServiceRevision service_revision);
+ void OnServiceRevisionWritten(bool success);
+ void StartNotifySession();
void OnStartNotifySession(
std::unique_ptr<BluetoothGattNotifySession> notify_session);
void OnStartNotifySessionError(
BluetoothGattService::GattErrorCode error_code);
- void OnConnectionError();
-
- const BluetoothRemoteGattService* GetFidoService() 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);
-
- scoped_refptr<BluetoothAdapter> adapter_;
std::unique_ptr<BluetoothGattConnection> connection_;
std::unique_ptr<BluetoothGattNotifySession> notify_session_;
- base::Optional<std::string> u2f_service_id_;
+ ConnectionCallback pending_connection_callback_;
+ bool waiting_for_gatt_discovery_ = false;
+
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<FidoBleConnection> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoBleConnection);
@@ -162,4 +126,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_CONNECTION_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_CONNECTION_H_
diff --git a/chromium/device/fido/ble/fido_ble_connection_unittest.cc b/chromium/device/fido/ble/fido_ble_connection_unittest.cc
new file mode 100644
index 00000000000..865a92fadf8
--- /dev/null
+++ b/chromium/device/fido/ble/fido_ble_connection_unittest.cc
@@ -0,0 +1,738 @@
+// 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/fido/ble/fido_ble_connection.h"
+
+#include <bitset>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.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/fido/ble/fido_ble_uuids.h"
+#include "device/fido/test_callback_receiver.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 {
+
+constexpr auto kDefaultServiceRevision =
+ static_cast<uint8_t>(FidoBleConnection::ServiceRevision::kFido2);
+
+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 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_;
+ }
+
+ FidoBleConnection::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};
+};
+
+using TestConnectionCallbackReceiver = test::ValueCallbackReceiver<bool>;
+
+using TestReadControlPointLengthCallback =
+ test::ValueCallbackReceiver<base::Optional<uint16_t>>;
+
+using TestReadServiceRevisionsCallback =
+ test::ValueCallbackReceiver<std::set<FidoBleConnection::ServiceRevision>>;
+
+using TestWriteCallback = test::ValueCallbackReceiver<bool>;
+} // namespace
+
+class FidoBleConnectionTest : public ::testing::Test {
+ public:
+ FidoBleConnectionTest() {
+ ON_CALL(*adapter_, GetDevice(_))
+ .WillByDefault(Invoke([this](const std::string& address) {
+ return GetMockDevice(adapter_.get(), address);
+ }));
+
+ BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
+ }
+
+ BluetoothAdapter* adapter() { return adapter_.get(); }
+ MockBluetoothDevice* device() { return fido_device_; }
+
+ void AddFidoDevice(const std::string& device_address) {
+ auto fido_device = std::make_unique<NiceMockBluetoothDevice>(
+ adapter_.get(), /* bluetooth_class */ 0u,
+ BluetoothTest::kTestDeviceNameU2f, device_address, /* paired */ true,
+ /* connected */ false);
+ fido_device_ = fido_device.get();
+ adapter_->AddMockDevice(std::move(fido_device));
+
+ ON_CALL(*fido_device_, GetGattServices())
+ .WillByDefault(
+ Invoke(fido_device_, &MockBluetoothDevice::GetMockServices));
+
+ ON_CALL(*fido_device_, GetGattService(_))
+ .WillByDefault(
+ Invoke(fido_device_, &MockBluetoothDevice::GetMockService));
+ AddFidoService();
+ }
+
+ void SetupConnectingFidoDevice(const std::string& device_address) {
+ ON_CALL(*fido_device_, CreateGattConnection)
+ .WillByDefault(
+ Invoke([this, &device_address](const auto& callback, auto&&) {
+ connection_ =
+ new NiceMockBluetoothGattConnection(adapter_, device_address);
+ callback.Run(std::move(base::WrapUnique(connection_)));
+ }));
+
+ ON_CALL(*fido_device_, IsGattServicesDiscoveryComplete)
+ .WillByDefault(Return(true));
+
+ ON_CALL(*fido_service_revision_bitfield_, ReadRemoteCharacteristic)
+ .WillByDefault(Invoke([=](auto&& callback, auto&&) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(callback,
+ std::vector<uint8_t>({kDefaultServiceRevision})));
+ }));
+
+ ON_CALL(*fido_service_revision_bitfield_, WriteRemoteCharacteristic)
+ .WillByDefault(Invoke([=](auto&&, auto&& callback, auto&&) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+ }));
+
+ ON_CALL(*fido_status_, StartNotifySession(_, _))
+ .WillByDefault(Invoke([this](const auto& callback, auto&&) {
+ notify_session_ = new NiceMockBluetoothGattNotifySession(
+ fido_status_->GetWeakPtr());
+ callback.Run(base::WrapUnique(notify_session_));
+ }));
+ }
+
+ void SimulateGattDiscoveryComplete(bool complete) {
+ EXPECT_CALL(*fido_device_, IsGattServicesDiscoveryComplete)
+ .WillOnce(Return(complete));
+ }
+
+ void SimulateGattConnectionError() {
+ EXPECT_CALL(*fido_device_, CreateGattConnection)
+ .WillOnce(Invoke([](auto&&, auto&& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback, BluetoothDevice::ERROR_FAILED));
+ }));
+ }
+
+ void SimulateGattNotifySessionStartError() {
+ EXPECT_CALL(*fido_status_, StartNotifySession(_, _))
+ .WillOnce(Invoke([](auto&&, auto&& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(error_callback,
+ BluetoothGattService::GATT_ERROR_FAILED));
+ }));
+ }
+
+ void NotifyStatusChanged(const std::vector<uint8_t>& value) {
+ for (auto& observer : adapter_->GetObservers())
+ observer.GattCharacteristicValueChanged(adapter_.get(), fido_status_,
+ value);
+ }
+
+ void NotifyGattServicesDiscovered() {
+ adapter_->NotifyGattServicesDiscovered(fido_device_);
+ }
+
+ void SetNextReadControlPointLengthReponse(bool success,
+ const std::vector<uint8_t>& value) {
+ EXPECT_CALL(*fido_control_point_length_, ReadRemoteCharacteristic(_, _))
+ .WillOnce(Invoke([success, value](const auto& callback,
+ const auto& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ success
+ ? base::BindOnce(callback, value)
+ : base::BindOnce(error_callback,
+ BluetoothGattService::GATT_ERROR_FAILED));
+ }));
+ }
+
+ void SetNextReadServiceRevisionResponse(bool success,
+ const std::vector<uint8_t>& value) {
+ EXPECT_CALL(*fido_service_revision_, ReadRemoteCharacteristic(_, _))
+ .WillOnce(Invoke([success, value](const auto& callback,
+ const auto& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ success
+ ? base::BindOnce(callback, value)
+ : base::BindOnce(error_callback,
+ BluetoothGattService::GATT_ERROR_FAILED));
+ }));
+ }
+
+ void SetNextReadServiceRevisionBitfieldResponse(
+ bool success,
+ const std::vector<uint8_t>& value) {
+ EXPECT_CALL(*fido_service_revision_bitfield_,
+ ReadRemoteCharacteristic(_, _))
+ .WillOnce(Invoke([success, value](const auto& callback,
+ const auto& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ success
+ ? base::BindOnce(callback, value)
+ : base::BindOnce(error_callback,
+ BluetoothGattService::GATT_ERROR_FAILED));
+ }));
+ }
+
+ void SetNextWriteControlPointResponse(bool success) {
+// For performance reasons we try writes without responses first on macOS.
+#if defined(OS_MACOSX)
+ EXPECT_CALL(*fido_control_point_, WriteWithoutResponse)
+ .WillOnce(Return(success));
+ if (success)
+ return;
+#else
+ EXPECT_CALL(*fido_control_point_, WriteWithoutResponse).Times(0);
+#endif // defined(OS_MACOSX)
+
+ EXPECT_CALL(*fido_control_point_, WriteRemoteCharacteristic(_, _, _))
+ .WillOnce(Invoke([success](const auto& data, const auto& callback,
+ const auto& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ success
+ ? base::BindOnce(callback)
+ : base::BindOnce(error_callback,
+ BluetoothGattService::GATT_ERROR_FAILED));
+ }));
+ }
+
+ void SetNextWriteServiceRevisionResponse(std::vector<uint8_t> expected_data,
+ bool success) {
+ EXPECT_CALL(*fido_service_revision_bitfield_,
+ WriteRemoteCharacteristic(expected_data, _, _))
+ .WillOnce(Invoke([success](const auto& data, const auto& callback,
+ const auto& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ success
+ ? base::BindOnce(callback)
+ : base::BindOnce(error_callback,
+ BluetoothGattService::GATT_ERROR_FAILED));
+ }));
+ }
+
+ void AddFidoService() {
+ auto fido_service = std::make_unique<NiceMockBluetoothGattService>(
+ fido_device_, "fido_service", BluetoothUUID(kFidoServiceUUID),
+ /* is_primary */ true, /* is_local */ false);
+ fido_service_ = fido_service.get();
+ fido_device_->AddMockService(std::move(fido_service));
+
+ static constexpr bool kIsLocal = false;
+ {
+ auto fido_control_point =
+ std::make_unique<NiceMockBluetoothGattCharacteristic>(
+ fido_service_, "fido_control_point",
+ BluetoothUUID(kFidoControlPointUUID), kIsLocal,
+ BluetoothGattCharacteristic::PROPERTY_WRITE,
+ BluetoothGattCharacteristic::PERMISSION_NONE);
+ fido_control_point_ = fido_control_point.get();
+ fido_service_->AddMockCharacteristic(std::move(fido_control_point));
+ }
+
+ {
+ auto fido_status = std::make_unique<NiceMockBluetoothGattCharacteristic>(
+ fido_service_, "fido_status", BluetoothUUID(kFidoStatusUUID),
+ kIsLocal, BluetoothGattCharacteristic::PROPERTY_NOTIFY,
+ BluetoothGattCharacteristic::PERMISSION_NONE);
+ fido_status_ = fido_status.get();
+ fido_service_->AddMockCharacteristic(std::move(fido_status));
+ }
+
+ {
+ auto fido_control_point_length =
+ std::make_unique<NiceMockBluetoothGattCharacteristic>(
+ fido_service_, "fido_control_point_length",
+ BluetoothUUID(kFidoControlPointLengthUUID), kIsLocal,
+ BluetoothGattCharacteristic::PROPERTY_READ,
+ BluetoothGattCharacteristic::PERMISSION_NONE);
+ fido_control_point_length_ = fido_control_point_length.get();
+ fido_service_->AddMockCharacteristic(
+ std::move(fido_control_point_length));
+ }
+
+ {
+ auto fido_service_revision =
+ std::make_unique<NiceMockBluetoothGattCharacteristic>(
+ fido_service_, "fido_service_revision",
+ BluetoothUUID(kFidoServiceRevisionUUID), kIsLocal,
+ BluetoothGattCharacteristic::PROPERTY_READ,
+ BluetoothGattCharacteristic::PERMISSION_NONE);
+ fido_service_revision_ = fido_service_revision.get();
+ fido_service_->AddMockCharacteristic(std::move(fido_service_revision));
+ }
+
+ {
+ auto fido_service_revision_bitfield =
+ std::make_unique<NiceMockBluetoothGattCharacteristic>(
+ fido_service_, "fido_service_revision_bitfield",
+ BluetoothUUID(kFidoServiceRevisionBitfieldUUID), kIsLocal,
+ BluetoothGattCharacteristic::PROPERTY_READ |
+ BluetoothGattCharacteristic::PROPERTY_WRITE,
+ BluetoothGattCharacteristic::PERMISSION_NONE);
+ fido_service_revision_bitfield_ = fido_service_revision_bitfield.get();
+ fido_service_->AddMockCharacteristic(
+ std::move(fido_service_revision_bitfield));
+ }
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+ scoped_refptr<MockBluetoothAdapter> adapter_ =
+ base::MakeRefCounted<NiceMockBluetoothAdapter>();
+
+ MockBluetoothDevice* fido_device_;
+ MockBluetoothGattService* fido_service_;
+
+ MockBluetoothGattCharacteristic* fido_control_point_;
+ MockBluetoothGattCharacteristic* fido_status_;
+ MockBluetoothGattCharacteristic* fido_control_point_length_;
+ MockBluetoothGattCharacteristic* fido_service_revision_;
+ MockBluetoothGattCharacteristic* fido_service_revision_bitfield_;
+
+ MockBluetoothGattConnection* connection_;
+ MockBluetoothGattNotifySession* notify_session_;
+};
+
+TEST_F(FidoBleConnectionTest, Address) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ FidoBleConnection connection(adapter(), device_address, base::DoNothing());
+ connection.Connect(base::DoNothing());
+ EXPECT_EQ(device_address, connection.address());
+}
+
+TEST_F(FidoBleConnectionTest, DeviceNotPresent) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, PreConnected) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address, base::DoNothing());
+
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, NoConnectionWithoutCompletedGattDiscovery) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address, base::DoNothing());
+
+ SimulateGattDiscoveryComplete(false);
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(connection_callback_receiver.was_called());
+
+ NotifyGattServicesDiscovered();
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, GattServicesDiscoveredIgnoredBeforeConnection) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+ NotifyGattServicesDiscovered();
+
+ SimulateGattDiscoveryComplete(false);
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(connection_callback_receiver.was_called());
+
+ NotifyGattServicesDiscovered();
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, GattServicesDiscoveredAgain) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ NotifyGattServicesDiscovered();
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+
+ // A second call to the event handler should not trigger another attempt to
+ // obtain Gatt Services.
+ EXPECT_CALL(*device(), GetGattServices).Times(0);
+ EXPECT_CALL(*device(), GetGattService).Times(0);
+ NotifyGattServicesDiscovered();
+}
+
+TEST_F(FidoBleConnectionTest, SimulateGattConnectionError) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address, base::DoNothing());
+
+ SimulateGattConnectionError();
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, SimulateGattNotifySessionStartError) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+
+ SimulateGattNotifySessionStartError();
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, MultipleServiceRevisions) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+
+ static constexpr struct {
+ std::bitset<8> supported_revisions;
+ std::bitset<8> selected_revision;
+ } test_cases[] = {
+ // Only U2F 1.1 is supported, pick it.
+ {0b1000'0000, 0b1000'0000},
+ // Only U2F 1.2 is supported, pick it.
+ {0b0100'0000, 0b0100'0000},
+ // U2F 1.1 and U2F 1.2 are supported, pick U2F 1.2.
+ {0b1100'0000, 0b0100'0000},
+ // Only FIDO2 is supported, pick it.
+ {0b0010'0000, 0b0010'0000},
+ // U2F 1.1 and FIDO2 are supported, pick FIDO2.
+ {0b1010'0000, 0b0010'0000},
+ // U2F 1.2 and FIDO2 are supported, pick FIDO2.
+ {0b0110'0000, 0b0010'0000},
+ // U2F 1.1, U2F 1.2 and FIDO2 are supported, pick FIDO2.
+ {0b1110'0000, 0b0010'0000},
+ // U2F 1.1 and a future revision are supported, pick U2F 1.1.
+ {0b1000'1000, 0b1000'0000},
+ // U2F 1.2 and a future revision are supported, pick U2F 1.2.
+ {0b0100'1000, 0b0100'0000},
+ // FIDO2 and a future revision are supported, pick FIDO2.
+ {0b0010'1000, 0b0010'0000},
+ };
+
+ for (const auto& test_case : test_cases) {
+ SCOPED_TRACE(::testing::Message()
+ << "Supported Revisions: " << test_case.supported_revisions
+ << ", Selected Revision: " << test_case.selected_revision);
+ SetNextReadServiceRevisionBitfieldResponse(
+ true, {test_case.supported_revisions.to_ulong()});
+
+ SetNextWriteServiceRevisionResponse(
+ {test_case.selected_revision.to_ulong()}, true);
+
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+ }
+}
+
+TEST_F(FidoBleConnectionTest, UnsupportedServiceRevisions) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+
+ // Test failure cases.
+ static constexpr struct {
+ std::bitset<8> supported_revisions;
+ } test_cases[] = {
+ {0b0000'0000}, // No Service Revision.
+ {0b0001'0000}, // Unsupported Service Revision (4th bit).
+ {0b0000'1000}, // Unsupported Service Revision (3th bit).
+ {0b0000'0100}, // Unsupported Service Revision (2th bit).
+ {0b0000'0010}, // Unsupported Service Revision (1th bit).
+ {0b0000'0001}, // Unsupported Service Revision (0th bit).
+ };
+
+ for (const auto& test_case : test_cases) {
+ SCOPED_TRACE(::testing::Message()
+ << "Supported Revisions: " << test_case.supported_revisions);
+ SetNextReadServiceRevisionBitfieldResponse(
+ true, {test_case.supported_revisions.to_ulong()});
+
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+ }
+}
+
+TEST_F(FidoBleConnectionTest, ReadServiceRevisionsFails) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ SetNextReadServiceRevisionBitfieldResponse(false, {});
+
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, WriteServiceRevisionsFails) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ SetNextReadServiceRevisionBitfieldResponse(true, {kDefaultServiceRevision});
+ SetNextWriteServiceRevisionResponse({kDefaultServiceRevision}, false);
+
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+}
+
+TEST_F(FidoBleConnectionTest, ReadStatusNotifications) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ TestReadCallback read_callback;
+
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ read_callback.GetCallback());
+
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+
+ 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(FidoBleConnectionTest, ReadControlPointLength) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+
+ {
+ TestReadControlPointLengthCallback length_callback;
+ SetNextReadControlPointLengthReponse(false, {});
+ connection.ReadControlPointLength(length_callback.callback());
+ length_callback.WaitForCallback();
+ EXPECT_EQ(base::nullopt, length_callback.value());
+ }
+
+ // The Control Point Length should consist of exactly two bytes, hence we
+ // EXPECT_EQ(base::nullopt) for payloads of size 0, 1 and 3.
+ {
+ TestReadControlPointLengthCallback length_callback;
+ SetNextReadControlPointLengthReponse(true, {});
+ connection.ReadControlPointLength(length_callback.callback());
+ length_callback.WaitForCallback();
+ EXPECT_EQ(base::nullopt, length_callback.value());
+ }
+
+ {
+ TestReadControlPointLengthCallback length_callback;
+ SetNextReadControlPointLengthReponse(true, {0xAB});
+ connection.ReadControlPointLength(length_callback.callback());
+ length_callback.WaitForCallback();
+ EXPECT_EQ(base::nullopt, length_callback.value());
+ }
+
+ {
+ TestReadControlPointLengthCallback length_callback;
+ SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD});
+ connection.ReadControlPointLength(length_callback.callback());
+ length_callback.WaitForCallback();
+ EXPECT_EQ(0xABCD, *length_callback.value());
+ }
+
+ {
+ TestReadControlPointLengthCallback length_callback;
+ SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD, 0xEF});
+ connection.ReadControlPointLength(length_callback.callback());
+ length_callback.WaitForCallback();
+ EXPECT_EQ(base::nullopt, length_callback.value());
+ }
+}
+
+TEST_F(FidoBleConnectionTest, WriteControlPoint) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_TRUE(connection_callback_receiver.value());
+
+ {
+ TestWriteCallback write_callback;
+ SetNextWriteControlPointResponse(false);
+ connection.WriteControlPoint({}, write_callback.callback());
+ write_callback.WaitForCallback();
+ EXPECT_FALSE(write_callback.value());
+ }
+
+ {
+ TestWriteCallback write_callback;
+ SetNextWriteControlPointResponse(true);
+ connection.WriteControlPoint({}, write_callback.callback());
+ write_callback.WaitForCallback();
+ EXPECT_TRUE(write_callback.value());
+ }
+}
+
+TEST_F(FidoBleConnectionTest, ReadsAndWriteFailWhenDisconnected) {
+ const std::string device_address = BluetoothTest::kTestDeviceAddress1;
+
+ AddFidoDevice(device_address);
+ SetupConnectingFidoDevice(device_address);
+ FidoBleConnection connection(adapter(), device_address,
+ base::DoNothing());
+
+ SimulateGattConnectionError();
+ TestConnectionCallbackReceiver connection_callback_receiver;
+ connection.Connect(connection_callback_receiver.callback());
+ connection_callback_receiver.WaitForCallback();
+ EXPECT_FALSE(connection_callback_receiver.value());
+
+ // Reads should always fail on a disconnected device.
+ TestReadControlPointLengthCallback length_callback;
+ connection.ReadControlPointLength(length_callback.callback());
+ length_callback.WaitForCallback();
+ EXPECT_EQ(base::nullopt, length_callback.value());
+
+ // Writes should always fail on a disconnected device.
+ TestWriteCallback write_callback;
+ connection.WriteControlPoint({}, write_callback.callback());
+ write_callback.WaitForCallback();
+ EXPECT_FALSE(write_callback.value());
+}
+
+} // namespace device
diff --git a/chromium/device/fido/fido_ble_device.cc b/chromium/device/fido/ble/fido_ble_device.cc
index e292afb9a11..89b7a1733dc 100644
--- a/chromium/device/fido/fido_ble_device.cc
+++ b/chromium/device/fido/ble/fido_ble_device.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_device.h"
+#include "device/fido/ble/fido_ble_device.h"
#include <bitset>
@@ -10,17 +10,16 @@
#include "base/strings/string_piece.h"
#include "components/apdu/apdu_response.h"
#include "device/bluetooth/bluetooth_uuid.h"
-#include "device/fido/fido_ble_frames.h"
-#include "device/fido/fido_ble_uuids.h"
+#include "device/fido/ble/fido_ble_frames.h"
+#include "device/fido/ble/fido_ble_uuids.h"
#include "device/fido/fido_constants.h"
namespace device {
-FidoBleDevice::FidoBleDevice(std::string address) : weak_factory_(this) {
+FidoBleDevice::FidoBleDevice(BluetoothAdapter* adapter, std::string address)
+ : weak_factory_(this) {
connection_ = std::make_unique<FidoBleConnection>(
- std::move(address),
- base::BindRepeating(&FidoBleDevice::OnConnectionStatus,
- base::Unretained(this)),
+ adapter, std::move(address),
base::BindRepeating(&FidoBleDevice::OnStatusMessage,
base::Unretained(this)));
}
@@ -36,7 +35,8 @@ void FidoBleDevice::Connect() {
StartTimeout();
state_ = State::kBusy;
- connection_->Connect();
+ connection_->Connect(
+ base::BindOnce(&FidoBleDevice::OnConnected, base::Unretained(this)));
}
void FidoBleDevice::SendPing(std::vector<uint8_t> data,
@@ -67,6 +67,10 @@ std::string FidoBleDevice::GetId() const {
return GetId(connection_->address());
}
+FidoTransportProtocol FidoBleDevice::DeviceTransport() const {
+ return FidoTransportProtocol::kBluetoothLowEnergy;
+}
+
bool FidoBleDevice::IsInPairingMode() const {
const BluetoothDevice* const ble_device = connection_->GetBleDevice();
if (!ble_device)
@@ -96,12 +100,6 @@ bool FidoBleDevice::IsInPairingMode() const {
static_cast<int>(FidoServiceDataFlags::kPairingMode)) != 0;
}
-FidoBleConnection::ConnectionStatusCallback
-FidoBleDevice::GetConnectionStatusCallbackForTesting() {
- return base::BindRepeating(&FidoBleDevice::OnConnectionStatus,
- base::Unretained(this));
-}
-
FidoBleConnection::ReadCallback FidoBleDevice::GetReadCallbackForTesting() {
return base::BindRepeating(&FidoBleDevice::OnStatusMessage,
base::Unretained(this));
@@ -156,6 +154,7 @@ void FidoBleDevice::Transition() {
break;
case State::kBusy:
break;
+ case State::kMsgError:
case State::kDeviceError:
auto self = GetWeakPtr();
// Executing callbacks may free |this|. Check |self| first.
@@ -174,16 +173,13 @@ void FidoBleDevice::AddToPendingFrames(FidoBleDeviceCommand cmd,
DeviceCallback callback) {
pending_frames_.emplace(
FidoBleFrame(cmd, std::move(request)),
- base::BindOnce(
- [](DeviceCallback callback, base::Optional<FidoBleFrame> frame) {
- std::move(callback).Run(frame ? base::make_optional(frame->data())
- : base::nullopt);
- },
- std::move(callback)));
+ base::BindOnce(&FidoBleDevice::OnBleResponseReceived,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+
Transition();
}
-void FidoBleDevice::OnConnectionStatus(bool success) {
+void FidoBleDevice::OnConnected(bool success) {
StopTimeout();
state_ = success ? State::kConnected : State::kDeviceError;
Transition();
@@ -227,4 +223,36 @@ void FidoBleDevice::OnTimeout() {
state_ = State::kDeviceError;
}
+void FidoBleDevice::OnBleResponseReceived(DeviceCallback callback,
+ base::Optional<FidoBleFrame> frame) {
+ if (!frame || !frame->IsValid()) {
+ state_ = State::kDeviceError;
+ std::move(callback).Run(base::nullopt);
+ return;
+ }
+
+ if (frame->command() == FidoBleDeviceCommand::kError) {
+ ProcessBleDeviceError(frame->data());
+ std::move(callback).Run(base::nullopt);
+ return;
+ }
+
+ std::move(callback).Run(frame->data());
+}
+
+void FidoBleDevice::ProcessBleDeviceError(base::span<const uint8_t> data) {
+ DCHECK_EQ(1u, data.size());
+ const auto error_constant = data[0];
+ if (error_constant ==
+ base::strict_cast<uint8_t>(FidoBleFrame::ErrorCode::INVALID_CMD) ||
+ error_constant ==
+ base::strict_cast<uint8_t>(FidoBleFrame::ErrorCode::INVALID_PAR) ||
+ error_constant ==
+ base::strict_cast<uint8_t>(FidoBleFrame::ErrorCode::INVALID_LEN)) {
+ state_ = State::kMsgError;
+ } else {
+ state_ = State::kDeviceError;
+ }
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_ble_device.h b/chromium/device/fido/ble/fido_ble_device.h
index 41d617e2b55..1c62bf46d7c 100644
--- a/chromium/device/fido/fido_ble_device.h
+++ b/chromium/device/fido/ble/fido_ble_device.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_DEVICE_H_
-#define DEVICE_FIDO_FIDO_BLE_DEVICE_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_DEVICE_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_DEVICE_H_
#include <memory>
#include <string>
@@ -17,19 +17,20 @@
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/timer/timer.h"
-#include "device/fido/fido_ble_connection.h"
-#include "device/fido/fido_ble_transaction.h"
+#include "device/fido/ble/fido_ble_connection.h"
+#include "device/fido/ble/fido_ble_transaction.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
namespace device {
+class BluetoothAdapter;
class FidoBleFrame;
class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
public:
using FrameCallback = FidoBleTransaction::FrameCallback;
- explicit FidoBleDevice(std::string address);
+ FidoBleDevice(BluetoothAdapter* adapter, std::string address);
explicit FidoBleDevice(std::unique_ptr<FidoBleConnection> connection);
~FidoBleDevice() override;
@@ -41,13 +42,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
void TryWink(WinkCallback callback) override;
void Cancel() override;
std::string GetId() const override;
+ FidoTransportProtocol DeviceTransport() const override;
// Returns whether or not the underlying BLE device is currently in pairing
// mode by investigating the advertisement payload.
bool IsInPairingMode() const;
- FidoBleConnection::ConnectionStatusCallback
- GetConnectionStatusCallbackForTesting();
FidoBleConnection::ReadCallback GetReadCallbackForTesting();
protected:
@@ -65,7 +65,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
void ResetTransaction();
private:
- void OnConnectionStatus(bool success);
+ void OnConnected(bool success);
void OnStatusMessage(std::vector<uint8_t> data);
void ReadControlPointLength();
@@ -78,6 +78,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
void StopTimeout();
void OnTimeout();
+ void OnBleResponseReceived(DeviceCallback callback,
+ base::Optional<FidoBleFrame> frame);
+ void ProcessBleDeviceError(base::span<const uint8_t> data);
+
base::OneShotTimer timer_;
std::unique_ptr<FidoBleConnection> connection_;
@@ -93,4 +97,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_DEVICE_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_DEVICE_H_
diff --git a/chromium/device/fido/fido_ble_device_unittest.cc b/chromium/device/fido/ble/fido_ble_device_unittest.cc
index 4632da30f09..190c2e17b8a 100644
--- a/chromium/device/fido/fido_ble_device_unittest.cc
+++ b/chromium/device/fido/ble/fido_ble_device_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_device.h"
+#include "device/fido/ble/fido_ble_device.h"
#include "base/optional.h"
#include "base/test/bind_test_util.h"
@@ -11,10 +11,10 @@
#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/fido/fido_ble_uuids.h"
+#include "device/fido/ble/fido_ble_uuids.h"
+#include "device/fido/ble/mock_fido_ble_connection.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
-#include "device/fido/mock_fido_ble_connection.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -75,20 +75,19 @@ class FidoBleDeviceTest : public Test {
public:
FidoBleDeviceTest() {
auto connection = std::make_unique<MockFidoBleConnection>(
- BluetoothTestBase::kTestDeviceAddress1);
+ adapter_.get(), BluetoothTestBase::kTestDeviceAddress1);
connection_ = connection.get();
device_ = std::make_unique<FidoBleDevice>(std::move(connection));
- connection_->connection_status_callback() =
- device_->GetConnectionStatusCallbackForTesting();
connection_->read_callback() = device_->GetReadCallbackForTesting();
}
+ MockBluetoothAdapter* adapter() { return adapter_.get(); }
FidoBleDevice* device() { return device_.get(); }
MockFidoBleConnection* connection() { return connection_; }
void ConnectWithLength(uint16_t length) {
- EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
- connection()->connection_status_callback().Run(true);
+ EXPECT_CALL(*connection(), ConnectPtr).WillOnce(Invoke([](auto* callback) {
+ std::move(*callback).Run(true);
}));
EXPECT_CALL(*connection(), ReadControlPointLengthPtr(_))
@@ -102,14 +101,17 @@ class FidoBleDeviceTest : public Test {
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
private:
+ scoped_refptr<MockBluetoothAdapter> adapter_ =
+ base::MakeRefCounted<NiceMockBluetoothAdapter>();
MockFidoBleConnection* connection_;
std::unique_ptr<FidoBleDevice> device_;
};
TEST_F(FidoBleDeviceTest, ConnectionFailureTest) {
- EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
- connection()->connection_status_callback().Run(false);
+ EXPECT_CALL(*connection(), ConnectPtr).WillOnce(Invoke([](auto* callback) {
+ std::move(*callback).Run(false);
}));
+
device()->Connect();
}
@@ -223,23 +225,21 @@ TEST_F(FidoBleDeviceTest, IsInPairingMode) {
// By default, a device is not in pairing mode.
EXPECT_FALSE(device()->IsInPairingMode());
- // Initiate default connection behavior, which will attempt to obtain an
- // adapter.
- auto mock_adapter = base::MakeRefCounted<NiceMockBluetoothAdapter>();
- BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
- EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
- connection()->FidoBleConnection::Connect();
- }));
+ // Initiate default connection behavior.
+ EXPECT_CALL(*connection(), ConnectPtr)
+ .WillOnce(Invoke([this](auto* callback) {
+ connection()->FidoBleConnection::Connect(std::move(*callback));
+ }));
device()->Connect();
// Add a mock fido device. This should also not be considered to be in pairing
// mode.
auto mock_bluetooth_device = std::make_unique<NiceMockBluetoothDevice>(
- mock_adapter.get(), /* bluetooth_class */ 0u,
+ adapter(), /* bluetooth_class */ 0u,
BluetoothTestBase::kTestDeviceNameU2f,
BluetoothTestBase::kTestDeviceAddress1, /* paired */ true,
/* connected */ false);
- EXPECT_CALL(*mock_adapter, GetDevice(BluetoothTestBase::kTestDeviceAddress1))
+ EXPECT_CALL(*adapter(), GetDevice(BluetoothTestBase::kTestDeviceAddress1))
.WillRepeatedly(Return(mock_bluetooth_device.get()));
EXPECT_FALSE(device()->IsInPairingMode());
@@ -283,4 +283,29 @@ TEST_F(FidoBleDeviceTest, IsInPairingMode) {
EXPECT_FALSE(device()->IsInPairingMode());
}
+TEST_F(FidoBleDeviceTest, DeviceMsgErrorTest) {
+ // kError(BF), followed by payload length(0001), followed by INVALID_CMD(01).
+ constexpr uint8_t kBleInvalidCommandError[] = {0xbf, 0x00, 0x01, 0x01};
+ ConnectWithLength(kControlPointLength);
+
+ EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
+ .WillOnce(::testing::WithArg<1>(
+ Invoke([this, kBleInvalidCommandError](auto* cb) {
+ scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(*cb), true));
+
+ scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(connection()->read_callback(),
+ fido_parsing_utils::Materialize(
+ kBleInvalidCommandError)));
+ })));
+
+ TestDeviceCallbackReceiver callback_receiver;
+ const auto payload = fido_parsing_utils::Materialize(kTestData);
+ device()->SendPing(payload, callback_receiver.callback());
+
+ callback_receiver.WaitForCallback();
+ EXPECT_EQ(FidoDevice::State::kMsgError, device()->state());
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_ble_discovery.cc b/chromium/device/fido/ble/fido_ble_discovery.cc
index 16de4ad732f..65270699888 100644
--- a/chromium/device/fido/fido_ble_discovery.cc
+++ b/chromium/device/fido/ble/fido_ble_discovery.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_discovery.h"
+#include "device/fido/ble/fido_ble_discovery.h"
#include <utility>
@@ -12,12 +12,14 @@
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_uuid.h"
-#include "device/fido/fido_ble_device.h"
-#include "device/fido/fido_ble_uuids.h"
+#include "device/fido/ble/fido_ble_device.h"
+#include "device/fido/ble/fido_ble_uuids.h"
namespace device {
-FidoBleDiscovery::FidoBleDiscovery() : weak_factory_(this) {}
+FidoBleDiscovery::FidoBleDiscovery()
+ : FidoBleDiscoveryBase(FidoTransportProtocol::kBluetoothLowEnergy),
+ weak_factory_(this) {}
FidoBleDiscovery::~FidoBleDiscovery() = default;
@@ -32,9 +34,11 @@ void FidoBleDiscovery::OnSetPowered() {
VLOG(2) << "Adapter " << adapter()->GetAddress() << " is powered on.";
for (BluetoothDevice* device : adapter()->GetDevices()) {
- if (base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
+ if (!CheckForExcludedDeviceAndCacheAddress(device) &&
+ base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
VLOG(2) << "U2F BLE device: " << device->GetAddress();
- AddDevice(std::make_unique<FidoBleDevice>(device->GetAddress()));
+ AddDevice(
+ std::make_unique<FidoBleDevice>(adapter(), device->GetAddress()));
}
}
@@ -54,19 +58,21 @@ void FidoBleDiscovery::OnSetPowered() {
void FidoBleDiscovery::DeviceAdded(BluetoothAdapter* adapter,
BluetoothDevice* device) {
- if (base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
+ if (!CheckForExcludedDeviceAndCacheAddress(device) &&
+ base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) {
VLOG(2) << "Discovered U2F BLE device: " << device->GetAddress();
- AddDevice(std::make_unique<FidoBleDevice>(device->GetAddress()));
+ AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress()));
}
}
void FidoBleDiscovery::DeviceChanged(BluetoothAdapter* adapter,
BluetoothDevice* device) {
- if (base::ContainsKey(device->GetUUIDs(), FidoServiceUUID()) &&
+ if (!CheckForExcludedDeviceAndCacheAddress(device) &&
+ base::ContainsKey(device->GetUUIDs(), FidoServiceUUID()) &&
!GetDevice(FidoBleDevice::GetId(device->GetAddress()))) {
VLOG(2) << "Discovered U2F service on existing BLE device: "
<< device->GetAddress();
- AddDevice(std::make_unique<FidoBleDevice>(device->GetAddress()));
+ AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress()));
}
}
@@ -78,4 +84,35 @@ void FidoBleDiscovery::DeviceRemoved(BluetoothAdapter* adapter,
}
}
+void FidoBleDiscovery::AdapterPoweredChanged(BluetoothAdapter* adapter,
+ bool powered) {
+ // If Bluetooth adapter is powered on, resume scanning for nearby FIDO
+ // devices. Previously inactive discovery sessions would be terminated upon
+ // invocation of OnSetPowered().
+ if (powered)
+ OnSetPowered();
+}
+
+bool FidoBleDiscovery::CheckForExcludedDeviceAndCacheAddress(
+ const BluetoothDevice* device) {
+ std::string device_address = device->GetAddress();
+ auto address_position =
+ excluded_cable_device_addresses_.lower_bound(device_address);
+ if (address_position != excluded_cable_device_addresses_.end() &&
+ *address_position == device_address) {
+ return true;
+ }
+
+ // IsCableDevice() is not stable, and can change throughout the lifetime. As
+ // so, cache device address for known Cable devices so that we do not attempt
+ // to connect to these devices.
+ if (IsCableDevice(device)) {
+ excluded_cable_device_addresses_.insert(address_position,
+ std::move(device_address));
+ return true;
+ }
+
+ return false;
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_ble_discovery.h b/chromium/device/fido/ble/fido_ble_discovery.h
index abf6f27476f..cc804cf38c3 100644
--- a/chromium/device/fido/fido_ble_discovery.h
+++ b/chromium/device/fido/ble/fido_ble_discovery.h
@@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_DISCOVERY_H_
-#define DEVICE_FIDO_FIDO_BLE_DISCOVERY_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_
#include <memory>
+#include <set>
+#include <string>
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "device/fido/fido_ble_discovery_base.h"
+#include "device/fido/ble/fido_ble_discovery_base.h"
namespace device {
@@ -35,7 +37,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscovery
BluetoothDevice* device) override;
void DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) override;
+ void AdapterPoweredChanged(BluetoothAdapter* adapter, bool powered) override;
+ // Returns true if |device| is a Cable device. If so, add address of |device|
+ // to |blacklisted_cable_device_addresses_|.
+ bool CheckForExcludedDeviceAndCacheAddress(const BluetoothDevice* device);
+
+ std::set<std::string> excluded_cable_device_addresses_;
base::WeakPtrFactory<FidoBleDiscovery> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoBleDiscovery);
@@ -43,4 +51,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscovery
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_DISCOVERY_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_
diff --git a/chromium/device/fido/fido_ble_discovery_base.cc b/chromium/device/fido/ble/fido_ble_discovery_base.cc
index b118bc1e1e7..6ef5475d1c7 100644
--- a/chromium/device/fido/fido_ble_discovery_base.cc
+++ b/chromium/device/fido/ble/fido_ble_discovery_base.cc
@@ -2,24 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_discovery_base.h"
+#include "device/fido/ble/fido_ble_discovery_base.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
+#include "base/no_destructor.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
+#include "device/fido/ble/fido_ble_uuids.h"
namespace device {
-FidoBleDiscoveryBase::FidoBleDiscoveryBase()
- : FidoDiscovery(FidoTransportProtocol::kBluetoothLowEnergy),
- weak_factory_(this) {}
+FidoBleDiscoveryBase::FidoBleDiscoveryBase(FidoTransportProtocol transport)
+ : FidoDiscovery(transport), weak_factory_(this) {}
FidoBleDiscoveryBase::~FidoBleDiscoveryBase() {
if (adapter_)
@@ -28,6 +29,13 @@ FidoBleDiscoveryBase::~FidoBleDiscoveryBase() {
// Destroying |discovery_session_| will best-effort-stop discovering.
}
+// static
+const BluetoothUUID& FidoBleDiscoveryBase::CableAdvertisementUUID() {
+ static const base::NoDestructor<BluetoothUUID> service_uuid(
+ kCableAdvertisementUUID128);
+ return *service_uuid;
+}
+
void FidoBleDiscoveryBase::OnStartDiscoverySessionWithFilter(
std::unique_ptr<BluetoothDiscoverySession> session) {
SetDiscoverySession(std::move(session));
@@ -50,25 +58,28 @@ void FidoBleDiscoveryBase::SetDiscoverySession(
discovery_session_ = std::move(discovery_session);
}
+bool FidoBleDiscoveryBase::IsCableDevice(const BluetoothDevice* device) const {
+ const auto& uuid = CableAdvertisementUUID();
+ return base::ContainsKey(device->GetServiceData(), uuid) ||
+ base::ContainsKey(device->GetUUIDs(), uuid);
+}
+
void FidoBleDiscoveryBase::OnGetAdapter(
scoped_refptr<BluetoothAdapter> adapter) {
+ if (!adapter->IsPresent()) {
+ DVLOG(2) << "bluetooth adapter is not available in current system.";
+ NotifyDiscoveryStarted(false);
+ return;
+ }
+
DCHECK(!adapter_);
adapter_ = std::move(adapter);
DCHECK(adapter_);
- VLOG(2) << "Got adapter " << adapter_->GetAddress();
+ DVLOG(2) << "Got adapter " << adapter_->GetAddress();
adapter_->AddObserver(this);
- if (adapter_->IsPowered()) {
+ if (adapter_->IsPowered())
OnSetPowered();
- } else {
- adapter_->SetPowered(
- true,
- base::AdaptCallbackForRepeating(base::BindOnce(
- &FidoBleDiscoveryBase::OnSetPowered, weak_factory_.GetWeakPtr())),
- base::AdaptCallbackForRepeating(
- base::BindOnce(&FidoBleDiscoveryBase::OnSetPoweredError,
- weak_factory_.GetWeakPtr())));
- }
}
void FidoBleDiscoveryBase::StartInternal() {
diff --git a/chromium/device/fido/fido_ble_discovery_base.h b/chromium/device/fido/ble/fido_ble_discovery_base.h
index dd4a0f14e54..3a1f64c4b3a 100644
--- a/chromium/device/fido/fido_ble_discovery_base.h
+++ b/chromium/device/fido/ble/fido_ble_discovery_base.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_DISCOVERY_BASE_H_
-#define DEVICE_FIDO_FIDO_BLE_DISCOVERY_BASE_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_BASE_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_BASE_H_
#include <memory>
@@ -12,6 +12,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_uuid.h"
#include "device/fido/fido_discovery.h"
namespace device {
@@ -22,10 +23,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscoveryBase
: public FidoDiscovery,
public BluetoothAdapter::Observer {
public:
- FidoBleDiscoveryBase();
+ explicit FidoBleDiscoveryBase(FidoTransportProtocol transport);
~FidoBleDiscoveryBase() override;
protected:
+ static const BluetoothUUID& CableAdvertisementUUID();
+
virtual void OnSetPowered() = 0;
virtual void OnStartDiscoverySessionWithFilter(
std::unique_ptr<BluetoothDiscoverySession>);
@@ -34,6 +37,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscoveryBase
void OnStartDiscoverySessionError();
void SetDiscoverySession(
std::unique_ptr<BluetoothDiscoverySession> discovery_session);
+ bool IsCableDevice(const BluetoothDevice* device) const;
BluetoothAdapter* adapter() { return adapter_.get(); }
@@ -53,4 +57,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscoveryBase
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_DISCOVERY_BASE_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_BASE_H_
diff --git a/chromium/device/fido/fido_ble_discovery_unittest.cc b/chromium/device/fido/ble/fido_ble_discovery_unittest.cc
index 89f689a43a1..74efa866290 100644
--- a/chromium/device/fido/fido_ble_discovery_unittest.cc
+++ b/chromium/device/fido/ble/fido_ble_discovery_unittest.cc
@@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_discovery.h"
+#include "device/fido/ble/fido_ble_discovery.h"
#include <string>
#include "base/bind.h"
#include "base/run_loop.h"
#include "build/build_config.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/bluetooth_test.h"
-#include "device/fido/fido_ble_device.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/fido/ble/fido_ble_device.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -37,6 +39,36 @@ MATCHER_P(IdMatches, id, "") {
return arg->GetId() == std::string("ble:") + id;
}
+TEST_F(BluetoothTest, FidoBleDiscoveryNotifyObserverWhenAdapterNotPresent) {
+ FidoBleDiscovery discovery;
+ MockFidoDiscoveryObserver observer;
+ discovery.set_observer(&observer);
+ auto mock_adapter =
+ base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
+ EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*mock_adapter, SetPowered).Times(0);
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
+ EXPECT_CALL(observer, DiscoveryStarted(&discovery, false));
+ discovery.Start();
+}
+
+TEST_F(BluetoothTest, FidoBleDiscoveryResumeScanningAfterPoweredOn) {
+ FidoBleDiscovery discovery;
+ MockFidoDiscoveryObserver observer;
+ discovery.set_observer(&observer);
+ auto mock_adapter =
+ base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
+ EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true));
+ EXPECT_CALL(*mock_adapter, IsPowered()).WillOnce(::testing::Return(false));
+
+ // After BluetoothAdapter is powered on, we expect that discovery session
+ // starts again.
+ EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw);
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
+ discovery.Start();
+ mock_adapter->NotifyAdapterPoweredChanged(true);
+}
+
TEST_F(BluetoothTest, FidoBleDiscoveryNoAdapter) {
// We purposefully construct a temporary and provide no fake adapter,
// simulating cases where the discovery is destroyed before obtaining a handle
@@ -166,4 +198,38 @@ TEST_F(BluetoothTest, FidoBleDiscoveryFindsUpdatedDevice) {
}
}
+TEST_F(BluetoothTest, FidoBleDiscoveryRejectsCableDevice) {
+ if (!PlatformSupportsLowEnergy()) {
+ LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+ return;
+ }
+ InitWithFakeAdapter();
+
+ FidoBleDiscovery discovery;
+ MockFidoDiscoveryObserver observer;
+ discovery.set_observer(&observer);
+
+ {
+ base::RunLoop run_loop;
+ auto quit = run_loop.QuitClosure();
+ EXPECT_CALL(observer, DiscoveryStarted(&discovery, true))
+ .WillOnce(ReturnFromAsyncCall(quit));
+
+ discovery.Start();
+ run_loop.Run();
+ }
+
+ EXPECT_CALL(observer, DeviceAdded(&discovery, _)).Times(0);
+
+ // Simulates a discovery of two Cable devices one of which is an Android Cable
+ // authenticator and other is IOS Cable authenticator.
+ SimulateLowEnergyDevice(8);
+ SimulateLowEnergyDevice(9);
+
+ // Simulates a device change update received from the BluetoothAdapter. As the
+ // updated device has an address that we know is an Cable device, this should
+ // not trigger DeviceAdded().
+ SimulateLowEnergyDevice(7);
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_ble_frames.cc b/chromium/device/fido/ble/fido_ble_frames.cc
index 411b560d1ef..0c50b567a7c 100644
--- a/chromium/device/fido/fido_ble_frames.cc
+++ b/chromium/device/fido/ble/fido_ble_frames.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_frames.h"
+#include "device/fido/ble/fido_ble_frames.h"
#include <algorithm>
#include <limits>
diff --git a/chromium/device/fido/fido_ble_frames.h b/chromium/device/fido/ble/fido_ble_frames.h
index d326251caa7..ea4d16ac784 100644
--- a/chromium/device/fido/fido_ble_frames.h
+++ b/chromium/device/fido/ble/fido_ble_frames.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_FRAMES_H_
-#define DEVICE_FIDO_FIDO_BLE_FRAMES_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_FRAMES_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_FRAMES_H_
#include <stdint.h>
@@ -181,4 +181,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleFrameAssembler {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_FRAMES_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_FRAMES_H_
diff --git a/chromium/device/fido/fido_ble_frames_fuzzer.cc b/chromium/device/fido/ble/fido_ble_frames_fuzzer.cc
index 1e3627dba0b..63b86064d17 100644
--- a/chromium/device/fido/fido_ble_frames_fuzzer.cc
+++ b/chromium/device/fido/ble/fido_ble_frames_fuzzer.cc
@@ -7,7 +7,7 @@
#include <vector>
-#include "device/fido/fido_ble_frames.h"
+#include "device/fido/ble/fido_ble_frames.h"
#include "device/fido/fido_constants.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* raw_data, size_t size) {
diff --git a/chromium/device/fido/fido_ble_frames_unittest.cc b/chromium/device/fido/ble/fido_ble_frames_unittest.cc
index 226c58f0d9b..2d20c8d5370 100644
--- a/chromium/device/fido/fido_ble_frames_unittest.cc
+++ b/chromium/device/fido/ble/fido_ble_frames_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_frames.h"
+#include "device/fido/ble/fido_ble_frames.h"
#include <vector>
diff --git a/chromium/device/fido/fido_ble_transaction.cc b/chromium/device/fido/ble/fido_ble_transaction.cc
index 578395b0abf..59ac87697c6 100644
--- a/chromium/device/fido/fido_ble_transaction.cc
+++ b/chromium/device/fido/ble/fido_ble_transaction.cc
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_transaction.h"
+#include "device/fido/ble/fido_ble_transaction.h"
#include <utility>
-#include "device/fido/fido_ble_connection.h"
+#include "device/fido/ble/fido_ble_connection.h"
#include "device/fido/fido_constants.h"
namespace device {
@@ -50,7 +50,7 @@ void FidoBleTransaction::WriteRequestFragment(
void FidoBleTransaction::OnRequestFragmentWritten(bool success) {
StopTimeout();
if (!success) {
- OnError();
+ OnError(base::nullopt);
return;
}
@@ -72,7 +72,7 @@ void FidoBleTransaction::OnResponseFragment(std::vector<uint8_t> data) {
FidoBleFrameInitializationFragment fragment;
if (!FidoBleFrameInitializationFragment::Parse(data, &fragment)) {
DLOG(ERROR) << "Malformed Frame Initialization Fragment";
- OnError();
+ OnError(base::nullopt);
return;
}
@@ -81,7 +81,7 @@ void FidoBleTransaction::OnResponseFragment(std::vector<uint8_t> data) {
FidoBleFrameContinuationFragment fragment;
if (!FidoBleFrameContinuationFragment::Parse(data, &fragment)) {
DLOG(ERROR) << "Malformed Frame Continuation Fragment";
- OnError();
+ OnError(base::nullopt);
return;
}
@@ -118,22 +118,26 @@ void FidoBleTransaction::ProcessResponseFrame(FidoBleFrame response_frame) {
DCHECK_EQ(response_frame.command(), FidoBleDeviceCommand::kError);
DLOG(ERROR) << "CMD_ERROR: "
<< static_cast<uint8_t>(response_frame.GetErrorCode());
- OnError();
+ OnError(response_frame.IsValid()
+ ? base::make_optional(std::move(response_frame))
+ : base::nullopt);
}
void FidoBleTransaction::StartTimeout() {
- timer_.Start(FROM_HERE, kDeviceTimeout, this, &FidoBleTransaction::OnError);
+ timer_.Start(FROM_HERE, kDeviceTimeout,
+ base::BindOnce(&FidoBleTransaction::OnError,
+ base::Unretained(this), base::nullopt));
}
void FidoBleTransaction::StopTimeout() {
timer_.Stop();
}
-void FidoBleTransaction::OnError() {
+void FidoBleTransaction::OnError(base::Optional<FidoBleFrame> response_frame) {
request_frame_.reset();
request_cont_fragments_ = base::queue<FidoBleFrameContinuationFragment>();
response_frame_assembler_.reset();
- std::move(callback_).Run(base::nullopt);
+ std::move(callback_).Run(std::move(response_frame));
}
} // namespace device
diff --git a/chromium/device/fido/fido_ble_transaction.h b/chromium/device/fido/ble/fido_ble_transaction.h
index d316086d687..a2d585da707 100644
--- a/chromium/device/fido/fido_ble_transaction.h
+++ b/chromium/device/fido/ble/fido_ble_transaction.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_TRANSACTION_H_
-#define DEVICE_FIDO_FIDO_BLE_TRANSACTION_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_TRANSACTION_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_TRANSACTION_H_
#include <memory>
#include <vector>
@@ -13,7 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/timer/timer.h"
-#include "device/fido/fido_ble_frames.h"
+#include "device/fido/ble/fido_ble_frames.h"
namespace device {
@@ -40,7 +40,7 @@ class FidoBleTransaction {
void StartTimeout();
void StopTimeout();
- void OnError();
+ void OnError(base::Optional<FidoBleFrame> response_frame);
FidoBleConnection* connection_;
uint16_t control_point_length_;
@@ -61,4 +61,4 @@ class FidoBleTransaction {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_TRANSACTION_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_TRANSACTION_H_
diff --git a/chromium/device/fido/fido_ble_uuids.cc b/chromium/device/fido/ble/fido_ble_uuids.cc
index 2f96f75d4dc..4e2c5438817 100644
--- a/chromium/device/fido/fido_ble_uuids.cc
+++ b/chromium/device/fido/ble/fido_ble_uuids.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_ble_uuids.h"
+#include "device/fido/ble/fido_ble_uuids.h"
#include "build/build_config.h"
@@ -17,10 +17,8 @@ const char kFidoServiceRevisionUUID[] = "00002a28-0000-1000-8000-00805f9b34fb";
const char kFidoServiceRevisionBitfieldUUID[] =
"f1d0fff4-deaa-ecee-b42f-c9ba7ed623bb";
-#if defined(OS_MACOSX)
-const char kCableAdvertisementUUID[] = "fde2";
-#else
-const char kCableAdvertisementUUID[] = "0000fde2-0000-1000-8000-00805f9b34fb";
-#endif
+const char kCableAdvertisementUUID16[] = "fde2";
+const char kCableAdvertisementUUID128[] =
+ "0000fde2-0000-1000-8000-00805f9b34fb";
} // namespace device
diff --git a/chromium/device/fido/fido_ble_uuids.h b/chromium/device/fido/ble/fido_ble_uuids.h
index a2ead64b534..a608c74ccc7 100644
--- a/chromium/device/fido/fido_ble_uuids.h
+++ b/chromium/device/fido/ble/fido_ble_uuids.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_BLE_UUIDS_H_
-#define DEVICE_FIDO_FIDO_BLE_UUIDS_H_
+#ifndef DEVICE_FIDO_BLE_FIDO_BLE_UUIDS_H_
+#define DEVICE_FIDO_BLE_FIDO_BLE_UUIDS_H_
#include "base/component_export.h"
@@ -24,8 +24,9 @@ COMPONENT_EXPORT(DEVICE_FIDO)
extern const char kFidoServiceRevisionBitfieldUUID[];
// TODO(hongjunchoi): Add URL to the specification once CaBLE protocol is
// standardized.
-COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableAdvertisementUUID[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableAdvertisementUUID16[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableAdvertisementUUID128[];
} // namespace device
-#endif // DEVICE_FIDO_FIDO_BLE_UUIDS_H_
+#endif // DEVICE_FIDO_BLE_FIDO_BLE_UUIDS_H_
diff --git a/chromium/device/fido/mock_fido_ble_connection.cc b/chromium/device/fido/ble/mock_fido_ble_connection.cc
index c9bbab33e40..a8c0573e9a0 100644
--- a/chromium/device/fido/mock_fido_ble_connection.cc
+++ b/chromium/device/fido/ble/mock_fido_ble_connection.cc
@@ -2,36 +2,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/mock_fido_ble_connection.h"
+#include "device/fido/ble/mock_fido_ble_connection.h"
#include <utility>
namespace device {
-MockFidoBleConnection::MockFidoBleConnection(std::string device_address)
- : FidoBleConnection(std::move(device_address)) {}
+MockFidoBleConnection::MockFidoBleConnection(BluetoothAdapter* adapter,
+ std::string device_address)
+ : FidoBleConnection(adapter, std::move(device_address)) {}
MockFidoBleConnection::~MockFidoBleConnection() = default;
+void MockFidoBleConnection::Connect(ConnectionCallback callback) {
+ ConnectPtr(&callback);
+}
+
void MockFidoBleConnection::ReadControlPointLength(
ControlPointLengthCallback callback) {
ReadControlPointLengthPtr(&callback);
}
-void MockFidoBleConnection::ReadServiceRevisions(
- ServiceRevisionsCallback callback) {
- ReadServiceRevisionsPtr(&callback);
-}
-
void MockFidoBleConnection::WriteControlPoint(const std::vector<uint8_t>& data,
WriteCallback callback) {
WriteControlPointPtr(data, &callback);
}
-void MockFidoBleConnection::WriteServiceRevision(
- ServiceRevision service_revision,
- WriteCallback callback) {
- WriteServiceRevisionPtr(service_revision, &callback);
-}
-
} // namespace device
diff --git a/chromium/device/fido/mock_fido_ble_connection.h b/chromium/device/fido/ble/mock_fido_ble_connection.h
index a2db59fa0ed..775859b1cfe 100644
--- a/chromium/device/fido/mock_fido_ble_connection.h
+++ b/chromium/device/fido/ble/mock_fido_ble_connection.h
@@ -2,45 +2,38 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_MOCK_FIDO_BLE_CONNECTION_H_
-#define DEVICE_FIDO_MOCK_FIDO_BLE_CONNECTION_H_
+#ifndef DEVICE_FIDO_BLE_MOCK_FIDO_BLE_CONNECTION_H_
+#define DEVICE_FIDO_BLE_MOCK_FIDO_BLE_CONNECTION_H_
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
-#include "device/fido/fido_ble_connection.h"
+#include "device/fido/ble/fido_ble_connection.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace device {
+class BluetoothAdapter;
+
class MockFidoBleConnection : public FidoBleConnection {
public:
- explicit MockFidoBleConnection(std::string device_address);
+ MockFidoBleConnection(BluetoothAdapter* adapter, std::string device_address);
~MockFidoBleConnection() override;
- MOCK_METHOD0(Connect, void());
// GMock cannot mock a method taking a move-only type.
// TODO(https://crbug.com/729950): Remove these workarounds once support for
// move-only types is added to GMock.
+ MOCK_METHOD1(ConnectPtr, void(ConnectionCallback* cb));
MOCK_METHOD1(ReadControlPointLengthPtr, void(ControlPointLengthCallback* cb));
- MOCK_METHOD1(ReadServiceRevisionsPtr, void(ServiceRevisionsCallback* cb));
MOCK_METHOD2(WriteControlPointPtr,
void(const std::vector<uint8_t>& data, WriteCallback* cb));
- MOCK_METHOD2(WriteServiceRevisionPtr,
- void(ServiceRevision service_revision, WriteCallback* cb));
+ void Connect(ConnectionCallback cb) override;
void ReadControlPointLength(ControlPointLengthCallback cb) override;
- void ReadServiceRevisions(ServiceRevisionsCallback cb) override;
void WriteControlPoint(const std::vector<uint8_t>& data,
WriteCallback cb) override;
- void WriteServiceRevision(ServiceRevision service_revision,
- WriteCallback cb) override;
-
- ConnectionStatusCallback& connection_status_callback() {
- return connection_status_callback_;
- }
ReadCallback& read_callback() { return read_callback_; }
@@ -50,4 +43,4 @@ class MockFidoBleConnection : public FidoBleConnection {
} // namespace device
-#endif // DEVICE_FIDO_MOCK_FIDO_BLE_CONNECTION_H_
+#endif // DEVICE_FIDO_BLE_MOCK_FIDO_BLE_CONNECTION_H_
diff --git a/chromium/device/fido/ble_adapter_power_manager.cc b/chromium/device/fido/ble_adapter_power_manager.cc
new file mode 100644
index 00000000000..7c7a8ec31e4
--- /dev/null
+++ b/chromium/device/fido/ble_adapter_power_manager.cc
@@ -0,0 +1,51 @@
+// Copyright 2018 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/fido/ble_adapter_power_manager.h"
+
+#include <utility>
+
+#include "base/bind_helpers.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+
+namespace device {
+
+BleAdapterPowerManager::BleAdapterPowerManager(
+ FidoRequestHandlerBase* request_handler)
+ : request_handler_(request_handler), weak_factory_(this) {
+ BluetoothAdapterFactory::Get().GetAdapter(base::BindRepeating(
+ &BleAdapterPowerManager::Start, weak_factory_.GetWeakPtr()));
+}
+
+BleAdapterPowerManager::~BleAdapterPowerManager() {
+ if (adapter_powered_on_programmatically_)
+ SetAdapterPower(false /* set_power_on */);
+
+ if (adapter_)
+ adapter_->RemoveObserver(this);
+}
+
+void BleAdapterPowerManager::SetAdapterPower(bool set_power_on) {
+ if (set_power_on)
+ adapter_powered_on_programmatically_ = true;
+
+ adapter_->SetPowered(set_power_on, base::DoNothing(), base::DoNothing());
+}
+
+void BleAdapterPowerManager::AdapterPoweredChanged(BluetoothAdapter* adapter,
+ bool powered) {
+ request_handler_->OnBluetoothAdapterPowerChanged(powered);
+}
+
+void BleAdapterPowerManager::Start(scoped_refptr<BluetoothAdapter> adapter) {
+ DCHECK(!adapter_);
+ adapter_ = std::move(adapter);
+ DCHECK(adapter_);
+ adapter_->AddObserver(this);
+
+ request_handler_->OnBluetoothAdapterEnumerated(
+ adapter_->IsPresent(), adapter_->IsPowered(), adapter_->CanPower());
+}
+
+} // namespace device
diff --git a/chromium/device/fido/ble_adapter_power_manager.h b/chromium/device/fido/ble_adapter_power_manager.h
new file mode 100644
index 00000000000..194e0defbf7
--- /dev/null
+++ b/chromium/device/fido/ble_adapter_power_manager.h
@@ -0,0 +1,49 @@
+// Copyright 2018 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_FIDO_BLE_ADAPTER_POWER_MANAGER_H_
+#define DEVICE_FIDO_BLE_ADAPTER_POWER_MANAGER_H_
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/fido/fido_request_handler_base.h"
+
+namespace device {
+
+class COMPONENT_EXPORT(DEVICE_FIDO) BleAdapterPowerManager
+ : public BluetoothAdapter::Observer {
+ public:
+ // Handles notifying |request_handler| when BluetoothAdapter is powered on and
+ // off. Exposes API for |request_handler| to power on BluetoothAdapter
+ // programmatically, and if BluetoothAdapter was powered on programmatically,
+ // powers off BluetoothAdapter when |this| goes out of scope.
+ // |request_handler| must outlive |this|.
+ BleAdapterPowerManager(FidoRequestHandlerBase* request_handler);
+ ~BleAdapterPowerManager() override;
+
+ void SetAdapterPower(bool set_power_on);
+
+ private:
+ friend class FidoBleAdapterPowerManagerTest;
+
+ // BluetoothAdapter::Observer:
+ void AdapterPoweredChanged(BluetoothAdapter* adapter, bool powered) override;
+
+ void Start(scoped_refptr<BluetoothAdapter> adapter);
+
+ FidoRequestHandlerBase* const request_handler_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+ bool adapter_powered_on_programmatically_ = false;
+
+ base::WeakPtrFactory<BleAdapterPowerManager> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BleAdapterPowerManager);
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_BLE_ADAPTER_POWER_MANAGER_H_
diff --git a/chromium/device/fido/ble_adapter_power_manager_unittest.cc b/chromium/device/fido/ble_adapter_power_manager_unittest.cc
new file mode 100644
index 00000000000..8e62fc5a9d9
--- /dev/null
+++ b/chromium/device/fido/ble_adapter_power_manager_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2018 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/fido/ble_adapter_power_manager.h"
+
+#include <memory>
+
+#include "base/test/scoped_task_environment.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/fido/fido_request_handler_base.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+using ::testing::_;
+
+class MockTransportAvailabilityObserver
+ : public FidoRequestHandlerBase::TransportAvailabilityObserver {
+ public:
+ MockTransportAvailabilityObserver() = default;
+ ~MockTransportAvailabilityObserver() override = default;
+
+ MOCK_METHOD1(OnTransportAvailabilityEnumerated,
+ void(FidoRequestHandlerBase::TransportAvailabilityInfo data));
+ MOCK_METHOD1(EmbedderControlsAuthenticatorDispatch,
+ bool(const FidoAuthenticator& authenticator));
+ MOCK_METHOD1(BluetoothAdapterPowerChanged, void(bool is_powered_on));
+ MOCK_METHOD1(FidoAuthenticatorAdded,
+ void(const FidoAuthenticator& authenticator));
+ MOCK_METHOD1(FidoAuthenticatorRemoved, void(base::StringPiece device_id));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockTransportAvailabilityObserver);
+};
+
+class FakeFidoRequestHandlerBase : public FidoRequestHandlerBase {
+ public:
+ explicit FakeFidoRequestHandlerBase(
+ MockTransportAvailabilityObserver* observer)
+ : FidoRequestHandlerBase(nullptr, {}) {
+ set_observer(observer);
+ }
+
+ private:
+ void DispatchRequest(FidoAuthenticator*) override {}
+
+ DISALLOW_COPY_AND_ASSIGN(FakeFidoRequestHandlerBase);
+};
+
+} // namespace
+
+class FidoBleAdapterPowerManagerTest : public ::testing::Test {
+ public:
+ FidoBleAdapterPowerManagerTest() {
+ BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
+ }
+
+ std::unique_ptr<BleAdapterPowerManager> CreateTestBleAdapterPowerManager() {
+ return std::make_unique<BleAdapterPowerManager>(
+ fake_request_handler_.get());
+ }
+
+ MockBluetoothAdapter* adapter() { return adapter_.get(); }
+ MockTransportAvailabilityObserver* observer() { return mock_observer_.get(); }
+ bool adapter_powered_on_programmatically(
+ const BleAdapterPowerManager& adapter_power_manager) {
+ return adapter_power_manager.adapter_powered_on_programmatically_;
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ scoped_refptr<MockBluetoothAdapter> adapter_ =
+ base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
+ std::unique_ptr<MockTransportAvailabilityObserver> mock_observer_ =
+ std::make_unique<MockTransportAvailabilityObserver>();
+ std::unique_ptr<FakeFidoRequestHandlerBase> fake_request_handler_ =
+ std::make_unique<FakeFidoRequestHandlerBase>(mock_observer_.get());
+};
+
+TEST_F(FidoBleAdapterPowerManagerTest, AdapaterNotPresent) {
+ EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*adapter(), IsPowered()).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*adapter(), CanPower()).WillOnce(::testing::Return(false));
+
+ FidoRequestHandlerBase::TransportAvailabilityInfo data;
+ EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
+ .WillOnce(::testing::SaveArg<0>(&data));
+
+ CreateTestBleAdapterPowerManager();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(data.is_ble_powered);
+ EXPECT_FALSE(data.can_power_on_ble_adapter);
+}
+
+TEST_F(FidoBleAdapterPowerManagerTest, AdapaterPresentAndPowered) {
+ EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(true));
+ EXPECT_CALL(*adapter(), IsPowered()).WillOnce(::testing::Return(true));
+ EXPECT_CALL(*adapter(), CanPower()).WillOnce(::testing::Return(false));
+
+ FidoRequestHandlerBase::TransportAvailabilityInfo data;
+ EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
+ .WillOnce(::testing::SaveArg<0>(&data));
+
+ CreateTestBleAdapterPowerManager();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_TRUE(data.is_ble_powered);
+ EXPECT_FALSE(data.can_power_on_ble_adapter);
+}
+
+TEST_F(FidoBleAdapterPowerManagerTest, AdapaterPresentAndCanBePowered) {
+ EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(true));
+ EXPECT_CALL(*adapter(), IsPowered()).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*adapter(), CanPower()).WillOnce(::testing::Return(true));
+
+ FidoRequestHandlerBase::TransportAvailabilityInfo data;
+ EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
+ .WillOnce(::testing::SaveArg<0>(&data));
+
+ CreateTestBleAdapterPowerManager();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(data.is_ble_powered);
+ EXPECT_TRUE(data.can_power_on_ble_adapter);
+}
+
+TEST_F(FidoBleAdapterPowerManagerTest, TestSetBluetoothPowerOn) {
+ auto power_manager = CreateTestBleAdapterPowerManager();
+ ::testing::InSequence s;
+ EXPECT_CALL(*adapter(), SetPowered(true, _, _));
+ EXPECT_CALL(*adapter(), SetPowered(false, _, _));
+ power_manager->SetAdapterPower(true /* set_power_on */);
+ EXPECT_TRUE(adapter_powered_on_programmatically(*power_manager));
+ power_manager.reset();
+}
+
+} // namespace device
diff --git a/chromium/device/fido/cable/cable_discovery_data.h b/chromium/device/fido/cable/cable_discovery_data.h
new file mode 100644
index 00000000000..67bfccef2ee
--- /dev/null
+++ b/chromium/device/fido/cable/cable_discovery_data.h
@@ -0,0 +1,45 @@
+// Copyright 2018 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_FIDO_CABLE_CABLE_DISCOVERY_DATA_H_
+#define DEVICE_FIDO_CABLE_CABLE_DISCOVERY_DATA_H_
+
+#include <stdint.h>
+#include <array>
+
+#include "base/component_export.h"
+
+namespace device {
+
+constexpr size_t kEphemeralIdSize = 16;
+constexpr size_t kSessionPreKeySize = 32;
+
+using EidArray = std::array<uint8_t, kEphemeralIdSize>;
+using SessionPreKeyArray = std::array<uint8_t, kSessionPreKeySize>;
+
+// Encapsulates information required to discover Cable device per single
+// credential. When multiple credentials are enrolled to a single account
+// (i.e. more than one phone has been enrolled to an user account as a
+// security key), then FidoCableDiscovery must advertise for all of the client
+// EID received from the relying party.
+// TODO(hongjunchoi): Add discovery data required for MakeCredential request.
+// See: https://crbug.com/837088
+struct COMPONENT_EXPORT(DEVICE_FIDO) CableDiscoveryData {
+ CableDiscoveryData(uint8_t version,
+ const EidArray& client_eid,
+ const EidArray& authenticator_eid,
+ const SessionPreKeyArray& session_pre_key);
+ CableDiscoveryData(const CableDiscoveryData& data);
+ CableDiscoveryData& operator=(const CableDiscoveryData& other);
+ ~CableDiscoveryData();
+
+ uint8_t version;
+ EidArray client_eid;
+ EidArray authenticator_eid;
+ SessionPreKeyArray session_pre_key;
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_CABLE_CABLE_DISCOVERY_DATA_H_
diff --git a/chromium/device/fido/fido_cable_device.cc b/chromium/device/fido/cable/fido_cable_device.cc
index 31cf975dfb0..1c5dd607dd0 100644
--- a/chromium/device/fido/fido_cable_device.cc
+++ b/chromium/device/fido/cable/fido_cable_device.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_cable_device.h"
+#include "device/fido/cable/fido_cable_device.h"
#include <utility>
#include "base/strings/string_piece.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "device/fido/fido_ble_connection.h"
-#include "device/fido/fido_ble_frames.h"
+#include "device/fido/ble/fido_ble_connection.h"
+#include "device/fido/ble/fido_ble_frames.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
@@ -113,8 +113,8 @@ FidoCableDevice::EncryptionData::~EncryptionData() = default;
// FidoCableDevice::EncryptionData ----------------------------------------
-FidoCableDevice::FidoCableDevice(std::string address)
- : FidoBleDevice(std::move(address)), weak_factory_(this) {}
+FidoCableDevice::FidoCableDevice(BluetoothAdapter* adapter, std::string address)
+ : FidoBleDevice(adapter, std::move(address)), weak_factory_(this) {}
FidoCableDevice::FidoCableDevice(std::unique_ptr<FidoBleConnection> connection)
: FidoBleDevice(std::move(connection)), weak_factory_(this) {}
@@ -128,9 +128,9 @@ void FidoCableDevice::DeviceTransact(std::vector<uint8_t> command,
FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
state_ = State::kDeviceError;
return;
- }
+ }
- ++encryption_data_->write_sequence_num;
+ ++encryption_data_->write_sequence_num;
AddToPendingFrames(FidoBleDeviceCommand::kMsg, std::move(command),
std::move(callback));
@@ -177,4 +177,8 @@ void FidoCableDevice::SetEncryptionData(std::string session_key,
encryption_data_.emplace(std::move(session_key), nonce);
}
+FidoTransportProtocol FidoCableDevice::DeviceTransport() const {
+ return FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy;
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_cable_device.h b/chromium/device/fido/cable/fido_cable_device.h
index 993df405760..29e3907f165 100644
--- a/chromium/device/fido/fido_cable_device.h
+++ b/chromium/device/fido/cable/fido_cable_device.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_CABLE_DEVICE_H_
-#define DEVICE_FIDO_FIDO_CABLE_DEVICE_H_
+#ifndef DEVICE_FIDO_CABLE_FIDO_CABLE_DEVICE_H_
+#define DEVICE_FIDO_CABLE_FIDO_CABLE_DEVICE_H_
#include <array>
#include <memory>
@@ -16,12 +16,13 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "crypto/aead.h"
-#include "device/fido/fido_ble_device.h"
+#include "device/fido/ble/fido_ble_device.h"
namespace device {
-class FidoBleFrame;
+class BluetoothAdapter;
class FidoBleConnection;
+class FidoBleFrame;
class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
public:
@@ -44,7 +45,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
using FrameCallback = FidoBleTransaction::FrameCallback;
- FidoCableDevice(std::string address);
+ FidoCableDevice(BluetoothAdapter* adapter, std::string address);
// Constructor used for testing purposes.
FidoCableDevice(std::unique_ptr<FidoBleConnection> connection);
~FidoCableDevice() override;
@@ -61,6 +62,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
void SetEncryptionData(std::string session_key,
base::span<const uint8_t, 8> nonce);
+ FidoTransportProtocol DeviceTransport() const override;
private:
FRIEND_TEST_ALL_PREFIXES(FidoCableDeviceTest,
@@ -76,4 +78,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_CABLE_DEVICE_H_
+#endif // DEVICE_FIDO_CABLE_FIDO_CABLE_DEVICE_H_
diff --git a/chromium/device/fido/fido_cable_device_unittest.cc b/chromium/device/fido/cable/fido_cable_device_unittest.cc
index ec76ea0b098..e99e691a8d1 100644
--- a/chromium/device/fido/fido_cable_device_unittest.cc
+++ b/chromium/device/fido/cable/fido_cable_device_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_cable_device.h"
+#include "device/fido/cable/fido_cable_device.h"
#include <array>
#include <limits>
@@ -16,8 +16,9 @@
#include "base/threading/sequenced_task_runner_handle.h"
#include "crypto/aead.h"
#include "device/bluetooth/test/bluetooth_test.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/fido/ble/mock_fido_ble_connection.h"
#include "device/fido/fido_parsing_utils.h"
-#include "device/fido/mock_fido_ble_connection.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,6 +32,7 @@ using ::testing::Invoke;
using ::testing::Test;
using TestDeviceCallbackReceiver =
test::ValueCallbackReceiver<base::Optional<std::vector<uint8_t>>>;
+using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
// Sufficiently large test control point length as we are not interested
// in testing fragmentations of BLE messages. All Cable messages are encrypted
@@ -128,19 +130,16 @@ class FidoCableDeviceTest : public Test {
public:
FidoCableDeviceTest() {
auto connection = std::make_unique<MockFidoBleConnection>(
- BluetoothTestBase::kTestDeviceAddress1);
+ adapter_.get(), BluetoothTestBase::kTestDeviceAddress1);
connection_ = connection.get();
device_ = std::make_unique<FidoCableDevice>(std::move(connection));
device_->SetEncryptionData(kTestSessionKey, kTestEncryptionNonce);
-
- connection_->connection_status_callback() =
- device_->GetConnectionStatusCallbackForTesting();
connection_->read_callback() = device_->GetReadCallbackForTesting();
}
void ConnectWithLength(uint16_t length) {
- EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
- connection()->connection_status_callback().Run(true);
+ EXPECT_CALL(*connection(), ConnectPtr).WillOnce(Invoke([](auto* callback) {
+ std::move(*callback).Run(true);
}));
EXPECT_CALL(*connection(), ReadControlPointLengthPtr(_))
@@ -157,6 +156,8 @@ class FidoCableDeviceTest : public Test {
base::test::ScopedTaskEnvironment scoped_task_environment_;
private:
+ scoped_refptr<MockBluetoothAdapter> adapter_ =
+ base::MakeRefCounted<NiceMockBluetoothAdapter>();
FakeCableAuthenticator authenticator_;
MockFidoBleConnection* connection_;
std::unique_ptr<FidoCableDevice> device_;
diff --git a/chromium/device/fido/fido_cable_discovery.cc b/chromium/device/fido/cable/fido_cable_discovery.cc
index 5911dda11f3..2934fa7248b 100644
--- a/chromium/device/fido/fido_cable_discovery.cc
+++ b/chromium/device/fido/cable/fido_cable_discovery.cc
@@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_cable_discovery.h"
+#include "device/fido/cable/fido_cable_discovery.h"
#include <algorithm>
#include <memory>
#include <utility>
+#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
@@ -17,53 +18,15 @@
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_uuid.h"
-#include "device/fido/fido_ble_uuids.h"
-#include "device/fido/fido_cable_device.h"
-#include "device/fido/fido_cable_handshake_handler.h"
+#include "device/fido/ble/fido_ble_uuids.h"
+#include "device/fido/cable/fido_cable_device.h"
+#include "device/fido/cable/fido_cable_handshake_handler.h"
#include "device/fido/fido_parsing_utils.h"
namespace device {
namespace {
-#if defined(OS_MACOSX)
-
-// Convert byte array into GUID formatted string as defined by RFC 4122.
-// As we are converting 128 bit UUID, |bytes| must be have length of 16.
-// https://tools.ietf.org/html/rfc4122
-std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes) {
- uint64_t most_significant_bytes = 0;
- for (size_t i = 0; i < sizeof(uint64_t); i++) {
- most_significant_bytes |= base::strict_cast<uint64_t>(bytes[i])
- << 8 * (7 - i);
- }
-
- uint64_t least_significant_bytes = 0;
- for (size_t i = 0; i < sizeof(uint64_t); i++) {
- least_significant_bytes |= base::strict_cast<uint64_t>(bytes[i + 8])
- << 8 * (7 - i);
- }
-
- return base::StringPrintf(
- "%08x-%04x-%04x-%04x-%012llx",
- static_cast<unsigned int>(most_significant_bytes >> 32),
- static_cast<unsigned int>((most_significant_bytes >> 16) & 0x0000ffff),
- static_cast<unsigned int>(most_significant_bytes & 0x0000ffff),
- static_cast<unsigned int>(least_significant_bytes >> 48),
- least_significant_bytes & 0x0000ffff'ffffffffULL);
-}
-
-#endif
-
-const BluetoothUUID& CableAdvertisementUUID() {
- static const BluetoothUUID service_uuid(kCableAdvertisementUUID);
- return service_uuid;
-}
-
-bool IsCableDevice(const BluetoothDevice* device) {
- return base::ContainsKey(device->GetServiceData(), CableAdvertisementUUID());
-}
-
// Construct advertisement data with different formats depending on client's
// operating system. Ideally, we advertise EIDs as part of Service Data, but
// this isn't available on all platforms. On Windows we use Manufacturer Data
@@ -71,32 +34,48 @@ bool IsCableDevice(const BluetoothDevice* device) {
// with the EID as its UUID.
std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
uint8_t version_number,
- base::span<const uint8_t, FidoCableDiscovery::kEphemeralIdSize>
- client_eid) {
+ base::span<const uint8_t, kEphemeralIdSize> client_eid) {
auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>(
BluetoothAdvertisement::AdvertisementType::ADVERTISEMENT_TYPE_BROADCAST);
#if defined(OS_MACOSX)
auto list = std::make_unique<BluetoothAdvertisement::UUIDList>();
- list->emplace_back(kCableAdvertisementUUID);
- list->emplace_back(ConvertBytesToUuid(client_eid));
+ list->emplace_back(kCableAdvertisementUUID16);
+ list->emplace_back(fido_parsing_utils::ConvertBytesToUuid(client_eid));
advertisement_data->set_service_uuids(std::move(list));
#elif defined(OS_WIN)
- constexpr uint16_t kFidoManufacturerId = 0xFFFD;
- constexpr std::array<uint8_t, 2> kFidoManufacturerDataHeader = {0x51, 0xFE};
+ // References:
+ // https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
+ // go/google-ble-manufacturer-data-format
+ static constexpr uint16_t kGoogleManufacturerId = 0x00E0;
+ static constexpr uint8_t kCableGoogleManufacturerDataType = 0x15;
+
+ // Reference:
+ // https://github.com/arnar/fido-2-specs/blob/fido-client-to-authenticator-protocol.bs#L4314
+ static constexpr uint8_t kCableFlags = 0x20;
+
+ static constexpr uint8_t kCableGoogleManufacturerDataLength =
+ 3u + kEphemeralIdSize;
+ std::array<uint8_t, 4> kCableGoogleManufacturerDataHeader = {
+ kCableGoogleManufacturerDataLength, kCableGoogleManufacturerDataType,
+ kCableFlags, version_number};
auto manufacturer_data =
std::make_unique<BluetoothAdvertisement::ManufacturerData>();
std::vector<uint8_t> manufacturer_data_value;
fido_parsing_utils::Append(&manufacturer_data_value,
- kFidoManufacturerDataHeader);
+ kCableGoogleManufacturerDataHeader);
fido_parsing_utils::Append(&manufacturer_data_value, client_eid);
- manufacturer_data->emplace(kFidoManufacturerId,
+ manufacturer_data->emplace(kGoogleManufacturerId,
std::move(manufacturer_data_value));
advertisement_data->set_manufacturer_data(std::move(manufacturer_data));
#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+ // Reference:
+ // https://github.com/arnar/fido-2-specs/blob/fido-client-to-authenticator-protocol.bs#L4314
+ static constexpr uint8_t kCableFlags = 0x20;
+
// Service data for ChromeOS and Linux is 1 byte corresponding to Cable flags,
// followed by 1 byte corresponding to Cable version number, followed by 16
// bytes corresponding to client EID.
@@ -104,11 +83,12 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
std::vector<uint8_t> service_data_value(18, 0);
// Since the remainder of this service data field is a Cable EID, set the 5th
// bit of the flag byte.
- service_data_value[0] = 1 << 5;
+ service_data_value[0] = kCableFlags;
service_data_value[1] = version_number;
std::copy(client_eid.begin(), client_eid.end(),
service_data_value.begin() + 2);
- service_data->emplace(kCableAdvertisementUUID, std::move(service_data_value));
+ service_data->emplace(kCableAdvertisementUUID128,
+ std::move(service_data_value));
advertisement_data->set_service_data(std::move(service_data));
#endif
@@ -117,9 +97,9 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
} // namespace
-// FidoCableDiscovery::CableDiscoveryData -------------------------------------
+// CableDiscoveryData -------------------------------------
-FidoCableDiscovery::CableDiscoveryData::CableDiscoveryData(
+CableDiscoveryData::CableDiscoveryData(
uint8_t version,
const EidArray& client_eid,
const EidArray& authenticator_eid,
@@ -129,19 +109,30 @@ FidoCableDiscovery::CableDiscoveryData::CableDiscoveryData(
authenticator_eid(authenticator_eid),
session_pre_key(session_pre_key) {}
-FidoCableDiscovery::CableDiscoveryData::CableDiscoveryData(
- const CableDiscoveryData& data) = default;
+CableDiscoveryData::CableDiscoveryData(const CableDiscoveryData& data) =
+ default;
-FidoCableDiscovery::CableDiscoveryData& FidoCableDiscovery::CableDiscoveryData::
-operator=(const CableDiscoveryData& other) = default;
+CableDiscoveryData& CableDiscoveryData::operator=(
+ const CableDiscoveryData& other) = default;
-FidoCableDiscovery::CableDiscoveryData::~CableDiscoveryData() = default;
+CableDiscoveryData::~CableDiscoveryData() = default;
// FidoCableDiscovery ---------------------------------------------------------
FidoCableDiscovery::FidoCableDiscovery(
std::vector<CableDiscoveryData> discovery_data)
- : discovery_data_(std::move(discovery_data)), weak_factory_(this) {}
+ : FidoBleDiscoveryBase(
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy),
+ discovery_data_(std::move(discovery_data)),
+ weak_factory_(this) {
+// Windows currently does not support multiple EIDs, thus we ignore any extra
+// discovery data.
+// TODO(https://crbug.com/837088): Add support for multiple EIDs on Windows.
+#if defined(OS_WIN)
+ if (discovery_data_.size() > 1u)
+ discovery_data_.erase(discovery_data_.begin() + 1, discovery_data_.end());
+#endif
+}
// This is a workaround for https://crbug.com/846522
FidoCableDiscovery::~FidoCableDiscovery() {
@@ -185,14 +176,49 @@ void FidoCableDiscovery::DeviceRemoved(BluetoothAdapter* adapter,
}
}
+void FidoCableDiscovery::AdapterPoweredChanged(BluetoothAdapter* adapter,
+ bool powered) {
+ // If Bluetooth adapter is powered on, resume scanning for nearby Cable
+ // devices and start advertising client EIDs.
+ if (powered) {
+ StartCableDiscovery();
+ } else {
+ // In order to prevent duplicate client EIDs from being advertised when
+ // BluetoothAdapter is powered back on, unregister all existing client
+ // EIDs.
+ StopAdvertisements(base::DoNothing());
+ }
+}
+
void FidoCableDiscovery::OnSetPowered() {
DCHECK(adapter());
base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&FidoCableDiscovery::StartAdvertisement,
+ FROM_HERE, base::BindOnce(&FidoCableDiscovery::StartCableDiscovery,
weak_factory_.GetWeakPtr()));
}
+void FidoCableDiscovery::StartCableDiscovery() {
+ // Error callback OnStartDiscoverySessionError() is defined in the base class
+ // FidoBleDiscoveryBase.
+ adapter()->StartDiscoverySessionWithFilter(
+ std::make_unique<BluetoothDiscoveryFilter>(
+ BluetoothTransport::BLUETOOTH_TRANSPORT_LE),
+ base::AdaptCallbackForRepeating(
+ base::BindOnce(&FidoCableDiscovery::OnStartDiscoverySessionWithFilter,
+ weak_factory_.GetWeakPtr())),
+ base::AdaptCallbackForRepeating(
+ base::BindOnce(&FidoCableDiscovery::OnStartDiscoverySessionError,
+ weak_factory_.GetWeakPtr())));
+}
+
+void FidoCableDiscovery::OnStartDiscoverySessionWithFilter(
+ std::unique_ptr<BluetoothDiscoverySession> session) {
+ SetDiscoverySession(std::move(session));
+ DVLOG(2) << "Discovery session started.";
+ StartAdvertisement();
+}
+
void FidoCableDiscovery::StartAdvertisement() {
DCHECK(adapter());
@@ -208,6 +234,20 @@ void FidoCableDiscovery::StartAdvertisement() {
}
}
+void FidoCableDiscovery::StopAdvertisements(base::OnceClosure callback) {
+ auto barrier_closure =
+ base::BarrierClosure(advertisement_success_counter_, std::move(callback));
+ for (auto advertisement : advertisements_)
+ advertisement.second->Unregister(barrier_closure, base::DoNothing());
+
+#if !defined(OS_WIN)
+ // On Windows the discovery is the only owner of the advertisements, meaning
+ // the advertisements would be destroyed before |barrier_closure| could be
+ // invoked.
+ advertisements_.clear();
+#endif // !defined(OS_WIN)
+}
+
void FidoCableDiscovery::OnAdvertisementRegistered(
const EidArray& client_eid,
scoped_refptr<BluetoothAdvertisement> advertisement) {
@@ -223,32 +263,16 @@ void FidoCableDiscovery::OnAdvertisementRegisterError(
}
void FidoCableDiscovery::RecordAdvertisementResult(bool is_success) {
- is_success ? ++advertisement_success_counter_
- : ++advertisement_failure_counter_;
-
- // Wait until all advertisements are sent out.
- if (advertisement_success_counter_ + advertisement_failure_counter_ !=
- discovery_data_.size()) {
+ // If at least one advertisement succeeds, then notify discovery start.
+ if (is_success) {
+ if (!advertisement_success_counter_++)
+ NotifyDiscoveryStarted(true);
return;
}
- // No advertisements succeeded, no point in starting scanning.
- if (!advertisement_success_counter_) {
+ // No advertisements succeeded, no point in continuing with Cable discovery.
+ if (++advertisement_failure_counter_ == discovery_data_.size())
NotifyDiscoveryStarted(false);
- return;
- }
-
- // At least one advertisement succeeded and all advertisement has been
- // processed. Start scanning.
- adapter()->StartDiscoverySessionWithFilter(
- std::make_unique<BluetoothDiscoveryFilter>(
- BluetoothTransport::BLUETOOTH_TRANSPORT_LE),
- base::AdaptCallbackForRepeating(
- base::BindOnce(&FidoCableDiscovery::OnStartDiscoverySessionWithFilter,
- weak_factory_.GetWeakPtr())),
- base::AdaptCallbackForRepeating(
- base::BindOnce(&FidoCableDiscovery::OnStartDiscoverySessionError,
- weak_factory_.GetWeakPtr())));
}
void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter,
@@ -265,18 +289,22 @@ void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter,
if (!extract_success)
return;
- auto cable_device = std::make_unique<FidoCableDevice>(device->GetAddress());
- // At most one handshake messages should be exchanged for each Cable device.
- if (!base::ContainsKey(cable_handshake_handlers_, cable_device->GetId())) {
- ConductEncryptionHandshake(std::move(cable_device),
- found_cable_device_data->session_pre_key, nonce);
- }
+ auto cable_device =
+ std::make_unique<FidoCableDevice>(adapter, device->GetAddress());
+ StopAdvertisements(
+ base::BindOnce(&FidoCableDiscovery::ConductEncryptionHandshake,
+ weak_factory_.GetWeakPtr(), std::move(cable_device),
+ found_cable_device_data->session_pre_key, nonce));
}
void FidoCableDiscovery::ConductEncryptionHandshake(
std::unique_ptr<FidoCableDevice> cable_device,
base::span<const uint8_t, kSessionPreKeySize> session_pre_key,
base::span<const uint8_t, 8> nonce) {
+ // At most one handshake messages should be exchanged for each Cable device.
+ if (base::ContainsKey(cable_handshake_handlers_, cable_device->GetId()))
+ return;
+
auto handshake_handler =
CreateHandshakeHandler(cable_device.get(), session_pre_key, nonce);
auto* const handshake_handler_ptr = handshake_handler.get();
@@ -296,18 +324,34 @@ void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage(
if (!handshake_response)
return;
- if (!handshake_handler->ValidateAuthenticatorHandshakeMessage(
- *handshake_response))
- return;
+ if (handshake_handler->ValidateAuthenticatorHandshakeMessage(
+ *handshake_response)) {
+ DVLOG(2) << "Authenticator handshake validated";
+ AddDevice(std::move(cable_device));
+ } else {
+ DVLOG(2) << "Authenticator handshake invalid";
+ }
+}
+
+const CableDiscoveryData* FidoCableDiscovery::GetFoundCableDiscoveryData(
+ const BluetoothDevice* device) const {
+ const auto* discovery_data =
+ GetFoundCableDiscoveryDataFromServiceData(device);
+ if (discovery_data != nullptr) {
+ return discovery_data;
+ }
- AddDevice(std::move(cable_device));
+ return GetFoundCableDiscoveryDataFromServiceUUIDs(device);
}
-const FidoCableDiscovery::CableDiscoveryData*
-FidoCableDiscovery::GetFoundCableDiscoveryData(
+const CableDiscoveryData*
+FidoCableDiscovery::GetFoundCableDiscoveryDataFromServiceData(
const BluetoothDevice* device) const {
const auto* service_data =
device->GetServiceDataForUUID(CableAdvertisementUUID());
+ if (!service_data) {
+ return nullptr;
+ }
DCHECK(service_data);
// Received service data from authenticator must have a flag that signals that
@@ -332,4 +376,26 @@ FidoCableDiscovery::GetFoundCableDiscoveryData(
: nullptr;
}
+const CableDiscoveryData*
+FidoCableDiscovery::GetFoundCableDiscoveryDataFromServiceUUIDs(
+ const BluetoothDevice* device) const {
+ const auto service_uuids = device->GetUUIDs();
+ for (const auto& uuid : service_uuids) {
+ if (uuid == CableAdvertisementUUID())
+ continue;
+
+ auto discovery_data_iterator = std::find_if(
+ discovery_data_.begin(), discovery_data_.end(),
+ [&uuid](const auto& data) {
+ std::string received_eid_string =
+ fido_parsing_utils::ConvertBytesToUuid(data.authenticator_eid);
+ return uuid == BluetoothUUID(received_eid_string);
+ });
+ if (discovery_data_iterator != discovery_data_.end()) {
+ return &(*discovery_data_iterator);
+ }
+ }
+ return nullptr;
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_cable_discovery.h b/chromium/device/fido/cable/fido_cable_discovery.h
index a3b9ea27678..9bbad3b69f3 100644
--- a/chromium/device/fido/fido_cable_discovery.h
+++ b/chromium/device/fido/cable/fido_cable_discovery.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_CABLE_DISCOVERY_H_
-#define DEVICE_FIDO_FIDO_CABLE_DISCOVERY_H_
+#ifndef DEVICE_FIDO_CABLE_FIDO_CABLE_DISCOVERY_H_
+#define DEVICE_FIDO_CABLE_FIDO_CABLE_DISCOVERY_H_
#include <stdint.h>
@@ -18,7 +18,8 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "device/fido/fido_ble_discovery_base.h"
+#include "device/fido/ble/fido_ble_discovery_base.h"
+#include "device/fido/cable/cable_discovery_data.h"
namespace device {
@@ -30,33 +31,6 @@ class FidoCableHandshakeHandler;
class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
: public FidoBleDiscoveryBase {
public:
- static constexpr size_t kEphemeralIdSize = 16;
- static constexpr size_t kSessionPreKeySize = 32;
- using EidArray = std::array<uint8_t, kEphemeralIdSize>;
- using SessionPreKeyArray = std::array<uint8_t, kSessionPreKeySize>;
-
- // Encapsulates information required to discover Cable device per single
- // credential. When multiple credentials are enrolled to a single account
- // (i.e. more than one phone has been enrolled to an user account as a
- // security key), then FidoCableDiscovery must advertise for all of the client
- // EID received from the relying party.
- // TODO(hongjunchoi): Add discovery data required for MakeCredential request.
- // See: https://crbug.com/837088
- struct COMPONENT_EXPORT(DEVICE_FIDO) CableDiscoveryData {
- CableDiscoveryData(uint8_t version,
- const EidArray& client_eid,
- const EidArray& authenticator_eid,
- const SessionPreKeyArray& session_pre_key);
- CableDiscoveryData(const CableDiscoveryData& data);
- CableDiscoveryData& operator=(const CableDiscoveryData& other);
- ~CableDiscoveryData();
-
- uint8_t version;
- EidArray client_eid;
- EidArray authenticator_eid;
- SessionPreKeyArray session_pre_key;
- };
-
FidoCableDiscovery(std::vector<CableDiscoveryData> discovery_data);
~FidoCableDiscovery() override;
@@ -68,6 +42,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
private:
FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest,
+ TestDiscoveryWithAdvertisementFailures);
+ FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest,
TestUnregisterAdvertisementUponDestruction);
// BluetoothAdapter::Observer:
@@ -76,10 +52,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
BluetoothDevice* device) override;
void DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) override;
+ void AdapterPoweredChanged(BluetoothAdapter* adapter, bool powered) override;
// FidoBleDiscoveryBase:
void OnSetPowered() override;
+ void OnStartDiscoverySessionWithFilter(
+ std::unique_ptr<BluetoothDiscoverySession>) override;
+ void StartCableDiscovery();
void StartAdvertisement();
void OnAdvertisementRegistered(
const EidArray& client_eid,
@@ -91,6 +71,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
// invoke NotifyDiscoveryStarted(false). Otherwise kick off discovery session
// once all advertisements has been processed.
void RecordAdvertisementResult(bool is_success);
+ // Attempt to stop all on-going advertisements in best-effort basis.
+ // Once all the callbacks for Unregister() function is received, invoke
+ // |callback|.
+ void StopAdvertisements(base::OnceClosure callback);
void CableDeviceFound(BluetoothAdapter* adapter, BluetoothDevice* device);
void ConductEncryptionHandshake(
std::unique_ptr<FidoCableDevice> device,
@@ -103,6 +87,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
const CableDiscoveryData* GetFoundCableDiscoveryData(
const BluetoothDevice* device) const;
+ const CableDiscoveryData* GetFoundCableDiscoveryDataFromServiceData(
+ const BluetoothDevice* device) const;
+ const CableDiscoveryData* GetFoundCableDiscoveryDataFromServiceUUIDs(
+ const BluetoothDevice* device) const;
std::vector<CableDiscoveryData> discovery_data_;
size_t advertisement_success_counter_ = 0;
@@ -117,4 +105,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
} // namespace device
-#endif // DEVICE_FIDO_FIDO_CABLE_DISCOVERY_H_
+#endif // DEVICE_FIDO_CABLE_FIDO_CABLE_DISCOVERY_H_
diff --git a/chromium/device/fido/fido_cable_discovery_unittest.cc b/chromium/device/fido/cable/fido_cable_discovery_unittest.cc
index 859955de745..8af70961669 100644
--- a/chromium/device/fido/fido_cable_discovery_unittest.cc
+++ b/chromium/device/fido/cable/fido_cable_discovery_unittest.cc
@@ -2,28 +2,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_cable_discovery.h"
+#include "device/fido/cable/fido_cable_discovery.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/containers/span.h"
+#include "base/run_loop.h"
#include "base/stl_util.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/test/bluetooth_test.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
-#include "device/fido/fido_ble_device.h"
-#include "device/fido/fido_ble_uuids.h"
-#include "device/fido/fido_cable_handshake_handler.h"
+#include "device/fido/ble/fido_ble_device.h"
+#include "device/fido/ble/fido_ble_uuids.h"
+#include "device/fido/cable/fido_cable_handshake_handler.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
+using ::testing::Sequence;
using ::testing::NiceMock;
namespace device {
@@ -34,41 +36,44 @@ constexpr uint8_t kTestCableVersionNumber = 0x01;
// Constants required for discovering and constructing a Cable device that
// are given by the relying party via an extension.
-constexpr FidoCableDiscovery::EidArray kClientEid = {
- {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11,
- 0x12, 0x13, 0x14, 0x15}};
+constexpr EidArray kClientEid = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15}};
constexpr char kUuidFormattedClientEid[] =
"00010203-0405-0607-0809-101112131415";
-constexpr FidoCableDiscovery::EidArray kAuthenticatorEid = {
- {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01}};
+constexpr EidArray kAuthenticatorEid = {{0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01}};
-constexpr FidoCableDiscovery::EidArray kInvalidAuthenticatorEid = {
+constexpr EidArray kInvalidAuthenticatorEid = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}};
-constexpr FidoCableDiscovery::SessionPreKeyArray kTestSessionPreKey = {
+constexpr SessionPreKeyArray kTestSessionPreKey = {
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
-constexpr FidoCableDiscovery::EidArray kSecondaryClientEid = {
- {0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
- 0x03, 0x02, 0x01, 0x00}};
+// TODO(https://crbug.com/837088): Add support for multiple EIDs on Windows.
+#if !defined(OS_WIN)
+constexpr EidArray kSecondaryClientEid = {{0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
+ 0x03, 0x02, 0x01, 0x00}};
constexpr char kUuidFormattedSecondaryClientEid[] =
"15141312-1110-0908-0706-050403020100";
-constexpr FidoCableDiscovery::EidArray kSecondaryAuthenticatorEid = {
+constexpr EidArray kSecondaryAuthenticatorEid = {
{0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
0xee, 0xee, 0xee, 0xee}};
-constexpr FidoCableDiscovery::SessionPreKeyArray kSecondarySessionPreKey = {
+constexpr SessionPreKeyArray kSecondarySessionPreKey = {
{0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd}};
+#endif // !defined(OS_WIN)
// Below constants are used to construct MockBluetoothDevice for testing.
constexpr char kTestBleDeviceAddress[] = "11:12:13:14:15:16";
@@ -81,6 +86,10 @@ std::unique_ptr<MockBluetoothDevice> CreateTestBluetoothDevice() {
kTestBleDeviceAddress, true /* paired */, true /* connected */);
}
+ACTION_P(ReturnFromAsyncCall, closure) {
+ closure.Run();
+}
+
// Matcher to compare the content of advertisement data received from the
// client.
MATCHER_P2(IsAdvertisementContent,
@@ -96,18 +105,24 @@ MATCHER_P2(IsAdvertisementContent,
#elif defined(OS_WIN)
const auto manufacturer_data = arg->manufacturer_data();
- const auto manufacturer_data_value = manufacturer_data->find(0xFFFD);
+ const auto manufacturer_data_value = manufacturer_data->find(0x00E0);
if (manufacturer_data_value == manufacturer_data->end())
return false;
- return fido_parsing_utils::ExtractSuffixSpan(manufacturer_data_value->second,
- 2) == expected_client_eid;
+ const auto& manufacturer_data_payload = manufacturer_data_value->second;
+ return manufacturer_data_payload.size() >= 4u &&
+ manufacturer_data_payload[0] == manufacturer_data_payload.size() - 1 &&
+ manufacturer_data_payload[1] == 0x15 && // Manufacturer Data Type
+ manufacturer_data_payload[2] == 0x20 && // Cable Flags
+ manufacturer_data_payload[3] == kTestCableVersionNumber &&
+ base::make_span(manufacturer_data_payload).subspan(4) ==
+ expected_client_eid;
#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
const auto service_data = arg->service_data();
const auto service_data_with_uuid =
- service_data->find(kCableAdvertisementUUID);
+ service_data->find(kCableAdvertisementUUID128);
if (service_data_with_uuid == service_data->end())
return false;
@@ -148,8 +163,7 @@ class CableMockAdapter : public MockBluetoothAdapter {
const AdvertisementErrorCallback&));
void AddNewTestBluetoothDevice(
- base::span<const uint8_t, FidoCableDiscovery::kEphemeralIdSize>
- authenticator_eid) {
+ base::span<const uint8_t, kEphemeralIdSize> authenticator_eid) {
auto mock_device = CreateTestBluetoothDevice();
std::vector<uint8_t> service_data(18);
@@ -157,7 +171,8 @@ class CableMockAdapter : public MockBluetoothAdapter {
std::copy(authenticator_eid.begin(), authenticator_eid.end(),
service_data.begin() + 2);
BluetoothDevice::ServiceDataMap service_data_map;
- service_data_map.emplace(kCableAdvertisementUUID, std::move(service_data));
+ service_data_map.emplace(kCableAdvertisementUUID128,
+ std::move(service_data));
mock_device->UpdateAdvertisementData(
1 /* rssi */, base::nullopt /* flags */, BluetoothDevice::UUIDList(),
@@ -171,35 +186,75 @@ class CableMockAdapter : public MockBluetoothAdapter {
observer.DeviceAdded(this, mock_device_ptr);
}
+ void AddNewTestAppleBluetoothDevice(
+ base::span<const uint8_t, kEphemeralIdSize> authenticator_eid) {
+ auto mock_device = CreateTestBluetoothDevice();
+ // Apple doesn't allow advertising service data, so we advertise a 16 bit
+ // UUID plus the EID converted into 128 bit UUID.
+ mock_device->AddUUID(BluetoothUUID("fde2"));
+ mock_device->AddUUID(BluetoothUUID(
+ fido_parsing_utils::ConvertBytesToUuid(authenticator_eid)));
+
+ auto* mock_device_ptr = mock_device.get();
+ AddMockDevice(std::move(mock_device));
+
+ for (auto& observer : GetObservers())
+ observer.DeviceAdded(this, mock_device_ptr);
+ }
+
void ExpectRegisterAdvertisementWithResponse(
bool simulate_success,
base::span<const uint8_t> expected_client_eid,
base::StringPiece expected_uuid_formatted_client_eid,
- scoped_refptr<CableMockBluetoothAdvertisement> advertisement_ptr =
- nullptr) {
- if (!advertisement_ptr)
- advertisement_ptr =
- base::MakeRefCounted<CableMockBluetoothAdvertisement>();
+ Sequence sequence = Sequence(),
+ scoped_refptr<CableMockBluetoothAdvertisement> advertisement = nullptr) {
+ if (!advertisement) {
+ advertisement = base::MakeRefCounted<CableMockBluetoothAdvertisement>();
+ EXPECT_CALL(*advertisement, Unregister(_, _))
+ .WillRepeatedly(::testing::WithArg<0>(
+ [](const auto& callback) { callback.Run(); }));
+ }
EXPECT_CALL(*this,
RegisterAdvertisement(
IsAdvertisementContent(expected_client_eid,
expected_uuid_formatted_client_eid),
_, _))
+ .InSequence(sequence)
.WillOnce(::testing::WithArgs<1, 2>(
- [simulate_success, advertisement_ptr](
- const auto& success_callback, const auto& failure_callback) {
+ [simulate_success, advertisement](const auto& success_callback,
+ const auto& failure_callback) {
simulate_success
- ? success_callback.Run(advertisement_ptr)
+ ? success_callback.Run(advertisement)
: failure_callback.Run(BluetoothAdvertisement::ErrorCode::
INVALID_ADVERTISEMENT_ERROR_CODE);
}));
}
- void ExpectSuccessCallbackToSetPowered() {
- EXPECT_CALL(*this, SetPowered(true, _, _))
+ void ExpectSuccessCallbackToIsPowered() {
+ EXPECT_CALL(*this, IsPresent()).WillOnce(::testing::Return(true));
+ EXPECT_CALL(*this, IsPowered()).WillOnce(::testing::Return(true));
+ }
+
+ void ExpectDiscoveryWithScanCallback() {
+ EXPECT_CALL(*this, StartDiscoverySessionWithFilterRaw(_, _, _))
+ .WillOnce(::testing::WithArg<1>(
+ [this](const auto& callback) { callback.Run(nullptr); }));
+ }
+
+ void ExpectDiscoveryWithScanCallback(
+ base::span<const uint8_t, kEphemeralIdSize> eid,
+ bool is_apple_device = false) {
+ EXPECT_CALL(*this, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillOnce(::testing::WithArg<1>(
- [](const auto& callback) { callback.Run(); }));
+ [this, eid, is_apple_device](const auto& callback) {
+ callback.Run(nullptr);
+ if (is_apple_device) {
+ AddNewTestAppleBluetoothDevice(eid);
+ } else {
+ AddNewTestBluetoothDevice(eid);
+ }
+ }));
}
protected:
@@ -249,7 +304,7 @@ class FakeFidoCableDiscovery : public FidoCableDiscovery {
class FidoCableDiscoveryTest : public ::testing::Test {
public:
std::unique_ptr<FidoCableDiscovery> CreateDiscovery() {
- std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
+ std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
kAuthenticatorEid, kTestSessionPreKey);
return std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
@@ -267,15 +322,29 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
- ::testing::InSequence testing_sequence;
- mock_adapter->ExpectSuccessCallbackToSetPowered();
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
+ mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
+ mock_adapter->ExpectRegisterAdvertisementWithResponse(
+ true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
+
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
+ cable_discovery->Start();
+ scoped_task_environment_.RunUntilIdle();
+}
+
+// Tests successful discovery flow for Apple Cable device.
+TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewAppleDevice) {
+ auto cable_discovery = CreateDiscovery();
+ NiceMock<MockFidoDiscoveryObserver> mock_observer;
+ EXPECT_CALL(mock_observer, DeviceAdded(_, _));
+ cable_discovery->set_observer(&mock_observer);
+
+ auto mock_adapter =
+ base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
+ mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid, true);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
- EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
- .WillOnce(::testing::WithArg<1>([&mock_adapter](const auto& callback) {
- mock_adapter->AddNewTestBluetoothDevice(kAuthenticatorEid);
- callback.Run(nullptr);
- }));
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
@@ -293,26 +362,26 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsIncorrectDevice) {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
- ::testing::InSequence testing_sequence;
- mock_adapter->ExpectSuccessCallbackToSetPowered();
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
- EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
- .WillOnce(::testing::WithArg<1>([&mock_adapter](const auto& callback) {
- mock_adapter->AddNewTestBluetoothDevice(kInvalidAuthenticatorEid);
- callback.Run(nullptr);
- }));
+ mock_adapter->ExpectDiscoveryWithScanCallback(kInvalidAuthenticatorEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
scoped_task_environment_.RunUntilIdle();
}
+// Windows currently does not support multiple EIDs, so the following tests are
+// not applicable.
+// TODO(https://crbug.com/837088): Support multiple EIDs on Windows and enable
+// these tests.
+#if !defined(OS_WIN)
// Tests Cable discovery flow when multiple(2) sets of client/authenticator EIDs
// are passed on from the relying party. We should expect 2 invocations of
// BluetoothAdapter::RegisterAdvertisement().
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) {
- std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
+ std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
kAuthenticatorEid, kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid,
@@ -320,24 +389,22 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) {
kSecondarySessionPreKey);
auto cable_discovery =
std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
+ auto mock_adapter =
+ base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
+ mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
+
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, DeviceAdded(_, _));
cable_discovery->set_observer(&mock_observer);
- auto mock_adapter =
- base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
- ::testing::InSequence testing_sequence;
- mock_adapter->ExpectSuccessCallbackToSetPowered();
+ Sequence sequence;
mock_adapter->ExpectRegisterAdvertisementWithResponse(
- true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
+ true /* simulate_success */, kClientEid, kUuidFormattedClientEid,
+ sequence);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true /* simulate_success */, kSecondaryClientEid,
- kUuidFormattedSecondaryClientEid);
- EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
- .WillOnce(::testing::WithArg<1>([&mock_adapter](const auto& callback) {
- mock_adapter->AddNewTestBluetoothDevice(kAuthenticatorEid);
- callback.Run(nullptr);
- }));
+ kUuidFormattedSecondaryClientEid, sequence);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
@@ -348,7 +415,7 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) {
// successfully. Since at least one advertisement are successfully processed,
// scanning process should be invoked.
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) {
- std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
+ std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
kAuthenticatorEid, kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid,
@@ -362,18 +429,15 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
- ::testing::InSequence testing_sequence;
- mock_adapter->ExpectSuccessCallbackToSetPowered();
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
+ Sequence sequence;
mock_adapter->ExpectRegisterAdvertisementWithResponse(
- true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
+ true /* simulate_success */, kClientEid, kUuidFormattedClientEid,
+ sequence);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
false /* simulate_success */, kSecondaryClientEid,
- kUuidFormattedSecondaryClientEid);
- EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
- .WillOnce(::testing::WithArg<1>([&mock_adapter](const auto& callback) {
- mock_adapter->AddNewTestBluetoothDevice(kAuthenticatorEid);
- callback.Run(nullptr);
- }));
+ kUuidFormattedSecondaryClientEid, sequence);
+ mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
@@ -382,7 +446,7 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) {
// Test the scenario when all advertisement for client EID's fails.
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) {
- std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
+ std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
kAuthenticatorEid, kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid,
@@ -397,20 +461,22 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
- ::testing::InSequence testing_sequence;
- mock_adapter->ExpectSuccessCallbackToSetPowered();
+ Sequence sequence;
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
mock_adapter->ExpectRegisterAdvertisementWithResponse(
- false /* simulate_success */, kClientEid, kUuidFormattedClientEid);
+ false /* simulate_success */, kClientEid, kUuidFormattedClientEid,
+ sequence);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
false /* simulate_success */, kSecondaryClientEid,
- kUuidFormattedSecondaryClientEid);
- EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
- .Times(0);
+ kUuidFormattedSecondaryClientEid, sequence);
+ mock_adapter->ExpectDiscoveryWithScanCallback();
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
scoped_task_environment_.RunUntilIdle();
+ EXPECT_TRUE(cable_discovery->advertisements_.empty());
}
+#endif // !defined(OS_WIN)
TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponDestruction) {
auto cable_discovery = CreateDiscovery();
@@ -418,13 +484,13 @@ TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponDestruction) {
new CableMockBluetoothAdvertisement();
EXPECT_CALL(*advertisement, Unregister(_, _)).Times(1);
- ::testing::InSequence testing_sequence;
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
- mock_adapter->ExpectSuccessCallbackToSetPowered();
+ mock_adapter->ExpectSuccessCallbackToIsPowered();
+ mock_adapter->ExpectDiscoveryWithScanCallback();
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true /* simulate_success */, kClientEid, kUuidFormattedClientEid,
- base::WrapRefCounted(advertisement));
+ Sequence(), base::WrapRefCounted(advertisement));
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
@@ -434,4 +500,39 @@ TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponDestruction) {
cable_discovery.reset();
}
+// Tests that cable discovery resumes after Bluetooth adapter is powered on.
+TEST_F(FidoCableDiscoveryTest, TestResumeDiscoveryAfterPoweredOn) {
+ auto cable_discovery = CreateDiscovery();
+ NiceMock<MockFidoDiscoveryObserver> mock_observer;
+ EXPECT_CALL(mock_observer, DeviceAdded);
+ cable_discovery->set_observer(&mock_observer);
+
+ auto mock_adapter =
+ base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
+ EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true));
+
+ // After BluetoothAdapter is powered on, we expect that Cable discovery starts
+ // again.
+ mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
+ mock_adapter->ExpectRegisterAdvertisementWithResponse(
+ true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
+
+ // Wait until error callback for SetPowered() is invoked. Then, simulate
+ // Bluetooth adapter power change by invoking
+ // MockBluetoothAdapter::NotifyAdapterPoweredChanged().
+ {
+ base::RunLoop run_loop;
+ auto quit = run_loop.QuitClosure();
+ EXPECT_CALL(*mock_adapter, IsPowered)
+ .WillOnce(::testing::DoAll(ReturnFromAsyncCall(quit),
+ ::testing::Return(false)));
+
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
+ cable_discovery->Start();
+ run_loop.Run();
+ }
+
+ mock_adapter->NotifyAdapterPoweredChanged(true);
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_cable_handshake_handler.cc b/chromium/device/fido/cable/fido_cable_handshake_handler.cc
index a47c5d0f7cb..a227377a096 100644
--- a/chromium/device/fido/fido_cable_handshake_handler.cc
+++ b/chromium/device/fido/cable/fido_cable_handshake_handler.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_cable_handshake_handler.h"
+#include "device/fido/cable/fido_cable_handshake_handler.h"
#include <algorithm>
#include <utility>
+#include "base/containers/span.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/cbor/cbor_reader.h"
#include "components/cbor/cbor_values.h"
@@ -14,7 +15,7 @@
#include "crypto/hkdf.h"
#include "crypto/hmac.h"
#include "crypto/random.h"
-#include "device/fido/fido_cable_device.h"
+#include "device/fido/cable/fido_cable_device.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
@@ -149,8 +150,8 @@ bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage(
}
cable_device_->SetEncryptionData(
- GetEncryptionKeyAfterSuccessfulHandshake(
- authenticator_random_nonce->second.GetBytestring()),
+ GetEncryptionKeyAfterSuccessfulHandshake(base::make_span<16>(
+ authenticator_random_nonce->second.GetBytestring())),
nonce_);
return true;
diff --git a/chromium/device/fido/fido_cable_handshake_handler.h b/chromium/device/fido/cable/fido_cable_handshake_handler.h
index fd764e53a30..175b55ddabb 100644
--- a/chromium/device/fido/fido_cable_handshake_handler.h
+++ b/chromium/device/fido/cable/fido_cable_handshake_handler.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_CABLE_HANDSHAKE_HANDLER_H_
-#define DEVICE_FIDO_FIDO_CABLE_HANDSHAKE_HANDLER_H_
+#ifndef DEVICE_FIDO_CABLE_FIDO_CABLE_HANDSHAKE_HANDLER_H_
+#define DEVICE_FIDO_CABLE_FIDO_CABLE_HANDSHAKE_HANDLER_H_
#include <stdint.h>
@@ -59,4 +59,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableHandshakeHandler {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_CABLE_HANDSHAKE_HANDLER_H_
+#endif // DEVICE_FIDO_CABLE_FIDO_CABLE_HANDSHAKE_HANDLER_H_
diff --git a/chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc b/chromium/device/fido/cable/fido_cable_handshake_handler_fuzzer.cc
index 64a9a7afd74..5b0432e0d1d 100644
--- a/chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc
+++ b/chromium/device/fido/cable/fido_cable_handshake_handler_fuzzer.cc
@@ -8,9 +8,13 @@
#include <array>
#include "base/containers/span.h"
-#include "device/fido/fido_cable_device.h"
-#include "device/fido/fido_cable_handshake_handler.h"
+#include "base/memory/ref_counted.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/fido/cable/fido_cable_device.h"
+#include "device/fido/cable/fido_cable_handshake_handler.h"
#include "device/fido/fido_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -30,7 +34,9 @@ constexpr char kTestDeviceAddress[] = "Fake_Address";
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* raw_data, size_t size) {
auto data_span = base::make_span(raw_data, size);
- device::FidoCableDevice test_cable_device(kTestDeviceAddress);
+ auto adapter =
+ base::MakeRefCounted<::testing::NiceMock<device::MockBluetoothAdapter>>();
+ device::FidoCableDevice test_cable_device(adapter.get(), kTestDeviceAddress);
device::FidoCableHandshakeHandler handshake_handler(
&test_cable_device, kTestNonce, kTestSessionPreKey);
handshake_handler.ValidateAuthenticatorHandshakeMessage(data_span);
diff --git a/chromium/device/fido/fido_cable_handshake_handler_unittest.cc b/chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc
index 554707d1e9a..49ea55d3169 100644
--- a/chromium/device/fido/fido_cable_handshake_handler_unittest.cc
+++ b/chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_cable_handshake_handler.h"
+#include "device/fido/cable/fido_cable_handshake_handler.h"
#include <array>
#include <limits>
@@ -19,11 +19,12 @@
#include "crypto/hkdf.h"
#include "crypto/hmac.h"
#include "device/bluetooth/test/bluetooth_test.h"
-#include "device/fido/fido_ble_frames.h"
-#include "device/fido/fido_cable_device.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/fido/ble/fido_ble_frames.h"
+#include "device/fido/ble/mock_fido_ble_connection.h"
+#include "device/fido/cable/fido_cable_device.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
-#include "device/fido/mock_fido_ble_connection.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -37,6 +38,7 @@ using ::testing::Invoke;
using ::testing::Test;
using TestDeviceCallbackReceiver =
test::ValueCallbackReceiver<base::Optional<std::vector<uint8_t>>>;
+using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
// Sufficiently large test control point length as we are not interested
// in testing fragmentations of BLE messages. All Cable messages are encrypted
@@ -254,12 +256,10 @@ class FidoCableHandshakeHandlerTest : public Test {
public:
FidoCableHandshakeHandlerTest() {
auto connection = std::make_unique<MockFidoBleConnection>(
- BluetoothTestBase::kTestDeviceAddress1);
+ adapter_.get(), BluetoothTestBase::kTestDeviceAddress1);
connection_ = connection.get();
device_ = std::make_unique<FidoCableDevice>(std::move(connection));
- connection_->connection_status_callback() =
- device_->GetConnectionStatusCallbackForTesting();
connection_->read_callback() = device_->GetReadCallbackForTesting();
}
@@ -271,8 +271,8 @@ class FidoCableHandshakeHandlerTest : public Test {
}
void ConnectWithLength(uint16_t length) {
- EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
- connection()->connection_status_callback().Run(true);
+ EXPECT_CALL(*connection(), ConnectPtr).WillOnce(Invoke([](auto* callback) {
+ std::move(*callback).Run(true);
}));
EXPECT_CALL(*connection(), ReadControlPointLengthPtr(_))
@@ -290,6 +290,8 @@ class FidoCableHandshakeHandlerTest : public Test {
base::test::ScopedTaskEnvironment scoped_task_environment_;
private:
+ scoped_refptr<MockBluetoothAdapter> adapter_ =
+ base::MakeRefCounted<NiceMockBluetoothAdapter>();
FakeCableAuthenticator authenticator_;
MockFidoBleConnection* connection_;
std::unique_ptr<FidoCableDevice> device_;
diff --git a/chromium/device/fido/ctap_get_assertion_request.cc b/chromium/device/fido/ctap_get_assertion_request.cc
index ac7a8282870..64285cb5565 100644
--- a/chromium/device/fido/ctap_get_assertion_request.cc
+++ b/chromium/device/fido/ctap_get_assertion_request.cc
@@ -146,7 +146,7 @@ CtapGetAssertionRequest& CtapGetAssertionRequest::SetPinProtocol(
}
CtapGetAssertionRequest& CtapGetAssertionRequest::SetCableExtension(
- std::vector<FidoCableDiscovery::CableDiscoveryData> cable_extension) {
+ std::vector<CableDiscoveryData> cable_extension) {
cable_extension_ = std::move(cable_extension);
return *this;
}
diff --git a/chromium/device/fido/ctap_get_assertion_request.h b/chromium/device/fido/ctap_get_assertion_request.h
index f9eafd70932..4264606d9bb 100644
--- a/chromium/device/fido/ctap_get_assertion_request.h
+++ b/chromium/device/fido/ctap_get_assertion_request.h
@@ -14,7 +14,7 @@
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/optional.h"
-#include "device/fido/fido_cable_discovery.h"
+#include "device/fido/cable/cable_discovery_data.h"
#include "device/fido/fido_constants.h"
#include "device/fido/public_key_credential_descriptor.h"
@@ -47,7 +47,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
CtapGetAssertionRequest& SetPinAuth(std::vector<uint8_t> pin_auth);
CtapGetAssertionRequest& SetPinProtocol(uint8_t pin_protocol);
CtapGetAssertionRequest& SetCableExtension(
- std::vector<FidoCableDiscovery::CableDiscoveryData> cable_extension);
+ std::vector<CableDiscoveryData> cable_extension);
CtapGetAssertionRequest& SetAlternativeApplicationParameter(
base::span<const uint8_t, kRpIdHashLength>
alternative_application_parameter);
@@ -77,8 +77,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
}
const base::Optional<uint8_t>& pin_protocol() const { return pin_protocol_; }
- const base::Optional<std::vector<FidoCableDiscovery::CableDiscoveryData>>&
- cable_extension() const {
+ const base::Optional<std::vector<CableDiscoveryData>>& cable_extension()
+ const {
return cable_extension_;
}
const base::Optional<std::array<uint8_t, kRpIdHashLength>>&
@@ -96,8 +96,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list_;
base::Optional<std::vector<uint8_t>> pin_auth_;
base::Optional<uint8_t> pin_protocol_;
- base::Optional<std::vector<FidoCableDiscovery::CableDiscoveryData>>
- cable_extension_;
+ base::Optional<std::vector<CableDiscoveryData>> cable_extension_;
base::Optional<std::array<uint8_t, kRpIdHashLength>>
alternative_application_parameter_;
};
diff --git a/chromium/device/fido/ctap_request_unittest.cc b/chromium/device/fido/ctap_request_unittest.cc
index 30850b1a96f..2599eca7c9b 100644
--- a/chromium/device/fido/ctap_request_unittest.cc
+++ b/chromium/device/fido/ctap_request_unittest.cc
@@ -63,7 +63,8 @@ TEST(CTAPRequestTest, TestConstructGetAssertionRequest) {
auto serialized_data = get_assertion_req.EncodeAsCBOR();
EXPECT_THAT(serialized_data,
- ::testing::ElementsAreArray(test_data::kCtapGetAssertionRequest));
+ ::testing::ElementsAreArray(
+ test_data::kTestComplexCtapGetAssertionRequest));
}
TEST(CTAPRequestTest, TestConstructCtapAuthenticatorRequestParam) {
@@ -127,7 +128,8 @@ TEST(CTAPRequestTest, ParseGetAssertionRequestForVirtualCtapKey) {
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
const auto request = ParseCtapGetAssertionRequest(
- base::make_span(test_data::kCtapGetAssertionRequest).subspan(1));
+ base::make_span(test_data::kTestComplexCtapGetAssertionRequest)
+ .subspan(1));
ASSERT_TRUE(request);
EXPECT_THAT(request->client_data_hash(),
::testing::ElementsAreArray(test_data::kClientDataHash));
diff --git a/chromium/device/fido/ctap_response_unittest.cc b/chromium/device/fido/ctap_response_unittest.cc
index 7b20e1e30a2..fa8b455c252 100644
--- a/chromium/device/fido/ctap_response_unittest.cc
+++ b/chromium/device/fido/ctap_response_unittest.cc
@@ -544,7 +544,7 @@ TEST(CTAPResponseTest, TestParseU2fSignWithNullCorruptedSignature) {
TEST(CTAPResponseTest, TestReadGetInfoResponse) {
auto get_info_response =
- ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse);
+ ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice);
ASSERT_TRUE(get_info_response);
ASSERT_TRUE(get_info_response->max_msg_size());
EXPECT_EQ(*get_info_response->max_msg_size(), 1200u);
@@ -574,8 +574,7 @@ TEST(CTAPResponseTest, TestReadGetInfoResponseWithIncorrectFormat) {
TEST(CTAPResponseTest, TestSerializeGetInfoResponse) {
AuthenticatorGetInfoResponse response(
- {ProtocolVersion::kCtap, ProtocolVersion::kU2f},
- fido_parsing_utils::Materialize(kTestDeviceAaguid));
+ {ProtocolVersion::kCtap, ProtocolVersion::kU2f}, kTestDeviceAaguid);
response.SetExtensions({"uvm", "hmac-secret"});
AuthenticatorSupportedOptions options;
options.SetSupportsResidentKey(true);
@@ -592,7 +591,7 @@ TEST(CTAPResponseTest, TestSerializeGetInfoResponse) {
EXPECT_THAT(EncodeToCBOR(response),
::testing::ElementsAreArray(
- base::make_span(test_data::kTestAuthenticatorGetInfoResponse)
+ base::make_span(test_data::kTestGetInfoResponsePlatformDevice)
.subspan(1)));
}
diff --git a/chromium/device/fido/device_operation.h b/chromium/device/fido/device_operation.h
index 79b907b6554..757996323a4 100644
--- a/chromium/device/fido/device_operation.h
+++ b/chromium/device/fido/device_operation.h
@@ -10,9 +10,11 @@
#include <utility>
#include <vector>
+#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/optional.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
@@ -39,8 +41,9 @@ class DeviceOperation {
// TODO(hongjunchoi): Refactor so that |command| is never base::nullopt.
void DispatchDeviceRequest(base::Optional<std::vector<uint8_t>> command,
FidoDevice::DeviceCallback callback) {
- if (!command) {
- std::move(callback).Run(base::nullopt);
+ if (!command || device_->state() == FidoDevice::State::kDeviceError) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
return;
}
diff --git a/chromium/device/fido/device_response_converter.cc b/chromium/device/fido/device_response_converter.cc
index 45e77197690..e4ee349100d 100644
--- a/chromium/device/fido/device_response_converter.cc
+++ b/chromium/device/fido/device_response_converter.cc
@@ -8,6 +8,7 @@
#include <string>
#include <utility>
+#include "base/containers/span.h"
#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "base/stl_util.h"
@@ -169,8 +170,8 @@ base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
auto protocol = ConvertStringToProtocolVersion(version.GetString());
if (protocol == ProtocolVersion::kUnknown) {
- DLOG(ERROR) << "Unexpected protocol version received.";
- return base::nullopt;
+ VLOG(2) << "Unexpected protocol version received.";
+ continue;
}
if (!protocol_versions.insert(protocol).second)
@@ -186,8 +187,9 @@ base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
return base::nullopt;
}
- AuthenticatorGetInfoResponse response(std::move(protocol_versions),
- it->second.GetBytestring());
+ AuthenticatorGetInfoResponse response(
+ std::move(protocol_versions),
+ base::make_span<kAaguidLength>(it->second.GetBytestring()));
it = response_map.find(CBOR(2));
if (it != response_map.end()) {
diff --git a/chromium/device/fido/fake_fido_discovery_unittest.cc b/chromium/device/fido/fake_fido_discovery_unittest.cc
index d3a8c37d642..61a9bef8856 100644
--- a/chromium/device/fido/fake_fido_discovery_unittest.cc
+++ b/chromium/device/fido/fake_fido_discovery_unittest.cc
@@ -118,9 +118,6 @@ TEST_F(FakeFidoDiscoveryTest, AddDevice) {
auto device0 = std::make_unique<MockFidoDevice>();
EXPECT_CALL(*device0, GetId()).WillOnce(::testing::Return("device0"));
- device0->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
base::RunLoop device0_done;
EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_))
.WillOnce(testing::InvokeWithoutArgs(
@@ -135,9 +132,6 @@ TEST_F(FakeFidoDiscoveryTest, AddDevice) {
auto device1 = std::make_unique<MockFidoDevice>();
EXPECT_CALL(*device1, GetId()).WillOnce(::testing::Return("device1"));
- device1->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
base::RunLoop device1_done;
EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_))
.WillOnce(testing::InvokeWithoutArgs(
diff --git a/chromium/device/fido/fido_authenticator.h b/chromium/device/fido/fido_authenticator.h
index 258c9bf6a33..b2a254d23a5 100644
--- a/chromium/device/fido/fido_authenticator.h
+++ b/chromium/device/fido/fido_authenticator.h
@@ -10,9 +10,11 @@
#include "base/callback_forward.h"
#include "base/component_export.h"
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/fido_transport_protocol.h"
namespace device {
@@ -35,6 +37,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAuthenticator {
FidoAuthenticator() = default;
virtual ~FidoAuthenticator() = default;
+ // Sends GetInfo request to connected authenticator. Once response to GetInfo
+ // call is received, |callback| is invoked. Below MakeCredential() and
+ // GetAssertion() must only called after |callback| is invoked.
+ virtual void InitializeAuthenticator(base::OnceClosure callback) = 0;
virtual void MakeCredential(
CtapMakeCredentialRequest request,
MakeCredentialCallback callback) = 0;
@@ -43,6 +49,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAuthenticator {
virtual void Cancel() = 0;
virtual std::string GetId() const = 0;
virtual const AuthenticatorSupportedOptions& Options() const = 0;
+ virtual FidoTransportProtocol AuthenticatorTransport() const = 0;
+ virtual base::WeakPtr<FidoAuthenticator> GetWeakPtr() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(FidoAuthenticator);
diff --git a/chromium/device/fido/fido_ble_connection.cc b/chromium/device/fido/fido_ble_connection.cc
deleted file mode 100644
index f6eff882dfa..00000000000
--- a/chromium/device/fido/fido_ble_connection.cc
+++ /dev/null
@@ -1,593 +0,0 @@
-// 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/fido/fido_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/fido/fido_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
-
-FidoBleConnection::FidoBleConnection(std::string device_address)
- : address_(std::move(device_address)), weak_factory_(this) {}
-
-FidoBleConnection::FidoBleConnection(
- 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) {
- DCHECK(!address_.empty());
-}
-
-FidoBleConnection::~FidoBleConnection() {
- if (adapter_)
- adapter_->RemoveObserver(this);
-}
-
-const BluetoothDevice* FidoBleConnection::GetBleDevice() const {
- return adapter_ ? adapter_->GetDevice(address()) : nullptr;
-}
-
-void FidoBleConnection::Connect() {
- BluetoothAdapterFactory::GetAdapter(
- base::Bind(&FidoBleConnection::OnGetAdapter, weak_factory_.GetWeakPtr()));
-}
-
-void FidoBleConnection::ReadControlPointLength(
- ControlPointLengthCallback callback) {
- const BluetoothRemoteGattService* u2f_service = GetFidoService();
- 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 FidoBleConnection::ReadServiceRevisions(
- ServiceRevisionsCallback callback) {
- const BluetoothRemoteGattService* u2f_service = GetFidoService();
- 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(&FidoBleConnection::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(&FidoBleConnection::OnReadServiceRevision,
- weak_factory_.GetWeakPtr(), copyable_callback),
- base::Bind(&FidoBleConnection::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(&FidoBleConnection::OnReadServiceRevisionBitfield,
- weak_factory_.GetWeakPtr(), copyable_callback),
- base::Bind(&FidoBleConnection::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(&FidoBleConnection::OnReadServiceRevision,
- weak_factory_.GetWeakPtr(), barrier_closure),
- base::Bind(&FidoBleConnection::OnReadServiceRevisionError,
- weak_factory_.GetWeakPtr(), barrier_closure));
-
- service_revision_bitfield->ReadRemoteCharacteristic(
- base::Bind(&FidoBleConnection::OnReadServiceRevisionBitfield,
- weak_factory_.GetWeakPtr(), barrier_closure),
- base::Bind(&FidoBleConnection::OnReadServiceRevisionBitfieldError,
- weak_factory_.GetWeakPtr(), barrier_closure));
-}
-
-void FidoBleConnection::WriteControlPoint(const std::vector<uint8_t>& data,
- WriteCallback callback) {
- const BluetoothRemoteGattService* u2f_service = GetFidoService();
- 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;
- }
-
- // Attempt a write without response for performance reasons. Fall back to a
- // confirmed write in case of failure, e.g. when the characteristic does not
- // provide the required property.
- if (control_point->WriteWithoutResponse(data)) {
- DVLOG(2) << "Write without response succeeded.";
- std::move(callback).Run(true);
- } else {
- auto copyable_callback =
- base::AdaptCallbackForRepeating(std::move(callback));
- control_point->WriteRemoteCharacteristic(
- data, base::Bind(OnWrite, copyable_callback),
- base::Bind(OnWriteError, copyable_callback));
- }
-}
-
-void FidoBleConnection::WriteServiceRevision(ServiceRevision service_revision,
- WriteCallback callback) {
- const BluetoothRemoteGattService* u2f_service = GetFidoService();
- 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 FidoBleConnection::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 FidoBleConnection::CreateGattConnection() {
- BluetoothDevice* device = adapter_->GetDevice(address_);
- if (!device) {
- DLOG(ERROR) << "Failed to get Device.";
- OnConnectionError();
- return;
- }
-
- device->CreateGattConnection(
- base::Bind(&FidoBleConnection::OnCreateGattConnection,
- weak_factory_.GetWeakPtr()),
- base::Bind(&FidoBleConnection::OnCreateGattConnectionError,
- weak_factory_.GetWeakPtr()));
-}
-
-void FidoBleConnection::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 FidoBleConnection::OnCreateGattConnectionError(
- BluetoothDevice::ConnectErrorCode error_code) {
- DLOG(ERROR) << "CreateGattConnection() failed: " << ToString(error_code);
- OnConnectionError();
-}
-
-void FidoBleConnection::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() == kFidoServiceUUID;
- });
-
- 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 == kFidoControlPointLengthUUID) {
- control_point_length_id_ = characteristic->GetIdentifier();
- DVLOG(2) << "Got U2F Control Point Length: " << *control_point_length_id_;
- } else if (uuid == kFidoControlPointUUID) {
- control_point_id_ = characteristic->GetIdentifier();
- DVLOG(2) << "Got U2F Control Point: " << *control_point_id_;
- } else if (uuid == kFidoStatusUUID) {
- status_id_ = characteristic->GetIdentifier();
- DVLOG(2) << "Got U2F Status: " << *status_id_;
- } else if (uuid == kFidoServiceRevisionUUID) {
- service_revision_id_ = characteristic->GetIdentifier();
- DVLOG(2) << "Got U2F Service Revision: " << *service_revision_id_;
- } else if (uuid == kFidoServiceRevisionBitfieldUUID) {
- 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(&FidoBleConnection::OnStartNotifySession,
- weak_factory_.GetWeakPtr()),
- base::Bind(&FidoBleConnection::OnStartNotifySessionError,
- weak_factory_.GetWeakPtr()));
-}
-
-void FidoBleConnection::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 FidoBleConnection::OnStartNotifySessionError(
- BluetoothGattService::GattErrorCode error_code) {
- DLOG(ERROR) << "StartNotifySession() failed: " << ToString(error_code);
- OnConnectionError();
-}
-
-void FidoBleConnection::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* FidoBleConnection::GetFidoService() 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 FidoBleConnection::DeviceAdded(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- if (adapter != adapter_ || device->GetAddress() != address_)
- return;
- CreateGattConnection();
-}
-
-void FidoBleConnection::DeviceAddressChanged(BluetoothAdapter* adapter,
- BluetoothDevice* device,
- const std::string& old_address) {
- if (adapter != adapter_ || old_address != address_)
- return;
- address_ = device->GetAddress();
-}
-
-void FidoBleConnection::DeviceChanged(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- if (adapter != adapter_ || device->GetAddress() != address_)
- return;
- if (connection_ && !device->IsGattConnected()) {
- DLOG(ERROR) << "GATT Disconnected: " << device->GetAddress();
- OnConnectionError();
- }
-}
-
-void FidoBleConnection::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 FidoBleConnection::GattServicesDiscovered(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- if (adapter != adapter_ || device->GetAddress() != address_)
- return;
- ConnectToU2fService();
-}
-
-// static
-void FidoBleConnection::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 FidoBleConnection::OnReadControlPointLengthError(
- ControlPointLengthCallback callback,
- BluetoothGattService::GattErrorCode error_code) {
- DLOG(ERROR) << "Error reading Control Point Length: " << ToString(error_code);
- std::move(callback).Run(base::nullopt);
-}
-
-void FidoBleConnection::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 FidoBleConnection::OnReadServiceRevisionError(
- base::OnceClosure callback,
- BluetoothGattService::GattErrorCode error_code) {
- DLOG(ERROR) << "Error reading Service Revision: " << ToString(error_code);
- std::move(callback).Run();
-}
-
-void FidoBleConnection::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 FidoBleConnection::OnReadServiceRevisionBitfieldError(
- base::OnceClosure callback,
- BluetoothGattService::GattErrorCode error_code) {
- DLOG(ERROR) << "Error reading Service Revision Bitfield: "
- << ToString(error_code);
- std::move(callback).Run();
-}
-
-void FidoBleConnection::ReturnServiceRevisions(
- ServiceRevisionsCallback callback) {
- std::move(callback).Run(std::move(service_revisions_));
-}
-
-// static
-void FidoBleConnection::OnWrite(WriteCallback callback) {
- DVLOG(2) << "Write succeeded.";
- std::move(callback).Run(true);
-}
-
-// static
-void FidoBleConnection::OnWriteError(
- WriteCallback callback,
- BluetoothGattService::GattErrorCode error_code) {
- DLOG(ERROR) << "Write Failed: " << ToString(error_code);
- std::move(callback).Run(false);
-}
-
-} // namespace device
diff --git a/chromium/device/fido/fido_ble_connection_unittest.cc b/chromium/device/fido/fido_ble_connection_unittest.cc
deleted file mode 100644
index 4e767f5e977..00000000000
--- a/chromium/device/fido/fido_ble_connection_unittest.cc
+++ /dev/null
@@ -1,708 +0,0 @@
-// 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/fido/fido_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/fido/fido_ble_uuids.h"
-#include "device/fido/test_callback_receiver.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_;
- }
-
- FidoBleConnection::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_;
- }
-
- FidoBleConnection::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};
-};
-
-using TestReadControlPointLengthCallback =
- test::ValueCallbackReceiver<base::Optional<uint16_t>>;
-
-using TestReadServiceRevisionsCallback =
- test::ValueCallbackReceiver<std::set<FidoBleConnection::ServiceRevision>>;
-
-using TestWriteCallback = test::ValueCallbackReceiver<bool>;
-} // namespace
-
-class FidoBleConnectionTest : public ::testing::Test {
- public:
- FidoBleConnectionTest() {
- 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(kFidoServiceUUID),
- /* is_primary */ true, /* is_local */ false);
- u2f_service_ = u2f_service.get();
- u2f_device_->AddMockService(std::move(u2f_service));
-
- AddU2fCharacteristics();
- }
-
- void AddU2fCharacteristics() {
- const bool is_local = false;
- {
- auto u2f_control_point =
- std::make_unique<NiceMockBluetoothGattCharacteristic>(
- u2f_service_, "u2f_control_point",
- BluetoothUUID(kFidoControlPointUUID), 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(kFidoStatusUUID), 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(kFidoControlPointLengthUUID), 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(kFidoServiceRevisionUUID), 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(kFidoServiceRevisionBitfieldUUID), 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(FidoBleConnectionTest, Address) {
- const std::string device_address = BluetoothTest::kTestDeviceAddress1;
- auto connect_do_nothing = [](bool) {};
- auto read_do_nothing = [](std::vector<uint8_t>) {};
-
- FidoBleConnection connection(device_address,
- base::BindRepeating(connect_do_nothing),
- base::BindRepeating(read_do_nothing));
- connection.Connect();
- EXPECT_EQ(device_address, connection.address());
- AddU2Device(device_address);
-
- SimulateDeviceAddressChange(device_address, "new_device_address");
- EXPECT_EQ("new_device_address", connection.address());
-}
-
-TEST_F(FidoBleConnectionTest, DeviceNotPresent) {
- const std::string device_address = BluetoothTest::kTestDeviceAddress1;
- TestConnectionStatusCallback connection_status_callback;
- auto do_nothing = [](std::vector<uint8_t>) {};
-
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(do_nothing));
- connection.Connect();
- bool result = connection_status_callback.WaitForResult();
- EXPECT_FALSE(result);
-}
-
-TEST_F(FidoBleConnectionTest, 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>) {};
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(do_nothing));
- connection.Connect();
- EXPECT_TRUE(connection_status_callback.WaitForResult());
-}
-
-TEST_F(FidoBleConnectionTest, PostConnected) {
- const std::string device_address = BluetoothTest::kTestDeviceAddress1;
- TestConnectionStatusCallback connection_status_callback;
- auto do_nothing = [](std::vector<uint8_t>) {};
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(do_nothing));
- connection.Connect();
- 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(FidoBleConnectionTest, 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>) {};
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(do_nothing));
- connection.Connect();
- bool result = connection_status_callback.WaitForResult();
- EXPECT_TRUE(result);
-
- SimulateDisconnect(device_address);
- result = connection_status_callback.WaitForResult();
- EXPECT_FALSE(result);
-}
-
-TEST_F(FidoBleConnectionTest, ReadStatusNotifications) {
- const std::string device_address = BluetoothTest::kTestDeviceAddress1;
- TestConnectionStatusCallback connection_status_callback;
- TestReadCallback read_callback;
-
- AddU2Device(device_address);
- SetupConnectingU2fDevice(device_address);
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- read_callback.GetCallback());
- connection.Connect();
- 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(FidoBleConnectionTest, 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>) {};
-
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(read_do_nothing));
- connection.Connect();
- EXPECT_TRUE(connection_status_callback.WaitForResult());
-
- TestReadControlPointLengthCallback length_callback;
- SetNextReadControlPointLengthReponse(false, {});
- connection.ReadControlPointLength(length_callback.callback());
- EXPECT_EQ(base::nullopt, length_callback.value());
-
- // 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.callback());
- EXPECT_EQ(base::nullopt, length_callback.value());
-
- SetNextReadControlPointLengthReponse(true, {0xAB});
- connection.ReadControlPointLength(length_callback.callback());
- EXPECT_EQ(base::nullopt, length_callback.value());
-
- SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD});
- connection.ReadControlPointLength(length_callback.callback());
- EXPECT_EQ(0xABCD, *length_callback.value());
-
- SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD, 0xEF});
- connection.ReadControlPointLength(length_callback.callback());
- EXPECT_EQ(base::nullopt, length_callback.value());
-}
-
-TEST_F(FidoBleConnectionTest, 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>) {};
-
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(read_do_nothing));
- connection.Connect();
- EXPECT_TRUE(connection_status_callback.WaitForResult());
-
- TestReadServiceRevisionsCallback revisions_callback;
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(false, {});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(), IsEmpty());
-
- SetNextReadServiceRevisionResponse(true, ToByteVector("bogus"));
- SetNextReadServiceRevisionBitfieldResponse(false, {});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(), IsEmpty());
-
- SetNextReadServiceRevisionResponse(true, ToByteVector("1.0"));
- SetNextReadServiceRevisionBitfieldResponse(false, {});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_0));
-
- SetNextReadServiceRevisionResponse(true, ToByteVector("1.1"));
- SetNextReadServiceRevisionBitfieldResponse(false, {});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_1));
-
- SetNextReadServiceRevisionResponse(true, ToByteVector("1.2"));
- SetNextReadServiceRevisionBitfieldResponse(false, {});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::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.callback());
- EXPECT_THAT(revisions_callback.value(), IsEmpty());
-
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(true, {0x00});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(), IsEmpty());
-
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(true, {0x80});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_1));
-
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(true, {0x40});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_2));
-
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(true, {0xC0});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_1,
- FidoBleConnection::ServiceRevision::VERSION_1_2));
-
- // All bits except the first two should be ignored.
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(true, {0xFF});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_1,
- FidoBleConnection::ServiceRevision::VERSION_1_2));
-
- // All bytes except the first one should be ignored.
- SetNextReadServiceRevisionResponse(false, {});
- SetNextReadServiceRevisionBitfieldResponse(true, {0xC0, 0xFF});
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_1,
- FidoBleConnection::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.callback());
- EXPECT_THAT(revisions_callback.value(),
- ElementsAre(FidoBleConnection::ServiceRevision::VERSION_1_0,
- FidoBleConnection::ServiceRevision::VERSION_1_1,
- FidoBleConnection::ServiceRevision::VERSION_1_2));
-}
-
-TEST_F(FidoBleConnectionTest, 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>) {};
-
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(read_do_nothing));
- connection.Connect();
- bool result = connection_status_callback.WaitForResult();
- EXPECT_TRUE(result);
-
- TestWriteCallback write_callback;
- SetNextWriteControlPointResponse(false);
- connection.WriteControlPoint({}, write_callback.callback());
- result = write_callback.value();
- EXPECT_FALSE(result);
-
- SetNextWriteControlPointResponse(true);
- connection.WriteControlPoint({}, write_callback.callback());
- result = write_callback.value();
- EXPECT_TRUE(result);
-}
-
-TEST_F(FidoBleConnectionTest, 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>) {};
-
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(read_do_nothing));
- connection.Connect();
- bool result = connection_status_callback.WaitForResult();
- EXPECT_TRUE(result);
-
- // Expect that errors are properly propagated.
- TestWriteCallback write_callback;
- SetNextWriteServiceRevisionResponse(false);
- connection.WriteServiceRevision(
- FidoBleConnection::ServiceRevision::VERSION_1_1,
- write_callback.callback());
- result = write_callback.value();
- EXPECT_FALSE(result);
-
- // Expect a successful write of version 1.1.
- SetNextWriteServiceRevisionResponse(true);
- connection.WriteServiceRevision(
- FidoBleConnection::ServiceRevision::VERSION_1_1,
- write_callback.callback());
- result = write_callback.value();
- EXPECT_TRUE(result);
-
- // Expect a successful write of version 1.2.
- SetNextWriteServiceRevisionResponse(true);
- connection.WriteServiceRevision(
- FidoBleConnection::ServiceRevision::VERSION_1_2,
- write_callback.callback());
- result = write_callback.value();
- EXPECT_TRUE(result);
-
- // Writing version 1.0 to the bitfield is not intended, so this should fail.
- connection.WriteServiceRevision(
- FidoBleConnection::ServiceRevision::VERSION_1_0,
- write_callback.callback());
- result = write_callback.value();
- EXPECT_FALSE(result);
-}
-
-TEST_F(FidoBleConnectionTest, 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>) {};
- FidoBleConnection connection(device_address,
- connection_status_callback.GetCallback(),
- base::BindRepeating(do_nothing));
- connection.Connect();
- 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.callback());
- EXPECT_EQ(base::nullopt, length_callback.value());
-
- TestReadServiceRevisionsCallback revisions_callback;
- connection.ReadServiceRevisions(revisions_callback.callback());
- EXPECT_THAT(revisions_callback.value(), IsEmpty());
-
- // Writes should always fail on a disconnected device.
- TestWriteCallback write_callback;
- connection.WriteServiceRevision(
- FidoBleConnection::ServiceRevision::VERSION_1_1,
- write_callback.callback());
- result = write_callback.value();
- EXPECT_FALSE(result);
-
- connection.WriteControlPoint({}, write_callback.callback());
- result = write_callback.value();
- EXPECT_FALSE(result);
-}
-
-} // namespace device
diff --git a/chromium/device/fido/fido_constants.h b/chromium/device/fido/fido_constants.h
index 184ea90ae8d..2dc2a2136ac 100644
--- a/chromium/device/fido/fido_constants.h
+++ b/chromium/device/fido/fido_constants.h
@@ -27,6 +27,8 @@ enum class FidoReturnCode : uint8_t {
// authenticator), but none of the provided credentials were recognized by
// the authenticator.
kUserConsentButCredentialNotRecognized,
+ // The user explicitly refused to provide consent.
+ kUserConsentDenied,
};
enum class ProtocolVersion {
diff --git a/chromium/device/fido/fido_device.cc b/chromium/device/fido/fido_device.cc
index e3f448c052b..b9b30015b56 100644
--- a/chromium/device/fido/fido_device.cc
+++ b/chromium/device/fido/fido_device.cc
@@ -42,6 +42,10 @@ bool FidoDevice::SupportedProtocolIsInitialized() {
void FidoDevice::OnDeviceInfoReceived(
base::OnceClosure done,
base::Optional<std::vector<uint8_t>> response) {
+ // TODO(hongjunchoi): Add tests that verify this behavior.
+ if (state_ == FidoDevice::State::kDeviceError)
+ return;
+
state_ = FidoDevice::State::kReady;
base::Optional<AuthenticatorGetInfoResponse> get_info_response =
diff --git a/chromium/device/fido/fido_device.h b/chromium/device/fido/fido_device.h
index defeec56cea..ec5a77371cf 100644
--- a/chromium/device/fido/fido_device.h
+++ b/chromium/device/fido/fido_device.h
@@ -17,6 +17,7 @@
#include "base/optional.h"
#include "device/fido/authenticator_get_info_response.h"
#include "device/fido/fido_constants.h"
+#include "device/fido/fido_transport_protocol.h"
namespace device {
@@ -32,8 +33,21 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
using DeviceCallback =
base::OnceCallback<void(base::Optional<std::vector<uint8_t>>)>;
- // Internal state machine states.
- enum class State { kInit, kConnected, kBusy, kReady, kDeviceError };
+ // Internal state machine states. kMsgError represents a state where error
+ // has been received from the connected device because an
+ // unexpected/incorrectly formatted request was sent from the client. Devices
+ // in this state can be recovered by re-sending a well-formed command. On the
+ // other hand, kDeviceError represents a state where error occurred due to
+ // connection failure/unknown reasons and is considered an unrecoverable
+ // error.
+ enum class State {
+ kInit,
+ kConnected,
+ kBusy,
+ kReady,
+ kMsgError,
+ kDeviceError,
+ };
FidoDevice();
virtual ~FidoDevice();
@@ -45,6 +59,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
virtual void TryWink(WinkCallback callback) = 0;
virtual void Cancel() = 0;
virtual std::string GetId() const = 0;
+ virtual FidoTransportProtocol DeviceTransport() const = 0;
+ virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0;
// Sends a speculative AuthenticatorGetInfo request to determine whether the
// device supports the CTAP2 protocol, and initializes supported_protocol_
@@ -66,8 +82,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
State state() const { return state_; }
protected:
- virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0;
-
void OnDeviceInfoReceived(base::OnceClosure done,
base::Optional<std::vector<uint8_t>> response);
void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
diff --git a/chromium/device/fido/fido_device_authenticator.cc b/chromium/device/fido/fido_device_authenticator.cc
index 93071cefe4e..afb1d8aee16 100644
--- a/chromium/device/fido/fido_device_authenticator.cc
+++ b/chromium/device/fido/fido_device_authenticator.cc
@@ -6,7 +6,9 @@
#include <utility>
+#include "base/bind.h"
#include "base/logging.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "device/fido/authenticator_supported_options.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
@@ -17,14 +19,22 @@
namespace device {
FidoDeviceAuthenticator::FidoDeviceAuthenticator(FidoDevice* device)
- : device_(device) {
- DCHECK(device_->SupportedProtocolIsInitialized());
-}
+ : device_(device), weak_factory_(this) {}
FidoDeviceAuthenticator::~FidoDeviceAuthenticator() = default;
+void FidoDeviceAuthenticator::InitializeAuthenticator(
+ base::OnceClosure callback) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&FidoDevice::DiscoverSupportedProtocolAndDeviceInfo,
+ device()->GetWeakPtr(), std::move(callback)));
+}
+
void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
MakeCredentialCallback callback) {
DCHECK(!task_);
+ DCHECK(device_->SupportedProtocolIsInitialized())
+ << "InitializeAuthenticator() must be called first.";
// TODO(martinkr): Change FidoTasks to take all request parameters by const
// reference, so we can avoid copying these from the RequestHandler.
task_ = std::make_unique<MakeCredentialTask>(device_, std::move(request),
@@ -33,6 +43,8 @@ void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
void FidoDeviceAuthenticator::GetAssertion(CtapGetAssertionRequest request,
GetAssertionCallback callback) {
+ DCHECK(device_->SupportedProtocolIsInitialized())
+ << "InitializeAuthenticator() must be called first.";
task_ = std::make_unique<GetAssertionTask>(device_, std::move(request),
std::move(callback));
}
@@ -63,9 +75,17 @@ const AuthenticatorSupportedOptions& FidoDeviceAuthenticator::Options() const {
return default_options;
}
+FidoTransportProtocol FidoDeviceAuthenticator::AuthenticatorTransport() const {
+ return device_->DeviceTransport();
+}
+
void FidoDeviceAuthenticator::SetTaskForTesting(
std::unique_ptr<FidoTask> task) {
task_ = std::move(task);
}
+base::WeakPtr<FidoAuthenticator> FidoDeviceAuthenticator::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_device_authenticator.h b/chromium/device/fido/fido_device_authenticator.h
index ec62fefdd9c..e755f1fe0c2 100644
--- a/chromium/device/fido/fido_device_authenticator.h
+++ b/chromium/device/fido/fido_device_authenticator.h
@@ -5,11 +5,13 @@
#ifndef DEVICE_FIDO_FIDO_DEVICE_AUTHENTICATOR_H_
#define DEVICE_FIDO_FIDO_DEVICE_AUTHENTICATOR_H_
+#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "device/fido/fido_authenticator.h"
@@ -30,6 +32,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator
~FidoDeviceAuthenticator() override;
// FidoAuthenticator:
+ void InitializeAuthenticator(base::OnceClosure callback) override;
void MakeCredential(
CtapMakeCredentialRequest request,
MakeCredentialCallback callback) override;
@@ -38,6 +41,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator
void Cancel() override;
std::string GetId() const override;
const AuthenticatorSupportedOptions& Options() const override;
+ FidoTransportProtocol AuthenticatorTransport() const override;
+ base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
protected:
void OnCtapMakeCredentialResponseReceived(
@@ -53,6 +58,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator
private:
FidoDevice* const device_;
std::unique_ptr<FidoTask> task_;
+ base::WeakPtrFactory<FidoDeviceAuthenticator> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoDeviceAuthenticator);
};
diff --git a/chromium/device/fido/fido_discovery.cc b/chromium/device/fido/fido_discovery.cc
index 98d4f678947..4de2ef64f05 100644
--- a/chromium/device/fido/fido_discovery.cc
+++ b/chromium/device/fido/fido_discovery.cc
@@ -8,12 +8,13 @@
#include "base/bind.h"
#include "build/build_config.h"
-#include "device/fido/fido_ble_discovery.h"
+#include "device/fido/ble/fido_ble_discovery.h"
+#include "device/fido/cable/fido_cable_discovery.h"
#include "device/fido/fido_device.h"
// HID is not supported on Android.
#if !defined(OS_ANDROID)
-#include "device/fido/fido_hid_discovery.h"
+#include "device/fido/hid/fido_hid_discovery.h"
#endif // !defined(OS_ANDROID)
namespace device {
@@ -34,12 +35,9 @@ std::unique_ptr<FidoDiscovery> CreateFidoDiscoveryImpl(
#endif // !defined(OS_ANDROID)
case FidoTransportProtocol::kBluetoothLowEnergy:
return std::make_unique<FidoBleDiscovery>();
- // FidoCaBleDiscovery is not constructed using FidoDiscovery factory
- // function and instead constructed in FidoGetAssertionRequestHandler as it
- // requires extensions passed on from the relying party.
case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy:
- NOTREACHED()
- << "Cable discovery is not constructed using factory method.";
+ NOTREACHED() << "Cable discovery is constructed using the dedicated "
+ "factory method.";
return nullptr;
case FidoTransportProtocol::kNearFieldCommunication:
// TODO(https://crbug.com/825949): Add NFC support.
@@ -52,6 +50,11 @@ std::unique_ptr<FidoDiscovery> CreateFidoDiscoveryImpl(
return nullptr;
}
+std::unique_ptr<FidoDiscovery> CreateCableDiscoveryImpl(
+ std::vector<CableDiscoveryData> cable_data) {
+ return std::make_unique<FidoCableDiscovery>(std::move(cable_data));
+}
+
} // namespace
FidoDiscovery::Observer::~Observer() = default;
@@ -61,12 +64,22 @@ FidoDiscovery::FactoryFuncPtr FidoDiscovery::g_factory_func_ =
&CreateFidoDiscoveryImpl;
// static
+FidoDiscovery::CableFactoryFuncPtr FidoDiscovery::g_cable_factory_func_ =
+ &CreateCableDiscoveryImpl;
+
+// static
std::unique_ptr<FidoDiscovery> FidoDiscovery::Create(
FidoTransportProtocol transport,
service_manager::Connector* connector) {
return (*g_factory_func_)(transport, connector);
}
+// static
+std::unique_ptr<FidoDiscovery> FidoDiscovery::CreateCable(
+ std::vector<CableDiscoveryData> cable_data) {
+ return (*g_cable_factory_func_)(std::move(cable_data));
+}
+
FidoDiscovery::FidoDiscovery(FidoTransportProtocol transport)
: transport_(transport), weak_factory_(this) {}
@@ -136,11 +149,8 @@ bool FidoDiscovery::AddDevice(std::unique_ptr<FidoDevice> device) {
if (!result.second) {
return false; // Duplicate device id.
}
- FidoDevice* device_ptr = result.first->second.get();
- // Determine the device protocol version before notifying observers.
- device_ptr->DiscoverSupportedProtocolAndDeviceInfo(
- base::BindOnce(&FidoDiscovery::NotifyDeviceAdded,
- weak_factory_.GetWeakPtr(), device_ptr));
+
+ NotifyDeviceAdded(result.first->second.get());
return true;
}
@@ -165,11 +175,15 @@ ScopedFidoDiscoveryFactory::ScopedFidoDiscoveryFactory() {
original_factory_func_ =
std::exchange(FidoDiscovery::g_factory_func_,
&ForwardCreateFidoDiscoveryToCurrentFactory);
+ original_cable_factory_func_ =
+ std::exchange(FidoDiscovery::g_cable_factory_func_,
+ &ForwardCreateCableDiscoveryToCurrentFactory);
}
ScopedFidoDiscoveryFactory::~ScopedFidoDiscoveryFactory() {
g_current_factory = nullptr;
FidoDiscovery::g_factory_func_ = original_factory_func_;
+ FidoDiscovery::g_cable_factory_func_ = original_cable_factory_func_;
}
// static
@@ -182,6 +196,17 @@ ScopedFidoDiscoveryFactory::ForwardCreateFidoDiscoveryToCurrentFactory(
}
// static
+std::unique_ptr<FidoDiscovery>
+ScopedFidoDiscoveryFactory::ForwardCreateCableDiscoveryToCurrentFactory(
+ std::vector<CableDiscoveryData> cable_data) {
+ DCHECK(g_current_factory);
+ g_current_factory->set_last_cable_data(std::move(cable_data));
+ return g_current_factory->CreateFidoDiscovery(
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+ nullptr /* connector */);
+}
+
+// static
ScopedFidoDiscoveryFactory* ScopedFidoDiscoveryFactory::g_current_factory =
nullptr;
diff --git a/chromium/device/fido/fido_discovery.h b/chromium/device/fido/fido_discovery.h
index 68aadcc9a9f..cfc4cada6f9 100644
--- a/chromium/device/fido/fido_discovery.h
+++ b/chromium/device/fido/fido_discovery.h
@@ -16,6 +16,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece.h"
+#include "device/fido/cable/cable_discovery_data.h"
#include "device/fido/fido_transport_protocol.h"
namespace service_manager {
@@ -43,7 +44,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscovery {
virtual ~Observer();
// It is guaranteed that this is never invoked synchronously from Start().
- virtual void DiscoveryStarted(FidoDiscovery* discovery, bool success) = 0;
+ virtual void DiscoveryStarted(FidoDiscovery* discovery, bool success) {}
// It is guaranteed that DeviceAdded/DeviceRemoved() will not be invoked
// before the client of FidoDiscovery calls FidoDiscovery::Start(). However,
@@ -59,14 +60,17 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscovery {
FidoDevice* device) = 0;
};
- // Factory function to construct an instance that discovers authenticators on
- // the given |transport| protocol.
+ // Factory functions to construct an instance that discovers authenticators on
+ // the given |transport| protocol. The first variant is for everything except
+ // for cloud-assisted BLE which is handled by the second variant.
//
// FidoTransportProtocol::kUsbHumanInterfaceDevice requires specifying a valid
// |connector| on Desktop, and is not valid on Android.
static std::unique_ptr<FidoDiscovery> Create(
FidoTransportProtocol transport,
::service_manager::Connector* connector);
+ static std::unique_ptr<FidoDiscovery> CreateCable(
+ std::vector<CableDiscoveryData> cable_data);
virtual ~FidoDiscovery();
@@ -113,7 +117,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscovery {
// Factory function can be overridden by tests to construct fakes.
using FactoryFuncPtr = decltype(&Create);
+ using CableFactoryFuncPtr = decltype(&CreateCable);
static FactoryFuncPtr g_factory_func_;
+ static CableFactoryFuncPtr g_cable_factory_func_;
const FidoTransportProtocol transport_;
State state_ = State::kIdle;
@@ -136,7 +142,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) ScopedFidoDiscoveryFactory {
ScopedFidoDiscoveryFactory();
virtual ~ScopedFidoDiscoveryFactory();
+ const std::vector<CableDiscoveryData>& last_cable_data() const {
+ return last_cable_data_;
+ }
+
protected:
+ void set_last_cable_data(std::vector<CableDiscoveryData> cable_data) {
+ last_cable_data_ = std::move(cable_data);
+ }
+
virtual std::unique_ptr<FidoDiscovery> CreateFidoDiscovery(
FidoTransportProtocol transport,
::service_manager::Connector* connector) = 0;
@@ -147,8 +161,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) ScopedFidoDiscoveryFactory {
FidoTransportProtocol transport,
::service_manager::Connector* connector);
+ static std::unique_ptr<FidoDiscovery>
+ ForwardCreateCableDiscoveryToCurrentFactory(
+ std::vector<CableDiscoveryData> cable_data);
+
static ScopedFidoDiscoveryFactory* g_current_factory;
+
FidoDiscovery::FactoryFuncPtr original_factory_func_;
+ FidoDiscovery::CableFactoryFuncPtr original_cable_factory_func_;
+ std::vector<CableDiscoveryData> last_cable_data_;
DISALLOW_COPY_AND_ASSIGN(ScopedFidoDiscoveryFactory);
};
diff --git a/chromium/device/fido/fido_discovery_unittest.cc b/chromium/device/fido/fido_discovery_unittest.cc
index 922c559c080..46827b87acb 100644
--- a/chromium/device/fido/fido_discovery_unittest.cc
+++ b/chromium/device/fido/fido_discovery_unittest.cc
@@ -102,9 +102,6 @@ TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
// Expect successful insertion.
auto device0 = std::make_unique<MockFidoDevice>();
auto* device0_raw = device0.get();
- device0->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
base::RunLoop device0_done;
EXPECT_CALL(observer, DeviceAdded(&discovery, device0_raw))
.WillOnce(testing::InvokeWithoutArgs(
@@ -113,16 +110,11 @@ TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
EXPECT_TRUE(discovery.AddDevice(std::move(device0)));
device0_done.Run();
::testing::Mock::VerifyAndClearExpectations(&observer);
- // Device should have been initialized as a CTAP device.
- EXPECT_EQ(ProtocolVersion::kCtap, device0_raw->supported_protocol());
- EXPECT_TRUE(device0_raw->device_info());
// Expect successful insertion.
base::RunLoop device1_done;
auto device1 = std::make_unique<MockFidoDevice>();
auto* device1_raw = device1.get();
- device1->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
EXPECT_CALL(observer, DeviceAdded(&discovery, device1_raw))
.WillOnce(testing::InvokeWithoutArgs(
[&device1_done]() { device1_done.Quit(); }));
@@ -130,9 +122,6 @@ TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
EXPECT_TRUE(discovery.AddDevice(std::move(device1)));
device1_done.Run();
::testing::Mock::VerifyAndClearExpectations(&observer);
- // Device should have been initialized as a U2F device.
- EXPECT_EQ(ProtocolVersion::kU2f, device1_raw->supported_protocol());
- EXPECT_FALSE(device1_raw->device_info());
// Inserting a device with an already present id should be prevented.
auto device1_dup = std::make_unique<MockFidoDevice>();
diff --git a/chromium/device/fido/fido_parsing_utils.cc b/chromium/device/fido/fido_parsing_utils.cc
index a8448dce45c..661e19335a6 100644
--- a/chromium/device/fido/fido_parsing_utils.cc
+++ b/chromium/device/fido/fido_parsing_utils.cc
@@ -5,6 +5,8 @@
#include "device/fido/fido_parsing_utils.h"
#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
namespace device {
namespace fido_parsing_utils {
@@ -88,5 +90,27 @@ base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data) {
return {reinterpret_cast<const char*>(data.data()), data.size()};
}
+std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes) {
+ uint64_t most_significant_bytes = 0;
+ for (size_t i = 0; i < sizeof(uint64_t); i++) {
+ most_significant_bytes |= base::strict_cast<uint64_t>(bytes[i])
+ << 8 * (7 - i);
+ }
+
+ uint64_t least_significant_bytes = 0;
+ for (size_t i = 0; i < sizeof(uint64_t); i++) {
+ least_significant_bytes |= base::strict_cast<uint64_t>(bytes[i + 8])
+ << 8 * (7 - i);
+ }
+
+ return base::StringPrintf(
+ "%08x-%04x-%04x-%04x-%012llx",
+ static_cast<unsigned int>(most_significant_bytes >> 32),
+ static_cast<unsigned int>((most_significant_bytes >> 16) & 0x0000ffff),
+ static_cast<unsigned int>(most_significant_bytes & 0x0000ffff),
+ static_cast<unsigned int>(least_significant_bytes >> 48),
+ least_significant_bytes & 0x0000ffff'ffffffffULL);
+}
+
} // namespace fido_parsing_utils
} // namespace device
diff --git a/chromium/device/fido/fido_parsing_utils.h b/chromium/device/fido/fido_parsing_utils.h
index 278207baa44..6bf982dad91 100644
--- a/chromium/device/fido/fido_parsing_utils.h
+++ b/chromium/device/fido/fido_parsing_utils.h
@@ -114,6 +114,12 @@ std::array<uint8_t, crypto::kSHA256Length> CreateSHA256Hash(
COMPONENT_EXPORT(DEVICE_FIDO)
base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data);
+// Convert byte array into GUID formatted string as defined by RFC 4122.
+// As we are converting 128 bit UUID, |bytes| must be have length of 16.
+// https://tools.ietf.org/html/rfc4122
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes);
+
} // namespace fido_parsing_utils
} // namespace device
diff --git a/chromium/device/fido/fido_request_handler.h b/chromium/device/fido/fido_request_handler.h
index fa2e842ca8d..00cf7540a2b 100644
--- a/chromium/device/fido/fido_request_handler.h
+++ b/chromium/device/fido/fido_request_handler.h
@@ -27,24 +27,18 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
public:
using CompletionCallback =
base::OnceCallback<void(FidoReturnCode status_code,
- base::Optional<Response> response_data)>;
-
- FidoRequestHandler(service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- CompletionCallback completion_callback)
- : FidoRequestHandler(connector,
- transports,
- std::move(completion_callback),
- AddPlatformAuthenticatorCallback()) {}
+ base::Optional<Response> response_data,
+ FidoTransportProtocol transport_used)>;
+
+ // The |available_transports| should be the intersection of transports
+ // supported by the client and allowed by the relying party.
FidoRequestHandler(
service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- CompletionCallback completion_callback,
- AddPlatformAuthenticatorCallback add_platform_authenticator)
- : FidoRequestHandlerBase(connector,
- transports,
- std::move(add_platform_authenticator)),
+ const base::flat_set<FidoTransportProtocol>& available_transports,
+ CompletionCallback completion_callback)
+ : FidoRequestHandlerBase(connector, available_transports),
completion_callback_(std::move(completion_callback)) {}
+
~FidoRequestHandler() override {
if (!is_complete())
CancelOngoingTasks();
@@ -64,8 +58,9 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
return;
}
- const auto return_code = ConvertDeviceResponseCodeToFidoReturnCode(
- device_response_code, response_data.has_value());
+ base::Optional<FidoReturnCode> return_code =
+ ConvertDeviceResponseCodeToFidoReturnCode(device_response_code,
+ response_data.has_value());
// Any authenticator response codes that do not result from user consent
// imply that the authenticator should be dropped and that other on-going
@@ -78,7 +73,9 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
// Once response has been passed to the relying party, cancel all other on
// going requests.
CancelOngoingTasks(authenticator->GetId());
- std::move(completion_callback_).Run(*return_code, std::move(response_data));
+ std::move(completion_callback_)
+ .Run(*return_code, std::move(response_data),
+ authenticator->AuthenticatorTransport());
}
private:
@@ -99,6 +96,12 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
case CtapDeviceResponseCode::kCtap2ErrNoCredentials:
return FidoReturnCode::kUserConsentButCredentialNotRecognized;
+ // The user explicitly denied the operation. Touch ID returns this error
+ // when the user cancels the macOS prompt. External authenticators may
+ // return it e.g. after the user fails fingerprint verification.
+ case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
+ return FidoReturnCode::kUserConsentDenied;
+
// This error is returned by some authenticators (e.g. the "Yubico FIDO
// 2" CTAP2 USB keys) during GetAssertion **before the user interacted
// with the device**. The authenticator does this to avoid blinking (and
@@ -110,6 +113,8 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
case CtapDeviceResponseCode::kCtap2ErrInvalidCredential:
return base::nullopt;
+ // For all other errors, the authenticator will be dropped, and other
+ // authenticators may continue.
default:
return base::nullopt;
}
diff --git a/chromium/device/fido/fido_request_handler_base.cc b/chromium/device/fido/fido_request_handler_base.cc
index 7adbfddd277..57428aaa7d1 100644
--- a/chromium/device/fido/fido_request_handler_base.cc
+++ b/chromium/device/fido/fido_request_handler_base.cc
@@ -6,37 +6,83 @@
#include <utility>
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/location.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h"
+#include "device/fido/ble_adapter_power_manager.h"
#include "device/fido/fido_device.h"
#include "device/fido/fido_task.h"
#include "services/service_manager/public/cpp/connector.h"
namespace device {
-FidoRequestHandlerBase::FidoRequestHandlerBase(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports)
- : FidoRequestHandlerBase(connector,
- transports,
- AddPlatformAuthenticatorCallback()) {}
+// PlatformAuthenticatorInfo --------------------------
+
+PlatformAuthenticatorInfo::PlatformAuthenticatorInfo(
+ std::unique_ptr<FidoAuthenticator> authenticator_,
+ bool has_recognized_mac_touch_id_credential_)
+ : authenticator(std::move(authenticator_)),
+ has_recognized_mac_touch_id_credential(
+ has_recognized_mac_touch_id_credential_) {}
+PlatformAuthenticatorInfo::PlatformAuthenticatorInfo(
+ PlatformAuthenticatorInfo&&) = default;
+PlatformAuthenticatorInfo& PlatformAuthenticatorInfo::operator=(
+ PlatformAuthenticatorInfo&&) = default;
+PlatformAuthenticatorInfo::~PlatformAuthenticatorInfo() = default;
+
+// FidoRequestHandlerBase::TransportAvailabilityInfo --------------------------
+
+FidoRequestHandlerBase::TransportAvailabilityInfo::TransportAvailabilityInfo() =
+ default;
+
+FidoRequestHandlerBase::TransportAvailabilityInfo::TransportAvailabilityInfo(
+ const TransportAvailabilityInfo& data) = default;
+
+FidoRequestHandlerBase::TransportAvailabilityInfo&
+FidoRequestHandlerBase::TransportAvailabilityInfo::operator=(
+ const TransportAvailabilityInfo& other) = default;
+
+FidoRequestHandlerBase::TransportAvailabilityInfo::
+ ~TransportAvailabilityInfo() = default;
+
+// FidoRequestHandlerBase::TransportAvailabilityObserver ----------------------
+
+FidoRequestHandlerBase::TransportAvailabilityObserver::
+ ~TransportAvailabilityObserver() = default;
+
+// FidoRequestHandlerBase -----------------------------------------------------
FidoRequestHandlerBase::FidoRequestHandlerBase(
service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- AddPlatformAuthenticatorCallback add_platform_authenticator)
- : add_platform_authenticator_(std::move(add_platform_authenticator)) {
- for (const auto transport : transports) {
+ const base::flat_set<FidoTransportProtocol>& available_transports)
+ : weak_factory_(this) {
+ // The number of times |notify_observer_callback_| needs to be invoked before
+ // Observer::OnTransportAvailabilityEnumerated is dispatched. Essentially this
+ // is used to wait until all the parts of |transport_availability_info_| are
+ // filled out; the |notify_observer_callback_| is invoked once for each part
+ // once that part is ready, namely:
+ //
+ // 1) Once the platform authenticator related fields are filled out.
+ // 2) [Optionally, if BLE or caBLE enabled] if Bluetooth adapter is present.
+ //
+ // On top of that, we wait for (3) an invocation that happens when the
+ // |observer_| is set, so that OnTransportAvailabilityEnumerated is never
+ // called before the observer is set.
+ size_t transport_info_callback_count = 1u + 0u + 1u;
+
+ for (const auto transport : available_transports) {
// Construction of CaBleDiscovery is handled by the implementing class as it
// requires an extension passed on from the relying party.
if (transport == FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)
continue;
if (transport == FidoTransportProtocol::kInternal) {
- // Internal authenticators are injected through
- // AddPlatformAuthenticatorCallback.
- NOTREACHED();
+ // Platform authenticator availability is always indicated by
+ // |AuthenticatorImpl|.
continue;
}
@@ -46,13 +92,42 @@ FidoRequestHandlerBase::FidoRequestHandlerBase(
// HID transports are not configured.
continue;
}
+
discovery->set_observer(this);
discoveries_.push_back(std::move(discovery));
}
+
+ if (base::ContainsKey(
+ available_transports,
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) ||
+ base::ContainsKey(available_transports,
+ FidoTransportProtocol::kBluetoothLowEnergy)) {
+ ++transport_info_callback_count;
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&FidoRequestHandlerBase::ConstructBleAdapterPowerManager,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ transport_availability_info_.available_transports = available_transports;
+ notify_observer_callback_ = base::BarrierClosure(
+ transport_info_callback_count,
+ base::BindOnce(
+ &FidoRequestHandlerBase::NotifyObserverTransportAvailability,
+ weak_factory_.GetWeakPtr()));
}
FidoRequestHandlerBase::~FidoRequestHandlerBase() = default;
+void FidoRequestHandlerBase::StartAuthenticatorRequest(
+ const std::string& authenticator_id) {
+ auto authenticator = active_authenticators_.find(authenticator_id);
+ if (authenticator == active_authenticators_.end())
+ return;
+
+ InitializeAuthenticatorAndDispatchRequest(authenticator->second.get());
+}
+
void FidoRequestHandlerBase::CancelOngoingTasks(
base::StringPiece exclude_device_id) {
for (auto task_it = active_authenticators_.begin();
@@ -68,26 +143,45 @@ void FidoRequestHandlerBase::CancelOngoingTasks(
}
}
-void FidoRequestHandlerBase::Start() {
- for (const auto& discovery : discoveries_) {
- discovery->Start();
+void FidoRequestHandlerBase::OnBluetoothAdapterEnumerated(bool is_present,
+ bool is_powered_on,
+ bool can_power_on) {
+ if (!is_present) {
+ transport_availability_info_.available_transports.erase(
+ FidoTransportProtocol::kBluetoothLowEnergy);
+ transport_availability_info_.available_transports.erase(
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
}
- MaybeAddPlatformAuthenticator();
+
+ transport_availability_info_.is_ble_powered = is_powered_on;
+ transport_availability_info_.can_power_on_ble_adapter = can_power_on;
+ DCHECK(notify_observer_callback_);
+ notify_observer_callback_.Run();
}
-void FidoRequestHandlerBase::MaybeAddPlatformAuthenticator() {
- if (!add_platform_authenticator_) {
- return;
- }
- auto authenticator = std::move(add_platform_authenticator_).Run();
- if (!authenticator) {
+void FidoRequestHandlerBase::OnBluetoothAdapterPowerChanged(
+ bool is_powered_on) {
+ transport_availability_info_.is_ble_powered = is_powered_on;
+
+ if (observer_)
+ observer_->BluetoothAdapterPowerChanged(is_powered_on);
+}
+
+void FidoRequestHandlerBase::PowerOnBluetoothAdapter() {
+ if (!bluetooth_power_manager_)
return;
- }
- AddAuthenticator(std::move(authenticator));
+
+ bluetooth_power_manager_->SetAdapterPower(true /* set_power_on */);
}
-void FidoRequestHandlerBase::DiscoveryStarted(FidoDiscovery* discovery,
- bool success) {}
+base::WeakPtr<FidoRequestHandlerBase> FidoRequestHandlerBase::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void FidoRequestHandlerBase::Start() {
+ for (const auto& discovery : discoveries_)
+ discovery->Start();
+}
void FidoRequestHandlerBase::DeviceAdded(FidoDiscovery* discovery,
FidoDevice* device) {
@@ -107,16 +201,78 @@ void FidoRequestHandlerBase::DeviceRemoved(FidoDiscovery* discovery,
// ongoing_tasks_.erase() will have no effect for the devices that have been
// already removed due to processing error or due to invocation of
// CancelOngoingTasks().
+ DCHECK(device);
active_authenticators_.erase(device->GetId());
+
+ if (observer_)
+ observer_->FidoAuthenticatorRemoved(device->GetId());
}
void FidoRequestHandlerBase::AddAuthenticator(
std::unique_ptr<FidoAuthenticator> authenticator) {
- DCHECK(!base::ContainsKey(active_authenticators(), authenticator->GetId()));
+ DCHECK(authenticator &&
+ !base::ContainsKey(active_authenticators(), authenticator->GetId()));
FidoAuthenticator* authenticator_ptr = authenticator.get();
active_authenticators_.emplace(authenticator->GetId(),
std::move(authenticator));
- DispatchRequest(authenticator_ptr);
+
+ // If |observer_| exists, dispatching request to |authenticator_ptr| is
+ // delegated to |observer_|. Else, dispatch request to |authenticator_ptr|
+ // immediately.
+ bool embedder_controls_dispatch = false;
+ if (observer_) {
+ embedder_controls_dispatch =
+ observer_->EmbedderControlsAuthenticatorDispatch(*authenticator_ptr);
+ observer_->FidoAuthenticatorAdded(*authenticator_ptr);
+ }
+
+ if (!embedder_controls_dispatch) {
+ // Post |InitializeAuthenticatorAndDispatchRequest| into its own task. This
+ // avoids hairpinning, even if the authenticator immediately invokes the
+ // request callback.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &FidoRequestHandlerBase::InitializeAuthenticatorAndDispatchRequest,
+ GetWeakPtr(), authenticator_ptr));
+ }
+}
+
+void FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
+ base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info) {
+ if (platform_authenticator_info &&
+ base::ContainsKey(transport_availability_info_.available_transports,
+ FidoTransportProtocol::kInternal)) {
+ DCHECK(platform_authenticator_info->authenticator);
+ DCHECK(
+ (platform_authenticator_info->authenticator->AuthenticatorTransport() ==
+ FidoTransportProtocol::kInternal));
+ transport_availability_info_.has_recognized_mac_touch_id_credential =
+ platform_authenticator_info->has_recognized_mac_touch_id_credential;
+ AddAuthenticator(std::move(platform_authenticator_info->authenticator));
+ } else {
+ transport_availability_info_.available_transports.erase(
+ FidoTransportProtocol::kInternal);
+ }
+
+ DCHECK(notify_observer_callback_);
+ notify_observer_callback_.Run();
+}
+
+void FidoRequestHandlerBase::NotifyObserverTransportAvailability() {
+ DCHECK(observer_);
+ observer_->OnTransportAvailabilityEnumerated(transport_availability_info_);
+}
+
+void FidoRequestHandlerBase::InitializeAuthenticatorAndDispatchRequest(
+ FidoAuthenticator* authenticator) {
+ authenticator->InitializeAuthenticator(
+ base::BindOnce(&FidoRequestHandlerBase::DispatchRequest,
+ weak_factory_.GetWeakPtr(), authenticator));
+}
+
+void FidoRequestHandlerBase::ConstructBleAdapterPowerManager() {
+ bluetooth_power_manager_ = std::make_unique<BleAdapterPowerManager>(this);
}
} // namespace device
diff --git a/chromium/device/fido/fido_request_handler_base.h b/chromium/device/fido/fido_request_handler_base.h
index 6b9b7e3dc49..aed8c4164a5 100644
--- a/chromium/device/fido/fido_request_handler_base.h
+++ b/chromium/device/fido/fido_request_handler_base.h
@@ -8,13 +8,16 @@
#include <functional>
#include <map>
#include <memory>
+#include <set>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
+#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece_forward.h"
#include "device/fido/fido_device_authenticator.h"
#include "device/fido/fido_discovery.h"
@@ -26,10 +29,22 @@ class Connector;
namespace device {
+class BleAdapterPowerManager;
class FidoAuthenticator;
class FidoDevice;
class FidoTask;
+struct COMPONENT_EXPORT(DEVICE_FIDO) PlatformAuthenticatorInfo {
+ PlatformAuthenticatorInfo(std::unique_ptr<FidoAuthenticator> authenticator,
+ bool has_recognized_mac_touch_id_credential);
+ PlatformAuthenticatorInfo(PlatformAuthenticatorInfo&&);
+ PlatformAuthenticatorInfo& operator=(PlatformAuthenticatorInfo&& other);
+ ~PlatformAuthenticatorInfo();
+
+ std::unique_ptr<FidoAuthenticator> authenticator;
+ bool has_recognized_mac_touch_id_credential;
+};
+
// Base class that handles device discovery/removal. Each FidoRequestHandlerBase
// is owned by FidoRequestManager and its lifetime is equivalent to that of a
// single WebAuthn request. For each authenticator, the per-device work is
@@ -40,20 +55,76 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
public:
using AuthenticatorMap =
std::map<std::string, std::unique_ptr<FidoAuthenticator>, std::less<>>;
- using AddPlatformAuthenticatorCallback =
- base::OnceCallback<std::unique_ptr<FidoAuthenticator>()>;
+ using RequestCallback = base::RepeatingCallback<void(const std::string&)>;
+
+ enum class RequestType { kMakeCredential, kGetAssertion };
+
+ // Encapsulates data required to initiate WebAuthN UX dialog. Once all
+ // components of TransportAvailabilityInfo is set,
+ // AuthenticatorRequestClientDelegate should be notified.
+ // TODO(hongjunchoi): Add async calls to notify embedder when Bluetooth is
+ // powered on/off.
+ struct COMPONENT_EXPORT(DEVICE_FIDO) TransportAvailabilityInfo {
+ TransportAvailabilityInfo();
+ TransportAvailabilityInfo(const TransportAvailabilityInfo& other);
+ TransportAvailabilityInfo& operator=(
+ const TransportAvailabilityInfo& other);
+ ~TransportAvailabilityInfo();
+
+ // TODO(hongjunchoi): Factor |rp_id| and |request_type| from
+ // TransportAvailabilityInfo.
+ // See: https://crbug.com/875011
+ std::string rp_id;
+ RequestType request_type = RequestType::kMakeCredential;
+
+ // The intersection of transports supported by the client and allowed by the
+ // relying party.
+ base::flat_set<FidoTransportProtocol> available_transports;
+
+ bool has_recognized_mac_touch_id_credential = false;
+ bool is_ble_powered = false;
+ bool can_power_on_ble_adapter = false;
+ };
+
+ class COMPONENT_EXPORT(DEVICE_FIDO) TransportAvailabilityObserver {
+ public:
+ virtual ~TransportAvailabilityObserver();
+
+ // This method will not be invoked until the observer is set.
+ virtual void OnTransportAvailabilityEnumerated(
+ TransportAvailabilityInfo data) = 0;
+
+ // If true, the request handler will defer dispatch of its request onto the
+ // given authenticator to the embedder. The embedder needs to call
+ // |StartAuthenticatorRequest| when it wants to initiate request dispatch.
+ //
+ // This method is invoked before |FidoAuthenticatorAdded|, and may be
+ // invoked multiple times for the same authenticator. Depending on the
+ // result, the request handler might decide not to make the authenticator
+ // available, in which case it never gets passed to
+ // |FidoAuthenticatorAdded|.
+ virtual bool EmbedderControlsAuthenticatorDispatch(
+ const FidoAuthenticator& authenticator) = 0;
+
+ virtual void BluetoothAdapterPowerChanged(bool is_powered_on) = 0;
+ virtual void FidoAuthenticatorAdded(
+ const FidoAuthenticator& authenticator) = 0;
+ virtual void FidoAuthenticatorRemoved(base::StringPiece device_id) = 0;
+ };
// TODO(https://crbug.com/769631): Remove the dependency on Connector once
- // device/fido is servicified.
+ // device/fido is servicified. The |available_transports| should be the
+ // intersection of transports supported by the client and allowed by the
+ // relying party.
FidoRequestHandlerBase(
service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports);
- FidoRequestHandlerBase(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- AddPlatformAuthenticatorCallback add_platform_authenticator);
+ const base::flat_set<FidoTransportProtocol>& available_transports);
~FidoRequestHandlerBase() override;
+ // Triggers DispatchRequest() if |active_authenticators_| hold
+ // FidoAuthenticator with given |authenticator_id|.
+ void StartAuthenticatorRequest(const std::string& authenticator_id);
+
// Triggers cancellation of all per-device FidoTasks, except for the device
// with |exclude_device_id|, if one is provided. Cancelled tasks are
// immediately removed from |ongoing_tasks_|.
@@ -65,6 +136,31 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
// per-device tasks are cancelled.
// https://w3c.github.io/webauthn/#iface-pkcredential
void CancelOngoingTasks(base::StringPiece exclude_device_id = nullptr);
+ void OnBluetoothAdapterEnumerated(bool is_present,
+ bool is_powered_on,
+ bool can_power_on);
+ void OnBluetoothAdapterPowerChanged(bool is_powered_on);
+ void PowerOnBluetoothAdapter();
+
+ base::WeakPtr<FidoRequestHandlerBase> GetWeakPtr();
+
+ void set_observer(TransportAvailabilityObserver* observer) {
+ DCHECK(!observer_) << "Only one observer is supported.";
+ observer_ = observer;
+
+ DCHECK(notify_observer_callback_);
+ notify_observer_callback_.Run();
+ }
+
+ // Set the platform authenticator for this request, if one is available.
+ // |AuthenticatorImpl| must call this method after invoking |set_oberver| even
+ // if no platform authenticator is available, in which case it passes nullptr.
+ virtual void SetPlatformAuthenticatorOrMarkUnavailable(
+ base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info);
+
+ TransportAvailabilityInfo& transport_availability_info() {
+ return transport_availability_info_;
+ }
protected:
// Subclasses implement this method to dispatch their request onto the given
@@ -82,21 +178,31 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
std::vector<std::unique_ptr<FidoDiscovery>>& discoveries() {
return discoveries_;
}
+ TransportAvailabilityObserver* observer() const { return observer_; }
private:
// FidoDiscovery::Observer
- void DiscoveryStarted(FidoDiscovery* discovery, bool success) final;
void DeviceAdded(FidoDiscovery* discovery, FidoDevice* device) final;
void DeviceRemoved(FidoDiscovery* discovery, FidoDevice* device) final;
void AddAuthenticator(std::unique_ptr<FidoAuthenticator> authenticator);
+ void NotifyObserverTransportAvailability();
- void MaybeAddPlatformAuthenticator();
+ // Invokes FidoAuthenticator::InitializeAuthenticator(), followed by
+ // DispatchRequest(). InitializeAuthenticator() sends a GetInfo command
+ // to FidoDeviceAuthenticator instances in order to determine their protocol
+ // versions before a request can be dispatched.
+ void InitializeAuthenticatorAndDispatchRequest(FidoAuthenticator*);
+ void ConstructBleAdapterPowerManager();
AuthenticatorMap active_authenticators_;
std::vector<std::unique_ptr<FidoDiscovery>> discoveries_;
+ TransportAvailabilityObserver* observer_ = nullptr;
+ TransportAvailabilityInfo transport_availability_info_;
+ base::RepeatingClosure notify_observer_callback_;
+ std::unique_ptr<BleAdapterPowerManager> bluetooth_power_manager_;
- AddPlatformAuthenticatorCallback add_platform_authenticator_;
+ base::WeakPtrFactory<FidoRequestHandlerBase> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoRequestHandlerBase);
};
diff --git a/chromium/device/fido/fido_request_handler_unittest.cc b/chromium/device/fido/fido_request_handler_unittest.cc
index c497d0a7f2c..d617fb1d11b 100644
--- a/chromium/device/fido/fido_request_handler_unittest.cc
+++ b/chromium/device/fido/fido_request_handler_unittest.cc
@@ -9,6 +9,8 @@
#include "base/bind.h"
#include "base/numerics/safe_conversions.h"
#include "base/test/scoped_task_environment.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
@@ -30,17 +32,68 @@ namespace {
using FakeTaskCallback =
base::OnceCallback<void(CtapDeviceResponseCode status_code,
base::Optional<std::vector<uint8_t>>)>;
-using FakeHandlerCallback = base::OnceCallback<void(
- FidoReturnCode status_code,
- base::Optional<std::vector<uint8_t>> response_data)>;
+using FakeHandlerCallback =
+ base::OnceCallback<void(FidoReturnCode status_code,
+ base::Optional<std::vector<uint8_t>> response_data,
+ FidoTransportProtocol)>;
using FakeHandlerCallbackReceiver =
- test::StatusAndValueCallbackReceiver<FidoReturnCode,
- base::Optional<std::vector<uint8_t>>>;
+ test::StatusAndValuesCallbackReceiver<FidoReturnCode,
+ base::Optional<std::vector<uint8_t>>,
+ FidoTransportProtocol>;
enum class FakeTaskResponse : uint8_t {
kSuccess = 0x00,
kErrorReceivedAfterObtainingUserPresence = 0x01,
kProcessingError = 0x02,
+ kOperationDenied = 0x03,
+};
+
+class TestTransportAvailabilityObserver
+ : public FidoRequestHandlerBase::TransportAvailabilityObserver {
+ public:
+ using TransportAvailabilityNotificationReceiver = test::TestCallbackReceiver<
+ FidoRequestHandlerBase::TransportAvailabilityInfo>;
+
+ TestTransportAvailabilityObserver() {}
+ ~TestTransportAvailabilityObserver() override {}
+
+ void WaitForAndExpectAvailableTransportsAre(
+ base::flat_set<FidoTransportProtocol> expected_transports,
+ base::Optional<bool> has_recognized_mac_touch_id_credential =
+ base::nullopt) {
+ transport_availability_notification_receiver_.WaitForCallback();
+ auto result =
+ std::get<0>(*transport_availability_notification_receiver_.result());
+ EXPECT_THAT(result.available_transports,
+ ::testing::UnorderedElementsAreArray(expected_transports));
+ if (has_recognized_mac_touch_id_credential) {
+ EXPECT_EQ(*has_recognized_mac_touch_id_credential,
+ result.has_recognized_mac_touch_id_credential);
+ }
+ }
+
+ protected:
+ // FidoRequestHandlerBase::TransportAvailabilityObserver:
+ void OnTransportAvailabilityEnumerated(
+ FidoRequestHandlerBase::TransportAvailabilityInfo data) override {
+ transport_availability_notification_receiver_.callback().Run(
+ std::move(data));
+ }
+ bool EmbedderControlsAuthenticatorDispatch(
+ const FidoAuthenticator&) override {
+ return false;
+ }
+
+ void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
+ void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
+ }
+ void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
+
+ private:
+ TransportAvailabilityNotificationReceiver
+ transport_availability_notification_receiver_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTransportAvailabilityObserver);
};
// Fake FidoTask implementation that sends an empty byte array to the device
@@ -71,6 +124,10 @@ class FakeFidoTask : public FidoTask {
std::vector<uint8_t>());
return;
+ case FakeTaskResponse::kOperationDenied:
+ std::move(callback_).Run(
+ CtapDeviceResponseCode::kCtap2ErrOperationDenied, base::nullopt);
+ return;
case FakeTaskResponse::kProcessingError:
default:
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
@@ -86,7 +143,8 @@ class FakeFidoTask : public FidoTask {
class FakeFidoAuthenticator : public FidoDeviceAuthenticator {
public:
- FakeFidoAuthenticator(FidoDevice* device) : FidoDeviceAuthenticator(device) {}
+ explicit FakeFidoAuthenticator(FidoDevice* device)
+ : FidoDeviceAuthenticator(device) {}
void RunFakeTask(FakeTaskCallback callback) {
SetTaskForTesting(
@@ -96,15 +154,11 @@ class FakeFidoAuthenticator : public FidoDeviceAuthenticator {
class FakeFidoRequestHandler : public FidoRequestHandler<std::vector<uint8_t>> {
public:
- FakeFidoRequestHandler(
- const base::flat_set<FidoTransportProtocol>& protocols,
- FakeHandlerCallback callback,
- AddPlatformAuthenticatorCallback add_platform_authenticator =
- AddPlatformAuthenticatorCallback())
+ FakeFidoRequestHandler(const base::flat_set<FidoTransportProtocol>& protocols,
+ FakeHandlerCallback callback)
: FidoRequestHandler(nullptr /* connector */,
protocols,
- std::move(callback),
- std::move(add_platform_authenticator)),
+ std::move(callback)),
weak_factory_(this) {
Start();
}
@@ -139,39 +193,50 @@ std::vector<uint8_t> CreateFakeDeviceProcesssingError() {
return {base::strict_cast<uint8_t>(FakeTaskResponse::kProcessingError)};
}
+std::vector<uint8_t> CreateFakeOperationDeniedError() {
+ return {base::strict_cast<uint8_t>(FakeTaskResponse::kOperationDenied)};
+}
+
} // namespace
class FidoRequestHandlerTest : public ::testing::Test {
public:
+ FidoRequestHandlerTest() {
+ mock_adapter_ =
+ base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
+ }
+
void ForgeNextHidDiscovery() {
discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
+ ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
}
std::unique_ptr<FakeFidoRequestHandler> CreateFakeHandler() {
ForgeNextHidDiscovery();
- return std::make_unique<FakeFidoRequestHandler>(
+ auto handler = std::make_unique<FakeFidoRequestHandler>(
base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy}),
cb_.callback());
- }
-
- std::unique_ptr<FakeFidoRequestHandler>
- CreateFakeHandlerWithPlatformAuthenticatorCallback(
- FidoRequestHandlerBase::AddPlatformAuthenticatorCallback
- add_platform_authenticator) {
- return std::make_unique<FakeFidoRequestHandler>(
- base::flat_set<FidoTransportProtocol>(), cb_.callback(),
- std::move(add_platform_authenticator));
+ handler->SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
+ return handler;
}
test::FakeFidoDiscovery* discovery() const { return discovery_; }
+ test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
+ scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> adapter() {
+ return mock_adapter_;
+ }
FakeHandlerCallbackReceiver& callback() { return cb_; }
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
+ scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
test::FakeFidoDiscovery* discovery_;
+ test::FakeFidoDiscovery* ble_discovery_;
FakeHandlerCallbackReceiver cb_;
};
@@ -222,8 +287,8 @@ TEST_F(FidoRequestHandlerTest, TestAuthenticatorHandlerReset) {
request_handler.reset();
}
-// Test a scenario where 2 devices are connected and a response is received from
-// only a single device(device1) and the remaining device hangs.
+// Test a scenario where 2 devices are connected and a response is received
+// from only a single device(device1) and the remaining device hangs.
TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleDevices) {
auto request_handler = CreateFakeHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
@@ -296,11 +361,11 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleSuccessResponses) {
}
// Test a scenario where 3 devices respond with a processing error, an UP(user
-// presence) verified failure response with small time delay, and an UP verified
-// failure response with big time delay, respectively. Request for device with
-// processing error should be immediately dropped. Also, for UP verified
-// failures, the first received response should be passed on to the relying
-// party and cancel command should be sent to the remaining device.
+// presence) verified failure response with small time delay, and an UP
+// verified failure response with big time delay, respectively. Request for
+// device with processing error should be immediately dropped. Also, for UP
+// verified failures, the first received response should be passed on to the
+// relying party and cancel command should be sent to the remaining device.
TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
auto request_handler = CreateFakeHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
@@ -349,31 +414,177 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
callback().status());
}
-// Requests should be dispatched to the authenticator returned from the
-// AddPlatformAuthenticatorCallback if one is passed.
-TEST_F(FidoRequestHandlerTest, TestPlatformAuthenticatorCallback) {
+// If a device with transport type kInternal returns a
+// CTAP2_ERR_OPERATION_DENIED error, the request should be cancelled on all
+// pending authenticators.
+TEST_F(FidoRequestHandlerTest,
+ TestRequestWithOperationDeniedErrorInternalTransport) {
+ auto request_handler = CreateFakeHandler();
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ // Device will send CTAP2_ERR_OPERATION_DENIED.
+ auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponsePlatformDevice);
+ device0->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+ CreateFakeOperationDeniedError(),
+ base::TimeDelta::FromMicroseconds(10));
+
+ auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
+ EXPECT_CALL(*device1, Cancel());
+
+ discovery()->AddDevice(std::move(device0));
+ discovery()->AddDevice(std::move(device1));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ callback().WaitForCallback();
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
+}
+
+// Like |TestRequestWithOperationDeniedErrorInternalTransport|, but with a
+// cross-platform authenticator.
+TEST_F(FidoRequestHandlerTest,
+ TestRequestWithOperationDeniedErrorCrossPlatform) {
+ auto request_handler = CreateFakeHandler();
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ // Device will send CTAP2_ERR_OPERATION_DENIED.
+ auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device0->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
+ device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+ CreateFakeOperationDeniedError());
+
+ auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
+ EXPECT_CALL(*device1, Cancel());
+
+ discovery()->AddDevice(std::move(device0));
+ discovery()->AddDevice(std::move(device1));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ callback().WaitForCallback();
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
+}
+
+// Requests should be dispatched to the authenticator passed to
+// SetPlatformAuthenticatorOrMarkUnavailable.
+TEST_F(FidoRequestHandlerTest, TestSetPlatformAuthenticator) {
// A platform authenticator usually wouldn't usually use a FidoDevice, but
// that's not the point of the test here. The test is only trying to ensure
// the authenticator gets injected and used.
auto device = MockFidoDevice::MakeCtap();
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestGetInfoResponsePlatformDevice);
// Device returns success response.
device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeSuccessDeviceResponse());
+ device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ auto authenticator = std::make_unique<FakeFidoAuthenticator>(device.get());
- FidoRequestHandlerBase::AddPlatformAuthenticatorCallback
- make_platform_authenticator = base::BindOnce(
- [](FidoDevice* device) -> std::unique_ptr<FidoAuthenticator> {
- return std::make_unique<FakeFidoAuthenticator>(device);
- },
- device.get());
- auto request_handler = CreateFakeHandlerWithPlatformAuthenticatorCallback(
- std::move(make_platform_authenticator));
+ TestTransportAvailabilityObserver observer;
+ auto request_handler = std::make_unique<FakeFidoRequestHandler>(
+ base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
+ callback().callback());
+ request_handler->set_observer(&observer);
+ request_handler->SetPlatformAuthenticatorOrMarkUnavailable(
+ PlatformAuthenticatorInfo(std::move(authenticator), false));
+
+ observer.WaitForAndExpectAvailableTransportsAre(
+ {FidoTransportProtocol::kInternal},
+ false /* has_recognized_mac_touch_id_credential */);
+
+ callback().WaitForCallback();
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+}
+
+// SetPlatformAuthenticatorOrMarkUnavailable should propagate the
+// has_recognized_mac_touch_id_credential field.
+TEST_F(FidoRequestHandlerTest,
+ TestSetPlatformAuthenticatorHasTouchIdCredential) {
+ // A platform authenticator usually wouldn't usually use a FidoDevice, but
+ // that's not the point of the test here. The test is only trying to ensure
+ // the authenticator gets injected and used.
+ auto device = MockFidoDevice::MakeCtap();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ // Device returns success response.
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
+ device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+ CreateFakeSuccessDeviceResponse());
+ device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ auto authenticator = std::make_unique<FakeFidoAuthenticator>(device.get());
+
+ TestTransportAvailabilityObserver observer;
+ auto request_handler = std::make_unique<FakeFidoRequestHandler>(
+ base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
+ callback().callback());
+ request_handler->set_observer(&observer);
+ request_handler->SetPlatformAuthenticatorOrMarkUnavailable(
+ PlatformAuthenticatorInfo(std::move(authenticator), true));
+
+ observer.WaitForAndExpectAvailableTransportsAre(
+ {FidoTransportProtocol::kInternal},
+ true /* has_recognized_mac_touch_id_credential */);
- scoped_task_environment_.FastForwardUntilNoTasksRemain();
callback().WaitForCallback();
EXPECT_TRUE(request_handler->is_complete());
EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
}
+TEST_F(FidoRequestHandlerTest, InternalTransportDisallowedIfMarkedUnavailable) {
+ TestTransportAvailabilityObserver observer;
+ auto request_handler = std::make_unique<FakeFidoRequestHandler>(
+ base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
+ callback().callback());
+ request_handler->set_observer(&observer);
+ request_handler->SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
+
+ observer.WaitForAndExpectAvailableTransportsAre({});
+}
+
+TEST_F(FidoRequestHandlerTest, BleTransportAllowedIfBluetoothAdapterPresent) {
+ EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(true));
+
+ TestTransportAvailabilityObserver observer;
+ auto request_handler = CreateFakeHandler();
+ request_handler->set_observer(&observer);
+
+ observer.WaitForAndExpectAvailableTransportsAre(
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy});
+}
+
+TEST_F(FidoRequestHandlerTest,
+ BleTransportDisallowedBluetoothAdapterNotPresent) {
+ EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(false));
+
+ TestTransportAvailabilityObserver observer;
+ auto request_handler = CreateFakeHandler();
+ request_handler->set_observer(&observer);
+
+ observer.WaitForAndExpectAvailableTransportsAre(
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice});
+}
+
+TEST_F(FidoRequestHandlerTest,
+ TransportAvailabilityNotificationOnObserverSetLate) {
+ EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(true));
+
+ TestTransportAvailabilityObserver observer;
+ auto request_handler = CreateFakeHandler();
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+
+ request_handler->set_observer(&observer);
+ observer.WaitForAndExpectAvailableTransportsAre(
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy});
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_strings.grd b/chromium/device/fido/fido_strings.grd
new file mode 100644
index 00000000000..8eaa3873834
--- /dev/null
+++ b/chromium/device/fido/fido_strings.grd
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+This file contains the strings for //device/fido.
+-->
+
+<grit base_dir="." latest_public_release="0" current_release="1"
+ output_all_resource_defines="false" source_lang_id="en" enc_check="möl">
+ <outputs>
+ <output filename="grit/fido_strings.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="fido_strings_am.pak" type="data_package" lang="am" />
+ <output filename="fido_strings_ar.pak" type="data_package" lang="ar" />
+ <output filename="fido_strings_bg.pak" type="data_package" lang="bg" />
+ <output filename="fido_strings_bn.pak" type="data_package" lang="bn" />
+ <output filename="fido_strings_ca.pak" type="data_package" lang="ca" />
+ <output filename="fido_strings_cs.pak" type="data_package" lang="cs" />
+ <output filename="fido_strings_da.pak" type="data_package" lang="da" />
+ <output filename="fido_strings_de.pak" type="data_package" lang="de" />
+ <output filename="fido_strings_el.pak" type="data_package" lang="el" />
+ <output filename="fido_strings_en-GB.pak" type="data_package" lang="en-GB" />
+ <output filename="fido_strings_en-US.pak" type="data_package" lang="en" />
+ <output filename="fido_strings_es.pak" type="data_package" lang="es" />
+ <output filename="fido_strings_es-419.pak" type="data_package" lang="es-419" />
+ <output filename="fido_strings_et.pak" type="data_package" lang="et" />
+ <output filename="fido_strings_fa.pak" type="data_package" lang="fa" />
+ <output filename="fido_strings_fake-bidi.pak" type="data_package" lang="fake-bidi" />
+ <output filename="fido_strings_fi.pak" type="data_package" lang="fi" />
+ <output filename="fido_strings_fil.pak" type="data_package" lang="fil" />
+ <output filename="fido_strings_fr.pak" type="data_package" lang="fr" />
+ <output filename="fido_strings_gu.pak" type="data_package" lang="gu" />
+ <output filename="fido_strings_he.pak" type="data_package" lang="he" />
+ <output filename="fido_strings_hi.pak" type="data_package" lang="hi" />
+ <output filename="fido_strings_hr.pak" type="data_package" lang="hr" />
+ <output filename="fido_strings_hu.pak" type="data_package" lang="hu" />
+ <output filename="fido_strings_id.pak" type="data_package" lang="id" />
+ <output filename="fido_strings_it.pak" type="data_package" lang="it" />
+ <output filename="fido_strings_ja.pak" type="data_package" lang="ja" />
+ <output filename="fido_strings_kn.pak" type="data_package" lang="kn" />
+ <output filename="fido_strings_ko.pak" type="data_package" lang="ko" />
+ <output filename="fido_strings_lt.pak" type="data_package" lang="lt" />
+ <output filename="fido_strings_lv.pak" type="data_package" lang="lv" />
+ <output filename="fido_strings_ml.pak" type="data_package" lang="ml" />
+ <output filename="fido_strings_mr.pak" type="data_package" lang="mr" />
+ <output filename="fido_strings_ms.pak" type="data_package" lang="ms" />
+ <output filename="fido_strings_nl.pak" type="data_package" lang="nl" />
+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+ be 'nb'. -->
+ <output filename="fido_strings_nb.pak" type="data_package" lang="no" />
+ <output filename="fido_strings_pl.pak" type="data_package" lang="pl" />
+ <output filename="fido_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
+ <output filename="fido_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
+ <output filename="fido_strings_ro.pak" type="data_package" lang="ro" />
+ <output filename="fido_strings_ru.pak" type="data_package" lang="ru" />
+ <output filename="fido_strings_sk.pak" type="data_package" lang="sk" />
+ <output filename="fido_strings_sl.pak" type="data_package" lang="sl" />
+ <output filename="fido_strings_sr.pak" type="data_package" lang="sr" />
+ <output filename="fido_strings_sv.pak" type="data_package" lang="sv" />
+ <output filename="fido_strings_sw.pak" type="data_package" lang="sw" />
+ <output filename="fido_strings_ta.pak" type="data_package" lang="ta" />
+ <output filename="fido_strings_te.pak" type="data_package" lang="te" />
+ <output filename="fido_strings_th.pak" type="data_package" lang="th" />
+ <output filename="fido_strings_tr.pak" type="data_package" lang="tr" />
+ <output filename="fido_strings_uk.pak" type="data_package" lang="uk" />
+ <output filename="fido_strings_vi.pak" type="data_package" lang="vi" />
+ <output filename="fido_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
+ <output filename="fido_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+ </outputs>
+ <translations>
+ <file path="strings/fido_strings_am.xtb" lang="am" />
+ <file path="strings/fido_strings_ar.xtb" lang="ar" />
+ <file path="strings/fido_strings_bg.xtb" lang="bg" />
+ <file path="strings/fido_strings_bn.xtb" lang="bn" />
+ <file path="strings/fido_strings_ca.xtb" lang="ca" />
+ <file path="strings/fido_strings_cs.xtb" lang="cs" />
+ <file path="strings/fido_strings_da.xtb" lang="da" />
+ <file path="strings/fido_strings_de.xtb" lang="de" />
+ <file path="strings/fido_strings_el.xtb" lang="el" />
+ <file path="strings/fido_strings_en-GB.xtb" lang="en-GB" />
+ <file path="strings/fido_strings_es.xtb" lang="es" />
+ <file path="strings/fido_strings_es-419.xtb" lang="es-419" />
+ <file path="strings/fido_strings_et.xtb" lang="et" />
+ <file path="strings/fido_strings_fa.xtb" lang="fa" />
+ <file path="strings/fido_strings_fi.xtb" lang="fi" />
+ <file path="strings/fido_strings_fil.xtb" lang="fil" />
+ <file path="strings/fido_strings_fr.xtb" lang="fr" />
+ <file path="strings/fido_strings_gu.xtb" lang="gu" />
+ <file path="strings/fido_strings_hi.xtb" lang="hi" />
+ <file path="strings/fido_strings_hr.xtb" lang="hr" />
+ <file path="strings/fido_strings_hu.xtb" lang="hu" />
+ <file path="strings/fido_strings_id.xtb" lang="id" />
+ <file path="strings/fido_strings_it.xtb" lang="it" />
+ <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
+ <file path="strings/fido_strings_iw.xtb" lang="he" />
+ <file path="strings/fido_strings_ja.xtb" lang="ja" />
+ <file path="strings/fido_strings_kn.xtb" lang="kn" />
+ <file path="strings/fido_strings_ko.xtb" lang="ko" />
+ <file path="strings/fido_strings_lt.xtb" lang="lt" />
+ <file path="strings/fido_strings_lv.xtb" lang="lv" />
+ <file path="strings/fido_strings_ml.xtb" lang="ml" />
+ <file path="strings/fido_strings_mr.xtb" lang="mr" />
+ <file path="strings/fido_strings_ms.xtb" lang="ms" />
+ <file path="strings/fido_strings_nl.xtb" lang="nl" />
+ <file path="strings/fido_strings_no.xtb" lang="no" />
+ <file path="strings/fido_strings_pl.xtb" lang="pl" />
+ <file path="strings/fido_strings_pt-BR.xtb" lang="pt-BR" />
+ <file path="strings/fido_strings_pt-PT.xtb" lang="pt-PT" />
+ <file path="strings/fido_strings_ro.xtb" lang="ro" />
+ <file path="strings/fido_strings_ru.xtb" lang="ru" />
+ <file path="strings/fido_strings_sk.xtb" lang="sk" />
+ <file path="strings/fido_strings_sl.xtb" lang="sl" />
+ <file path="strings/fido_strings_sr.xtb" lang="sr" />
+ <file path="strings/fido_strings_sv.xtb" lang="sv" />
+ <file path="strings/fido_strings_sw.xtb" lang="sw" />
+ <file path="strings/fido_strings_ta.xtb" lang="ta" />
+ <file path="strings/fido_strings_te.xtb" lang="te" />
+ <file path="strings/fido_strings_th.xtb" lang="th" />
+ <file path="strings/fido_strings_tr.xtb" lang="tr" />
+ <file path="strings/fido_strings_uk.xtb" lang="uk" />
+ <file path="strings/fido_strings_vi.xtb" lang="vi" />
+ <file path="strings/fido_strings_zh-CN.xtb" lang="zh-CN" />
+ <file path="strings/fido_strings_zh-TW.xtb" lang="zh-TW" />
+ </translations>
+ <release seq="1" allow_pseudo="false">
+ <messages fallback_to_english="true">
+ <if expr="is_macosx">
+ <message name="IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON" desc="Sentence fragment (lower case). Shown after 'Google Chrome is trying to ' (or in some languages, 'Google Chrome wants to ') in a dialog message asking the user to confirm or cancel this action.">
+ verify your identity on <ph name="APP_NAME">$1<ex>google.com</ex></ph>
+ </message>
+ </if>
+ </messages>
+ </release>
+</grit>
+
+
diff --git a/chromium/device/fido/fido_task.h b/chromium/device/fido/fido_task.h
index b2ca94b87bf..ab4c8ad786a 100644
--- a/chromium/device/fido/fido_task.h
+++ b/chromium/device/fido/fido_task.h
@@ -18,13 +18,9 @@
namespace device {
// Encapsulates per-device request logic shared between MakeCredential and
-// GetAssertion. Handles issuing the AuthenticatorGetInfo command to tokens,
-// caching device info, and distinguishing U2F tokens from CTAP tokens.
+// GetAssertion.
//
-// FidoTask is owned by FidoRequestHandler and manages all interaction with
-// |device_|. It is created when a new device is discovered by FidoDiscovery and
-// destroyed when the device is removed or when a successful response has been
-// issued to the relying party from another authenticator.
+// TODO(martinkr): FidoTask should be subsumed by FidoDeviceAuthenticator.
class COMPONENT_EXPORT(DEVICE_FIDO) FidoTask {
public:
// The |device| must outlive the FidoTask instance.
diff --git a/chromium/device/fido/fido_test_data.h b/chromium/device/fido/fido_test_data.h
index e36d0da259f..88673b1e8b7 100644
--- a/chromium/device/fido/fido_test_data.h
+++ b/chromium/device/fido/fido_test_data.h
@@ -918,7 +918,7 @@ constexpr uint8_t kCtapMakeCredentialRequest[] = {
// True(21)
0xf5};
-constexpr uint8_t kCtapGetAssertionRequest[] = {
+constexpr uint8_t kTestComplexCtapGetAssertionRequest[] = {
// authenticatorGetAssertion command
0x02,
// map(4)
@@ -977,12 +977,93 @@ constexpr uint8_t kCtapGetAssertionRequest[] = {
// key - "uv"
0x62, 0x75, 0x76,
// value - True(21)
- 0xf5};
+ 0xf5,
+};
+
+constexpr uint8_t kCtapGetAssertionRequest[] = {
+ // authenticatorGetAssertion command
+ 0x02,
+ // map(3)
+ 0xa3,
+ // key(01) -rpId
+ 0x01,
+ // value - "acme.com"
+ 0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ // key(02) - client data hash
+ 0x02,
+ // value - bytes(32)
+ 0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e,
+ 0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
+ 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41,
+ // key(03) - allow list
+ 0x03,
+ // value - array(1)
+ 0x81,
+ // map(2)
+ 0xa2,
+ // key - "id"
+ 0x62, 0x69, 0x64,
+ // value - credential ID
+ 0x58, 0x40, 0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE,
+ 0x9C, 0x26, 0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17,
+ 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7,
+ 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39,
+ 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15,
+ 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
+ // key - "type"
+ 0x64, 0x74, 0x79, 0x70, 0x65,
+ // value - "public-key"
+ 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+};
+
+constexpr uint8_t kCtapSilentGetAssertionRequest[] = {
+ // authenticatorGetAssertion command
+ 0x02,
+ // map(4)
+ 0xa4,
+ // key(01) -rpId
+ 0x01,
+ // value - "acme.com"
+ 0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ // key(02) - client data hash
+ 0x02,
+ // value - bytes(32)
+ 0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e,
+ 0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
+ 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41,
+ // key(03) - allow list
+ 0x03,
+ // value - array(1)
+ 0x81,
+ // map(2)
+ 0xa2,
+ // key - "id"
+ 0x62, 0x69, 0x64,
+ // value - credential ID
+ 0x58, 0x40, 0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE,
+ 0x9C, 0x26, 0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17,
+ 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7,
+ 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39,
+ 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15,
+ 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
+ // key - "type"
+ 0x64, 0x74, 0x79, 0x70, 0x65,
+ // value - "public-key"
+ 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+ // unsigned(5) - options
+ 0x05,
+ // map(1)
+ 0xa1,
+ // key -"up"
+ 0x62, 0x75, 0x70,
+ // value - False(20)
+ 0xf4,
+};
// CTAP responses --------------------------------------------------------------
-// A sample well formed response to CTAP AuthenticatorGetInfo request. Supports
-// platform device, resident key, and user verification.
+// A sample well formed response to CTAP AuthenticatorGetInfo request. Cross
+// platform device that supports resident key, and user verification.
constexpr uint8_t kTestAuthenticatorGetInfoResponse[] = {
0x00, 0xA6, 0x01, 0x82, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F,
0x30, 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, 0x02, 0x82, 0x63, 0x75,
@@ -990,11 +1071,24 @@ constexpr uint8_t kTestAuthenticatorGetInfoResponse[] = {
0x65, 0x74, 0x03, 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15,
0x80, 0x06, 0x17, 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x04, 0xA5, 0x62, 0x72,
0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x62, 0x75, 0x76, 0xF5, 0x64, 0x70,
- 0x6C, 0x61, 0x74, 0xF5, 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50,
+ 0x6C, 0x61, 0x74, 0xF4, 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50,
0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
};
-// AuthenticatorGetInfo request with all configurations equal to that of
+// AuthenticatorGetInfo response with all configurations equal to that of
+// kTestAuthenticatorGetInfoResponse except that U2F protocol is not supported.
+constexpr uint8_t kTestCtap2OnlyAuthenticatorGetInfoResponse[] = {
+ 0x00, 0xA6, 0x01, 0x81, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32,
+ 0x5F, 0x30, 0x02, 0x82, 0x63, 0x75, 0x76, 0x6D, 0x6B, 0x68, 0x6D,
+ 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x03, 0x50,
+ 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+ 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x04, 0xA5, 0x62, 0x72, 0x6B, 0xF5,
+ 0x62, 0x75, 0x70, 0xF5, 0x62, 0x75, 0x76, 0xF5, 0x64, 0x70, 0x6C,
+ 0x61, 0x74, 0xF4, 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50,
+ 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
+};
+
+// AuthenticatorGetInfo response with all configurations equal to that of
// kTestAuthenticatorGetInfoResponse except user verification option is set to
// false.
constexpr uint8_t kTestGetInfoResponseWithoutUvSupport[] = {
@@ -1030,16 +1124,16 @@ constexpr uint8_t kTestGetInfoResponseWithoutResidentKeySupport[] = {
// AuthenticatorGetInfo request with all configurations equal to that of
// kTestAuthenticatorGetInfoResponse except platform device option is set to
-// false.
-constexpr uint8_t kTestGetInfoResponseCrossPlatformDevice[] = {
+// true.
+constexpr uint8_t kTestGetInfoResponsePlatformDevice[] = {
0x00, 0xA6, 0x01, 0x82, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F,
0x30, 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, 0x02, 0x82, 0x63, 0x75,
0x76, 0x6D, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72,
0x65, 0x74, 0x03, 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15,
0x80, 0x06, 0x17, 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x04, 0xA5, 0x62, 0x72,
0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x62, 0x75, 0x76, 0xF5, 0x64, 0x70,
- // platform device : false
- 0x6C, 0x61, 0x74, 0xF4,
+ // platform device : true
+ 0x6C, 0x61, 0x74, 0xF5,
// End of platform_device setting.
0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05,
0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
diff --git a/chromium/device/fido/fido_transport_protocol.cc b/chromium/device/fido/fido_transport_protocol.cc
new file mode 100644
index 00000000000..2c98c529be7
--- /dev/null
+++ b/chromium/device/fido/fido_transport_protocol.cc
@@ -0,0 +1,57 @@
+// Copyright 2018 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/fido/fido_transport_protocol.h"
+
+namespace device {
+
+const char kUsbHumanInterfaceDevice[] = "usb";
+const char kNearFieldCommunication[] = "nfc";
+const char kBluetoothLowEnergy[] = "ble";
+const char kCloudAssistedBluetoothLowEnergy[] = "cable";
+const char kInternal[] = "internal";
+
+base::flat_set<FidoTransportProtocol> GetAllTransportProtocols() {
+ return {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kInternal};
+}
+
+base::Optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
+ base::StringPiece protocol) {
+ if (protocol == kUsbHumanInterfaceDevice)
+ return FidoTransportProtocol::kUsbHumanInterfaceDevice;
+ else if (protocol == kNearFieldCommunication)
+ return FidoTransportProtocol::kNearFieldCommunication;
+ else if (protocol == kBluetoothLowEnergy)
+ return FidoTransportProtocol::kBluetoothLowEnergy;
+ else if (protocol == kCloudAssistedBluetoothLowEnergy)
+ return FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy;
+ else if (protocol == kInternal)
+ return FidoTransportProtocol::kInternal;
+ else
+ return base::nullopt;
+}
+
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::string ToString(FidoTransportProtocol protocol) {
+ switch (protocol) {
+ case FidoTransportProtocol::kUsbHumanInterfaceDevice:
+ return kUsbHumanInterfaceDevice;
+ case FidoTransportProtocol::kNearFieldCommunication:
+ return kNearFieldCommunication;
+ case FidoTransportProtocol::kBluetoothLowEnergy:
+ return kBluetoothLowEnergy;
+ case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy:
+ return kCloudAssistedBluetoothLowEnergy;
+ case FidoTransportProtocol::kInternal:
+ return kInternal;
+ }
+ NOTREACHED();
+ return "";
+}
+
+} // namespace device
diff --git a/chromium/device/fido/fido_transport_protocol.h b/chromium/device/fido/fido_transport_protocol.h
index 71b09d95902..cf4847c8fbe 100644
--- a/chromium/device/fido/fido_transport_protocol.h
+++ b/chromium/device/fido/fido_transport_protocol.h
@@ -5,11 +5,18 @@
#ifndef DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
#define DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
+#include <string>
+
+#include "base/component_export.h"
+#include "base/containers/flat_set.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+
namespace device {
// This enum represents the transport protocols over which Fido WebAuthN API is
// currently supported.
-enum class FidoTransportProtocol {
+enum class FidoTransportProtocol : uint8_t {
kUsbHumanInterfaceDevice,
kNearFieldCommunication,
kBluetoothLowEnergy,
@@ -17,6 +24,23 @@ enum class FidoTransportProtocol {
kInternal,
};
+// String representation of above FidoTransportProtocol enum.
+extern const char kUsbHumanInterfaceDevice[];
+extern const char kNearFieldCommunication[];
+extern const char kBluetoothLowEnergy[];
+extern const char kCloudAssistedBluetoothLowEnergy[];
+extern const char kInternal[];
+
+COMPONENT_EXPORT(DEVICE_FIDO)
+base::flat_set<FidoTransportProtocol> GetAllTransportProtocols();
+
+COMPONENT_EXPORT(DEVICE_FIDO)
+base::Optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
+ base::StringPiece protocol);
+
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::string ToString(FidoTransportProtocol protocol);
+
} // namespace device
#endif // DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
diff --git a/chromium/device/fido/get_assertion_handler_unittest.cc b/chromium/device/fido/get_assertion_handler_unittest.cc
index ed8267e85f3..e762657803b 100644
--- a/chromium/device/fido/get_assertion_handler_unittest.cc
+++ b/chromium/device/fido/get_assertion_handler_unittest.cc
@@ -5,11 +5,16 @@
#include <memory>
#include <utility>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "device/base/features.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/device_response_converter.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
@@ -25,37 +30,108 @@ namespace device {
namespace {
-using TestGetAssertionRequestCallback = test::StatusAndValueCallbackReceiver<
+constexpr uint8_t kBogusCredentialId[] = {0x01, 0x02, 0x03, 0x04};
+
+using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
FidoReturnCode,
- base::Optional<AuthenticatorGetAssertionResponse>>;
+ base::Optional<AuthenticatorGetAssertionResponse>,
+ FidoTransportProtocol>;
} // namespace
class FidoGetAssertionHandlerTest : public ::testing::Test {
public:
- void ForgeNextHidDiscovery() {
+ FidoGetAssertionHandlerTest() {
+ mock_adapter_ =
+ base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
+ }
+
+ void ForgeDiscoveries() {
discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
+ ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
+ cable_discovery_ = scoped_fake_discovery_factory_.ForgeNextCableDiscovery();
+ nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
+ }
+
+ CtapGetAssertionRequest CreateTestRequestWithCableExtension() {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request.SetCableExtension({});
+ return request;
}
- std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandler() {
+ std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
CtapGetAssertionRequest request(test_data::kRelyingPartyId,
test_data::kClientDataHash);
request.SetAllowList(
{{CredentialType::kPublicKey,
fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
+ return CreateGetAssertionHandlerWithRequest(std::move(request));
+ }
+ std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request.SetAllowList({{CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId)}});
return CreateGetAssertionHandlerWithRequest(std::move(request));
}
std::unique_ptr<GetAssertionRequestHandler>
CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
- ForgeNextHidDiscovery();
+ ForgeDiscoveries();
+
+ auto handler = std::make_unique<GetAssertionRequestHandler>(
+ nullptr /* connector */, supported_transports_, std::move(request),
+ get_assertion_cb_.callback());
+ handler->SetPlatformAuthenticatorOrMarkUnavailable(
+ CreatePlatformAuthenticator());
+ return handler;
+ }
+
+ void ExpectAllowedTransportsForRequestAre(
+ GetAssertionRequestHandler* request_handler,
+ base::flat_set<FidoTransportProtocol> transports) {
+ using Transport = FidoTransportProtocol;
+ if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+ ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+ if (base::ContainsKey(transports,
+ Transport::kCloudAssistedBluetoothLowEnergy))
+ cable_discovery()->WaitForCallToStartAndSimulateSuccess();
+ if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
+ nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_assertion_callback().was_called());
+
+ if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+ EXPECT_FALSE(discovery()->is_start_requested());
+ if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+ EXPECT_FALSE(ble_discovery()->is_start_requested());
+ if (!base::ContainsKey(transports,
+ Transport::kCloudAssistedBluetoothLowEnergy))
+ EXPECT_FALSE(cable_discovery()->is_start_requested());
+ if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
+ EXPECT_FALSE(nfc_discovery()->is_start_requested());
+
+ // Even with FidoTransportProtocol::kInternal allowed, unless the platform
+ // authenticator factory returns a FidoAuthenticator instance (which it will
+ // not be default), the transport will be marked `unavailable`.
+ transports.erase(Transport::kInternal);
+
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAreArray(transports));
+ }
- return std::make_unique<GetAssertionRequestHandler>(
- nullptr /* connector */,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- std::move(request), get_assertion_cb_.callback());
+ void ExpectAllTransportsAreAllowedForRequest(
+ GetAssertionRequestHandler* request_handler) {
+ ExpectAllowedTransportsForRequestAre(request_handler,
+ GetAllTransportProtocols());
}
void InitFeatureListAndDisableCtapFlag() {
@@ -63,28 +139,61 @@ class FidoGetAssertionHandlerTest : public ::testing::Test {
}
test::FakeFidoDiscovery* discovery() const { return discovery_; }
+ test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
+ test::FakeFidoDiscovery* cable_discovery() const { return cable_discovery_; }
+ test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
TestGetAssertionRequestCallback& get_assertion_callback() {
return get_assertion_cb_;
}
+ void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
+ mock_platform_device_ = std::move(device);
+ }
+
+ void set_supported_transports(
+ base::flat_set<FidoTransportProtocol> transports) {
+ supported_transports_ = std::move(transports);
+ }
+
protected:
+ base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
+ if (!mock_platform_device_)
+ return base::nullopt;
+ return PlatformAuthenticatorInfo(
+ std::make_unique<FidoDeviceAuthenticator>(mock_platform_device_.get()),
+ false /* has_recognized_mac_touch_id_credential_available */);
+ }
+
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
base::test::ScopedFeatureList scoped_feature_list_;
test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
test::FakeFidoDiscovery* discovery_;
+ test::FakeFidoDiscovery* ble_discovery_;
+ test::FakeFidoDiscovery* cable_discovery_;
+ test::FakeFidoDiscovery* nfc_discovery_;
+ scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
+ std::unique_ptr<MockFidoDevice> mock_platform_device_;
TestGetAssertionRequestCallback get_assertion_cb_;
+ base::flat_set<FidoTransportProtocol> supported_transports_ =
+ GetAllTransportProtocols();
};
-TEST_F(FidoGetAssertionHandlerTest, TestGetAssertionRequestOnSingleDevice) {
- auto request_handler = CreateGetAssertionHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
+TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) {
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
+ test_data::kRelyingPartyId, test_data::kClientDataHash));
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion,
+ request_handler->transport_availability_info().request_type);
+ EXPECT_EQ(test_data::kRelyingPartyId,
+ request_handler->transport_availability_info().rp_id);
+}
+
+TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
+ auto request_handler = CreateGetAssertionHandlerCtap();
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
@@ -93,19 +202,16 @@ TEST_F(FidoGetAssertionHandlerTest, TestGetAssertionRequestOnSingleDevice) {
get_assertion_callback().WaitForCallback();
EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
- EXPECT_TRUE(get_assertion_callback().value());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
EXPECT_TRUE(request_handler->is_complete());
}
// Test a scenario where the connected authenticator is a U2F device.
TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
- auto request_handler = CreateGetAssertionHandler();
+ auto request_handler = CreateGetAssertionHandlerU2f();
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+ auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
device->ExpectRequestAndRespondWith(
test_data::kU2fCheckOnlySignCommandApdu,
test_data::kApduEncodedNoErrorSignResponse);
@@ -116,7 +222,7 @@ TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
- EXPECT_TRUE(get_assertion_callback().value());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
EXPECT_TRUE(request_handler->is_complete());
}
@@ -124,7 +230,7 @@ TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
// "WebAuthenticationCtap2" flag is not enabled.
TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
InitFeatureListAndDisableCtapFlag();
- auto request_handler = CreateGetAssertionHandler();
+ auto request_handler = CreateGetAssertionHandlerU2f();
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<MockFidoDevice>();
@@ -139,7 +245,7 @@ TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
- EXPECT_TRUE(get_assertion_callback().value());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
EXPECT_TRUE(request_handler->is_complete());
}
@@ -151,10 +257,7 @@ TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
CreateGetAssertionHandlerWithRequest(std::move(request));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
test_data::kTestGetInfoResponseWithoutUvSupport);
discovery()->AddDevice(std::move(device));
@@ -175,10 +278,356 @@ TEST_F(FidoGetAssertionHandlerTest,
CreateGetAssertionHandlerWithRequest(std::move(request));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
+ auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
+TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
+ test_data::kRelyingPartyId, test_data::kClientDataHash));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
+// Tests a scenario where the authenticator responds with credential ID that
+// is not included in the allowed list.
+TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request.SetAllowList(
+ {{CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ // Resident Keys must be disabled, otherwise allow list check is skipped.
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponseWithoutResidentKeySupport);
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
+// Tests a scenario where authenticator responds without user entity in its
+// response but client is expecting a resident key credential.
+TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
+ // Use a GetAssertion request with an empty allow list.
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
+ test_data::kRelyingPartyId, test_data::kClientDataHash));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ AllTransportsAllowedIfAllowCredentialsListUndefined) {
+ auto request = CreateTestRequestWithCableExtension();
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ ExpectAllTransportsAreAllowedForRequest(request_handler.get());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ AllTransportsAllowedIfAllowCredentialsListIsEmpty) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({});
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ ExpectAllTransportsAreAllowedForRequest(request_handler.get());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kBluetoothLowEnergy}},
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(kBogusCredentialId)},
+ });
+
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ ExpectAllTransportsAreAllowedForRequest(request_handler.get());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ AllowedTransportsAreUnionOfTransportsLists) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kBluetoothLowEnergy}},
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(kBogusCredentialId),
+ {FidoTransportProtocol::kInternal,
+ FidoTransportProtocol::kNearFieldCommunication}},
+ });
+
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ ExpectAllowedTransportsForRequestAre(
+ request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kInternal,
+ FidoTransportProtocol::kNearFieldCommunication});
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ CableDisabledIfAllowCredentialsListUndefinedButCableExtensionMissing) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ ASSERT_FALSE(!!request.cable_extension());
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ ExpectAllowedTransportsForRequestAre(
+ request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kInternal});
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ CableDisabledIfExplicitlyAllowedButCableExtensionMissing) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ ASSERT_FALSE(!!request.cable_extension());
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+ FidoTransportProtocol::kUsbHumanInterfaceDevice}},
+ });
+
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ ExpectAllowedTransportsForRequestAre(
+ request_handler.get(), {FidoTransportProtocol::kUsbHumanInterfaceDevice});
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
+ const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
+ FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication,
+ };
+
+ set_supported_transports(kBleAndNfc);
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler = CreateGetAssertionHandlerWithRequest(
+ CreateTestRequestWithCableExtension());
+ ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ SupportedTransportsAreOnlyCableAndInternal) {
+ const base::flat_set<FidoTransportProtocol> kCableAndInternal = {
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+ FidoTransportProtocol::kInternal,
+ };
+
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ set_supported_transports(kCableAndInternal);
+ auto request_handler = CreateGetAssertionHandlerWithRequest(
+ CreateTestRequestWithCableExtension());
+ ExpectAllowedTransportsForRequestAre(request_handler.get(),
+ kCableAndInternal);
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
+ });
+
+ set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
+
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ discovery()->AddDevice(std::move(device));
+
+ get_assertion_callback().WaitForCallback();
+
+ EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAre(
+ FidoTransportProtocol::kUsbHumanInterfaceDevice));
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyBleTransportAllowed) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kBluetoothLowEnergy}},
+ });
+
+ set_supported_transports({FidoTransportProtocol::kBluetoothLowEnergy});
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->SetDeviceTransport(FidoTransportProtocol::kBluetoothLowEnergy);
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
+ ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+ ble_discovery()->AddDevice(std::move(device));
+
+ get_assertion_callback().WaitForCallback();
+
+ EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAre(
+ FidoTransportProtocol::kBluetoothLowEnergy));
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kNearFieldCommunication}},
+ });
+
+ set_supported_transports({FidoTransportProtocol::kNearFieldCommunication});
+
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->SetDeviceTransport(FidoTransportProtocol::kNearFieldCommunication);
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
+ nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+ nfc_discovery()->AddDevice(std::move(device));
+
+ get_assertion_callback().WaitForCallback();
+
+ EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAre(
+ FidoTransportProtocol::kNearFieldCommunication));
+}
+
+TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kInternal}},
+ });
+
+ set_supported_transports({FidoTransportProtocol::kInternal});
+
+ auto device = MockFidoDevice::MakeCtap(
+ ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestGetInfoResponsePlatformDevice);
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
+ set_mock_platform_device(std::move(device));
+
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ get_assertion_callback().WaitForCallback();
+
+ EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
+ EXPECT_TRUE(get_assertion_callback().value<0>());
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
+}
+
+// Tests a scenario where authenticator of incorrect transport type was used to
+// conduct CTAP GetAssertion call.
+//
+// TODO(engedy): This should not happen, instead |allowCredentials| should be
+// filtered to only contain items compatible with the transport actually used to
+// talk to the authenticator.
+TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
+ // GetAssertion request that expects GetAssertion call for credential
+ // |CredentialType::kPublicKey| to be signed with Cable authenticator.
+ auto request = CreateTestRequestWithCableExtension();
+ request.SetAllowList({
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(
+ test_data::kTestGetAssertionCredentialId),
+ {FidoTransportProtocol::kBluetoothLowEnergy}},
+ {CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(kBogusCredentialId),
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
+ });
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ // Since transport type of |device| is different from what the relying party
+ // defined in |request| above, this request should fail.
+ device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ test_data::kTestGetAssertionResponse);
discovery()->AddDevice(std::move(device));
@@ -186,4 +635,53 @@ TEST_F(FidoGetAssertionHandlerTest,
EXPECT_FALSE(get_assertion_callback().was_called());
}
+// If a device with transport type kInternal returns a
+// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
+// FidoReturnCode::kUserConsentDenied. Pending authenticators should be
+// cancelled.
+TEST_F(FidoGetAssertionHandlerTest,
+ TestRequestWithOperationDeniedErrorPlatform) {
+ auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponsePlatformDevice);
+ platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ platform_device->ExpectCtap2CommandAndRespondWithError(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ CtapDeviceResponseCode::kCtap2ErrOperationDenied,
+ base::TimeDelta::FromMicroseconds(10));
+ set_mock_platform_device(std::move(platform_device));
+
+ auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ other_device->ExpectCtap2CommandAndDoNotRespond(
+ CtapRequestCommand::kAuthenticatorGetAssertion);
+ EXPECT_CALL(*other_device, Cancel);
+
+ auto request_handler = CreateGetAssertionHandlerCtap();
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ discovery()->AddDevice(std::move(other_device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_TRUE(get_assertion_callback().was_called());
+ EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
+ get_assertion_callback().status());
+}
+
+// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
+// cross-platform device.
+TEST_F(FidoGetAssertionHandlerTest,
+ TestRequestWithOperationDeniedErrorCrossPlatform) {
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->ExpectCtap2CommandAndRespondWithError(
+ CtapRequestCommand::kAuthenticatorGetAssertion,
+ CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+
+ auto request_handler = CreateGetAssertionHandlerCtap();
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_TRUE(get_assertion_callback().was_called());
+ EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
+ get_assertion_callback().status());
+}
+
} // namespace device
diff --git a/chromium/device/fido/get_assertion_request_handler.cc b/chromium/device/fido/get_assertion_request_handler.cc
index dcf5aa04b6b..1e7888a8068 100644
--- a/chromium/device/fido/get_assertion_request_handler.cc
+++ b/chromium/device/fido/get_assertion_request_handler.cc
@@ -4,54 +4,85 @@
#include "device/fido/get_assertion_request_handler.h"
+#include <algorithm>
+#include <set>
#include <utility>
#include "base/bind.h"
+#include "base/stl_util.h"
#include "device/fido/authenticator_get_assertion_response.h"
+#include "device/fido/cable/fido_cable_discovery.h"
#include "device/fido/fido_authenticator.h"
-#include "device/fido/fido_cable_discovery.h"
#include "device/fido/get_assertion_task.h"
namespace device {
-GetAssertionRequestHandler::GetAssertionRequestHandler(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CtapGetAssertionRequest request,
- SignResponseCallback completion_callback)
- : GetAssertionRequestHandler(connector,
- protocols,
- std::move(request),
- std::move(completion_callback),
- AddPlatformAuthenticatorCallback()) {}
+namespace {
-GetAssertionRequestHandler::GetAssertionRequestHandler(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CtapGetAssertionRequest request,
- SignResponseCallback completion_callback,
- AddPlatformAuthenticatorCallback add_platform_authenticator)
- : FidoRequestHandler(connector,
- protocols,
- std::move(completion_callback),
- std::move(add_platform_authenticator)),
- request_(std::move(request)),
- weak_factory_(this) {
- if (base::ContainsKey(
- protocols, FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) &&
- request_.cable_extension()) {
- auto discovery =
- std::make_unique<FidoCableDiscovery>(*request_.cable_extension());
- discovery->set_observer(this);
- discoveries().push_back(std::move(discovery));
+// PublicKeyUserEntity field in GetAssertion response is optional with the
+// following constraints:
+// - If assertion has been made without user verification, user identifiable
+// information must not be included.
+// - For resident key credentials, user id of the user entity is mandatory.
+// - When multiple accounts exist for specified RP ID, user entity is
+// mandatory.
+// TODO(hongjunchoi) : Add link to section of the CTAP spec once it is
+// published.
+bool CheckRequirementsOnResponseUserEntity(
+ const CtapGetAssertionRequest& request,
+ const AuthenticatorGetAssertionResponse& response) {
+ // If assertion has been made without user verification, user identifiable
+ // information must not be included.
+ const auto& user_entity = response.user_entity();
+ const bool has_user_identifying_info =
+ user_entity && (user_entity->user_display_name() ||
+ user_entity->user_name() || user_entity->user_icon_url());
+ if (!response.auth_data().obtained_user_verification() &&
+ has_user_identifying_info) {
+ return false;
}
- Start();
-}
+ // For resident key credentials, user id of the user entity is mandatory.
+ if ((!request.allow_list() || request.allow_list()->empty()) &&
+ !user_entity) {
+ return false;
+ }
-GetAssertionRequestHandler::~GetAssertionRequestHandler() = default;
+ // When multiple accounts exist for specified RP ID, user entity is mandatory.
+ if (response.num_credentials().value_or(0u) > 1 && !user_entity) {
+ return false;
+ }
-namespace {
+ return true;
+}
+
+// Checks whether credential ID returned from the authenticator and transport
+// type used matches the transport type and credential ID defined in
+// PublicKeyCredentialDescriptor of the allowed list. If the device has resident
+// key support, returned credential ID may be resident credential. Thus,
+// returned credential ID need not be in allowed list.
+// TODO(hongjunchoi) : Add link to section of the CTAP spec once it is
+// published.
+bool CheckResponseCredentialIdMatchesRequestAllowList(
+ const FidoAuthenticator& authenticator,
+ const CtapGetAssertionRequest request,
+ const AuthenticatorGetAssertionResponse& response) {
+ const auto& allow_list = request.allow_list();
+ if (!allow_list || allow_list->empty()) {
+ // Allow list can't be empty for authenticators w/o resident key support.
+ return authenticator.Options().supports_resident_key();
+ }
+ // Credential ID may be omitted if allow list has size 1. Otherwise, it needs
+ // to match.
+ const auto transport_used = authenticator.AuthenticatorTransport();
+ return (allow_list->size() == 1 && !response.credential()) ||
+ std::any_of(allow_list->cbegin(), allow_list->cend(),
+ [&response, transport_used](const auto& credential) {
+ return credential.id() == response.raw_credential_id() &&
+ base::ContainsKey(credential.transports(),
+ transport_used);
+ });
+}
// Checks UserVerificationRequirement enum passed from the relying party is
// compatible with the authenticator, and updates the request to the
@@ -86,8 +117,74 @@ bool CheckUserVerificationCompatible(FidoAuthenticator* authenticator,
return false;
}
+base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP(
+ const CtapGetAssertionRequest& request) {
+ const base::flat_set<FidoTransportProtocol> kAllTransports = {
+ FidoTransportProtocol::kInternal,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy};
+
+ // TODO(https://crbug.com/874479): |allowed_list| will |has_value| even if the
+ // WebAuthn request has `allowCredential` undefined.
+ const auto& allowed_list = request.allow_list();
+ if (!allowed_list || allowed_list->empty()) {
+ return kAllTransports;
+ }
+
+ base::flat_set<FidoTransportProtocol> transports;
+ for (const auto credential : *allowed_list) {
+ if (credential.transports().empty())
+ return kAllTransports;
+ transports.insert(credential.transports().begin(),
+ credential.transports().end());
+ }
+
+ return transports;
+}
+
+base::flat_set<FidoTransportProtocol> GetTransportsAllowedAndConfiguredByRP(
+ const CtapGetAssertionRequest& request) {
+ auto transports = GetTransportsAllowedByRP(request);
+ if (!request.cable_extension())
+ transports.erase(FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
+ return transports;
+}
+
} // namespace
+GetAssertionRequestHandler::GetAssertionRequestHandler(
+ service_manager::Connector* connector,
+ const base::flat_set<FidoTransportProtocol>& supported_transports,
+ CtapGetAssertionRequest request,
+ SignResponseCallback completion_callback)
+ : FidoRequestHandler(
+ connector,
+ base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>(
+ supported_transports,
+ GetTransportsAllowedAndConfiguredByRP(request)),
+ std::move(completion_callback)),
+ request_(std::move(request)),
+ weak_factory_(this) {
+ transport_availability_info().rp_id = request_.rp_id();
+ transport_availability_info().request_type =
+ FidoRequestHandlerBase::RequestType::kGetAssertion;
+
+ if (base::ContainsKey(
+ transport_availability_info().available_transports,
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)) {
+ DCHECK(request_.cable_extension());
+ auto discovery = FidoDiscovery::CreateCable(*request_.cable_extension());
+ discovery->set_observer(this);
+ discoveries().push_back(std::move(discovery));
+ }
+
+ Start();
+}
+
+GetAssertionRequestHandler::~GetAssertionRequestHandler() = default;
+
void GetAssertionRequestHandler::DispatchRequest(
FidoAuthenticator* authenticator) {
// The user verification field of the request may be adjusted to the
@@ -99,8 +196,29 @@ void GetAssertionRequestHandler::DispatchRequest(
authenticator->GetAssertion(
std::move(request_copy),
- base::BindOnce(&GetAssertionRequestHandler::OnAuthenticatorResponse,
+ base::BindOnce(&GetAssertionRequestHandler::HandleResponse,
weak_factory_.GetWeakPtr(), authenticator));
}
+void GetAssertionRequestHandler::HandleResponse(
+ FidoAuthenticator* authenticator,
+ CtapDeviceResponseCode response_code,
+ base::Optional<AuthenticatorGetAssertionResponse> response) {
+ if (response_code != CtapDeviceResponseCode::kSuccess) {
+ OnAuthenticatorResponse(authenticator, response_code, base::nullopt);
+ return;
+ }
+
+ if (!response || !request_.CheckResponseRpIdHash(response->GetRpIdHash()) ||
+ !CheckResponseCredentialIdMatchesRequestAllowList(*authenticator,
+ request_, *response) ||
+ !CheckRequirementsOnResponseUserEntity(request_, *response)) {
+ OnAuthenticatorResponse(
+ authenticator, CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
+ OnAuthenticatorResponse(authenticator, response_code, std::move(response));
+}
+
} // namespace device
diff --git a/chromium/device/fido/get_assertion_request_handler.h b/chromium/device/fido/get_assertion_request_handler.h
index 594a89c1657..de6e0d378f9 100644
--- a/chromium/device/fido/get_assertion_request_handler.h
+++ b/chromium/device/fido/get_assertion_request_handler.h
@@ -27,28 +27,28 @@ class AuthenticatorGetAssertionResponse;
using SignResponseCallback =
base::OnceCallback<void(FidoReturnCode,
- base::Optional<AuthenticatorGetAssertionResponse>)>;
+ base::Optional<AuthenticatorGetAssertionResponse>,
+ FidoTransportProtocol)>;
class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler
: public FidoRequestHandler<AuthenticatorGetAssertionResponse> {
public:
GetAssertionRequestHandler(
service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
+ const base::flat_set<FidoTransportProtocol>& supported_transports,
CtapGetAssertionRequest request_parameter,
SignResponseCallback completion_callback);
- GetAssertionRequestHandler(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CtapGetAssertionRequest request_parameter,
- SignResponseCallback completion_callback,
- AddPlatformAuthenticatorCallback add_platform_authenticator);
~GetAssertionRequestHandler() override;
private:
// FidoRequestHandlerBase:
void DispatchRequest(FidoAuthenticator* authenticator) override;
+ void HandleResponse(
+ FidoAuthenticator* authenticator,
+ CtapDeviceResponseCode response_code,
+ base::Optional<AuthenticatorGetAssertionResponse> response);
+
CtapGetAssertionRequest request_;
base::WeakPtrFactory<GetAssertionRequestHandler> weak_factory_;
diff --git a/chromium/device/fido/get_assertion_task.cc b/chromium/device/fido/get_assertion_task.cc
index b9ae015deb0..8fb08533bcb 100644
--- a/chromium/device/fido/get_assertion_task.cc
+++ b/chromium/device/fido/get_assertion_task.cc
@@ -4,30 +4,27 @@
#include "device/fido/get_assertion_task.h"
-#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "device/base/features.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap2_device_operation.h"
-#include "device/fido/ctap_empty_authenticator_request.h"
-#include "device/fido/device_response_converter.h"
-#include "device/fido/u2f_command_constructor.h"
#include "device/fido/u2f_sign_operation.h"
namespace device {
namespace {
-bool ResponseContainsUserIdentifiableInfo(
- const AuthenticatorGetAssertionResponse& response) {
- const auto& user_entity = response.user_entity();
- if (!user_entity)
- return false;
-
- return user_entity->user_display_name() || user_entity->user_name() ||
- user_entity->user_icon_url();
+bool MayFallbackToU2fWithAppIdExtension(
+ const FidoDevice& device,
+ const CtapGetAssertionRequest& request) {
+ bool ctap2_device_supports_u2f =
+ device.device_info() &&
+ base::ContainsKey(device.device_info()->versions(),
+ ProtocolVersion::kU2f);
+ return request.alternative_application_parameter() &&
+ ctap2_device_supports_u2f;
}
} // namespace
@@ -51,87 +48,76 @@ void GetAssertionTask::StartTask() {
}
}
-void GetAssertionTask::GetAssertion() {
+void GetAssertionTask::GetAssertion(bool enforce_user_presence) {
+ // If appId extension was used in the request and device is a hybrid U2F/CTAP2
+ // device, then first issue a silent GetAssertionRequest. If no credentials in
+ // allowed credential list are recognized, it's possible that the credential
+ // is registered via U2F. Under these circumstances, the request should be
+ // issued via the U2F protocol. Otherwise, proceed with a normal GetAssertion
+ // request.
+ auto uv_configuration = request_.user_verification();
+ bool is_silent_authentication = false;
+ if (!enforce_user_presence &&
+ MayFallbackToU2fWithAppIdExtension(*device(), request_)) {
+ is_silent_authentication = true;
+ request_.SetUserPresenceRequired(false /* user_presence_required */);
+ request_.SetUserVerification(UserVerificationRequirement::kDiscouraged);
+ }
+
sign_operation_ =
std::make_unique<Ctap2DeviceOperation<CtapGetAssertionRequest,
AuthenticatorGetAssertionResponse>>(
device(), request_,
- base::BindOnce(&GetAssertionTask::OnCtapGetAssertionResponseReceived,
- weak_factory_.GetWeakPtr()),
+ base::BindOnce(&GetAssertionTask::GetAssertionCallbackWithU2fFallback,
+ weak_factory_.GetWeakPtr(), is_silent_authentication,
+ uv_configuration, std::move(callback_)),
base::BindOnce(&ReadCTAPGetAssertionResponse));
sign_operation_->Start();
}
void GetAssertionTask::U2fSign() {
- DCHECK(!device()->device_info());
DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
- sign_operation_ = std::make_unique<U2fSignOperation>(
- device(), request_,
- base::BindOnce(&GetAssertionTask::OnCtapGetAssertionResponseReceived,
- weak_factory_.GetWeakPtr()));
+ sign_operation_ = std::make_unique<U2fSignOperation>(device(), request_,
+ std::move(callback_));
sign_operation_->Start();
}
-bool GetAssertionTask::CheckRequirementsOnReturnedUserEntities(
- const AuthenticatorGetAssertionResponse& response) {
- // If assertion has been made without user verification, user identifiable
- // information must not be included.
- if (!response.auth_data().obtained_user_verification() &&
- ResponseContainsUserIdentifiableInfo(response)) {
- return false;
- }
-
- // For resident key credentials, user id of the user entity is mandatory.
- if ((!request_.allow_list() || request_.allow_list()->empty()) &&
- !response.user_entity()) {
- return false;
- }
-
- // When multiple accounts exist for specified RP ID, user entity is mandatory.
- if (response.num_credentials().value_or(0u) > 1 && !response.user_entity()) {
- return false;
- }
-
- return true;
-}
-
-bool GetAssertionTask::CheckRequirementsOnReturnedCredentialId(
- const AuthenticatorGetAssertionResponse& response) {
- if (device()->device_info() &&
- device()->device_info()->options().supports_resident_key()) {
- return true;
- }
-
- const auto& allow_list = request_.allow_list();
- return allow_list &&
- (allow_list->size() == 1 ||
- std::any_of(allow_list->cbegin(), allow_list->cend(),
- [&response](const auto& credential) {
- return credential.id() == response.raw_credential_id();
- }));
-}
-
-void GetAssertionTask::OnCtapGetAssertionResponseReceived(
+void GetAssertionTask::GetAssertionCallbackWithU2fFallback(
+ bool is_silent_authentication,
+ UserVerificationRequirement user_verification_configuration,
+ GetAssertionTaskCallback callback,
CtapDeviceResponseCode response_code,
- base::Optional<AuthenticatorGetAssertionResponse> device_response) {
- if (response_code != CtapDeviceResponseCode::kSuccess) {
- std::move(callback_).Run(response_code, base::nullopt);
+ base::Optional<AuthenticatorGetAssertionResponse> response_data) {
+ DCHECK(device()->device_info());
+ if (!(is_silent_authentication &&
+ MayFallbackToU2fWithAppIdExtension(*device(), request_))) {
+ std::move(callback).Run(response_code, std::move(response_data));
return;
}
- // TODO(martinkr): CheckRpIdHash invocation needs to move into the Request
- // handler. See https://crbug.com/863988.
- if (!device_response ||
- !request_.CheckResponseRpIdHash(device_response->GetRpIdHash()) ||
- !CheckRequirementsOnReturnedCredentialId(*device_response) ||
- !CheckRequirementsOnReturnedUserEntities(*device_response)) {
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
- return;
+ DCHECK(!callback_ && !request_.user_presence_required());
+ request_.SetUserPresenceRequired(true /* true */);
+ callback_ = std::move(callback);
+
+ // Credential was recognized by the device. As this authentication was
+ // silent authentication (i.e. user touch was not provided), try again with
+ // user presence enforced and with the original user verification
+ // configuration.
+ if (response_code == CtapDeviceResponseCode::kSuccess) {
+ DCHECK_EQ(UserVerificationRequirement::kDiscouraged,
+ request_.user_verification());
+ DCHECK_EQ(ProtocolVersion::kCtap, device()->supported_protocol());
+ request_.SetUserVerification(user_verification_configuration);
+ GetAssertion(true /* enforce_user_presence */);
+ } else {
+ // An error occurred or no credentials in the allowed list were recognized.
+ // However, as the relying party has provided appId extension, try again
+ // with U2F protocol to make sure that authentication via appID credential
+ // is also attempted.
+ device()->set_supported_protocol(ProtocolVersion::kU2f);
+ U2fSign();
}
-
- std::move(callback_).Run(response_code, std::move(device_response));
}
} // namespace device
diff --git a/chromium/device/fido/get_assertion_task.h b/chromium/device/fido/get_assertion_task.h
index b993d84c3f6..a5900235b48 100644
--- a/chromium/device/fido/get_assertion_task.h
+++ b/chromium/device/fido/get_assertion_task.h
@@ -43,33 +43,19 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionTask : public FidoTask {
// FidoTask:
void StartTask() override;
- void GetAssertion();
+ void GetAssertion(bool enforce_user_presence = false);
void U2fSign();
- // PublicKeyUserEntity field in GetAssertion response is optional with the
- // following constraints:
- // - If assertion has been made without user verification, user identifiable
- // information must not be included.
- // - For resident key credentials, user id of the user entity is mandatory.
- // - When multiple accounts exist for specified RP ID, user entity is
- // mandatory.
- // TODO(hongjunchoi) : Add link to section of the CTAP spec once it is
- // published.
- bool CheckRequirementsOnReturnedUserEntities(
- const AuthenticatorGetAssertionResponse& response);
-
- // Checks whether credential ID returned from the authenticator was included
- // in the allowed list for authenticators. If the device has resident key
- // support, returned credential ID may be resident credential. Thus, returned
- // credential ID need not be in allowed list.
- // TODO(hongjunchoi) : Add link to section of the CTAP spec once it is
- // published.
- bool CheckRequirementsOnReturnedCredentialId(
- const AuthenticatorGetAssertionResponse& response);
-
- void OnCtapGetAssertionResponseReceived(
- CtapDeviceResponseCode response_code,
- base::Optional<AuthenticatorGetAssertionResponse> device_response);
+ // Callback logic for CTAP2 GetAssertion. This will fall back to U2F on hybrid
+ // U2F/CTAP2 devices when:
+ // a) No credentials were recognized via CTAP2 and,
+ // b) The request contains the appID extension.
+ void GetAssertionCallbackWithU2fFallback(
+ bool is_silent_authentication,
+ UserVerificationRequirement user_verification_required,
+ GetAssertionTaskCallback callback,
+ CtapDeviceResponseCode,
+ base::Optional<AuthenticatorGetAssertionResponse>);
CtapGetAssertionRequest request_;
std::unique_ptr<SignOperation> sign_operation_;
diff --git a/chromium/device/fido/get_assertion_task_unittest.cc b/chromium/device/fido/get_assertion_task_unittest.cc
index db9183c8e7b..1ed026af0d3 100644
--- a/chromium/device/fido/get_assertion_task_unittest.cc
+++ b/chromium/device/fido/get_assertion_task_unittest.cc
@@ -177,13 +177,10 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fSignWithoutFlag) {
EXPECT_TRUE(get_assertion_callback_receiver().value());
}
-// Tests a scenario where the authenticator responds with credential ID that
-// is not included in the allowed list.
-TEST_F(FidoGetAssertionTaskTest, TestGetAssertionInvalidCredential) {
+TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetAssertion,
- test_data::kTestGetAssertionResponse);
+ CtapRequestCommand::kAuthenticatorGetAssertion, base::nullopt);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
@@ -197,78 +194,123 @@ TEST_F(FidoGetAssertionTaskTest, TestGetAssertionInvalidCredential) {
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
-// Tests a scenario where authenticator responds without user entity in its
-// response but client is expecting a resident key credential.
-TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectUserEntity) {
- auto device = MockFidoDevice::MakeCtap();
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetAssertion,
- test_data::kTestGetAssertionResponse);
+TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) {
+ auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+
+ auto device = MockFidoDevice::MakeU2f();
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fFakeRegisterCommand,
+ test_data::kApduEncodedNoErrorSignResponse);
auto task = std::make_unique<GetAssertionTask>(
- device.get(),
- CtapGetAssertionRequest(test_data::kRelyingPartyId,
- test_data::kClientDataHash),
+ device.get(), std::move(request),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
-TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectRpIdHash) {
+// Checks that when device supports both CTAP2 and U2F protocol and when
+// appId extension parameter is present, the browser first checks presence
+// of valid credentials via silent authentication.
+TEST_F(FidoGetAssertionTaskTest, TestSilentSignInWhenAppIdExtensionPresent) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+
+ std::vector<PublicKeyCredentialDescriptor> allowed_list;
+ allowed_list.push_back(PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+ request.SetAlternativeApplicationParameter(
+ test_data::kAlternativeApplicationParameter);
+ request.SetAllowList(std::move(allowed_list));
+
auto device = MockFidoDevice::MakeCtap();
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetAssertion,
- test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
+ device->ExpectRequestAndRespondWith(test_data::kCtapSilentGetAssertionRequest,
+ test_data::kTestGetAssertionResponse);
+ device->ExpectRequestAndRespondWith(test_data::kCtapGetAssertionRequest,
+ test_data::kTestGetAssertionResponse);
auto task = std::make_unique<GetAssertionTask>(
- device.get(),
- CtapGetAssertionRequest(test_data::kRelyingPartyId,
- test_data::kClientDataHash),
+ device.get(), std::move(request),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
get_assertion_callback_receiver().status());
- EXPECT_FALSE(get_assertion_callback_receiver().value());
}
-TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
+TEST_F(FidoGetAssertionTaskTest, TestU2fFallbackForAppIdExtension) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+
+ std::vector<PublicKeyCredentialDescriptor> allowed_list;
+ allowed_list.push_back(PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+ request.SetAlternativeApplicationParameter(
+ test_data::kAlternativeApplicationParameter);
+ request.SetAllowList(std::move(allowed_list));
+
+ ::testing::InSequence s;
auto device = MockFidoDevice::MakeCtap();
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetAssertion, base::nullopt);
+ std::array<uint8_t, 1> error{{base::strict_cast<uint8_t>(
+ CtapDeviceResponseCode::kCtap2ErrNoCredentials)}};
+ // First as device supports both CTAP and U2F protocol, browser will try CTAP
+ // GetAssertion.
+ device->ExpectRequestAndRespondWith(test_data::kCtapSilentGetAssertionRequest,
+ error);
+ // After falling back to U2F request will use the primary app_param, which
+ // will be rejected.
+ device->ExpectRequestAndRespondWith(test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kU2fWrongDataApduResponse);
+ // After the rejection, the U2F sign request with alternative application
+ // parameter should be tried.
+ device->ExpectRequestAndRespondWith(
+ test_data::
+ kU2fCheckOnlySignCommandApduWithAlternativeApplicationParameter,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApduWithAlternativeApplicationParameter,
+ test_data::kApduEncodedNoErrorSignResponse);
auto task = std::make_unique<GetAssertionTask>(
- device.get(),
- CtapGetAssertionRequest(test_data::kRelyingPartyId,
- test_data::kClientDataHash),
+ device.get(), std::move(request),
get_assertion_callback_receiver().callback());
-
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
get_assertion_callback_receiver().status());
- EXPECT_FALSE(get_assertion_callback_receiver().value());
}
-TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) {
- auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
- test_data::kClientDataHash);
+TEST_F(FidoGetAssertionTaskTest, TestAvoidSilentSignInForCtapOnlyDevice) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
- auto device = MockFidoDevice::MakeU2f();
- device->ExpectRequestAndRespondWith(
- test_data::kU2fFakeRegisterCommand,
- test_data::kApduEncodedNoErrorSignResponse);
+ std::vector<PublicKeyCredentialDescriptor> allowed_list;
+ allowed_list.push_back(PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+
+ request.SetAlternativeApplicationParameter(
+ test_data::kAlternativeApplicationParameter);
+ request.SetAllowList(std::move(allowed_list));
+
+ auto device = MockFidoDevice::MakeCtap(ReadCTAPGetInfoResponse(
+ test_data::kTestCtap2OnlyAuthenticatorGetInfoResponse));
+ std::array<uint8_t, 1> error{
+ {base::strict_cast<uint8_t>(CtapDeviceResponseCode::kCtap2ErrOther)}};
+ device->ExpectRequestAndRespondWith(test_data::kCtapGetAssertionRequest,
+ error);
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request),
get_assertion_callback_receiver().callback());
-
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
- EXPECT_FALSE(get_assertion_callback_receiver().value());
}
} // namespace
diff --git a/chromium/device/fido/fake_hid_impl_for_testing.cc b/chromium/device/fido/hid/fake_hid_impl_for_testing.cc
index dc2a5d56db8..cf86174175a 100644
--- a/chromium/device/fido/fake_hid_impl_for_testing.cc
+++ b/chromium/device/fido/hid/fake_hid_impl_for_testing.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fake_hid_impl_for_testing.h"
+#include "device/fido/hid/fake_hid_impl_for_testing.h"
#include <utility>
diff --git a/chromium/device/fido/fake_hid_impl_for_testing.h b/chromium/device/fido/hid/fake_hid_impl_for_testing.h
index 552adde891b..b69945adc0c 100644
--- a/chromium/device/fido/fake_hid_impl_for_testing.h
+++ b/chromium/device/fido/hid/fake_hid_impl_for_testing.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FAKE_HID_IMPL_FOR_TESTING_H_
-#define DEVICE_FIDO_FAKE_HID_IMPL_FOR_TESTING_H_
+#ifndef DEVICE_FIDO_HID_FAKE_HID_IMPL_FOR_TESTING_H_
+#define DEVICE_FIDO_HID_FAKE_HID_IMPL_FOR_TESTING_H_
#include <map>
#include <string>
@@ -120,4 +120,4 @@ class FakeHidManager : public device::mojom::HidManager {
} // namespace device
-#endif // DEVICE_FIDO_FAKE_HID_IMPL_FOR_TESTING_H_
+#endif // DEVICE_FIDO_HID_FAKE_HID_IMPL_FOR_TESTING_H_
diff --git a/chromium/device/fido/fido_hid_device.cc b/chromium/device/fido/hid/fido_hid_device.cc
index 5dee08aacc9..9734a739218 100644
--- a/chromium/device/fido/fido_hid_device.cc
+++ b/chromium/device/fido/hid/fido_hid_device.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_device.h"
+#include "device/fido/hid/fido_hid_device.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -10,7 +10,7 @@
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "crypto/random.h"
-#include "device/fido/fido_hid_message.h"
+#include "device/fido/hid/fido_hid_message.h"
#include "mojo/public/cpp/bindings/interface_request.h"
namespace device {
@@ -143,7 +143,7 @@ void FidoHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce,
timeout_callback_.Cancel();
- if (!message) {
+ if (!message || message->cmd() != FidoHidDeviceCommand::kInit) {
state_ = State::kDeviceError;
Transition(std::vector<uint8_t>(), std::move(callback));
return;
@@ -217,6 +217,7 @@ void FidoHidDevice::PacketWritten(base::Optional<FidoHidMessage> message,
void FidoHidDevice::ReadMessage(HidMessageCallback callback) {
if (!connection_) {
+ state_ = State::kDeviceError;
std::move(callback).Run(base::nullopt);
return;
}
@@ -230,6 +231,7 @@ void FidoHidDevice::OnRead(HidMessageCallback callback,
uint8_t report_id,
const base::Optional<std::vector<uint8_t>>& buf) {
if (!success) {
+ state_ = State::kDeviceError;
std::move(callback).Run(base::nullopt);
return;
}
@@ -267,6 +269,7 @@ void FidoHidDevice::OnReadContinuation(
uint8_t report_id,
const base::Optional<std::vector<uint8_t>>& buf) {
if (!success) {
+ state_ = State::kDeviceError;
std::move(callback).Run(base::nullopt);
return;
}
@@ -307,8 +310,7 @@ void FidoHidDevice::MessageReceived(DeviceCallback callback,
}
if (cmd != FidoHidDeviceCommand::kMsg && cmd != FidoHidDeviceCommand::kCbor) {
- DLOG(ERROR) << "Unexpected HID device command received.";
- state_ = State::kDeviceError;
+ ProcessHidError(cmd, message->GetMessagePayload());
Transition(std::vector<uint8_t>(), std::move(callback));
return;
}
@@ -373,10 +375,35 @@ void FidoHidDevice::OnTimeout(DeviceCallback callback) {
Transition(std::vector<uint8_t>(), std::move(callback));
}
+void FidoHidDevice::ProcessHidError(FidoHidDeviceCommand cmd,
+ base::span<const uint8_t> payload) {
+ if (cmd != FidoHidDeviceCommand::kError || payload.size() != 1) {
+ DLOG(ERROR) << "Unexpected HID device command received.";
+ state_ = State::kDeviceError;
+ return;
+ }
+
+ const auto error_constant = payload[0];
+ if (error_constant ==
+ base::strict_cast<uint8_t>(HidErrorConstant::kInvalidCommand) ||
+ error_constant ==
+ base::strict_cast<uint8_t>(HidErrorConstant::kInvalidParameter) ||
+ error_constant ==
+ base::strict_cast<uint8_t>(HidErrorConstant::kInvalidLength)) {
+ state_ = State::kMsgError;
+ } else {
+ state_ = State::kDeviceError;
+ }
+}
+
std::string FidoHidDevice::GetId() const {
return GetIdForDevice(*device_info_);
}
+FidoTransportProtocol FidoHidDevice::DeviceTransport() const {
+ return FidoTransportProtocol::kUsbHumanInterfaceDevice;
+}
+
// static
std::string FidoHidDevice::GetIdForDevice(
const device::mojom::HidDeviceInfo& device_info) {
diff --git a/chromium/device/fido/fido_hid_device.h b/chromium/device/fido/hid/fido_hid_device.h
index 10f34023244..64a2ac9bee7 100644
--- a/chromium/device/fido/fido_hid_device.h
+++ b/chromium/device/fido/hid/fido_hid_device.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_HID_DEVICE_H_
-#define DEVICE_FIDO_FIDO_HID_DEVICE_H_
+#ifndef DEVICE_FIDO_HID_FIDO_HID_DEVICE_H_
+#define DEVICE_FIDO_HID_FIDO_HID_DEVICE_H_
#include <string>
#include <utility>
@@ -26,6 +26,21 @@ class FidoHidMessage;
class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice {
public:
+ // HID transport layer error constants that are returned to the client.
+ // Carried in the payload section of the Error command.
+ // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#ctaphid-commands
+ enum class HidErrorConstant : uint8_t {
+ kInvalidCommand = 0x01,
+ kInvalidParameter = 0x02,
+ kInvalidLength = 0x03,
+ kInvalidSequence = 0x04,
+ kTimeout = 0x05,
+ kBusy = 0x06,
+ kLockRequired = 0x0a,
+ kInvalidChannel = 0x0b,
+ kOther = 0x7f,
+ };
+
FidoHidDevice(device::mojom::HidDeviceInfoPtr device_info,
device::mojom::HidManager* hid_manager);
~FidoHidDevice() final;
@@ -41,6 +56,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice {
void Cancel() final;
// Use a string identifier to compare to other devices.
std::string GetId() const final;
+ FidoTransportProtocol DeviceTransport() const final;
// Get a string identifier for a given device info.
static std::string GetIdForDevice(
@@ -97,6 +113,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice {
void OnWink(WinkCallback callback, base::Optional<FidoHidMessage> response);
void ArmTimeout(DeviceCallback callback);
void OnTimeout(DeviceCallback callback);
+ void ProcessHidError(FidoHidDeviceCommand cmd,
+ base::span<const uint8_t> payload);
base::WeakPtr<FidoDevice> GetWeakPtr() override;
@@ -120,4 +138,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_HID_DEVICE_H_
+#endif // DEVICE_FIDO_HID_FIDO_HID_DEVICE_H_
diff --git a/chromium/device/fido/fido_hid_device_unittest.cc b/chromium/device/fido/hid/fido_hid_device_unittest.cc
index 88c86732eb7..0942bf14226 100644
--- a/chromium/device/fido/fido_hid_device_unittest.cc
+++ b/chromium/device/fido/hid/fido_hid_device_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_device.h"
+#include "device/fido/hid/fido_hid_device.h"
#include <memory>
#include <tuple>
@@ -14,9 +14,9 @@
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "device/fido/fake_hid_impl_for_testing.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/hid/fake_hid_impl_for_testing.h"
#include "device/fido/test_callback_receiver.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
@@ -108,6 +108,38 @@ device::mojom::HidDeviceInfoPtr TestHidDevice() {
return hid_device;
}
+std::unique_ptr<MockHidConnection> CreateHidConnectionWithHidInitExpectations(
+ base::span<const uint8_t> channel_id,
+ FakeHidManager* fake_hid_manager,
+ ::testing::Sequence sequence) {
+ auto hid_device = TestHidDevice();
+ device::mojom::HidConnectionPtr connection_client;
+
+ // Replace device HID connection with custom client connection bound to mock
+ // server-side mojo connection.
+ auto mock_connection = std::make_unique<MockHidConnection>(
+ hid_device.Clone(), mojo::MakeRequest(&connection_client),
+ fido_parsing_utils::Materialize(channel_id));
+
+ // Initial write for establishing channel ID.
+ mock_connection->ExpectWriteHidInit();
+
+ EXPECT_CALL(*mock_connection, ReadPtr(_))
+ .InSequence(sequence)
+ // Response to HID_INIT request.
+ .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
+ std::move(*cb).Run(
+ true, 0,
+ CreateMockInitResponse(mock_connection->nonce(),
+ mock_connection->connection_channel_id()));
+ }));
+
+ // Add device and set mock connection to fake hid manager.
+ fake_hid_manager->AddDeviceAndSetConnection(std::move(hid_device),
+ std::move(connection_client));
+ return mock_connection;
+}
+
class FidoDeviceEnumerateCallbackReceiver
: public test::TestCallbackReceiver<std::vector<mojom::HidDeviceInfoPtr>> {
public:
@@ -256,13 +288,12 @@ TEST_F(FidoHidDeviceTest, TestRetryChannelAllocation) {
EXPECT_CALL(mock_connection, ReadPtr(_))
// First response to HID_INIT request with an incorrect nonce.
- .WillOnce(
- Invoke([kIncorrectNonce, &mock_connection](auto* cb) {
- std::move(*cb).Run(
- true, 0,
- CreateMockInitResponse(
- kIncorrectNonce, mock_connection.connection_channel_id()));
- }))
+ .WillOnce(Invoke([kIncorrectNonce, &mock_connection](auto* cb) {
+ std::move(*cb).Run(
+ true, 0,
+ CreateMockInitResponse(kIncorrectNonce,
+ mock_connection.connection_channel_id()));
+ }))
// Second response to HID_INIT request with a correct nonce.
.WillOnce(Invoke(
[&mock_connection](device::mojom::HidConnection::ReadCallback* cb) {
@@ -303,35 +334,20 @@ TEST_F(FidoHidDeviceTest, TestRetryChannelAllocation) {
TEST_F(FidoHidDeviceTest, TestKeepAliveMessage) {
constexpr uint8_t kChannelId[] = {0x01, 0x02, 0x03, 0x04};
-
- auto hid_device = TestHidDevice();
-
- // Replace device HID connection with custom client connection bound to mock
- // server-side mojo connection.
- device::mojom::HidConnectionPtr connection_client;
- MockHidConnection mock_connection(
- hid_device.Clone(), mojo::MakeRequest(&connection_client),
- fido_parsing_utils::Materialize(kChannelId));
-
- // Initial write for establishing channel ID.
- mock_connection.ExpectWriteHidInit();
+ ::testing::Sequence sequence;
+ auto mock_connection = CreateHidConnectionWithHidInitExpectations(
+ kChannelId, fake_hid_manager_.get(), sequence);
// HID_CBOR request to authenticator.
- mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
+ mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
- EXPECT_CALL(mock_connection, ReadPtr(_))
- // Response to HID_INIT request.
+ EXPECT_CALL(*mock_connection, ReadPtr(_))
+ .InSequence(sequence)
+ // Keep alive message sent from the authenticator.
.WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
std::move(*cb).Run(
true, 0,
- CreateMockInitResponse(mock_connection.nonce(),
- mock_connection.connection_channel_id()));
- }))
- // // Keep alive message sent from the authenticator.
- .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
- std::move(*cb).Run(
- true, 0,
- GetKeepAliveHidMessage(mock_connection.connection_channel_id()));
+ GetKeepAliveHidMessage(mock_connection->connection_channel_id()));
}))
// Repeated Read() invocation due to keep alive message. Sends a dummy
// response that corresponds to U2F version response.
@@ -342,14 +358,10 @@ TEST_F(FidoHidDeviceTest, TestKeepAliveMessage) {
std::move(*cb).Run(true, 0,
CreateMockResponseWithChannelId(
- mock_connection.connection_channel_id(),
+ mock_connection->connection_channel_id(),
kU2fMockResponseMessage));
}));
- // Add device and set mock connection to fake hid manager.
- fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
- std::move(connection_client));
-
FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
hid_manager_->GetDevices(receiver.callback());
receiver.WaitForCallback();
@@ -371,35 +383,20 @@ TEST_F(FidoHidDeviceTest, TestKeepAliveMessage) {
TEST_F(FidoHidDeviceTest, TestDeviceTimeoutAfterKeepAliveMessage) {
constexpr uint8_t kChannelId[] = {0x01, 0x02, 0x03, 0x04};
-
- auto hid_device = TestHidDevice();
-
- // Replace device HID connection with custom client connection bound to mock
- // server-side mojo connection.
- device::mojom::HidConnectionPtr connection_client;
- MockHidConnection mock_connection(
- hid_device.Clone(), mojo::MakeRequest(&connection_client),
- fido_parsing_utils::Materialize(kChannelId));
-
- // Initial write for establishing channel ID.
- mock_connection.ExpectWriteHidInit();
+ ::testing::Sequence sequence;
+ auto mock_connection = CreateHidConnectionWithHidInitExpectations(
+ kChannelId, fake_hid_manager_.get(), sequence);
// HID_CBOR request to authenticator.
- mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
+ mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
- EXPECT_CALL(mock_connection, ReadPtr(_))
- // Response to HID_INIT request.
- .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
- std::move(*cb).Run(
- true, 0,
- CreateMockInitResponse(mock_connection.nonce(),
- mock_connection.connection_channel_id()));
- }))
- // // Keep alive message sent from the authenticator.
+ EXPECT_CALL(*mock_connection, ReadPtr(_))
+ .InSequence(sequence)
+ // Keep alive message sent from the authenticator.
.WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
std::move(*cb).Run(
true, 0,
- GetKeepAliveHidMessage(mock_connection.connection_channel_id()));
+ GetKeepAliveHidMessage(mock_connection->connection_channel_id()));
}))
// Repeated Read() invocation due to keep alive message. The callback
// is invoked only after 3 seconds, which should cause device to timeout.
@@ -407,14 +404,10 @@ TEST_F(FidoHidDeviceTest, TestDeviceTimeoutAfterKeepAliveMessage) {
scoped_task_environment_.FastForwardBy(kDeviceTimeout);
std::move(*cb).Run(true, 0,
CreateMockResponseWithChannelId(
- mock_connection.connection_channel_id(),
+ mock_connection->connection_channel_id(),
kU2fMockResponseMessage));
}));
- // Add device and set mock connection to fake hid manager.
- fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
- std::move(connection_client));
-
FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
hid_manager_->GetDevices(receiver.callback());
receiver.WaitForCallback();
@@ -436,33 +429,18 @@ TEST_F(FidoHidDeviceTest, TestDeviceTimeoutAfterKeepAliveMessage) {
TEST_F(FidoHidDeviceTest, TestCancel) {
constexpr uint8_t kChannelId[] = {0x01, 0x02, 0x03, 0x04};
-
- auto hid_device = TestHidDevice();
-
- // Replace device HID connection with custom client connection bound to mock
- // server-side mojo connection.
- device::mojom::HidConnectionPtr connection_client;
- MockHidConnection mock_connection(
- hid_device.Clone(), mojo::MakeRequest(&connection_client),
- fido_parsing_utils::Materialize(kChannelId));
-
- // Initial write for establishing channel ID.
- mock_connection.ExpectWriteHidInit();
+ ::testing::Sequence sequence;
+ auto mock_connection = CreateHidConnectionWithHidInitExpectations(
+ kChannelId, fake_hid_manager_.get(), sequence);
// HID_CBOR request to authenticator.
- mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
+ mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
// Cancel request to authenticator.
- mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCancel);
+ mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCancel);
- EXPECT_CALL(mock_connection, ReadPtr(_))
- // Response to HID_INIT request.
- .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
- std::move(*cb).Run(
- true, 0,
- CreateMockInitResponse(mock_connection.nonce(),
- mock_connection.connection_channel_id()));
- }))
+ EXPECT_CALL(*mock_connection, ReadPtr(_))
+ .InSequence(sequence)
// Device response with a significant delay.
.WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
auto delay = base::TimeDelta::FromSeconds(2);
@@ -470,15 +448,11 @@ TEST_F(FidoHidDeviceTest, TestCancel) {
FROM_HERE,
base::BindOnce(std::move(*cb), true, 0,
CreateMockResponseWithChannelId(
- mock_connection.connection_channel_id(),
+ mock_connection->connection_channel_id(),
kU2fMockResponseMessage)),
delay);
}));
- // Add device and set mock connection to fake hid manager.
- fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
- std::move(connection_client));
-
FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
hid_manager_->GetDevices(receiver.callback());
receiver.WaitForCallback();
@@ -500,4 +474,88 @@ TEST_F(FidoHidDeviceTest, TestCancel) {
scoped_task_environment_.FastForwardUntilNoTasksRemain();
}
+TEST_F(FidoHidDeviceTest, TestGetInfoFailsOnDeviceError) {
+ constexpr uint8_t kChannelId[] = {0x01, 0x02, 0x03, 0x04};
+ // HID_ERROR(7F), followed by payload length(0001), followed by kUnknown(7F).
+ constexpr uint8_t kHidUnknownTransportError[] = {0x7F, 0x00, 0x01, 0x7F};
+ ::testing::Sequence sequence;
+ auto mock_connection = CreateHidConnectionWithHidInitExpectations(
+ kChannelId, fake_hid_manager_.get(), sequence);
+
+ // HID_CBOR request to authenticator.
+ mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
+
+ EXPECT_CALL(*mock_connection, ReadPtr(_))
+ .InSequence(sequence)
+ // Device response with a significant delay.
+ .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
+ auto delay = base::TimeDelta::FromSeconds(2);
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(std::move(*cb), true, 0,
+ CreateMockResponseWithChannelId(
+ mock_connection->connection_channel_id(),
+ kHidUnknownTransportError)),
+ delay);
+ }));
+
+ FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
+ hid_manager_->GetDevices(receiver.callback());
+ receiver.WaitForCallback();
+
+ std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
+ receiver.TakeReturnedDevicesFiltered();
+ ASSERT_EQ(1u, u2f_devices.size());
+ auto& device = u2f_devices.front();
+
+ device::test::TestCallbackReceiver<> get_info_callback;
+ device->DiscoverSupportedProtocolAndDeviceInfo(get_info_callback.callback());
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_info_callback.was_called());
+ EXPECT_EQ(FidoDevice::State::kDeviceError, device->state());
+}
+
+// Test that FidoHidDevice::DiscoverSupportedProtocolAndDeviceInfo() invokes
+// callback when device error outs with kMsgError state.
+TEST_F(FidoHidDeviceTest, TestDeviceMessageError) {
+ constexpr uint8_t kChannelId[] = {0x01, 0x02, 0x03, 0x04};
+ // HID_ERROR(BF), followed by payload length(0001), followed by
+ // kInvalidCommand(01).
+ constexpr uint8_t kHidUnknownCommandError[] = {0xBF, 0x00, 0x01, 0x01};
+ ::testing::Sequence sequence;
+ auto mock_connection = CreateHidConnectionWithHidInitExpectations(
+ kChannelId, fake_hid_manager_.get(), sequence);
+
+ // HID_CBOR request to authenticator.
+ mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
+
+ EXPECT_CALL(*mock_connection, ReadPtr(_))
+ .InSequence(sequence)
+ // Device response with a significant delay.
+ .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
+ auto delay = base::TimeDelta::FromSeconds(2);
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(std::move(*cb), true, 0,
+ CreateMockResponseWithChannelId(
+ mock_connection->connection_channel_id(),
+ kHidUnknownCommandError)),
+ delay);
+ }));
+
+ FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
+ hid_manager_->GetDevices(receiver.callback());
+ receiver.WaitForCallback();
+
+ std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
+ receiver.TakeReturnedDevicesFiltered();
+ ASSERT_EQ(1u, u2f_devices.size());
+ auto& device = u2f_devices.front();
+
+ device::test::TestCallbackReceiver<> get_info_callback;
+ device->DiscoverSupportedProtocolAndDeviceInfo(get_info_callback.callback());
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_TRUE(get_info_callback.was_called());
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_hid_discovery.cc b/chromium/device/fido/hid/fido_hid_discovery.cc
index 483b4b35e64..b1eb519d9f7 100644
--- a/chromium/device/fido/fido_hid_discovery.cc
+++ b/chromium/device/fido/hid/fido_hid_discovery.cc
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_discovery.h"
+#include "device/fido/hid/fido_hid_discovery.h"
#include <utility>
-#include "device/fido/fido_hid_device.h"
+#include "device/fido/hid/fido_hid_device.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
@@ -57,6 +57,7 @@ void FidoHidDiscovery::OnGetDevices(
std::vector<device::mojom::HidDeviceInfoPtr> device_infos) {
for (auto& device_info : device_infos)
DeviceAdded(std::move(device_info));
+
NotifyDiscoveryStarted(true);
}
diff --git a/chromium/device/fido/fido_hid_discovery.h b/chromium/device/fido/hid/fido_hid_discovery.h
index 94f70426a3a..c6a5e788063 100644
--- a/chromium/device/fido/fido_hid_discovery.h
+++ b/chromium/device/fido/hid/fido_hid_discovery.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_HID_DISCOVERY_H_
-#define DEVICE_FIDO_FIDO_HID_DISCOVERY_H_
+#ifndef DEVICE_FIDO_HID_FIDO_HID_DISCOVERY_H_
+#define DEVICE_FIDO_HID_FIDO_HID_DISCOVERY_H_
#include <memory>
#include <vector>
@@ -53,4 +53,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDiscovery
} // namespace device
-#endif // DEVICE_FIDO_FIDO_HID_DISCOVERY_H_
+#endif // DEVICE_FIDO_HID_FIDO_HID_DISCOVERY_H_
diff --git a/chromium/device/fido/fido_hid_discovery_unittest.cc b/chromium/device/fido/hid/fido_hid_discovery_unittest.cc
index 7f1cff5ea9c..75c1c70c60f 100644
--- a/chromium/device/fido/fido_hid_discovery_unittest.cc
+++ b/chromium/device/fido/hid/fido_hid_discovery_unittest.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_discovery.h"
+#include "device/fido/hid/fido_hid_discovery.h"
#include <string>
#include <utility>
#include "base/test/scoped_task_environment.h"
-#include "device/fido/fake_hid_impl_for_testing.h"
-#include "device/fido/fido_hid_device.h"
+#include "device/fido/hid/fake_hid_impl_for_testing.h"
+#include "device/fido/hid/fido_hid_device.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/device/public/mojom/hid.mojom.h"
diff --git a/chromium/device/fido/fido_hid_message.cc b/chromium/device/fido/hid/fido_hid_message.cc
index d95598c4a62..3040696c26d 100644
--- a/chromium/device/fido/fido_hid_message.cc
+++ b/chromium/device/fido/hid/fido_hid_message.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_message.h"
+#include "device/fido/hid/fido_hid_message.h"
#include <algorithm>
#include <numeric>
diff --git a/chromium/device/fido/fido_hid_message.h b/chromium/device/fido/hid/fido_hid_message.h
index ecf386cd39e..715c68e800e 100644
--- a/chromium/device/fido/fido_hid_message.h
+++ b/chromium/device/fido/hid/fido_hid_message.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_HID_MESSAGE_H_
-#define DEVICE_FIDO_FIDO_HID_MESSAGE_H_
+#ifndef DEVICE_FIDO_HID_FIDO_HID_MESSAGE_H_
+#define DEVICE_FIDO_HID_FIDO_HID_MESSAGE_H_
#include <stddef.h>
#include <stdint.h>
@@ -19,7 +19,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "device/fido/fido_constants.h"
-#include "device/fido/fido_hid_packet.h"
+#include "device/fido/hid/fido_hid_packet.h"
namespace device {
@@ -72,4 +72,4 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidMessage {
} // namespace device
-#endif // DEVICE_FIDO_FIDO_HID_MESSAGE_H_
+#endif // DEVICE_FIDO_HID_FIDO_HID_MESSAGE_H_
diff --git a/chromium/device/fido/fido_hid_message_fuzzer.cc b/chromium/device/fido/hid/fido_hid_message_fuzzer.cc
index 4357cb455b1..8b267c82901 100644
--- a/chromium/device/fido/fido_hid_message_fuzzer.cc
+++ b/chromium/device/fido/hid/fido_hid_message_fuzzer.cc
@@ -9,7 +9,7 @@
#include <vector>
#include "base/containers/span.h"
-#include "device/fido/fido_hid_message.h"
+#include "device/fido/hid/fido_hid_message.h"
namespace device {
diff --git a/chromium/device/fido/fido_hid_message_unittest.cc b/chromium/device/fido/hid/fido_hid_message_unittest.cc
index b819454dbe5..610af543756 100644
--- a/chromium/device/fido/fido_hid_message_unittest.cc
+++ b/chromium/device/fido/hid/fido_hid_message_unittest.cc
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_message.h"
+#include "device/fido/hid/fido_hid_message.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "device/fido/fido_constants.h"
-#include "device/fido/fido_hid_packet.h"
+#include "device/fido/hid/fido_hid_packet.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/device/fido/fido_hid_packet.cc b/chromium/device/fido/hid/fido_hid_packet.cc
index 10a1cebb939..0024cddc91e 100644
--- a/chromium/device/fido/fido_hid_packet.cc
+++ b/chromium/device/fido/hid/fido_hid_packet.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "device/fido/fido_hid_packet.h"
+#include "device/fido/hid/fido_hid_packet.h"
#include <algorithm>
#include <utility>
diff --git a/chromium/device/fido/fido_hid_packet.h b/chromium/device/fido/hid/fido_hid_packet.h
index 128f1ed2e35..2c3fbbc0b13 100644
--- a/chromium/device/fido/fido_hid_packet.h
+++ b/chromium/device/fido/hid/fido_hid_packet.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_FIDO_FIDO_HID_PACKET_H_
-#define DEVICE_FIDO_FIDO_HID_PACKET_H_
+#ifndef DEVICE_FIDO_HID_FIDO_HID_PACKET_H_
+#define DEVICE_FIDO_HID_FIDO_HID_PACKET_H_
#include <stddef.h>
#include <stdint.h>
diff --git a/chromium/device/fido/mac/OWNERS b/chromium/device/fido/mac/OWNERS
new file mode 100644
index 00000000000..eac9cee5ed5
--- /dev/null
+++ b/chromium/device/fido/mac/OWNERS
@@ -0,0 +1,5 @@
+martinkr@chromium.org
+martinkr@google.com
+
+# TEAM: security-dev@chromium.org
+# COMPONENT: Blink>WebAuthentication
diff --git a/chromium/device/fido/mac/authenticator.h b/chromium/device/fido/mac/authenticator.h
index 5030f64508a..090e643bb57 100644
--- a/chromium/device/fido/mac/authenticator.h
+++ b/chromium/device/fido/mac/authenticator.h
@@ -5,11 +5,16 @@
#ifndef DEVICE_FIDO_MAC_AUTHENTICATOR_H_
#define DEVICE_FIDO_MAC_AUTHENTICATOR_H_
+#include <memory>
+#include <string>
+
#include "base/component_export.h"
#include "base/mac/availability.h"
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece_forward.h"
#include "device/fido/fido_authenticator.h"
+#include "device/fido/fido_transport_protocol.h"
#include "device/fido/mac/operation.h"
namespace device {
@@ -21,6 +26,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) TouchIdAuthenticator
public:
// IsAvailable returns whether Touch ID is available and enrolled on the
// current device.
+ //
+ // Note that this may differ from the result of
+ // AuthenticatorImpl::IsUserVerifyingPlatformAuthenticatorAvailable, which
+ // also checks whether the embedder supports this authenticator, and if the
+ // request occurs from an off-the-record/incognito context.
static bool IsAvailable();
// CreateIfAvailable returns a TouchIdAuthenticator if IsAvailable() returns
@@ -35,15 +45,20 @@ class COMPONENT_EXPORT(DEVICE_FIDO) TouchIdAuthenticator
~TouchIdAuthenticator() override;
+ bool HasCredentialForGetAssertionRequest(
+ const CtapGetAssertionRequest& request);
+
// FidoAuthenticator
- void MakeCredential(
- CtapMakeCredentialRequest request,
- MakeCredentialCallback callback) override;
+ void InitializeAuthenticator(base::OnceClosure callback) override;
+ void MakeCredential(CtapMakeCredentialRequest request,
+ MakeCredentialCallback callback) override;
void GetAssertion(CtapGetAssertionRequest request,
GetAssertionCallback callback) override;
void Cancel() override;
std::string GetId() const override;
const AuthenticatorSupportedOptions& Options() const override;
+ FidoTransportProtocol AuthenticatorTransport() const override;
+ base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
private:
TouchIdAuthenticator(std::string keychain_access_group,
@@ -61,6 +76,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) TouchIdAuthenticator
std::unique_ptr<Operation> operation_;
+ base::WeakPtrFactory<TouchIdAuthenticator> weak_factory_;
+
private:
DISALLOW_COPY_AND_ASSIGN(TouchIdAuthenticator);
};
diff --git a/chromium/device/fido/mac/authenticator.mm b/chromium/device/fido/mac/authenticator.mm
index 76f9d482d58..b5e03ce84d4 100644
--- a/chromium/device/fido/mac/authenticator.mm
+++ b/chromium/device/fido/mac/authenticator.mm
@@ -4,13 +4,18 @@
#include "device/fido/mac/authenticator.h"
+#include <algorithm>
+
#import <LocalAuthentication/LocalAuthentication.h>
+#include "base/bind.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
+#include "base/stl_util.h"
#include "base/strings/string_piece.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "device/base/features.h"
#include "device/fido/authenticator_supported_options.h"
#include "device/fido/ctap_get_assertion_request.h"
@@ -28,10 +33,7 @@ namespace mac {
bool TouchIdAuthenticator::IsAvailable() {
if (base::FeatureList::IsEnabled(device::kWebAuthTouchId)) {
if (__builtin_available(macOS 10.12.2, *)) {
- base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
- return [context
- canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
- error:nil];
+ return TouchIdContext::TouchIdAvailable();
}
}
return false;
@@ -58,6 +60,40 @@ std::unique_ptr<TouchIdAuthenticator> TouchIdAuthenticator::CreateForTesting(
TouchIdAuthenticator::~TouchIdAuthenticator() = default;
+bool TouchIdAuthenticator::HasCredentialForGetAssertionRequest(
+ const CtapGetAssertionRequest& request) {
+ if (__builtin_available(macOS 10.12.2, *)) {
+ std::set<std::vector<uint8_t>> allow_list_credential_ids;
+ // Extract applicable credential IDs from the allowList, if the request has
+ // one. If not, any credential matching the RP works.
+ if (request.allow_list()) {
+ for (const auto& credential_descriptor : *request.allow_list()) {
+ if (credential_descriptor.credential_type() !=
+ CredentialType::kPublicKey)
+ continue;
+
+ if (!credential_descriptor.transports().empty() &&
+ !base::ContainsKey(credential_descriptor.transports(),
+ FidoTransportProtocol::kInternal))
+ continue;
+
+ allow_list_credential_ids.insert(credential_descriptor.id());
+ }
+ }
+
+ return FindCredentialInKeychain(keychain_access_group_, metadata_secret_,
+ request.rp_id(), allow_list_credential_ids,
+ nullptr /* LAContext */) != base::nullopt;
+ }
+ NOTREACHED();
+ return false;
+}
+
+void TouchIdAuthenticator::InitializeAuthenticator(base::OnceClosure callback) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ std::move(callback));
+}
+
void TouchIdAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
MakeCredentialCallback callback) {
if (__builtin_available(macOS 10.12.2, *)) {
@@ -96,6 +132,10 @@ std::string TouchIdAuthenticator::GetId() const {
return "TouchIdAuthenticator";
}
+FidoTransportProtocol TouchIdAuthenticator::AuthenticatorTransport() const {
+ return FidoTransportProtocol::kInternal;
+}
+
namespace {
AuthenticatorSupportedOptions TouchIdAuthenticatorOptions() {
@@ -117,10 +157,15 @@ const AuthenticatorSupportedOptions& TouchIdAuthenticator::Options() const {
return options;
}
+base::WeakPtr<FidoAuthenticator> TouchIdAuthenticator::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
TouchIdAuthenticator::TouchIdAuthenticator(std::string keychain_access_group,
std::string metadata_secret)
: keychain_access_group_(std::move(keychain_access_group)),
- metadata_secret_(std::move(metadata_secret)) {}
+ metadata_secret_(std::move(metadata_secret)),
+ weak_factory_(this) {}
} // namespace mac
} // namespace fido
diff --git a/chromium/device/fido/mac/fake_keychain.h b/chromium/device/fido/mac/fake_keychain.h
new file mode 100644
index 00000000000..97f275aaeb8
--- /dev/null
+++ b/chromium/device/fido/mac/fake_keychain.h
@@ -0,0 +1,57 @@
+// Copyright 2018 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_FIDO_MAC_FAKE_KEYCHAIN_H_
+#define DEVICE_FIDO_MAC_FAKE_KEYCHAIN_H_
+
+#include <string>
+#include <vector>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/macros.h"
+#include "device/fido/mac/keychain.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+class API_AVAILABLE(macos(10.12.2)) FakeKeychain : public Keychain {
+ public:
+ struct Item {
+ Item();
+ Item(Item&&);
+ Item& operator=(Item&&);
+ ~Item();
+
+ std::string label;
+ std::string application_label;
+ std::string application_tag;
+ base::ScopedCFTypeRef<SecKeyRef> private_key;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Item);
+ };
+
+ FakeKeychain();
+ ~FakeKeychain() override;
+
+ protected:
+ // Keychain:
+ base::ScopedCFTypeRef<SecKeyRef> KeyCreateRandomKey(
+ CFDictionaryRef params,
+ CFErrorRef* error) override;
+ OSStatus ItemCopyMatching(CFDictionaryRef query, CFTypeRef* result) override;
+ OSStatus ItemDelete(CFDictionaryRef query) override;
+
+ private:
+ std::vector<Item> items_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeKeychain);
+};
+
+} // namespace mac
+} // namespace fido
+} // namespace device
+
+#endif // DEVICE_FIDO_MAC_FAKE_KEYCHAIN_H_
diff --git a/chromium/device/fido/mac/fake_keychain.mm b/chromium/device/fido/mac/fake_keychain.mm
new file mode 100644
index 00000000000..1230468a89d
--- /dev/null
+++ b/chromium/device/fido/mac/fake_keychain.mm
@@ -0,0 +1,46 @@
+// Copyright 2018 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.
+
+#import <Security/Security.h>
+
+#include "device/fido/mac/fake_keychain.h"
+
+#include "device/fido/mac/keychain.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+FakeKeychain::FakeKeychain() = default;
+FakeKeychain::~FakeKeychain() = default;
+
+FakeKeychain::Item::Item() = default;
+FakeKeychain::Item::Item(Item&&) = default;
+FakeKeychain::Item& FakeKeychain::Item::operator=(Item&&) = default;
+FakeKeychain::Item::~Item() = default;
+
+base::ScopedCFTypeRef<SecKeyRef> FakeKeychain::KeyCreateRandomKey(
+ CFDictionaryRef parameters,
+ CFErrorRef* error) {
+ // TODO(martinkr): Implement.
+ NOTREACHED();
+ return base::ScopedCFTypeRef<SecKeyRef>();
+}
+
+OSStatus FakeKeychain::ItemCopyMatching(CFDictionaryRef query,
+ CFTypeRef* result) {
+ // TODO(martinkr): Implement.
+ NOTREACHED();
+ return errSecItemNotFound;
+}
+
+OSStatus FakeKeychain::ItemDelete(CFDictionaryRef query) {
+ // TODO(martinkr): Implement.
+ NOTREACHED();
+ return errSecItemNotFound;
+}
+
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/mac/fake_touch_id_context.h b/chromium/device/fido/mac/fake_touch_id_context.h
new file mode 100644
index 00000000000..28f552976f3
--- /dev/null
+++ b/chromium/device/fido/mac/fake_touch_id_context.h
@@ -0,0 +1,42 @@
+// Copyright 2018 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_FIDO_MAC_FAKE_TOUCH_ID_CONTEXT_H_
+#define DEVICE_FIDO_MAC_FAKE_TOUCH_ID_CONTEXT_H_
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "device/fido/mac/touch_id_context.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+class API_AVAILABLE(macosx(10.12.2)) FakeTouchIdContext
+ : public TouchIdContext {
+ public:
+ ~FakeTouchIdContext() override;
+
+ // TouchIdContext:
+ void PromptTouchId(const base::string16& reason, Callback callback) override;
+
+ void set_callback_result(bool callback_result) {
+ callback_result_ = callback_result;
+ }
+
+ private:
+ friend class ScopedTouchIdTestEnvironment;
+
+ FakeTouchIdContext();
+
+ bool callback_result_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTouchIdContext);
+};
+
+} // namespace mac
+} // namespace fido
+} // namespace device
+
+#endif // DEVICE_FIDO_MAC_FAKE_TOUCH_ID_CONTEXT_H_
diff --git a/chromium/device/fido/mac/fake_touch_id_context.mm b/chromium/device/fido/mac/fake_touch_id_context.mm
new file mode 100644
index 00000000000..ad9550426d7
--- /dev/null
+++ b/chromium/device/fido/mac/fake_touch_id_context.mm
@@ -0,0 +1,24 @@
+// Copyright 2018 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/fido/mac/fake_touch_id_context.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+FakeTouchIdContext::FakeTouchIdContext() = default;
+FakeTouchIdContext::~FakeTouchIdContext() = default;
+
+void FakeTouchIdContext::PromptTouchId(const base::string16& reason,
+ Callback callback) {
+ std::move(callback).Run(callback_result_);
+}
+
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/mac/get_assertion_operation.h b/chromium/device/fido/mac/get_assertion_operation.h
index 3146c8f9b77..d1ea2b5ce1d 100644
--- a/chromium/device/fido/mac/get_assertion_operation.h
+++ b/chromium/device/fido/mac/get_assertion_operation.h
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/mac/keychain.h"
#include "device/fido/mac/operation_base.h"
namespace device {
diff --git a/chromium/device/fido/mac/get_assertion_operation.mm b/chromium/device/fido/mac/get_assertion_operation.mm
index 4f1bec16d51..99fb012b428 100644
--- a/chromium/device/fido/mac/get_assertion_operation.mm
+++ b/chromium/device/fido/mac/get_assertion_operation.mm
@@ -12,12 +12,16 @@
#include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "device/fido/fido_constants.h"
#include "device/fido/mac/keychain.h"
#include "device/fido/mac/util.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "device/fido/public_key_credential_user_entity.h"
+#include "device/fido/strings/grit/fido_strings.h"
+#include "ui/base/l10n/l10n_util.h"
namespace device {
namespace fido {
@@ -47,9 +51,9 @@ void GetAssertionOperation::Run() {
return;
}
- // Prompt the user for consent.
- // TODO(martinkr): Localize reason strings.
- PromptTouchId("sign in to " + RpId());
+ // Display the macOS Touch ID prompt.
+ PromptTouchId(l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
+ base::UTF8ToUTF16(RpId())));
}
void GetAssertionOperation::PromptTouchIdDone(bool success) {
@@ -60,67 +64,31 @@ void GetAssertionOperation::PromptTouchIdDone(bool success) {
}
// Collect the credential ids from allowList. If allowList is absent, we will
- // just pick the first available credential for the RP below.
+ // pick the first available credential for the RP.
std::set<std::vector<uint8_t>> allowed_credential_ids;
if (request().allow_list()) {
for (const PublicKeyCredentialDescriptor& desc : *request().allow_list()) {
- if (desc.credential_type() != CredentialType::kPublicKey) {
+ if (desc.credential_type() != CredentialType::kPublicKey)
continue;
- }
+
+ if (!desc.transports().empty() &&
+ !base::ContainsKey(desc.transports(),
+ FidoTransportProtocol::kInternal))
+ continue;
+
allowed_credential_ids.insert(desc.id());
}
}
// Fetch credentials for RP from the request and current user profile.
- ScopedCFTypeRef<CFArrayRef> keychain_items;
- base::ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery();
- CFDictionarySetValue(query, kSecUseAuthenticationContext,
- authentication_context());
- CFDictionarySetValue(query, kSecReturnRef, @YES);
- CFDictionarySetValue(query, kSecReturnAttributes, @YES);
- CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
-
- OSStatus status = Keychain::GetInstance().ItemCopyMatching(
- query, reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto()));
- if (status == errSecItemNotFound) {
- DVLOG(1) << "no credentials found for RP";
- std::move(callback())
- .Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt);
- return;
- }
- if (status != errSecSuccess) {
- OSSTATUS_DLOG(ERROR, status) << "SecItemCopyMatching failed";
- std::move(callback())
- .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
- return;
- }
- SecKeyRef private_key = nil; // Owned by |keychain_items|.
- std::vector<uint8_t> credential_id;
- for (CFIndex i = 0; i < CFArrayGetCount(keychain_items); ++i) {
- CFDictionaryRef attributes = base::mac::CFCast<CFDictionaryRef>(
- CFArrayGetValueAtIndex(keychain_items, i));
- CFDataRef application_label = base::mac::GetValueFromDictionary<CFDataRef>(
- attributes, kSecAttrApplicationLabel);
- SecKeyRef key =
- base::mac::GetValueFromDictionary<SecKeyRef>(attributes, kSecValueRef);
- if (!application_label || !key) {
- // Corrupted keychain?
- DLOG(ERROR) << "could not find application label or key ref: "
- << attributes;
- continue;
- }
- std::vector<uint8_t> cid(CFDataGetBytePtr(application_label),
- CFDataGetBytePtr(application_label) +
- CFDataGetLength(application_label));
- if (allowed_credential_ids.empty() ||
- allowed_credential_ids.find(cid) != allowed_credential_ids.end()) {
- private_key = key;
- credential_id = std::move(cid);
- break;
- }
- }
- if (!private_key) {
- DVLOG(1) << "no allowed credential found";
+ base::Optional<Credential> credential = FindCredentialInKeychain(
+ keychain_access_group(), metadata_secret(), RpId(),
+ allowed_credential_ids, authentication_context());
+ if (!credential) {
+ // For now, don't show a Touch ID prompt if no credential exists.
+ // TODO(martinkr): Prompt for the fingerprint anyway, once dispatch to this
+ // authenticator is moved behind user interaction with the authenticator
+ // selection UI.
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt);
return;
@@ -129,7 +97,7 @@ void GetAssertionOperation::PromptTouchIdDone(bool success) {
// Decrypt the user entity from the credential ID.
base::Optional<CredentialMetadata::UserEntity> credential_user =
CredentialMetadata::UnsealCredentialId(metadata_secret(), RpId(),
- credential_id);
+ credential->credential_id);
if (!credential_user) {
// The keychain query already filtered for the RP ID encoded under this
// operation's metadata secret, so the credential id really should have
@@ -141,25 +109,27 @@ void GetAssertionOperation::PromptTouchIdDone(bool success) {
}
base::ScopedCFTypeRef<SecKeyRef> public_key(
- Keychain::GetInstance().KeyCopyPublicKey(private_key));
+ Keychain::GetInstance().KeyCopyPublicKey(credential->private_key));
if (!public_key) {
DLOG(ERROR) << "failed to get public key for credential id "
- << base::HexEncode(credential_id.data(), credential_id.size());
+ << base::HexEncode(credential->credential_id.data(),
+ credential->credential_id.size());
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
base::Optional<AuthenticatorData> authenticator_data = MakeAuthenticatorData(
- RpId(), credential_id, SecKeyRefToECPublicKey(public_key));
+ RpId(), credential->credential_id, SecKeyRefToECPublicKey(public_key));
if (!authenticator_data) {
DLOG(ERROR) << "MakeAuthenticatorData failed";
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
- base::Optional<std::vector<uint8_t>> signature = GenerateSignature(
- *authenticator_data, request().client_data_hash(), private_key);
+ base::Optional<std::vector<uint8_t>> signature =
+ GenerateSignature(*authenticator_data, request().client_data_hash(),
+ credential->private_key);
if (!signature) {
DLOG(ERROR) << "GenerateSignature failed";
std::move(callback())
@@ -169,7 +139,7 @@ void GetAssertionOperation::PromptTouchIdDone(bool success) {
auto response = AuthenticatorGetAssertionResponse(
std::move(*authenticator_data), std::move(*signature));
response.SetCredential(PublicKeyCredentialDescriptor(
- CredentialType::kPublicKey, std::move(credential_id)));
+ CredentialType::kPublicKey, std::move(credential->credential_id)));
response.SetUserEntity(credential_user->ToPublicKeyCredentialUserEntity());
std::move(callback())
diff --git a/chromium/device/fido/mac/keychain.h b/chromium/device/fido/mac/keychain.h
index 8772cea8997..d4e7558df5d 100644
--- a/chromium/device/fido/mac/keychain.h
+++ b/chromium/device/fido/mac/keychain.h
@@ -5,13 +5,20 @@
#ifndef DEVICE_FIDO_MAC_KEYCHAIN_H_
#define DEVICE_FIDO_MAC_KEYCHAIN_H_
+#include <stdint.h>
+#include <set>
+#include <string>
+#include <vector>
+
#import <Foundation/Foundation.h>
+#import <LocalAuthentication/LocalAuthentication.h>
#import <Security/Security.h>
#include "base/component_export.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/macros.h"
#include "base/no_destructor.h"
+#include "base/optional.h"
namespace device {
namespace fido {
@@ -25,37 +32,86 @@ namespace mac {
// keychain-access-group entitlements, and therefore requires code signing with
// a real Apple developer ID. We therefore group these function here, so they
// can be mocked out in testing.
-class COMPONENT_EXPORT(DEVICE_FIDO) API_AVAILABLE(macosx(10.12.2)) Keychain {
+class COMPONENT_EXPORT(DEVICE_FIDO) API_AVAILABLE(macos(10.12.2)) Keychain {
public:
- static const Keychain& GetInstance();
+ static Keychain& GetInstance();
// KeyCreateRandomKey wraps the |SecKeyCreateRandomKey| function.
virtual base::ScopedCFTypeRef<SecKeyRef> KeyCreateRandomKey(
CFDictionaryRef params,
- CFErrorRef* error) const;
+ CFErrorRef* error);
// KeyCreateSignature wraps the |SecKeyCreateSignature| function.
virtual base::ScopedCFTypeRef<CFDataRef> KeyCreateSignature(
SecKeyRef key,
SecKeyAlgorithm algorithm,
CFDataRef data,
- CFErrorRef* error) const;
+ CFErrorRef* error);
// KeyCopyPublicKey wraps the |SecKeyCopyPublicKey| function.
- virtual base::ScopedCFTypeRef<SecKeyRef> KeyCopyPublicKey(
- SecKeyRef key) const;
+ virtual base::ScopedCFTypeRef<SecKeyRef> KeyCopyPublicKey(SecKeyRef key);
// ItemCopyMatching wraps the |SecItemCopyMatching| function.
- virtual OSStatus ItemCopyMatching(CFDictionaryRef query,
- CFTypeRef* result) const;
+ virtual OSStatus ItemCopyMatching(CFDictionaryRef query, CFTypeRef* result);
// ItemDelete wraps the |SecItemDelete| function.
- virtual OSStatus ItemDelete(CFDictionaryRef query) const;
+ virtual OSStatus ItemDelete(CFDictionaryRef query);
+
+ protected:
+ Keychain();
+ virtual ~Keychain();
private:
friend class base::NoDestructor<Keychain>;
- Keychain();
+ friend class ScopedTouchIdTestEnvironment;
+
+ // Set an override to the singleton instance returned by |GetInstance|. The
+ // caller keeps ownership of the injected keychain and must remove the
+ // override by calling |ClearInstanceOverride| before deleting it.
+ static void SetInstanceOverride(Keychain* keychain);
+ static void ClearInstanceOverride();
DISALLOW_COPY_AND_ASSIGN(Keychain);
};
+// Credential represents a WebAuthn credential from the keychain.
+struct COMPONENT_EXPORT(FIDO) Credential {
+ Credential(base::ScopedCFTypeRef<SecKeyRef> private_key,
+ std::vector<uint8_t> credential_id);
+ ~Credential();
+ Credential(Credential&& other);
+ Credential& operator=(Credential&& other);
+
+ // An opaque reference to the private key that can be used for signing.
+ base::ScopedCFTypeRef<SecKeyRef> private_key;
+
+ // The credential ID is a handle to the key that gets passed to the RP. This
+ // ID is opaque to the RP, but is obtained by encrypting the credential
+ // metadata with a profile-specific metadata secret. See |CredentialMetadata|
+ // for more information.
+ std::vector<uint8_t> credential_id;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Credential);
+};
+
+// Tries to find a credential for the given |rp_id| in the keychain.
+// |credential_id_filter| may be used to select credentials by ID. If it is
+// empty, any credential for the RP matches. If multiple credentials are found,
+// only one is returned.
+//
+// An LAContext that has been successfully evaluated using |TouchIdContext| may
+// be passed in |authenticaton_context|, in order to authorize the credential's
+// private key for signing. The authentication may also be null if the caller
+// only wants to check for existence of a key, but does not intend to create a
+// signature from it. (I.e., the credential's SecKeyRef should not be passed to
+// |KeyCreateSignature| if no authentication context was provided, since that
+// would trigger a Touch ID prompt dialog).
+COMPONENT_EXPORT(FIDO)
+base::Optional<Credential> FindCredentialInKeychain(
+ const std::string& keychain_access_group,
+ const std::string& metadata_secret,
+ const std::string& rp_id,
+ const std::set<std::vector<uint8_t>>& credential_id_filter,
+ LAContext* authentication_context) API_AVAILABLE(macosx(10.12.2));
+
} // namespace mac
} // namespace fido
} // namespace device
diff --git a/chromium/device/fido/mac/keychain.mm b/chromium/device/fido/mac/keychain.mm
index 692b7c8a776..d2e0fa795d3 100644
--- a/chromium/device/fido/mac/keychain.mm
+++ b/chromium/device/fido/mac/keychain.mm
@@ -4,21 +4,48 @@
#include "device/fido/mac/keychain.h"
+#import <Foundation/Foundation.h>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_logging.h"
+#include "base/stl_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "device/fido/mac/credential_metadata.h"
+
namespace device {
namespace fido {
namespace mac {
+static API_AVAILABLE(macos(10.12.2)) Keychain* g_keychain_instance_override =
+ nullptr;
+
// static
-const Keychain& Keychain::GetInstance() {
- static const base::NoDestructor<Keychain> k;
+Keychain& Keychain::GetInstance() {
+ if (g_keychain_instance_override) {
+ return *g_keychain_instance_override;
+ }
+ static base::NoDestructor<Keychain> k;
return *k;
}
+// static
+void Keychain::SetInstanceOverride(Keychain* keychain) {
+ CHECK(!g_keychain_instance_override);
+ g_keychain_instance_override = keychain;
+}
+
+// static
+void Keychain::ClearInstanceOverride() {
+ CHECK(g_keychain_instance_override);
+ g_keychain_instance_override = nullptr;
+}
+
Keychain::Keychain() = default;
+Keychain::~Keychain() = default;
base::ScopedCFTypeRef<SecKeyRef> Keychain::KeyCreateRandomKey(
CFDictionaryRef params,
- CFErrorRef* error) const {
+ CFErrorRef* error) {
return base::ScopedCFTypeRef<SecKeyRef>(SecKeyCreateRandomKey(params, error));
}
@@ -26,25 +53,102 @@ base::ScopedCFTypeRef<CFDataRef> Keychain::KeyCreateSignature(
SecKeyRef key,
SecKeyAlgorithm algorithm,
CFDataRef data,
- CFErrorRef* error) const {
+ CFErrorRef* error) {
return base::ScopedCFTypeRef<CFDataRef>(
SecKeyCreateSignature(key, algorithm, data, error));
}
-base::ScopedCFTypeRef<SecKeyRef> Keychain::KeyCopyPublicKey(
- SecKeyRef key) const {
+base::ScopedCFTypeRef<SecKeyRef> Keychain::KeyCopyPublicKey(SecKeyRef key) {
return base::ScopedCFTypeRef<SecKeyRef>(SecKeyCopyPublicKey(key));
}
-OSStatus Keychain::ItemCopyMatching(CFDictionaryRef query,
- CFTypeRef* result) const {
+OSStatus Keychain::ItemCopyMatching(CFDictionaryRef query, CFTypeRef* result) {
return SecItemCopyMatching(query, result);
}
-OSStatus Keychain::ItemDelete(CFDictionaryRef query) const {
+OSStatus Keychain::ItemDelete(CFDictionaryRef query) {
return SecItemDelete(query);
}
+Credential::Credential(base::ScopedCFTypeRef<SecKeyRef> private_key_,
+ std::vector<uint8_t> credential_id_)
+ : private_key(std::move(private_key_)),
+ credential_id(std::move(credential_id_)) {}
+Credential::~Credential() = default;
+Credential::Credential(Credential&& other) = default;
+Credential& Credential::operator=(Credential&& other) = default;
+
+base::Optional<Credential> FindCredentialInKeychain(
+ const std::string& keychain_access_group,
+ const std::string& metadata_secret,
+ const std::string& rp_id,
+ const std::set<std::vector<uint8_t>>& credential_id_filter,
+ LAContext* authentication_context) {
+ base::Optional<std::string> encoded_rp_id =
+ CredentialMetadata::EncodeRpId(metadata_secret, rp_id);
+ if (!encoded_rp_id)
+ return base::nullopt;
+
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
+ CFDictionarySetValue(query, kSecClass, kSecClassKey);
+ CFDictionarySetValue(query, kSecAttrAccessGroup,
+ base::SysUTF8ToNSString(keychain_access_group));
+ CFDictionarySetValue(query, kSecAttrLabel,
+ base::SysUTF8ToNSString(*encoded_rp_id));
+ if (authentication_context) {
+ CFDictionarySetValue(query, kSecUseAuthenticationContext,
+ authentication_context);
+ }
+ CFDictionarySetValue(query, kSecReturnRef, @YES);
+ CFDictionarySetValue(query, kSecReturnAttributes, @YES);
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+
+ base::ScopedCFTypeRef<CFArrayRef> keychain_items;
+ OSStatus status = Keychain::GetInstance().ItemCopyMatching(
+ query, reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto()));
+ if (status == errSecItemNotFound) {
+ // No credentials for the RP.
+ return base::nullopt;
+ }
+ if (status != errSecSuccess) {
+ OSSTATUS_DLOG(ERROR, status) << "SecItemCopyMatching failed";
+ return base::nullopt;
+ }
+
+ // Credentials for the RP exist. Find a match.
+ std::vector<uint8_t> credential_id;
+ base::ScopedCFTypeRef<SecKeyRef> private_key(nil);
+ for (CFIndex i = 0; i < CFArrayGetCount(keychain_items); ++i) {
+ CFDictionaryRef attributes = base::mac::CFCast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(keychain_items, i));
+ CFDataRef application_label = base::mac::GetValueFromDictionary<CFDataRef>(
+ attributes, kSecAttrApplicationLabel);
+ SecKeyRef key =
+ base::mac::GetValueFromDictionary<SecKeyRef>(attributes, kSecValueRef);
+ if (!application_label || !key) {
+ // Corrupted keychain?
+ DLOG(ERROR) << "could not find application label or key ref: "
+ << attributes;
+ continue;
+ }
+ std::vector<uint8_t> cid(CFDataGetBytePtr(application_label),
+ CFDataGetBytePtr(application_label) +
+ CFDataGetLength(application_label));
+ if (credential_id_filter.empty() ||
+ base::ContainsKey(credential_id_filter, cid)) {
+ private_key.reset(key, base::scoped_policy::RETAIN);
+ credential_id = std::move(cid);
+ break;
+ }
+ }
+ if (private_key == nil) {
+ DVLOG(1) << "no allowed credential found";
+ return base::nullopt;
+ }
+ return Credential(std::move(private_key), std::move(credential_id));
+}
+
} // namespace mac
} // namespace fido
} // namespace device
diff --git a/chromium/device/fido/mac/make_credential_operation.mm b/chromium/device/fido/mac/make_credential_operation.mm
index e5669be33cd..b83f45b6498 100644
--- a/chromium/device/fido/mac/make_credential_operation.mm
+++ b/chromium/device/fido/mac/make_credential_operation.mm
@@ -11,12 +11,15 @@
#include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/utf_string_conversions.h"
#include "device/fido/attestation_statement_formats.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/mac/credential_metadata.h"
#include "device/fido/mac/keychain.h"
#include "device/fido/mac/util.h"
+#include "device/fido/strings/grit/fido_strings.h"
+#include "ui/base/l10n/l10n_util.h"
namespace device {
namespace fido {
@@ -65,9 +68,9 @@ void MakeCredentialOperation::Run() {
return;
}
- // Prompt the user for consent.
- // TODO(martinkr): Localize reason strings.
- PromptTouchId("register with " + RpId());
+ // Display the macOS Touch ID prompt.
+ PromptTouchId(l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
+ base::UTF8ToUTF16(RpId())));
}
void MakeCredentialOperation::PromptTouchIdDone(bool success) {
diff --git a/chromium/device/fido/mac/operation_base.h b/chromium/device/fido/mac/operation_base.h
index 545252023a5..5dc476b64fa 100644
--- a/chromium/device/fido/mac/operation_base.h
+++ b/chromium/device/fido/mac/operation_base.h
@@ -37,7 +37,7 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
metadata_secret_(std::move(metadata_secret)),
keychain_access_group_(std::move(keychain_access_group)),
callback_(std::move(callback)),
- touch_id_context_(std::make_unique<TouchIdContext>()) {}
+ touch_id_context_(TouchIdContext::Create()) {}
~OperationBase() override = default;
@@ -56,13 +56,13 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
// PromptTouchId triggers a Touch ID consent dialog with the given reason
// string. Subclasses implement the PromptTouchIdDone callback to receive the
// result.
- void PromptTouchId(std::string reason) {
+ void PromptTouchId(const base::string16& reason) {
// The callback passed to TouchIdContext::Prompt will not fire if the
// TouchIdContext itself has been deleted. Since that it is owned by this
// class, there is no need to bind the callback to a weak ref here.
touch_id_context_->PromptTouchId(
- std::move(reason), base::BindOnce(&OperationBase::PromptTouchIdDone,
- base::Unretained(this)));
+ reason, base::BindOnce(&OperationBase::PromptTouchIdDone,
+ base::Unretained(this)));
}
// Callback for |PromptTouchId|.
@@ -96,7 +96,9 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
}
const std::string& metadata_secret() const { return metadata_secret_; }
-
+ const std::string& keychain_access_group() const {
+ return keychain_access_group_;
+ }
const Request& request() const { return request_; }
Callback& callback() { return callback_; }
diff --git a/chromium/device/fido/mac/scoped_touch_id_test_environment.h b/chromium/device/fido/mac/scoped_touch_id_test_environment.h
new file mode 100644
index 00000000000..8e6e112352b
--- /dev/null
+++ b/chromium/device/fido/mac/scoped_touch_id_test_environment.h
@@ -0,0 +1,70 @@
+// Copyright 2018 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_FIDO_MAC_SCOPED_TOUCH_ID_TEST_ENVIRONMENT_H_
+#define DEVICE_FIDO_MAC_SCOPED_TOUCH_ID_TEST_ENVIRONMENT_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/mac/availability.h"
+#include "base/macros.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+class FakeKeychain;
+class FakeTouchIdContext;
+class TouchIdContext;
+
+// ScopedTouchIdTestEnvironment overrides behavior of the Touch ID
+// authenticator in testing. While in scope, it
+// - installs a fake Keychain to avoid writing to the macOS keychain, which
+// requires a valid code signature and keychain-access-group entitlement;
+// - allows faking TouchIdContext instances returned by TouchIdContext to stub
+// out Touch ID fingerprint prompts.
+// Overrides are reset when the instance is destroyed.
+class COMPONENT_EXPORT(DEVICE_FIDO)
+ API_AVAILABLE(macosx(10.12.2)) ScopedTouchIdTestEnvironment {
+ public:
+ ScopedTouchIdTestEnvironment();
+ ~ScopedTouchIdTestEnvironment();
+
+ // ForgeNextTouchIdContext sets up the FakeTouchIdContext returned by the
+ // next call to TouchIdContext::Create. The fake will invoke the callback
+ // passed to TouchIdContext::PromptTouchId with the given result.
+ //
+ // It is a fatal error to call TouchIdContext::Create without invoking this
+ // method first while the test environment is in scope.
+ void ForgeNextTouchIdContext(bool simulate_prompt_success);
+
+ // Sets the value returned by TouchIdContext::TouchIdAvailable. The default on
+ // instantiation of the test environment is true.
+ bool SetTouchIdAvailable(bool available);
+
+ private:
+ static std::unique_ptr<TouchIdContext> ForwardCreate();
+ static bool ForwardTouchIdAvailable();
+
+ std::unique_ptr<TouchIdContext> CreateTouchIdContext();
+ bool TouchIdAvailable();
+
+ using CreateFuncPtr = decltype(&ForwardCreate);
+ CreateFuncPtr touch_id_context_create_ptr_;
+ using TouchIdAvailableFuncPtr = decltype(&ForwardTouchIdAvailable);
+ TouchIdAvailableFuncPtr touch_id_context_touch_id_available_ptr_;
+
+ std::unique_ptr<FakeTouchIdContext> next_touch_id_context_;
+ std::unique_ptr<FakeKeychain> keychain_;
+ bool touch_id_available_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTouchIdTestEnvironment);
+};
+
+} // namespace mac
+} // namespace fido
+} // namespace device
+
+#endif // DEVICE_FIDO_MAC_SCOPED_TOUCH_ID_TEST_ENVIRONMENT_H_
diff --git a/chromium/device/fido/mac/scoped_touch_id_test_environment.mm b/chromium/device/fido/mac/scoped_touch_id_test_environment.mm
new file mode 100644
index 00000000000..01aa6f25e1c
--- /dev/null
+++ b/chromium/device/fido/mac/scoped_touch_id_test_environment.mm
@@ -0,0 +1,82 @@
+// Copyright 2018 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/fido/mac/scoped_touch_id_test_environment.h"
+
+#import <Security/Security.h>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "device/fido/mac/fake_keychain.h"
+#include "device/fido/mac/fake_touch_id_context.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+static API_AVAILABLE(macosx(10.12.2))
+ ScopedTouchIdTestEnvironment* g_current_environment = nullptr;
+
+ScopedTouchIdTestEnvironment::ScopedTouchIdTestEnvironment()
+ : keychain_(std::make_unique<FakeKeychain>()) {
+ DCHECK(!g_current_environment);
+ g_current_environment = this;
+
+ // Override TouchIdContext::Create and TouchIdContext::IsAvailable.
+ touch_id_context_create_ptr_ = TouchIdContext::g_create_;
+ TouchIdContext::g_create_ = &ForwardCreate;
+
+ touch_id_context_touch_id_available_ptr_ =
+ TouchIdContext::g_touch_id_available_;
+ TouchIdContext::g_touch_id_available_ = &ForwardTouchIdAvailable;
+
+ Keychain::SetInstanceOverride(static_cast<Keychain*>(keychain_.get()));
+}
+
+ScopedTouchIdTestEnvironment::~ScopedTouchIdTestEnvironment() {
+ DCHECK(touch_id_context_create_ptr_);
+ TouchIdContext::g_create_ = touch_id_context_create_ptr_;
+
+ DCHECK(touch_id_context_touch_id_available_ptr_);
+ TouchIdContext::g_touch_id_available_ =
+ touch_id_context_touch_id_available_ptr_;
+
+ Keychain::ClearInstanceOverride();
+}
+
+// static
+std::unique_ptr<TouchIdContext> ScopedTouchIdTestEnvironment::ForwardCreate() {
+ return g_current_environment->CreateTouchIdContext();
+}
+
+// static
+bool ScopedTouchIdTestEnvironment::ForwardTouchIdAvailable() {
+ return g_current_environment->TouchIdAvailable();
+}
+
+bool ScopedTouchIdTestEnvironment::SetTouchIdAvailable(bool available) {
+ return touch_id_available_ = available;
+}
+
+bool ScopedTouchIdTestEnvironment::TouchIdAvailable() {
+ return touch_id_available_;
+}
+
+void ScopedTouchIdTestEnvironment::ForgeNextTouchIdContext(
+ bool simulate_prompt_success) {
+ CHECK(!next_touch_id_context_);
+ next_touch_id_context_ = base::WrapUnique(new FakeTouchIdContext);
+ next_touch_id_context_->set_callback_result(simulate_prompt_success);
+}
+
+std::unique_ptr<TouchIdContext>
+ScopedTouchIdTestEnvironment::CreateTouchIdContext() {
+ CHECK(next_touch_id_context_) << "Call ForgeNextTouchIdContext() for every "
+ "context created in the test environment.";
+ return std::move(next_touch_id_context_);
+}
+
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/mac/touch_id_context.h b/chromium/device/fido/mac/touch_id_context.h
index e54f64d3d85..2eff4cad516 100644
--- a/chromium/device/fido/mac/touch_id_context.h
+++ b/chromium/device/fido/mac/touch_id_context.h
@@ -9,31 +9,48 @@
#import <Security/Security.h>
#include "base/callback.h"
+#include "base/component_export.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
namespace device {
namespace fido {
namespace mac {
// TouchIdContext wraps a macOS Touch ID consent prompt for signing with a
-// secure enclave key.
-class API_AVAILABLE(macosx(10.12.2)) TouchIdContext {
+// secure enclave key. It is a essentially a simpler facade for the LAContext
+// class from the macOS LocalAuthentication framework (c.f.
+// https://developer.apple.com/documentation/localauthentication/lacontext?language=objc).
+//
+// Use |Create| to instantiate a new context. Multiple instances can be created
+// at the same time. However, calling |PromptTouchId| on one instance will
+// cancel any other pending evaluations with an error. Deleting an instance
+// will invalidate any pending evaluation prompts (i.e. the dialog will
+// disappear and evaluation will fail with an error).
+class COMPONENT_EXPORT(DEVICE_FIDO)
+ API_AVAILABLE(macosx(10.12.2)) TouchIdContext {
public:
// The callback is invoked when the Touch ID prompt completes. It receives a
// boolean indicating whether obtaining the fingerprint was successful.
using Callback = base::OnceCallback<void(bool)>;
- TouchIdContext();
- ~TouchIdContext();
+ // Factory method for instantiating a TouchIdContext.
+ static std::unique_ptr<TouchIdContext> Create();
+
+ // Returns whether Touch ID is supported on the current hardware and set up to
+ // be used.
+ static bool TouchIdAvailable();
+
+ virtual ~TouchIdContext();
// PromptTouchId displays a Touch ID consent prompt with the provided reason
// string to the user. On completion or error, the provided callback is
// invoked, unless the TouchIdContext instance has been destroyed in the
// meantime (in which case nothing happens).
- void PromptTouchId(std::string reason, Callback callback);
+ virtual void PromptTouchId(const base::string16& reason, Callback callback);
// authentication_context returns the LAContext used for the Touch ID prompt.
LAContext* authentication_context() const { return context_; }
@@ -42,7 +59,19 @@ class API_AVAILABLE(macosx(10.12.2)) TouchIdContext {
// evaluated/authorized in the Touch ID prompt.
SecAccessControlRef access_control() const { return access_control_; }
+ protected:
+ TouchIdContext();
+
private:
+ using CreateFuncPtr = decltype(&Create);
+ using TouchIdAvailableFuncPtr = decltype(&TouchIdAvailable);
+
+ static CreateFuncPtr g_create_;
+ static TouchIdAvailableFuncPtr g_touch_id_available_;
+
+ static std::unique_ptr<TouchIdContext> CreateImpl();
+ static bool TouchIdAvailableImpl();
+
void RunCallback(bool success);
base::scoped_nsobject<LAContext> context_;
@@ -50,6 +79,7 @@ class API_AVAILABLE(macosx(10.12.2)) TouchIdContext {
Callback callback_;
base::WeakPtrFactory<TouchIdContext> weak_ptr_factory_;
+ friend class ScopedTouchIdTestEnvironment;
DISALLOW_COPY_AND_ASSIGN(TouchIdContext);
};
diff --git a/chromium/device/fido/mac/touch_id_context.mm b/chromium/device/fido/mac/touch_id_context.mm
index d196ea59c6f..25cef5ff10d 100644
--- a/chromium/device/fido/mac/touch_id_context.mm
+++ b/chromium/device/fido/mac/touch_id_context.mm
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
+#include "base/memory/ptr_util.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
@@ -20,14 +21,49 @@ namespace mac {
namespace {
API_AVAILABLE(macosx(10.12.2))
base::ScopedCFTypeRef<SecAccessControlRef> DefaultAccessControl() {
+ // The default access control policy used for WebAuthn credentials stored by
+ // the Touch ID platform authenticator.
return base::ScopedCFTypeRef<SecAccessControlRef>(
SecAccessControlCreateWithFlags(
kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
- kSecAccessControlPrivateKeyUsage | kSecAccessControlTouchIDAny,
+ kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence,
nullptr));
}
} // namespace
+// static
+std::unique_ptr<TouchIdContext> TouchIdContext::CreateImpl() {
+ return base::WrapUnique(new TouchIdContext());
+}
+
+// static
+TouchIdContext::CreateFuncPtr TouchIdContext::g_create_ =
+ &TouchIdContext::CreateImpl;
+
+// static
+std::unique_ptr<TouchIdContext> TouchIdContext::Create() {
+ // Testing seam to allow faking Touch ID in tests.
+ return (*g_create_)();
+}
+
+// static
+bool TouchIdContext::TouchIdAvailableImpl() {
+ base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
+ return
+ [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
+ error:nil];
+}
+
+// static
+TouchIdContext::TouchIdAvailableFuncPtr TouchIdContext::g_touch_id_available_ =
+ &TouchIdContext::TouchIdAvailableImpl;
+
+// static
+bool TouchIdContext::TouchIdAvailable() {
+ // Testing seam to allow faking Touch ID in tests.
+ return (*g_touch_id_available_)();
+}
+
TouchIdContext::TouchIdContext()
: context_([[LAContext alloc] init]),
access_control_(DefaultAccessControl()),
@@ -41,7 +77,8 @@ TouchIdContext::~TouchIdContext() {
[context_ invalidate];
}
-void TouchIdContext::PromptTouchId(std::string reason, Callback callback) {
+void TouchIdContext::PromptTouchId(const base::string16& reason,
+ Callback callback) {
callback_ = std::move(callback);
scoped_refptr<base::SequencedTaskRunner> runner =
base::SequencedTaskRunnerHandle::Get();
@@ -52,7 +89,7 @@ void TouchIdContext::PromptTouchId(std::string reason, Callback callback) {
// the sign bit there.
[context_ evaluateAccessControl:access_control_
operation:LAAccessControlOperationUseKeySign
- localizedReason:base::SysUTF8ToNSString(reason)
+ localizedReason:base::SysUTF16ToNSString(reason)
reply:^(BOOL success, NSError* error) {
// The reply block is invoked in a separate
// thread. We want to invoke the callback in the
diff --git a/chromium/device/fido/mac/util.h b/chromium/device/fido/mac/util.h
index 6f6d8caa3dc..0b4e56d5f5a 100644
--- a/chromium/device/fido/mac/util.h
+++ b/chromium/device/fido/mac/util.h
@@ -47,8 +47,6 @@ base::Optional<std::vector<uint8_t>> GenerateSignature(
std::unique_ptr<ECPublicKey> SecKeyRefToECPublicKey(SecKeyRef public_key_ref)
API_AVAILABLE(macosx(10.12.2));
-std::vector<uint8_t> TouchIdAaguid();
-
} // namespace mac
} // namespace fido
} // namespace device
diff --git a/chromium/device/fido/mac/util.mm b/chromium/device/fido/mac/util.mm
index af4f61051a6..b91556f5643 100644
--- a/chromium/device/fido/mac/util.mm
+++ b/chromium/device/fido/mac/util.mm
@@ -31,14 +31,11 @@ using base::scoped_nsobject;
using cbor::CBORWriter;
using cbor::CBORValue;
-// The authenticator AAGUID value.
-constexpr std::array<uint8_t, 16> kAaguid = {0xad, 0xce, 0x00, 0x02, 0x35, 0xbc,
- 0xc6, 0x0a, 0x64, 0x8b, 0x0b, 0x25,
- 0xf1, 0xf0, 0x55, 0x03};
-
-std::vector<uint8_t> TouchIdAaguid() {
- return std::vector<uint8_t>(kAaguid.begin(), kAaguid.end());
-}
+// WebAuthn requires an all-zero AAGUID for authenticators using
+// self-attestation.
+constexpr std::array<uint8_t, 16> kAaguid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
// SecKeyRefToECPublicKey converts a SecKeyRef for a public key into an
// equivalent |ECPublicKey| instance. It returns |nullptr| if the key cannot be
diff --git a/chromium/device/fido/make_credential_handler_unittest.cc b/chromium/device/fido/make_credential_handler_unittest.cc
index f5b277db77b..8b47f1b8f05 100644
--- a/chromium/device/fido/make_credential_handler_unittest.cc
+++ b/chromium/device/fido/make_credential_handler_unittest.cc
@@ -5,21 +5,28 @@
#include <memory>
#include <utility>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "device/base/features.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/authenticator_selection_criteria.h"
#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/device_response_converter.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
+#include "device/fido/fido_device_authenticator.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/make_credential_request_handler.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/test_callback_receiver.h"
+#include "device/fido/virtual_ctap2_device.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,16 +36,25 @@ namespace device {
namespace {
-using TestMakeCredentialRequestCallback = test::StatusAndValueCallbackReceiver<
+using TestMakeCredentialRequestCallback = test::StatusAndValuesCallbackReceiver<
FidoReturnCode,
- base::Optional<AuthenticatorMakeCredentialResponse>>;
+ base::Optional<AuthenticatorMakeCredentialResponse>,
+ FidoTransportProtocol>;
} // namespace
class FidoMakeCredentialHandlerTest : public ::testing::Test {
public:
- void ForgeNextHidDiscovery() {
+ FidoMakeCredentialHandlerTest() {
+ mock_adapter_ =
+ base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
+ BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
+ }
+
+ void ForgeDiscoveries() {
discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
+ ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
+ nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
}
std::unique_ptr<MakeCredentialRequestHandler> CreateMakeCredentialHandler() {
@@ -49,7 +65,7 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test {
std::unique_ptr<MakeCredentialRequestHandler>
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria authenticator_selection_criteria) {
- ForgeNextHidDiscovery();
+ ForgeDiscoveries();
PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
PublicKeyCredentialUserEntity user(
fido_parsing_utils::Materialize(test_data::kUserId));
@@ -60,12 +76,38 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test {
test_data::kClientDataHash, std::move(rp), std::move(user),
std::move(credential_params));
- return std::make_unique<MakeCredentialRequestHandler>(
- nullptr,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- std::move(request_parameter),
+ auto handler = std::make_unique<MakeCredentialRequestHandler>(
+ nullptr, supported_transports_, std::move(request_parameter),
std::move(authenticator_selection_criteria), cb_.callback());
+ handler->SetPlatformAuthenticatorOrMarkUnavailable(
+ CreatePlatformAuthenticator());
+ return handler;
+ }
+
+ void ExpectAllowedTransportsForRequestAre(
+ MakeCredentialRequestHandler* request_handler,
+ base::flat_set<FidoTransportProtocol> transports) {
+ using Transport = FidoTransportProtocol;
+ if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+ ble_discovery()->WaitForCallToStartAndSimulateSuccess();
+ if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
+ nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+
+ if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
+ EXPECT_FALSE(discovery()->is_start_requested());
+ if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
+ EXPECT_FALSE(ble_discovery()->is_start_requested());
+ if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
+ EXPECT_FALSE(nfc_discovery()->is_start_requested());
+
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAreArray(transports));
}
void InitFeatureListAndDisableCtapFlag() {
@@ -73,31 +115,61 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test {
}
test::FakeFidoDiscovery* discovery() const { return discovery_; }
+ test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
+ test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
TestMakeCredentialRequestCallback& callback() { return cb_; }
+ void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
+ mock_platform_device_ = std::move(device);
+ }
+
+ void set_supported_transports(
+ base::flat_set<FidoTransportProtocol> transports) {
+ supported_transports_ = std::move(transports);
+ }
+
protected:
+ base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
+ if (!mock_platform_device_)
+ return base::nullopt;
+ return PlatformAuthenticatorInfo(
+ std::make_unique<FidoDeviceAuthenticator>(mock_platform_device_.get()),
+ false /* has_recognized_mac_touch_id_credential_available */);
+ }
+
base::test::ScopedFeatureList scoped_feature_list_;
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
test::FakeFidoDiscovery* discovery_;
+ test::FakeFidoDiscovery* ble_discovery_;
+ test::FakeFidoDiscovery* nfc_discovery_;
+ scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
+ std::unique_ptr<MockFidoDevice> mock_platform_device_;
TestMakeCredentialRequestCallback cb_;
+ base::flat_set<FidoTransportProtocol> supported_transports_ =
+ GetAllTransportProtocols();
};
+TEST_F(FidoMakeCredentialHandlerTest, TransportAvailabilityInfo) {
+ auto request_handler = CreateMakeCredentialHandler();
+
+ EXPECT_EQ(FidoRequestHandlerBase::RequestType::kMakeCredential,
+ request_handler->transport_availability_info().request_type);
+ EXPECT_EQ(test_data::kRelyingPartyId,
+ request_handler->transport_availability_info().rp_id);
+}
+
TEST_F(FidoMakeCredentialHandlerTest, TestCtap2MakeCredentialWithFlagEnabled) {
auto request_handler = CreateMakeCredentialHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
test_data::kTestMakeCredentialResponse);
-
discovery()->AddDevice(std::move(device));
+
callback().WaitForCallback();
EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
EXPECT_TRUE(request_handler->is_complete());
@@ -108,15 +180,12 @@ TEST_F(FidoMakeCredentialHandlerTest, TestU2fRegisterWithFlagEnabled) {
auto request_handler = CreateMakeCredentialHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+ auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
device->ExpectRequestAndRespondWith(
test_data::kU2fRegisterCommandApdu,
test_data::kApduEncodedNoErrorRegisterResponse);
-
discovery()->AddDevice(std::move(device));
+
callback().WaitForCallback();
EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
EXPECT_TRUE(request_handler->is_complete());
@@ -150,83 +219,103 @@ TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithUserVerificationRequired) {
UserVerificationRequirement::kRequired));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
+ auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_FALSE(callback().was_called());
}
-TEST_F(FidoMakeCredentialHandlerTest,
- U2fRegisterWithPlatformDeviceRequirement) {
+TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithResidentKeyRequirement) {
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::
- kPlatform,
- false /* require_resident_key */,
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+ true /* require_resident_key */,
UserVerificationRequirement::kPreferred));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
+ auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_FALSE(callback().was_called());
}
-TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithResidentKeyRequirement) {
+TEST_F(FidoMakeCredentialHandlerTest, UserVerificationRequirementNotMet) {
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
- true /* require_resident_key */,
- UserVerificationRequirement::kPreferred));
+ false /* require_resident_key */,
+ UserVerificationRequirement::kRequired));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponseWithoutUvSupport);
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_FALSE(callback().was_called());
}
-TEST_F(FidoMakeCredentialHandlerTest,
- UserVerificationAuthenticatorSelectionCriteria) {
+// TODO(crbug.com/873710): Platform authenticators are temporarily disabled if
+// AuthenticatorAttachment is unset (kAny).
+TEST_F(FidoMakeCredentialHandlerTest, AnyAttachment) {
+ auto platform_device = MockFidoDevice::MakeCtap(
+ ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+ platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ set_mock_platform_device(std::move(platform_device));
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
false /* require_resident_key */,
- UserVerificationRequirement::kRequired));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutUvSupport);
-
- discovery()->AddDevice(std::move(device));
+ UserVerificationRequirement::kPreferred));
+ // MakeCredentialHandler will not dispatch the kAny request to the platform
+ // authenticator since the request does not get dispatched through UI. Despite
+ // setting a platform authenticator, the internal transport never becomes
+ // available.
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_FALSE(callback().was_called());
+
+ // kCloudAssistedBluetoothLowEnergy not yet supported for MakeCredential.
+ ExpectAllowedTransportsForRequestAre(
+ request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kUsbHumanInterfaceDevice});
}
-TEST_F(FidoMakeCredentialHandlerTest,
- PlatformDeviceAuthenticatorSelectionCriteria) {
+TEST_F(FidoMakeCredentialHandlerTest, CrossPlatformAttachment) {
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kCrossPlatform,
+ false /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+
+ // kCloudAssistedBluetoothLowEnergy not yet supported for MakeCredential.
+ ExpectAllowedTransportsForRequestAre(
+ request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kUsbHumanInterfaceDevice});
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, PlatformAttachment) {
+ // Add a platform device to trigger the transport actually becoming available.
+ // We don't care about the result of the request.
+ auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponsePlatformDevice);
+ platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ platform_device->ExpectCtap2CommandAndDoNotRespond(
+ CtapRequestCommand::kAuthenticatorMakeCredential);
+ EXPECT_CALL(*platform_device, Cancel());
+ set_mock_platform_device(std::move(platform_device));
+
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
@@ -234,14 +323,22 @@ TEST_F(FidoMakeCredentialHandlerTest,
kPlatform,
false /* require_resident_key */,
UserVerificationRequirement::kRequired));
- discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseCrossPlatformDevice);
+ ExpectAllowedTransportsForRequestAre(request_handler.get(),
+ {FidoTransportProtocol::kInternal});
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, ResidentKeyRequirementNotMet) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+ true /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponseWithoutResidentKeySupport);
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
@@ -249,29 +346,99 @@ TEST_F(FidoMakeCredentialHandlerTest,
}
TEST_F(FidoMakeCredentialHandlerTest,
- ResidentKeyAuthenticatorSelectionCriteria) {
+ AuthenticatorSelectionCriteriaSatisfiedByCrossPlatformDevice) {
+ set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kCrossPlatform,
true /* require_resident_key */,
- UserVerificationRequirement::kPreferred));
+ UserVerificationRequirement::kRequired));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorMakeCredential,
+ test_data::kTestMakeCredentialResponse);
+ discovery()->AddDevice(std::move(device));
+
+ callback().WaitForCallback();
+ EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAre(
+ FidoTransportProtocol::kUsbHumanInterfaceDevice));
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+ AuthenticatorSelectionCriteriaSatisfiedByPlatformDevice) {
+ set_supported_transports({FidoTransportProtocol::kInternal});
+ auto platform_device = MockFidoDevice::MakeCtap(
+ ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+ platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ EXPECT_CALL(*platform_device, GetId())
+ .WillRepeatedly(testing::Return("device0"));
+ platform_device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutResidentKeySupport);
+ test_data::kTestGetInfoResponsePlatformDevice);
+ platform_device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorMakeCredential,
+ test_data::kTestMakeCredentialResponse);
+ set_mock_platform_device(std::move(platform_device));
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kPlatform,
+ true /* require_resident_key */,
+ UserVerificationRequirement::kRequired));
+
+ callback().WaitForCallback();
+ EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+
+ EXPECT_THAT(
+ request_handler->transport_availability_info().available_transports,
+ ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
+}
+
+// A cross-platform authenticator claiming to be a platform authenticator as per
+// its GetInfo response is rejected.
+TEST_F(FidoMakeCredentialHandlerTest,
+ CrossPlatformAuthenticatorPretendingToBePlatform) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kCrossPlatform,
+ false /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponsePlatformDevice);
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_FALSE(callback().was_called());
}
+// A platform authenticator claiming to be a cross-platform authenticator as per
+// its GetInfo response is rejected.
TEST_F(FidoMakeCredentialHandlerTest,
- SatisfyAllAuthenticatorSelectionCriteria) {
+ PlatformAuthenticatorPretendingToBeCrossPlatform) {
+ auto platform_device = MockFidoDevice::MakeCtap(
+ ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse));
+ platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ EXPECT_CALL(*platform_device, GetId())
+ .WillRepeatedly(testing::Return("device0"));
+ platform_device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
+ set_mock_platform_device(std::move(platform_device));
+
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
@@ -279,17 +446,67 @@ TEST_F(FidoMakeCredentialHandlerTest,
kPlatform,
true /* require_resident_key */,
UserVerificationRequirement::kRequired));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
+ const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
+ FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication,
+ };
+
+ set_supported_transports(kBleAndNfc);
+ EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kCrossPlatform,
+ false /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+
+ ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, IncorrectRpIdHash) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+ false /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
- test_data::kTestMakeCredentialResponse);
+ test_data::kTestMakeCredentialResponseWithIncorrectRpIdHash);
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+// Tests that only authenticators with resident key support will successfully
+// process MakeCredential request when the relying party requires using resident
+// keys in AuthenicatorSelectionCriteria.
+TEST_F(FidoMakeCredentialHandlerTest,
+ SuccessfulMakeCredentialWithResidentKeyOption) {
+ auto device = std::make_unique<VirtualCtap2Device>();
+ AuthenticatorSupportedOptions option;
+ option.SetSupportsResidentKey(true);
+ device->SetAuthenticatorSupportedOptions(std::move(option));
+
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+ true /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
@@ -297,25 +514,73 @@ TEST_F(FidoMakeCredentialHandlerTest,
EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
}
-TEST_F(FidoMakeCredentialHandlerTest, IncompatibleUserVerificationSetting) {
+// Tests that MakeCredential request fails when asking to use resident keys with
+// authenticators that do not support resident key.
+TEST_F(FidoMakeCredentialHandlerTest,
+ MakeCredentialFailsForIncompatibleResidentKeyOption) {
+ auto device = std::make_unique<VirtualCtap2Device>();
auto request_handler =
CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
- false /* require_resident_key */,
- UserVerificationRequirement::kRequired));
+ true /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+
discovery()->WaitForCallToStartAndSimulateSuccess();
+ discovery()->AddDevice(std::move(device));
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutUvSupport);
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+// If a device with transport type kInternal returns a
+// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
+// FidoReturnCode::kUserConsentDenied.
+TEST_F(FidoMakeCredentialHandlerTest,
+ TestRequestWithOperationDeniedErrorPlatform) {
+ auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ test_data::kTestGetInfoResponsePlatformDevice);
+ platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+ platform_device->ExpectCtap2CommandAndRespondWithError(
+ CtapRequestCommand::kAuthenticatorMakeCredential,
+ CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+ set_mock_platform_device(std::move(platform_device));
+
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kPlatform,
+ false /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_TRUE(callback().was_called());
+ EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
+}
+
+// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
+// cross-platform device.
+TEST_F(FidoMakeCredentialHandlerTest,
+ TestRequestWithOperationDeniedErrorCrossPlatform) {
+ auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+ device->ExpectCtap2CommandAndRespondWithError(
+ CtapRequestCommand::kAuthenticatorMakeCredential,
+ CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+ false /* require_resident_key */,
+ UserVerificationRequirement::kPreferred));
+
+ discovery()->WaitForCallToStartAndSimulateSuccess();
discovery()->AddDevice(std::move(device));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
- EXPECT_FALSE(callback().was_called());
+ EXPECT_TRUE(callback().was_called());
+ EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
}
} // namespace device
diff --git a/chromium/device/fido/make_credential_request_handler.cc b/chromium/device/fido/make_credential_request_handler.cc
index 804bb75f5a3..ef72a287562 100644
--- a/chromium/device/fido/make_credential_request_handler.cc
+++ b/chromium/device/fido/make_credential_request_handler.cc
@@ -4,49 +4,20 @@
#include "device/fido/make_credential_request_handler.h"
+#include <set>
#include <utility>
#include "base/bind.h"
+#include "base/stl_util.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/fido_authenticator.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_transport_protocol.h"
#include "device/fido/make_credential_task.h"
#include "services/service_manager/public/cpp/connector.h"
namespace device {
-MakeCredentialRequestHandler::MakeCredentialRequestHandler(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CtapMakeCredentialRequest request,
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
- RegisterResponseCallback completion_callback)
- : MakeCredentialRequestHandler(connector,
- protocols,
- std::move(request),
- authenticator_selection_criteria,
- std::move(completion_callback),
- AddPlatformAuthenticatorCallback()) {}
-
-MakeCredentialRequestHandler::MakeCredentialRequestHandler(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CtapMakeCredentialRequest request,
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
- RegisterResponseCallback completion_callback,
- AddPlatformAuthenticatorCallback add_platform_authenticator)
- : FidoRequestHandler(connector,
- protocols,
- std::move(completion_callback),
- std::move(add_platform_authenticator)),
- request_parameter_(std::move(request)),
- authenticator_selection_criteria_(
- std::move(authenticator_selection_criteria)),
- weak_factory_(this) {
- Start();
-}
-
-MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
-
namespace {
bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
@@ -72,6 +43,8 @@ bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
!options.supports_resident_key()) {
return false;
}
+ request->SetResidentKeySupported(
+ authenticator_selection_criteria.require_resident_key());
const auto& user_verification_requirement =
authenticator_selection_criteria.user_verification_requirement();
@@ -85,8 +58,58 @@ bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
UvAvailability::kSupportedAndConfigured;
}
+base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP(
+ const AuthenticatorSelectionCriteria& authenticator_selection_criteria) {
+ using AttachmentType =
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment;
+ const auto attachment_type =
+ authenticator_selection_criteria.authenticator_attachement();
+ switch (attachment_type) {
+ case AttachmentType::kPlatform:
+ return {FidoTransportProtocol::kInternal};
+ case AttachmentType::kCrossPlatform:
+ // Cloud-assisted BLE is not yet supported for MakeCredential requests.
+ return {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication};
+ case AttachmentType::kAny:
+ // Cloud-assisted BLE is not yet supported for MakeCredential requests.
+ return {FidoTransportProtocol::kInternal,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy};
+ }
+
+ NOTREACHED();
+ return base::flat_set<FidoTransportProtocol>();
+}
+
} // namespace
+MakeCredentialRequestHandler::MakeCredentialRequestHandler(
+ service_manager::Connector* connector,
+ const base::flat_set<FidoTransportProtocol>& supported_transports,
+ CtapMakeCredentialRequest request,
+ AuthenticatorSelectionCriteria authenticator_selection_criteria,
+ RegisterResponseCallback completion_callback)
+ : FidoRequestHandler(
+ connector,
+ base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>(
+ supported_transports,
+ GetTransportsAllowedByRP(authenticator_selection_criteria)),
+ std::move(completion_callback)),
+ request_parameter_(std::move(request)),
+ authenticator_selection_criteria_(
+ std::move(authenticator_selection_criteria)),
+ weak_factory_(this) {
+ transport_availability_info().rp_id = request_parameter_.rp().rp_id();
+ transport_availability_info().request_type =
+ FidoRequestHandlerBase::RequestType::kMakeCredential;
+ Start();
+}
+
+MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
+
void MakeCredentialRequestHandler::DispatchRequest(
FidoAuthenticator* authenticator) {
// The user verification field of the request may be adjusted to the
@@ -99,8 +122,50 @@ void MakeCredentialRequestHandler::DispatchRequest(
authenticator->MakeCredential(
std::move(request_copy),
- base::BindOnce(&MakeCredentialRequestHandler::OnAuthenticatorResponse,
+ base::BindOnce(&MakeCredentialRequestHandler::HandleResponse,
weak_factory_.GetWeakPtr(), authenticator));
}
+void MakeCredentialRequestHandler::HandleResponse(
+ FidoAuthenticator* authenticator,
+ CtapDeviceResponseCode response_code,
+ base::Optional<AuthenticatorMakeCredentialResponse> response) {
+ if (response_code != CtapDeviceResponseCode::kSuccess) {
+ OnAuthenticatorResponse(authenticator, response_code, base::nullopt);
+ return;
+ }
+
+ const auto rp_id_hash =
+ fido_parsing_utils::CreateSHA256Hash(request_parameter_.rp().rp_id());
+
+ if (!response || response->GetRpIdHash() != rp_id_hash) {
+ OnAuthenticatorResponse(
+ authenticator, CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
+ OnAuthenticatorResponse(authenticator, response_code, std::move(response));
+}
+
+void MakeCredentialRequestHandler::SetPlatformAuthenticatorOrMarkUnavailable(
+ base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info) {
+ if (platform_authenticator_info) {
+ // TODO(crbug.com/873710): In the case of a request with
+ // AuthenticatorAttachment::kAny and when there is no embedder-provided
+ // transport selection UI, disable the platform authenticator to avoid the
+ // Touch ID fingerprint prompt competing with external devices.
+ const bool has_transport_selection_ui =
+ observer() && observer()->EmbedderControlsAuthenticatorDispatch(
+ *platform_authenticator_info->authenticator);
+ if (authenticator_selection_criteria_.authenticator_attachement() ==
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny &&
+ !has_transport_selection_ui) {
+ platform_authenticator_info = base::nullopt;
+ }
+ }
+
+ FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
+ std::move(platform_authenticator_info));
+}
+
} // namespace device
diff --git a/chromium/device/fido/make_credential_request_handler.h b/chromium/device/fido/make_credential_request_handler.h
index 6edfdc0f926..a5e799f0f7f 100644
--- a/chromium/device/fido/make_credential_request_handler.h
+++ b/chromium/device/fido/make_credential_request_handler.h
@@ -27,30 +27,35 @@ namespace device {
class FidoAuthenticator;
class AuthenticatorMakeCredentialResponse;
-using RegisterResponseCallback = base::OnceCallback<
- void(FidoReturnCode, base::Optional<AuthenticatorMakeCredentialResponse>)>;
+using RegisterResponseCallback =
+ base::OnceCallback<void(FidoReturnCode,
+ base::Optional<AuthenticatorMakeCredentialResponse>,
+ FidoTransportProtocol)>;
class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler
: public FidoRequestHandler<AuthenticatorMakeCredentialResponse> {
public:
MakeCredentialRequestHandler(
service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
+ const base::flat_set<FidoTransportProtocol>& supported_transports,
CtapMakeCredentialRequest request_parameter,
AuthenticatorSelectionCriteria authenticator_criteria,
RegisterResponseCallback completion_callback);
- MakeCredentialRequestHandler(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CtapMakeCredentialRequest request_parameter,
- AuthenticatorSelectionCriteria authenticator_criteria,
- RegisterResponseCallback completion_callback,
- AddPlatformAuthenticatorCallback add_platform_authenticator);
~MakeCredentialRequestHandler() override;
+ // FidoRequestHandlerBase:
+ void SetPlatformAuthenticatorOrMarkUnavailable(
+ base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info)
+ override;
+
private:
// FidoRequestHandlerBase:
- void DispatchRequest(FidoAuthenticator* authenticator) final;
+ void DispatchRequest(FidoAuthenticator* authenticator) override;
+
+ void HandleResponse(
+ FidoAuthenticator* authenticator,
+ CtapDeviceResponseCode response_code,
+ base::Optional<AuthenticatorMakeCredentialResponse> response);
CtapMakeCredentialRequest request_parameter_;
AuthenticatorSelectionCriteria authenticator_selection_criteria_;
diff --git a/chromium/device/fido/make_credential_task.cc b/chromium/device/fido/make_credential_task.cc
index 0d823468130..8d53f17c92b 100644
--- a/chromium/device/fido/make_credential_task.cc
+++ b/chromium/device/fido/make_credential_task.cc
@@ -9,9 +9,6 @@
#include "base/bind.h"
#include "device/base/features.h"
#include "device/fido/ctap2_device_operation.h"
-#include "device/fido/ctap_empty_authenticator_request.h"
-#include "device/fido/device_response_converter.h"
-#include "device/fido/fido_parsing_utils.h"
#include "device/fido/u2f_command_constructor.h"
#include "device/fido/u2f_register_operation.h"
@@ -61,6 +58,7 @@ void MakeCredentialTask::StartTask() {
IsClientPinOptionCompatible(device(), request_parameter_)) {
MakeCredential();
} else {
+ device()->set_supported_protocol(ProtocolVersion::kU2f);
U2fRegister();
}
}
@@ -68,9 +66,7 @@ void MakeCredentialTask::StartTask() {
void MakeCredentialTask::MakeCredential() {
register_operation_ = std::make_unique<Ctap2DeviceOperation<
CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
- device(), request_parameter_,
- base::BindOnce(&MakeCredentialTask::OnCtapMakeCredentialResponseReceived,
- weak_factory_.GetWeakPtr()),
+ device(), request_parameter_, std::move(callback_),
base::BindOnce(&ReadCTAPMakeCredentialResponse));
register_operation_->Start();
}
@@ -82,33 +78,10 @@ void MakeCredentialTask::U2fRegister() {
return;
}
+ DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
register_operation_ = std::make_unique<U2fRegisterOperation>(
- device(), request_parameter_,
- base::BindOnce(&MakeCredentialTask::OnCtapMakeCredentialResponseReceived,
- weak_factory_.GetWeakPtr()));
+ device(), request_parameter_, std::move(callback_));
register_operation_->Start();
}
-void MakeCredentialTask::OnCtapMakeCredentialResponseReceived(
- CtapDeviceResponseCode return_code,
- base::Optional<AuthenticatorMakeCredentialResponse> response_data) {
- if (return_code != CtapDeviceResponseCode::kSuccess) {
- std::move(callback_).Run(return_code, base::nullopt);
- return;
- }
-
- const auto rp_id_hash =
- fido_parsing_utils::CreateSHA256Hash(request_parameter_.rp().rp_id());
-
- // TODO(martinkr): CheckRpIdHash invocation needs to move into the Request
- // handler. See https://crbug.com/863988.
- if (!response_data || response_data->GetRpIdHash() != rp_id_hash) {
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
- return;
- }
-
- std::move(callback_).Run(return_code, std::move(response_data));
-}
-
} // namespace device
diff --git a/chromium/device/fido/make_credential_task.h b/chromium/device/fido/make_credential_task.h
index fc8ae97d776..61e35f6b4ed 100644
--- a/chromium/device/fido/make_credential_task.h
+++ b/chromium/device/fido/make_credential_task.h
@@ -45,9 +45,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialTask : public FidoTask {
void MakeCredential();
void U2fRegister();
- void OnCtapMakeCredentialResponseReceived(
- CtapDeviceResponseCode return_code,
- base::Optional<AuthenticatorMakeCredentialResponse> response_data);
CtapMakeCredentialRequest request_parameter_;
std::unique_ptr<RegisterOperation> register_operation_;
diff --git a/chromium/device/fido/make_credential_task_unittest.cc b/chromium/device/fido/make_credential_task_unittest.cc
index 7c49f6bdc34..b150e876166 100644
--- a/chromium/device/fido/make_credential_task_unittest.cc
+++ b/chromium/device/fido/make_credential_task_unittest.cc
@@ -107,19 +107,6 @@ TEST_F(FidoMakeCredentialTaskTest, TestRegisterSuccessWithFake) {
make_credential_callback_receiver().value()->raw_credential_id().size());
}
-TEST_F(FidoMakeCredentialTaskTest, MakeCredentialWithIncorrectRpIdHash) {
- auto device = MockFidoDevice::MakeCtap();
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorMakeCredential,
- test_data::kTestMakeCredentialResponseWithIncorrectRpIdHash);
-
- const auto task = CreateMakeCredentialTask(device.get());
- make_credential_callback_receiver().WaitForCallback();
-
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
- make_credential_callback_receiver().status());
-}
-
TEST_F(FidoMakeCredentialTaskTest, FallbackToU2fRegisterSuccess) {
auto device = MockFidoDevice::MakeU2f();
device->ExpectRequestAndRespondWith(
@@ -150,8 +137,7 @@ TEST_F(FidoMakeCredentialTaskTest, TestDefaultU2fRegisterOperationWithoutFlag) {
TEST_F(FidoMakeCredentialTaskTest, DefaultToU2fWhenClientPinSet) {
AuthenticatorGetInfoResponse device_info(
- {ProtocolVersion::kCtap, ProtocolVersion::kU2f},
- fido_parsing_utils::Materialize(kTestDeviceAaguid));
+ {ProtocolVersion::kCtap, ProtocolVersion::kU2f}, kTestDeviceAaguid);
AuthenticatorSupportedOptions options;
options.SetClientPinAvailability(
AuthenticatorSupportedOptions::ClientPinAvailability::
@@ -165,6 +151,7 @@ TEST_F(FidoMakeCredentialTaskTest, DefaultToU2fWhenClientPinSet) {
const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
+ EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
EXPECT_TRUE(make_credential_callback_receiver().value());
@@ -172,8 +159,7 @@ TEST_F(FidoMakeCredentialTaskTest, DefaultToU2fWhenClientPinSet) {
TEST_F(FidoMakeCredentialTaskTest, EnforceClientPinWhenUserVerificationSet) {
AuthenticatorGetInfoResponse device_info(
- {ProtocolVersion::kCtap, ProtocolVersion::kU2f},
- fido_parsing_utils::Materialize(kTestDeviceAaguid));
+ {ProtocolVersion::kCtap, ProtocolVersion::kU2f}, kTestDeviceAaguid);
AuthenticatorSupportedOptions options;
options.SetClientPinAvailability(
AuthenticatorSupportedOptions::ClientPinAvailability::
diff --git a/chromium/device/fido/mock_fido_device.cc b/chromium/device/fido/mock_fido_device.cc
index cf499d5b6cc..27030a57c8f 100644
--- a/chromium/device/fido/mock_fido_device.cc
+++ b/chromium/device/fido/mock_fido_device.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/location.h"
+#include "base/strings/strcat.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/apdu/apdu_response.h"
#include "device/fido/device_response_converter.h"
@@ -38,6 +39,29 @@ std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtap(
std::move(*device_info));
}
+// static
+std::unique_ptr<MockFidoDevice>
+MockFidoDevice::MakeU2fWithGetInfoExpectation() {
+ auto device = std::make_unique<MockFidoDevice>();
+ device->StubGetId();
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+ return device;
+}
+
+// static
+std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtapWithGetInfoExpectation(
+ base::Optional<base::span<const uint8_t>> get_info_response) {
+ auto device = std::make_unique<MockFidoDevice>();
+ device->StubGetId();
+ if (!get_info_response) {
+ get_info_response = test_data::kTestAuthenticatorGetInfoResponse;
+ }
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo, std::move(get_info_response));
+ return device;
+}
+
// Matcher to compare the fist byte of the incoming requests.
MATCHER_P(IsCtap2Command, expected_command, "") {
return !arg.empty() && arg[0] == base::strict_cast<uint8_t>(expected_command);
@@ -64,10 +88,26 @@ void MockFidoDevice::DeviceTransact(std::vector<uint8_t> command,
DeviceTransactPtr(command, cb);
}
+FidoTransportProtocol MockFidoDevice::DeviceTransport() const {
+ return transport_protocol_;
+}
+
+base::WeakPtr<FidoDevice> MockFidoDevice::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
void MockFidoDevice::ExpectWinkedAtLeastOnce() {
EXPECT_CALL(*this, TryWinkRef(::testing::_)).Times(::testing::AtLeast(1));
}
+void MockFidoDevice::StubGetId() {
+ // Use a counter to keep the device ID unique.
+ static size_t i = 0;
+ EXPECT_CALL(*this, GetId())
+ .WillRepeatedly(
+ testing::Return(base::StrCat({"mockdevice", std::to_string(i++)})));
+}
+
void MockFidoDevice::ExpectCtap2CommandAndRespondWith(
CtapRequestCommand command,
base::Optional<base::span<const uint8_t>> response,
@@ -82,6 +122,14 @@ void MockFidoDevice::ExpectCtap2CommandAndRespondWith(
.WillOnce(::testing::WithArg<1>(::testing::Invoke(send_response)));
}
+void MockFidoDevice::ExpectCtap2CommandAndRespondWithError(
+ CtapRequestCommand command,
+ CtapDeviceResponseCode response_code,
+ base::TimeDelta delay) {
+ std::array<uint8_t, 1> data{base::strict_cast<uint8_t>(response_code)};
+ return ExpectCtap2CommandAndRespondWith(std::move(command), data, delay);
+}
+
void MockFidoDevice::ExpectRequestAndRespondWith(
base::span<const uint8_t> request,
base::Optional<base::span<const uint8_t>> response,
@@ -110,8 +158,9 @@ void MockFidoDevice::ExpectRequestAndDoNotRespond(
DeviceTransactPtr(std::move(request_as_vector), ::testing::_));
}
-base::WeakPtr<FidoDevice> MockFidoDevice::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
+void MockFidoDevice::SetDeviceTransport(
+ FidoTransportProtocol transport_protocol) {
+ transport_protocol_ = transport_protocol;
}
} // namespace device
diff --git a/chromium/device/fido/mock_fido_device.h b/chromium/device/fido/mock_fido_device.h
index 0df85aa9b3d..2337341dca9 100644
--- a/chromium/device/fido/mock_fido_device.h
+++ b/chromium/device/fido/mock_fido_device.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <memory>
#include <string>
#include <vector>
@@ -17,15 +18,34 @@
#include "base/time/time.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
+#include "device/fido/fido_transport_protocol.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace device {
-class MockFidoDevice : public FidoDevice {
+class MockFidoDevice : public ::testing::StrictMock<FidoDevice> {
public:
+ // MakeU2f returns a fully initialized U2F device. This represents the state
+ // after |DiscoverSupportedProtocolAndDeviceInfo| has been called by the
+ // FidoDiscovery.
static std::unique_ptr<MockFidoDevice> MakeU2f();
+ // MakeCtap returns a fully initialized CTAP device. This represents the
+ // state after |DiscoverSupportedProtocolAndDeviceInfo| has been called by
+ // the FidoDiscovery.
static std::unique_ptr<MockFidoDevice> MakeCtap(
base::Optional<AuthenticatorGetInfoResponse> device_info = base::nullopt);
+ // MakeU2fWithDeviceInfoExpectation returns a uninitialized U2F device
+ // suitable for injecting into a FidoDiscovery, which will determine its
+ // protocol version by invoking |DiscoverSupportedProtocolAndDeviceInfo|.
+ static std::unique_ptr<MockFidoDevice> MakeU2fWithGetInfoExpectation();
+ // MakeCtapWithDeviceInfoExpectation returns a uninitialized CTAP device
+ // suitable for injecting into a FidoDiscovery, which will determine its
+ // protocol version by invoking |DiscoverSupportedProtocolAndDeviceInfo|. If a
+ // response is supplied, the mock will use that to reply; otherwise it will
+ // use |test_data::kTestAuthenticatorGetInfoResponse|.
+ static std::unique_ptr<MockFidoDevice> MakeCtapWithGetInfoExpectation(
+ base::Optional<base::span<const uint8_t>> get_info_response =
+ base::nullopt);
MockFidoDevice();
MockFidoDevice(ProtocolVersion protocol_version,
@@ -46,21 +66,32 @@ class MockFidoDevice : public FidoDevice {
MOCK_METHOD2(DeviceTransactPtr,
void(const std::vector<uint8_t>& command, DeviceCallback& cb));
void DeviceTransact(std::vector<uint8_t> command, DeviceCallback cb) override;
+
+ // FidoDevice:
+ FidoTransportProtocol DeviceTransport() const override;
+ base::WeakPtr<FidoDevice> GetWeakPtr() override;
+
void ExpectWinkedAtLeastOnce();
void ExpectCtap2CommandAndRespondWith(
CtapRequestCommand command,
base::Optional<base::span<const uint8_t>> response,
base::TimeDelta delay = base::TimeDelta());
+ void ExpectCtap2CommandAndRespondWithError(
+ CtapRequestCommand command,
+ CtapDeviceResponseCode response_code,
+ base::TimeDelta delay = base::TimeDelta());
void ExpectRequestAndRespondWith(
base::span<const uint8_t> request,
base::Optional<base::span<const uint8_t>> response,
base::TimeDelta delay = base::TimeDelta());
void ExpectCtap2CommandAndDoNotRespond(CtapRequestCommand command);
void ExpectRequestAndDoNotRespond(base::span<const uint8_t> request);
-
- base::WeakPtr<FidoDevice> GetWeakPtr() override;
+ void StubGetId();
+ void SetDeviceTransport(FidoTransportProtocol transport_protocol);
private:
+ FidoTransportProtocol transport_protocol_ =
+ FidoTransportProtocol::kUsbHumanInterfaceDevice;
base::WeakPtrFactory<FidoDevice> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MockFidoDevice);
diff --git a/chromium/device/fido/opaque_attestation_statement.cc b/chromium/device/fido/opaque_attestation_statement.cc
index 147bd7db199..69f85b3e6cf 100644
--- a/chromium/device/fido/opaque_attestation_statement.cc
+++ b/chromium/device/fido/opaque_attestation_statement.cc
@@ -8,20 +8,22 @@
#include "components/cbor/cbor_values.h"
+using cbor::CBORValue;
+
namespace device {
OpaqueAttestationStatement::OpaqueAttestationStatement(
std::string attestation_format,
- cbor::CBORValue attestation_statement_map)
+ CBORValue attestation_statement_map)
: AttestationStatement(std::move(attestation_format)),
attestation_statement_map_(std::move(attestation_statement_map)) {}
OpaqueAttestationStatement::~OpaqueAttestationStatement() = default;
// Returns the deep copied cbor map value of |attestation_statement_map_|.
-cbor::CBORValue::MapValue OpaqueAttestationStatement::GetAsCBORMap() const {
+CBORValue::MapValue OpaqueAttestationStatement::GetAsCBORMap() const {
DCHECK(attestation_statement_map_.is_map());
- cbor::CBORValue::MapValue new_map;
+ CBORValue::MapValue new_map;
new_map.reserve(attestation_statement_map_.GetMap().size());
for (const auto& map_it : attestation_statement_map_.GetMap()) {
new_map.try_emplace(new_map.end(), map_it.first.Clone(),
@@ -30,6 +32,16 @@ cbor::CBORValue::MapValue OpaqueAttestationStatement::GetAsCBORMap() const {
return new_map;
}
+bool OpaqueAttestationStatement::IsSelfAttestation() {
+ DCHECK(attestation_statement_map_.is_map());
+ const CBORValue::MapValue& m(attestation_statement_map_.GetMap());
+ const CBORValue alg("alg");
+ const CBORValue sig("sig");
+
+ return format_ == "packed" && m.size() == 2 && m.count(std::move(alg)) == 1 &&
+ m.count(std::move(sig)) == 1;
+}
+
bool OpaqueAttestationStatement::
IsAttestationCertificateInappropriatelyIdentifying() {
return false;
diff --git a/chromium/device/fido/opaque_attestation_statement.h b/chromium/device/fido/opaque_attestation_statement.h
index 89e281c1c66..e0d1d170a9e 100644
--- a/chromium/device/fido/opaque_attestation_statement.h
+++ b/chromium/device/fido/opaque_attestation_statement.h
@@ -24,6 +24,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) OpaqueAttestationStatement
// AttestationStatement:
cbor::CBORValue::MapValue GetAsCBORMap() const override;
+ bool IsSelfAttestation() override;
bool IsAttestationCertificateInappropriatelyIdentifying() override;
private:
diff --git a/chromium/device/fido/public_key_credential_descriptor.cc b/chromium/device/fido/public_key_credential_descriptor.cc
index af48eebcf34..8bce337ce19 100644
--- a/chromium/device/fido/public_key_credential_descriptor.cc
+++ b/chromium/device/fido/public_key_credential_descriptor.cc
@@ -41,7 +41,22 @@ PublicKeyCredentialDescriptor::CreateFromCBORValue(
PublicKeyCredentialDescriptor::PublicKeyCredentialDescriptor(
CredentialType credential_type,
std::vector<uint8_t> id)
- : credential_type_(credential_type), id_(std::move(id)) {}
+ : PublicKeyCredentialDescriptor(
+ credential_type,
+ std::move(id),
+ {FidoTransportProtocol::kUsbHumanInterfaceDevice,
+ FidoTransportProtocol::kBluetoothLowEnergy,
+ FidoTransportProtocol::kNearFieldCommunication,
+ FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
+ FidoTransportProtocol::kInternal}) {}
+
+PublicKeyCredentialDescriptor::PublicKeyCredentialDescriptor(
+ CredentialType credential_type,
+ std::vector<uint8_t> id,
+ base::flat_set<FidoTransportProtocol> transports)
+ : credential_type_(credential_type),
+ id_(std::move(id)),
+ transports_(std::move(transports)) {}
PublicKeyCredentialDescriptor::PublicKeyCredentialDescriptor(
const PublicKeyCredentialDescriptor& other) = default;
diff --git a/chromium/device/fido/public_key_credential_descriptor.h b/chromium/device/fido/public_key_credential_descriptor.h
index 02bc4389a37..626832666d9 100644
--- a/chromium/device/fido/public_key_credential_descriptor.h
+++ b/chromium/device/fido/public_key_credential_descriptor.h
@@ -10,9 +10,11 @@
#include <vector>
#include "base/component_export.h"
+#include "base/containers/flat_set.h"
#include "base/optional.h"
#include "components/cbor/cbor_values.h"
#include "device/fido/fido_constants.h"
+#include "device/fido/fido_transport_protocol.h"
namespace device {
@@ -27,6 +29,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialDescriptor {
PublicKeyCredentialDescriptor(CredentialType credential_type,
std::vector<uint8_t> id);
+ PublicKeyCredentialDescriptor(
+ CredentialType credential_type,
+ std::vector<uint8_t> id,
+ base::flat_set<FidoTransportProtocol> transports);
PublicKeyCredentialDescriptor(const PublicKeyCredentialDescriptor& other);
PublicKeyCredentialDescriptor(PublicKeyCredentialDescriptor&& other);
PublicKeyCredentialDescriptor& operator=(
@@ -39,10 +45,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialDescriptor {
CredentialType credential_type() const { return credential_type_; }
const std::vector<uint8_t>& id() const { return id_; }
+ const base::flat_set<FidoTransportProtocol>& transports() const {
+ return transports_;
+ }
private:
CredentialType credential_type_;
std::vector<uint8_t> id_;
+ base::flat_set<FidoTransportProtocol> transports_;
};
} // namespace device
diff --git a/chromium/device/fido/scoped_virtual_fido_device.cc b/chromium/device/fido/scoped_virtual_fido_device.cc
index 4449bc15249..b0807e34e1b 100644
--- a/chromium/device/fido/scoped_virtual_fido_device.cc
+++ b/chromium/device/fido/scoped_virtual_fido_device.cc
@@ -58,7 +58,7 @@ ScopedVirtualFidoDevice::~ScopedVirtualFidoDevice() = default;
void ScopedVirtualFidoDevice::SetSupportedProtocol(
ProtocolVersion supported_protocol) {
- supported_protocol_ = ProtocolVersion::kCtap;
+ supported_protocol_ = supported_protocol;
}
VirtualFidoDevice::State* ScopedVirtualFidoDevice::mutable_state() {
diff --git a/chromium/device/fido/strings/BUILD.gn b/chromium/device/fido/strings/BUILD.gn
new file mode 100644
index 00000000000..a07303dc777
--- /dev/null
+++ b/chromium/device/fido/strings/BUILD.gn
@@ -0,0 +1,66 @@
+# Copyright 2018 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.
+
+import("//tools/grit/grit_rule.gni")
+
+grit("strings") {
+ source = "../fido_strings.grd"
+ outputs = [
+ "grit/fido_strings.h",
+ "fido_strings_am.pak",
+ "fido_strings_ar.pak",
+ "fido_strings_bg.pak",
+ "fido_strings_bn.pak",
+ "fido_strings_ca.pak",
+ "fido_strings_cs.pak",
+ "fido_strings_da.pak",
+ "fido_strings_de.pak",
+ "fido_strings_el.pak",
+ "fido_strings_en-GB.pak",
+ "fido_strings_en-US.pak",
+ "fido_strings_es.pak",
+ "fido_strings_es-419.pak",
+ "fido_strings_et.pak",
+ "fido_strings_fa.pak",
+ "fido_strings_fake-bidi.pak",
+ "fido_strings_fi.pak",
+ "fido_strings_fil.pak",
+ "fido_strings_fr.pak",
+ "fido_strings_gu.pak",
+ "fido_strings_he.pak",
+ "fido_strings_hi.pak",
+ "fido_strings_hr.pak",
+ "fido_strings_hu.pak",
+ "fido_strings_id.pak",
+ "fido_strings_it.pak",
+ "fido_strings_ja.pak",
+ "fido_strings_kn.pak",
+ "fido_strings_ko.pak",
+ "fido_strings_lt.pak",
+ "fido_strings_lv.pak",
+ "fido_strings_ml.pak",
+ "fido_strings_mr.pak",
+ "fido_strings_ms.pak",
+ "fido_strings_nl.pak",
+ "fido_strings_nb.pak",
+ "fido_strings_pl.pak",
+ "fido_strings_pt-BR.pak",
+ "fido_strings_pt-PT.pak",
+ "fido_strings_ro.pak",
+ "fido_strings_ru.pak",
+ "fido_strings_sk.pak",
+ "fido_strings_sl.pak",
+ "fido_strings_sr.pak",
+ "fido_strings_sv.pak",
+ "fido_strings_sw.pak",
+ "fido_strings_ta.pak",
+ "fido_strings_te.pak",
+ "fido_strings_th.pak",
+ "fido_strings_tr.pak",
+ "fido_strings_uk.pak",
+ "fido_strings_vi.pak",
+ "fido_strings_zh-CN.pak",
+ "fido_strings_zh-TW.pak",
+ ]
+}
diff --git a/chromium/device/fido/strings/fido_strings_am.xtb b/chromium/device/fido/strings/fido_strings_am.xtb
new file mode 100644
index 00000000000..146c2abc3ab
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_am.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="am">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> ላይ ማንነትዎን ያረጋግጡ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ar.xtb b/chromium/device/fido/strings/fido_strings_ar.xtb
new file mode 100644
index 00000000000..28a99e14ef7
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ar.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ar">
+<translation id="6082592655150610743">إثبات هويتك من خلال <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_bg.xtb b/chromium/device/fido/strings/fido_strings_bg.xtb
new file mode 100644
index 00000000000..180f20bbd73
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_bg.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bg">
+<translation id="6082592655150610743">потвърди самоличността ви в(ъв) <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_bn.xtb b/chromium/device/fido/strings/fido_strings_bn.xtb
new file mode 100644
index 00000000000..54f47c3eb7c
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_bn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bn">
+<translation id="6082592655150610743"><ph name="APP_NAME" />-এ আপনার পরিচয় যাচাই করুন</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ca.xtb b/chromium/device/fido/strings/fido_strings_ca.xtb
new file mode 100644
index 00000000000..36c2e57e9d0
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ca.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ca">
+<translation id="6082592655150610743">verificar la teva identitat a <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_cs.xtb b/chromium/device/fido/strings/fido_strings_cs.xtb
new file mode 100644
index 00000000000..929ba6d70b0
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_cs.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="cs">
+<translation id="6082592655150610743">ověřit vaši identitu v aplikaci <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_da.xtb b/chromium/device/fido/strings/fido_strings_da.xtb
new file mode 100644
index 00000000000..5d86e402974
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_da.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="da">
+<translation id="6082592655150610743">bekræfte din identitet i <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_de.xtb b/chromium/device/fido/strings/fido_strings_de.xtb
new file mode 100644
index 00000000000..44b6bb1184e
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_de.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="de">
+<translation id="6082592655150610743">Ihre Identität bei <ph name="APP_NAME" /> bestätigen</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_el.xtb b/chromium/device/fido/strings/fido_strings_el.xtb
new file mode 100644
index 00000000000..0abd0958292
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_el.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="el">
+<translation id="6082592655150610743">επαληθεύσει την ταυτότητά σας στην εφαρμογή <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_en-GB.xtb b/chromium/device/fido/strings/fido_strings_en-GB.xtb
new file mode 100644
index 00000000000..55d0186a581
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_en-GB.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="en-GB">
+<translation id="6082592655150610743">verify your identity on <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_es-419.xtb b/chromium/device/fido/strings/fido_strings_es-419.xtb
new file mode 100644
index 00000000000..0a248696b53
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_es-419.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es-419">
+<translation id="6082592655150610743">verificar tu identidad en <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_es.xtb b/chromium/device/fido/strings/fido_strings_es.xtb
new file mode 100644
index 00000000000..e7e29f7a8a8
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_es.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es">
+<translation id="6082592655150610743">verificar tu identidad en <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_et.xtb b/chromium/device/fido/strings/fido_strings_et.xtb
new file mode 100644
index 00000000000..a0a94bfc34c
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_et.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="et">
+<translation id="6082592655150610743">rakenduses <ph name="APP_NAME" /> teie isikut kinnitada</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_fa.xtb b/chromium/device/fido/strings/fido_strings_fa.xtb
new file mode 100644
index 00000000000..1f8513b8d7f
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_fa.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fa">
+<translation id="6082592655150610743">هویتتان را در <ph name="APP_NAME" /> تأیید کنید</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_fi.xtb b/chromium/device/fido/strings/fido_strings_fi.xtb
new file mode 100644
index 00000000000..e64fe8f3e07
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_fi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fi">
+<translation id="6082592655150610743">vahvista henkilöllisyytesi sovelluksessa <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_fil.xtb b/chromium/device/fido/strings/fido_strings_fil.xtb
new file mode 100644
index 00000000000..dfdecc4771f
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_fil.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fil">
+<translation id="6082592655150610743">i-verify ang iyong pagkakakilanlan sa <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_fr.xtb b/chromium/device/fido/strings/fido_strings_fr.xtb
new file mode 100644
index 00000000000..5ca987ec33f
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_fr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fr">
+<translation id="6082592655150610743">valider votre identité sur <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_gu.xtb b/chromium/device/fido/strings/fido_strings_gu.xtb
new file mode 100644
index 00000000000..0f8a2d03e62
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_gu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="gu">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> પર તમારી ઓળખ ચકાસો</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_hi.xtb b/chromium/device/fido/strings/fido_strings_hi.xtb
new file mode 100644
index 00000000000..680928ecff4
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_hi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hi">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> पर अपनी पहचान की पुष्टि करें</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_hr.xtb b/chromium/device/fido/strings/fido_strings_hr.xtb
new file mode 100644
index 00000000000..a22898223de
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_hr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hr">
+<translation id="6082592655150610743">potvrdi svoj identitet na <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_hu.xtb b/chromium/device/fido/strings/fido_strings_hu.xtb
new file mode 100644
index 00000000000..d4f29501ce5
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_hu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hu">
+<translation id="6082592655150610743">ellenőrizni az Ön személyazonosságát a következőben: <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_id.xtb b/chromium/device/fido/strings/fido_strings_id.xtb
new file mode 100644
index 00000000000..14627d1f62a
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_id.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="id">
+<translation id="6082592655150610743">verifikasi identitas Anda di <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_it.xtb b/chromium/device/fido/strings/fido_strings_it.xtb
new file mode 100644
index 00000000000..73fcd973131
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_it.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="it">
+<translation id="6082592655150610743">verificare la tua identità su <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_iw.xtb b/chromium/device/fido/strings/fido_strings_iw.xtb
new file mode 100644
index 00000000000..d928685c72c
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_iw.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="iw">
+<translation id="6082592655150610743">אימות הזהות שלך ב-<ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ja.xtb b/chromium/device/fido/strings/fido_strings_ja.xtb
new file mode 100644
index 00000000000..fd0ef497826
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ja.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ja">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> での本人確認</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_kn.xtb b/chromium/device/fido/strings/fido_strings_kn.xtb
new file mode 100644
index 00000000000..924ca6580da
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_kn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="kn">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> ನಲ್ಲಿ ನಿಮ್ಮ ಗುರುತನ್ನು ದೃಢೀಕರಿಸಿ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ko.xtb b/chromium/device/fido/strings/fido_strings_ko.xtb
new file mode 100644
index 00000000000..6ed0e4e69b3
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ko.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ko">
+<translation id="6082592655150610743"><ph name="APP_NAME" />에서 본인 확인을 진행</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_lt.xtb b/chromium/device/fido/strings/fido_strings_lt.xtb
new file mode 100644
index 00000000000..1e8ef93bc8a
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_lt.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lt">
+<translation id="6082592655150610743">patvirtinkite savo tapatybę programoje „<ph name="APP_NAME" />“</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_lv.xtb b/chromium/device/fido/strings/fido_strings_lv.xtb
new file mode 100644
index 00000000000..f5237b591c7
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_lv.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lv">
+<translation id="6082592655150610743">verificēt jūsu identitāti vietnē <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ml.xtb b/chromium/device/fido/strings/fido_strings_ml.xtb
new file mode 100644
index 00000000000..7930d88649d
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ml.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ml">
+<translation id="6082592655150610743"><ph name="APP_NAME" />-ൽ നിങ്ങളുടെ ഐഡന്റിറ്റി പരിശോധിച്ച് ഉറപ്പിക്കുക</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_mr.xtb b/chromium/device/fido/strings/fido_strings_mr.xtb
new file mode 100644
index 00000000000..f22441665ff
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_mr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="mr">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> वर तुमच्या ओळखीची पडताळणी करा</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ms.xtb b/chromium/device/fido/strings/fido_strings_ms.xtb
new file mode 100644
index 00000000000..d6acdfd7683
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ms.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ms">
+<translation id="6082592655150610743">sahkan identiti anda pada <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_nl.xtb b/chromium/device/fido/strings/fido_strings_nl.xtb
new file mode 100644
index 00000000000..dda9a33246e
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_nl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="nl">
+<translation id="6082592655150610743">je identiteit te verifiëren op <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_no.xtb b/chromium/device/fido/strings/fido_strings_no.xtb
new file mode 100644
index 00000000000..5df8dfadb0e
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_no.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="no">
+<translation id="6082592655150610743">bekrefte identiteten din i <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_pl.xtb b/chromium/device/fido/strings/fido_strings_pl.xtb
new file mode 100644
index 00000000000..f8f09b79abe
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_pl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pl">
+<translation id="6082592655150610743">zweryfikować Twoją tożsamość na <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_pt-BR.xtb b/chromium/device/fido/strings/fido_strings_pt-BR.xtb
new file mode 100644
index 00000000000..584b5bda0ee
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_pt-BR.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-BR">
+<translation id="6082592655150610743">verificar sua identidade no <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_pt-PT.xtb b/chromium/device/fido/strings/fido_strings_pt-PT.xtb
new file mode 100644
index 00000000000..e5ecb9dba88
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_pt-PT.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-PT">
+<translation id="6082592655150610743">validar a sua identidade em <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ro.xtb b/chromium/device/fido/strings/fido_strings_ro.xtb
new file mode 100644
index 00000000000..e785fd5dee8
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ro.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ro">
+<translation id="6082592655150610743">să îți verifice identitatea în <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ru.xtb b/chromium/device/fido/strings/fido_strings_ru.xtb
new file mode 100644
index 00000000000..81f214b0e4d
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ru.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ru">
+<translation id="6082592655150610743">подтвердить вашу личность в приложении <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_sk.xtb b/chromium/device/fido/strings/fido_strings_sk.xtb
new file mode 100644
index 00000000000..1378163263c
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_sk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sk">
+<translation id="6082592655150610743">overiť vašu totožnosť v aplikácii <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_sl.xtb b/chromium/device/fido/strings/fido_strings_sl.xtb
new file mode 100644
index 00000000000..2736e96002b
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_sl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sl">
+<translation id="6082592655150610743">preveriti vašo identiteto v aplikaciji <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_sr.xtb b/chromium/device/fido/strings/fido_strings_sr.xtb
new file mode 100644
index 00000000000..51090252433
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_sr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sr">
+<translation id="6082592655150610743">верификује ваш идентитет у апликацији <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_sv.xtb b/chromium/device/fido/strings/fido_strings_sv.xtb
new file mode 100644
index 00000000000..66c9752e851
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_sv.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sv">
+<translation id="6082592655150610743">verifiera din identitet på <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_sw.xtb b/chromium/device/fido/strings/fido_strings_sw.xtb
new file mode 100644
index 00000000000..2b0b594ea6c
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_sw.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sw">
+<translation id="6082592655150610743">thibitisha utambulisho wako kwenye <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_ta.xtb b/chromium/device/fido/strings/fido_strings_ta.xtb
new file mode 100644
index 00000000000..8f0cfb40958
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_ta.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ta">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> இல் உங்கள் அடையாளத்தைச் சரிபார்க்கவும்</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_te.xtb b/chromium/device/fido/strings/fido_strings_te.xtb
new file mode 100644
index 00000000000..b3dfd4f8e9b
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_te.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="te">
+<translation id="6082592655150610743"><ph name="APP_NAME" />లో మీ గుర్తింపును ధృవీకరించండి</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_th.xtb b/chromium/device/fido/strings/fido_strings_th.xtb
new file mode 100644
index 00000000000..71200bfb27a
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_th.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="th">
+<translation id="6082592655150610743">ยืนยันตัวตนของคุณใน <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_tr.xtb b/chromium/device/fido/strings/fido_strings_tr.xtb
new file mode 100644
index 00000000000..fb9120f894f
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_tr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="tr">
+<translation id="6082592655150610743"><ph name="APP_NAME" /> uygulamasında kimliğinizi doğrulamak</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_uk.xtb b/chromium/device/fido/strings/fido_strings_uk.xtb
new file mode 100644
index 00000000000..50eee6c8d3f
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_uk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="uk">
+<translation id="6082592655150610743">підтвердити вашу особу в додатку <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_vi.xtb b/chromium/device/fido/strings/fido_strings_vi.xtb
new file mode 100644
index 00000000000..94960f9be46
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_vi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="vi">
+<translation id="6082592655150610743">xác minh danh tính của bạn trên <ph name="APP_NAME" /></translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_zh-CN.xtb b/chromium/device/fido/strings/fido_strings_zh-CN.xtb
new file mode 100644
index 00000000000..94b181300aa
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_zh-CN.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-CN">
+<translation id="6082592655150610743">在“<ph name="APP_NAME" />”上验证您的身份</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/strings/fido_strings_zh-TW.xtb b/chromium/device/fido/strings/fido_strings_zh-TW.xtb
new file mode 100644
index 00000000000..5c9de020e4c
--- /dev/null
+++ b/chromium/device/fido/strings/fido_strings_zh-TW.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-TW">
+<translation id="6082592655150610743">向 <ph name="APP_NAME" /> 驗證你的身分</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/device/fido/test_callback_receiver.h b/chromium/device/fido/test_callback_receiver.h
index 90363783a03..66783570bd1 100644
--- a/chromium/device/fido/test_callback_receiver.h
+++ b/chromium/device/fido/test_callback_receiver.h
@@ -114,6 +114,20 @@ class StatusAndValueCallbackReceiver
}
};
+template <class Status, class... Values>
+class StatusAndValuesCallbackReceiver
+ : public TestCallbackReceiver<Status, Values...> {
+ public:
+ const Status& status() const {
+ return std::get<0>(*TestCallbackReceiver<Status, Values...>::result());
+ }
+
+ template <size_t I>
+ const std::tuple_element_t<I, std::tuple<Values...>>& value() const {
+ return std::get<I + 1>(*TestCallbackReceiver<Status, Values...>::result());
+ }
+};
+
} // namespace test
} // namespace device
diff --git a/chromium/device/fido/virtual_ctap2_device.cc b/chromium/device/fido/virtual_ctap2_device.cc
index 60d4579038c..cdb1c03f48d 100644
--- a/chromium/device/fido/virtual_ctap2_device.cc
+++ b/chromium/device/fido/virtual_ctap2_device.cc
@@ -109,17 +109,19 @@ std::vector<uint8_t> ConstructSignatureBuffer(
}
std::vector<uint8_t> ConstructMakeCredentialResponse(
- base::span<const uint8_t> attestation_certificate,
+ const base::Optional<std::vector<uint8_t>> attestation_certificate,
base::span<const uint8_t> signature,
AuthenticatorData authenticator_data) {
cbor::CBORValue::MapValue attestation_map;
attestation_map.emplace("alg", -7);
attestation_map.emplace("sig", fido_parsing_utils::Materialize(signature));
- cbor::CBORValue::ArrayValue certificate_chain;
- certificate_chain.emplace_back(
- fido_parsing_utils::Materialize(attestation_certificate));
- attestation_map.emplace("x5c", std::move(certificate_chain));
+ if (attestation_certificate) {
+ cbor::CBORValue::ArrayValue certificate_chain;
+ certificate_chain.emplace_back(std::move(*attestation_certificate));
+ attestation_map.emplace("x5c", std::move(certificate_chain));
+ }
+
AuthenticatorMakeCredentialResponse make_credential_response(
AttestationObject(
std::move(authenticator_data),
@@ -253,9 +255,16 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential(
std::vector<uint8_t> key_handle(hash.begin(), hash.end());
std::array<uint8_t, 2> sha256_length = {0, crypto::kSHA256Length};
+ std::array<uint8_t, 16> kZeroAaguid = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ base::span<const uint8_t, 16> aaguid(kDeviceAaguid);
+ if (mutable_state()->self_attestation &&
+ !mutable_state()->non_zero_aaguid_with_self_attestation) {
+ aaguid = kZeroAaguid;
+ }
+
AttestedCredentialData attested_credential_data(
- kDeviceAaguid, sha256_length, key_handle,
- ConstructECPublicKey(public_key));
+ aaguid, sha256_length, key_handle, ConstructECPublicKey(public_key));
auto authenticator_data = ConstructAuthenticatorData(
rp_id_hash, 01ul, std::move(attested_credential_data));
@@ -271,14 +280,17 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential(
status = Sign(attestation_private_key.get(), std::move(sign_buffer), &sig);
DCHECK(status);
- auto attestation_cert = GenerateAttestationCertificate(
- false /* individual_attestation_requested */);
- if (!attestation_cert) {
- DLOG(ERROR) << "Failed to generate attestation certificate.";
- return CtapDeviceResponseCode::kCtap2ErrOther;
+ base::Optional<std::vector<uint8_t>> attestation_cert;
+ if (!mutable_state()->self_attestation) {
+ attestation_cert = GenerateAttestationCertificate(
+ false /* individual_attestation_requested */);
+ if (!attestation_cert) {
+ DLOG(ERROR) << "Failed to generate attestation certificate.";
+ return CtapDeviceResponseCode::kCtap2ErrOther;
+ }
}
- *response = ConstructMakeCredentialResponse(std::move(*attestation_cert), sig,
+ *response = ConstructMakeCredentialResponse(std::move(attestation_cert), sig,
std::move(authenticator_data));
StoreNewKey(rp_id_hash, key_handle, std::move(private_key));
diff --git a/chromium/device/fido/virtual_fido_device.cc b/chromium/device/fido/virtual_fido_device.cc
index 06c5dac3074..6abbdeb45d5 100644
--- a/chromium/device/fido/virtual_fido_device.cc
+++ b/chromium/device/fido/virtual_fido_device.cc
@@ -158,4 +158,9 @@ std::string VirtualFidoDevice::GetId() const {
return "VirtualFidoDevice-" + std::to_string((size_t)this % 0xffe1);
}
+FidoTransportProtocol VirtualFidoDevice::DeviceTransport() const {
+ // Virtual device are injected as HID devices.
+ return FidoTransportProtocol::kUsbHumanInterfaceDevice;
+}
+
} // namespace device
diff --git a/chromium/device/fido/virtual_fido_device.h b/chromium/device/fido/virtual_fido_device.h
index 92ad511ecd3..06a65f7b870 100644
--- a/chromium/device/fido/virtual_fido_device.h
+++ b/chromium/device/fido/virtual_fido_device.h
@@ -80,6 +80,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
// If true, causes the response from the device to be invalid.
bool simulate_invalid_response = false;
+ // If true, return a packed self-attestation rather than a generated
+ // certificate. This only has an effect for a CTAP2 device as
+ // self-attestation is not defined for CTAP1.
+ bool self_attestation = false;
+
+ // Only valid if |self_attestation| is true. Causes the AAGUID to be non-
+ // zero, in violation of the rules for self-attestation.
+ bool non_zero_aaguid_with_self_attestation = false;
+
// Adds a registration for the specified credential ID with the application
// parameter set to be valid for the given relying party ID (which would
// typically be a domain, e.g. "example.com").
@@ -133,6 +142,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
// FidoDevice:
void TryWink(WinkCallback cb) override;
std::string GetId() const override;
+ FidoTransportProtocol DeviceTransport() const override;
private:
scoped_refptr<State> state_ = base::MakeRefCounted<State>();
diff --git a/chromium/device/gamepad/gamepad_consumer.h b/chromium/device/gamepad/gamepad_consumer.h
index f80183d2e55..b91095a5c88 100644
--- a/chromium/device/gamepad/gamepad_consumer.h
+++ b/chromium/device/gamepad/gamepad_consumer.h
@@ -15,9 +15,11 @@ class DEVICE_GAMEPAD_EXPORT GamepadConsumer {
GamepadConsumer();
virtual ~GamepadConsumer();
- virtual void OnGamepadConnected(unsigned index, const Gamepad& gamepad) = 0;
- virtual void OnGamepadDisconnected(unsigned index,
+ virtual void OnGamepadConnected(uint32_t index, const Gamepad& gamepad) = 0;
+ virtual void OnGamepadDisconnected(uint32_t index,
const Gamepad& gamepad) = 0;
+ virtual void OnGamepadButtonOrAxisChanged(uint32_t index,
+ const Gamepad& gamepad) = 0;
};
} // namespace device
diff --git a/chromium/device/gamepad/gamepad_haptics_manager.cc b/chromium/device/gamepad/gamepad_haptics_manager.cc
index 656f35014a1..8171275c221 100644
--- a/chromium/device/gamepad/gamepad_haptics_manager.cc
+++ b/chromium/device/gamepad/gamepad_haptics_manager.cc
@@ -24,7 +24,7 @@ void GamepadHapticsManager::Create(
}
void GamepadHapticsManager::PlayVibrationEffectOnce(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticEffectType type,
mojom::GamepadEffectParametersPtr params,
PlayVibrationEffectOnceCallback callback) {
@@ -33,7 +33,7 @@ void GamepadHapticsManager::PlayVibrationEffectOnce(
}
void GamepadHapticsManager::ResetVibrationActuator(
- int pad_index,
+ uint32_t pad_index,
ResetVibrationActuatorCallback callback) {
GamepadService::GetInstance()->ResetVibrationActuator(pad_index,
std::move(callback));
diff --git a/chromium/device/gamepad/gamepad_haptics_manager.h b/chromium/device/gamepad/gamepad_haptics_manager.h
index 6c31e36d9b2..a95d65691d2 100644
--- a/chromium/device/gamepad/gamepad_haptics_manager.h
+++ b/chromium/device/gamepad/gamepad_haptics_manager.h
@@ -20,11 +20,11 @@ class DEVICE_GAMEPAD_EXPORT GamepadHapticsManager
static void Create(mojom::GamepadHapticsManagerRequest request);
// mojom::GamepadHapticsManager implementation.
- void PlayVibrationEffectOnce(int pad_index,
+ void PlayVibrationEffectOnce(uint32_t pad_index,
mojom::GamepadHapticEffectType,
mojom::GamepadEffectParametersPtr,
PlayVibrationEffectOnceCallback) override;
- void ResetVibrationActuator(int pad_index,
+ void ResetVibrationActuator(uint32_t pad_index,
ResetVibrationActuatorCallback) override;
private:
diff --git a/chromium/device/gamepad/gamepad_monitor.cc b/chromium/device/gamepad/gamepad_monitor.cc
index 27ba975651e..63d3d377c5e 100644
--- a/chromium/device/gamepad/gamepad_monitor.cc
+++ b/chromium/device/gamepad/gamepad_monitor.cc
@@ -27,18 +27,24 @@ void GamepadMonitor::Create(mojom::GamepadMonitorRequest request) {
std::move(request));
}
-void GamepadMonitor::OnGamepadConnected(unsigned index,
+void GamepadMonitor::OnGamepadConnected(uint32_t index,
const Gamepad& gamepad) {
if (gamepad_observer_)
gamepad_observer_->GamepadConnected(index, gamepad);
}
-void GamepadMonitor::OnGamepadDisconnected(unsigned index,
+void GamepadMonitor::OnGamepadDisconnected(uint32_t index,
const Gamepad& gamepad) {
if (gamepad_observer_)
gamepad_observer_->GamepadDisconnected(index, gamepad);
}
+void GamepadMonitor::OnGamepadButtonOrAxisChanged(uint32_t index,
+ const Gamepad& gamepad) {
+ if (gamepad_observer_)
+ gamepad_observer_->GamepadButtonOrAxisChanged(index, gamepad);
+}
+
void GamepadMonitor::GamepadStartPolling(GamepadStartPollingCallback callback) {
DCHECK(!is_started_);
is_started_ = true;
diff --git a/chromium/device/gamepad/gamepad_monitor.h b/chromium/device/gamepad/gamepad_monitor.h
index 7ee56f5b370..8e62bf3a81a 100644
--- a/chromium/device/gamepad/gamepad_monitor.h
+++ b/chromium/device/gamepad/gamepad_monitor.h
@@ -22,8 +22,10 @@ class DEVICE_GAMEPAD_EXPORT GamepadMonitor : public GamepadConsumer,
static void Create(mojom::GamepadMonitorRequest request);
// GamepadConsumer implementation.
- void OnGamepadConnected(unsigned index, const Gamepad& gamepad) override;
- void OnGamepadDisconnected(unsigned index, const Gamepad& gamepad) override;
+ void OnGamepadConnected(uint32_t index, const Gamepad& gamepad) override;
+ void OnGamepadDisconnected(uint32_t index, const Gamepad& gamepad) override;
+ void OnGamepadButtonOrAxisChanged(uint32_t index,
+ const Gamepad& gamepad) override;
// mojom::GamepadMonitor implementation.
void GamepadStartPolling(GamepadStartPollingCallback callback) override;
diff --git a/chromium/device/gamepad/gamepad_pad_state_provider.cc b/chromium/device/gamepad/gamepad_pad_state_provider.cc
index 5c793748ddd..11f1f37e4bd 100644
--- a/chromium/device/gamepad/gamepad_pad_state_provider.cc
+++ b/chromium/device/gamepad/gamepad_pad_state_provider.cc
@@ -20,7 +20,7 @@ const float kMinAxisResetValue = 0.1f;
GamepadPadStateProvider::GamepadPadStateProvider() {
pad_states_.reset(new PadState[Gamepads::kItemsLengthCap]);
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i)
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
ClearPadState(pad_states_.get()[i]);
}
@@ -50,8 +50,8 @@ PadState* GamepadPadStateProvider::GetPadState(GamepadSource source,
return empty_slot;
}
-PadState* GamepadPadStateProvider::GetConnectedPadState(int pad_index) {
- if (pad_index < 0 || pad_index >= (int)Gamepads::kItemsLengthCap)
+PadState* GamepadPadStateProvider::GetConnectedPadState(uint32_t pad_index) {
+ if (pad_index >= Gamepads::kItemsLengthCap)
return nullptr;
PadState& pad_state = pad_states_.get()[pad_index];
diff --git a/chromium/device/gamepad/gamepad_pad_state_provider.h b/chromium/device/gamepad/gamepad_pad_state_provider.h
index 0824b93f4db..8ffbfe42ab1 100644
--- a/chromium/device/gamepad/gamepad_pad_state_provider.h
+++ b/chromium/device/gamepad/gamepad_pad_state_provider.h
@@ -91,7 +91,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadPadStateProvider {
// Gets a PadState object for a connected gamepad by specifying its index in
// the pad_states_ array. Returns NULL if there is no connected gamepad at
// that index.
- PadState* GetConnectedPadState(int pad_index);
+ PadState* GetConnectedPadState(uint32_t pad_index);
protected:
void ClearPadState(PadState& state);
diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc b/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc
index a8d351bf757..f3ad3a00db7 100644
--- a/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc
+++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc
@@ -288,11 +288,7 @@ void GamepadPlatformDataFetcherLinux::EnumerateSubsystemDevices(
}
void GamepadPlatformDataFetcherLinux::ReadDeviceData(size_t index) {
- // Linker does not like CHECK_LT(index, Gamepads::kItemsLengthCap). =/
- if (index >= Gamepads::kItemsLengthCap) {
- CHECK(false);
- return;
- }
+ CHECK_LT(index, Gamepads::kItemsLengthCap);
GamepadDeviceLinux* device = GetDeviceWithJoydevIndex(index);
if (!device)
diff --git a/chromium/device/gamepad/gamepad_provider.cc b/chromium/device/gamepad/gamepad_provider.cc
index ed7fcfa8ef2..fb42a34430a 100644
--- a/chromium/device/gamepad/gamepad_provider.cc
+++ b/chromium/device/gamepad/gamepad_provider.cc
@@ -97,7 +97,7 @@ void GamepadProvider::GetCurrentGamepadData(Gamepads* data) {
}
void GamepadProvider::PlayVibrationEffectOnce(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticEffectType type,
mojom::GamepadEffectParametersPtr params,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) {
@@ -120,7 +120,7 @@ void GamepadProvider::PlayVibrationEffectOnce(
}
void GamepadProvider::ResetVibrationActuator(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) {
PadState* pad_state = GetConnectedPadState(pad_index);
if (!pad_state) {
@@ -284,7 +284,7 @@ void GamepadProvider::DoPoll() {
devices_changed_ = false;
}
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i)
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
pad_states_.get()[i].is_active = false;
// Loop through each registered data fetcher and poll its gamepad data.
@@ -300,7 +300,7 @@ void GamepadProvider::DoPoll() {
// Send out disconnect events using the last polled data before we wipe it out
// in the mapping step.
if (ever_had_user_gesture_) {
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
PadState& state = pad_states_.get()[i];
if (!state.is_newly_active && !state.is_active &&
@@ -319,7 +319,7 @@ void GamepadProvider::DoPoll() {
// Acquire the SeqLock. There is only ever one writer to this data.
// See gamepad_shared_buffer.h.
gamepad_shared_buffer_->WriteBegin();
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
PadState& state = pad_states_.get()[i];
// Must run through the map+sanitize here or CheckForUserGesture may fail.
MapAndSanitizeGamepadData(&state, &buffer->items[i], sanitize_);
@@ -328,7 +328,7 @@ void GamepadProvider::DoPoll() {
}
if (ever_had_user_gesture_) {
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
PadState& state = pad_states_.get()[i];
if (state.is_newly_active && buffer->items[i].connected) {
@@ -348,7 +348,7 @@ void GamepadProvider::DoPoll() {
// CheckForUserGesture call above. If we don't clear |is_newly_active| here,
// we will notify again for the same gamepad on the next polling cycle.
if (did_notify) {
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i)
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
pad_states_.get()[i].is_newly_active = false;
}
@@ -374,7 +374,7 @@ void GamepadProvider::ScheduleDoPoll() {
}
void GamepadProvider::OnGamepadConnectionChange(bool connected,
- int index,
+ uint32_t index,
const Gamepad& pad) {
if (connection_change_client_)
connection_change_client_->OnGamepadConnectionChange(connected, index, pad);
diff --git a/chromium/device/gamepad/gamepad_provider.h b/chromium/device/gamepad/gamepad_provider.h
index 03e31de56c7..35a856ea8e0 100644
--- a/chromium/device/gamepad/gamepad_provider.h
+++ b/chromium/device/gamepad/gamepad_provider.h
@@ -35,7 +35,7 @@ class GamepadDataFetcher;
class DEVICE_GAMEPAD_EXPORT GamepadConnectionChangeClient {
public:
virtual void OnGamepadConnectionChange(bool connected,
- int index,
+ uint32_t index,
const Gamepad& pad) = 0;
};
@@ -59,13 +59,13 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider
void GetCurrentGamepadData(Gamepads* data);
void PlayVibrationEffectOnce(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticEffectType,
mojom::GamepadEffectParametersPtr,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback);
void ResetVibrationActuator(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback);
// Pause and resume the background polling thread. Can be called from any
@@ -106,7 +106,9 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider
void DoPoll();
void ScheduleDoPoll();
- void OnGamepadConnectionChange(bool connected, int index, const Gamepad& pad);
+ void OnGamepadConnectionChange(bool connected,
+ uint32_t index,
+ const Gamepad& pad);
// Checks the gamepad state to see if the user has interacted with it. Returns
// true if any user gesture observers were notified.
diff --git a/chromium/device/gamepad/gamepad_service.cc b/chromium/device/gamepad/gamepad_service.cc
index 5eab297246d..37aa3070cda 100644
--- a/chromium/device/gamepad/gamepad_service.cc
+++ b/chromium/device/gamepad/gamepad_service.cc
@@ -72,7 +72,7 @@ void GamepadService::ConsumerBecameActive(device::GamepadConsumer* consumer) {
const std::vector<bool>& old_connected_state = consumer_state_it->second;
Gamepads gamepads;
provider_->GetCurrentGamepadData(&gamepads);
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
const Gamepad& gamepad = gamepads.items[i];
if (gamepad.connected) {
info.consumer->OnGamepadConnected(i, gamepad);
@@ -109,7 +109,7 @@ void GamepadService::ConsumerBecameInactive(device::GamepadConsumer* consumer) {
Gamepads gamepads;
provider_->GetCurrentGamepadData(&gamepads);
std::vector<bool> connected_state(Gamepads::kItemsLengthCap);
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i)
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
connected_state[i] = gamepads.items[i].connected;
inactive_consumer_state_[consumer] = connected_state;
}
@@ -136,7 +136,7 @@ void GamepadService::Terminate() {
}
void GamepadService::OnGamepadConnectionChange(bool connected,
- int index,
+ uint32_t index,
const Gamepad& pad) {
if (connected) {
main_thread_task_runner_->PostTask(
@@ -149,7 +149,7 @@ void GamepadService::OnGamepadConnectionChange(bool connected,
}
}
-void GamepadService::OnGamepadConnected(int index, const Gamepad& pad) {
+void GamepadService::OnGamepadConnected(uint32_t index, const Gamepad& pad) {
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
for (ConsumerSet::iterator it = consumers_.begin(); it != consumers_.end();
@@ -159,7 +159,7 @@ void GamepadService::OnGamepadConnected(int index, const Gamepad& pad) {
}
}
-void GamepadService::OnGamepadDisconnected(int index, const Gamepad& pad) {
+void GamepadService::OnGamepadDisconnected(uint32_t index, const Gamepad& pad) {
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
for (ConsumerSet::iterator it = consumers_.begin(); it != consumers_.end();
@@ -170,7 +170,7 @@ void GamepadService::OnGamepadDisconnected(int index, const Gamepad& pad) {
}
void GamepadService::PlayVibrationEffectOnce(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticEffectType type,
mojom::GamepadEffectParametersPtr params,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) {
@@ -187,7 +187,7 @@ void GamepadService::PlayVibrationEffectOnce(
}
void GamepadService::ResetVibrationActuator(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) {
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
@@ -220,7 +220,7 @@ void GamepadService::OnUserGesture() {
info.did_observe_user_gesture = true;
Gamepads gamepads;
provider_->GetCurrentGamepadData(&gamepads);
- for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
const Gamepad& pad = gamepads.items[i];
if (pad.connected)
info.consumer->OnGamepadConnected(i, pad);
diff --git a/chromium/device/gamepad/gamepad_service.h b/chromium/device/gamepad/gamepad_service.h
index 74857864674..12723ac4b79 100644
--- a/chromium/device/gamepad/gamepad_service.h
+++ b/chromium/device/gamepad/gamepad_service.h
@@ -77,16 +77,16 @@ class DEVICE_GAMEPAD_EXPORT GamepadService
void Terminate();
// Called on IO thread when a gamepad is connected.
- void OnGamepadConnected(int index, const Gamepad& pad);
+ void OnGamepadConnected(uint32_t index, const Gamepad& pad);
// Called on IO thread when a gamepad is disconnected.
- void OnGamepadDisconnected(int index, const Gamepad& pad);
+ void OnGamepadDisconnected(uint32_t index, const Gamepad& pad);
// Request playback of a haptic effect on the specified gamepad. Once effect
// playback is complete or is preempted by a different effect, the callback
// will be called.
void PlayVibrationEffectOnce(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticEffectType,
mojom::GamepadEffectParametersPtr,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback);
@@ -95,7 +95,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadService
// effects are currently being played, they are preempted and vibration is
// stopped.
void ResetVibrationActuator(
- int pad_index,
+ uint32_t pad_index,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback);
private:
@@ -116,7 +116,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadService
void OnUserGesture();
void OnGamepadConnectionChange(bool connected,
- int index,
+ uint32_t index,
const Gamepad& pad) override;
void SetSanitizationEnabled(bool sanitize);
diff --git a/chromium/device/gamepad/gamepad_service_unittest.cc b/chromium/device/gamepad/gamepad_service_unittest.cc
index b8d9b9d3381..a2a72fc72b4 100644
--- a/chromium/device/gamepad/gamepad_service_unittest.cc
+++ b/chromium/device/gamepad/gamepad_service_unittest.cc
@@ -25,12 +25,14 @@ class ConnectionListener : public device::GamepadConsumer {
public:
ConnectionListener() { ClearCounters(); }
- void OnGamepadConnected(unsigned index, const Gamepad& gamepad) override {
+ void OnGamepadConnected(uint32_t index, const Gamepad& gamepad) override {
connected_counter_++;
}
- void OnGamepadDisconnected(unsigned index, const Gamepad& gamepad) override {
+ void OnGamepadDisconnected(uint32_t index, const Gamepad& gamepad) override {
disconnected_counter_++;
}
+ void OnGamepadButtonOrAxisChanged(uint32_t index,
+ const Gamepad& gamepad) override {}
void ClearCounters() {
connected_counter_ = 0;
diff --git a/chromium/device/gamepad/gamepad_test_helpers.cc b/chromium/device/gamepad/gamepad_test_helpers.cc
index 8edd6068967..f6147ca579b 100644
--- a/chromium/device/gamepad/gamepad_test_helpers.cc
+++ b/chromium/device/gamepad/gamepad_test_helpers.cc
@@ -21,7 +21,7 @@ void MockGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
{
base::AutoLock lock(lock_);
- for (unsigned int i = 0; i < Gamepads::kItemsLengthCap; ++i) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
if (test_data_.items[i].connected) {
PadState* pad = GetPadState(i);
if (pad)
diff --git a/chromium/device/gamepad/gamepad_user_gesture.cc b/chromium/device/gamepad/gamepad_user_gesture.cc
index 6ae7a31421b..6fdf0a52bbf 100644
--- a/chromium/device/gamepad/gamepad_user_gesture.cc
+++ b/chromium/device/gamepad/gamepad_user_gesture.cc
@@ -18,7 +18,7 @@ const float kAxisMoveAmountThreshold = 0.5;
namespace device {
bool GamepadsHaveUserGesture(const Gamepads& gamepads) {
- for (unsigned int i = 0; i < Gamepads::kItemsLengthCap; i++) {
+ for (size_t i = 0; i < Gamepads::kItemsLengthCap; i++) {
const Gamepad& pad = gamepads.items[i];
// If the device is physically connected, then check the buttons and axes
@@ -33,14 +33,13 @@ bool GamepadsHaveUserGesture(const Gamepads& gamepads) {
if (pad.display_id != 0)
return true;
- for (unsigned int button_index = 0; button_index < pad.buttons_length;
+ for (size_t button_index = 0; button_index < pad.buttons_length;
button_index++) {
if (pad.buttons[button_index].pressed)
return true;
}
- for (unsigned int axes_index = 0; axes_index < pad.axes_length;
- axes_index++) {
+ for (size_t axes_index = 0; axes_index < pad.axes_length; axes_index++) {
if (fabs(pad.axes[axes_index]) > kAxisMoveAmountThreshold)
return true;
}
diff --git a/chromium/device/gamepad/public/cpp/BUILD.gn b/chromium/device/gamepad/public/cpp/BUILD.gn
index 26af5f7d3fd..18a0da7a3cb 100644
--- a/chromium/device/gamepad/public/cpp/BUILD.gn
+++ b/chromium/device/gamepad/public/cpp/BUILD.gn
@@ -17,6 +17,7 @@ source_set("shared_with_blink") {
sources = [
"gamepad.cc",
"gamepad.h",
+ "gamepads.cc",
"gamepads.h",
]
# Do not add deps here per the above comment.
diff --git a/chromium/device/gamepad/public/cpp/gamepad.cc b/chromium/device/gamepad/public/cpp/gamepad.cc
index bd5bf4ed77c..1eec089af6c 100644
--- a/chromium/device/gamepad/public/cpp/gamepad.cc
+++ b/chromium/device/gamepad/public/cpp/gamepad.cc
@@ -6,6 +6,11 @@
namespace device {
+const size_t Gamepad::kIdLengthCap;
+const size_t Gamepad::kMappingLengthCap;
+const size_t Gamepad::kAxesLengthCap;
+const size_t Gamepad::kButtonsLengthCap;
+
Gamepad::Gamepad()
: connected(false),
timestamp(0),
diff --git a/chromium/device/gamepad/public/cpp/gamepad_features.cc b/chromium/device/gamepad/public/cpp/gamepad_features.cc
index 75dfa028628..9e74a134c95 100644
--- a/chromium/device/gamepad/public/cpp/gamepad_features.cc
+++ b/chromium/device/gamepad/public/cpp/gamepad_features.cc
@@ -33,13 +33,35 @@ size_t OverrideIntervalIfValid(base::StringPiece param_value,
} // namespace
+// Enables gamepadbuttondown, gamepadbuttonup, gamepadbuttonchange,
+// gamepadaxismove non-standard gamepad events.
+const base::Feature kEnableGamepadButtonAxisEvents{
+ "EnableGamepadButtonAxisEvents", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Overrides the gamepad polling interval.
const base::Feature kGamepadPollingInterval{"GamepadPollingInterval",
base::FEATURE_DISABLED_BY_DEFAULT};
const char kGamepadPollingIntervalParamKey[] = "interval-ms";
+bool AreGamepadButtonAxisEventsEnabled() {
+ // Check if button and axis events are enabled by a field trial.
+ if (base::FeatureList::IsEnabled(kEnableGamepadButtonAxisEvents))
+ return true;
+
+ // Check if button and axis events are enabled by a command-line flag.
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line &&
+ command_line->HasSwitch(switches::kEnableGamepadButtonAxisEvents)) {
+ return true;
+ }
+
+ return false;
+}
+
size_t GetGamepadPollingInterval() {
- size_t polling_interval = kPollingIntervalMillisecondsMax;
+ // Default to the minimum polling interval.
+ size_t polling_interval = kPollingIntervalMillisecondsMin;
// Check if the polling interval is overridden by a field trial.
if (base::FeatureList::IsEnabled(kGamepadPollingInterval)) {
diff --git a/chromium/device/gamepad/public/cpp/gamepad_features.h b/chromium/device/gamepad/public/cpp/gamepad_features.h
index 462c501ec6d..e58cc7f0e30 100644
--- a/chromium/device/gamepad/public/cpp/gamepad_features.h
+++ b/chromium/device/gamepad/public/cpp/gamepad_features.h
@@ -9,9 +9,11 @@
namespace features {
+extern const base::Feature kEnableGamepadButtonAxisEvents;
extern const base::Feature kGamepadPollingInterval;
extern const char kGamepadPollingIntervalParamKey[];
+bool AreGamepadButtonAxisEventsEnabled();
size_t GetGamepadPollingInterval();
} // namespace features
diff --git a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc
index fc951a3bb9b..69fdd9808ee 100644
--- a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc
+++ b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc
@@ -2,15 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <utility>
-
+#include "device/gamepad/public/cpp/gamepad_mojom_traits.h"
#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/time/time.h"
#include "device/gamepad/public/cpp/gamepad.h"
-#include "device/gamepad/public/mojom/gamepad_mojom_traits_test.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "device/gamepad/public/mojom/gamepad.mojom.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
@@ -163,72 +159,51 @@ bool isWebGamepadEqual(const Gamepad& send, const Gamepad& echo) {
}
return true;
}
-
-void ExpectWebGamepad(const Gamepad& send,
- base::OnceClosure closure,
- const Gamepad& echo) {
- EXPECT_EQ(true, isWebGamepadEqual(send, echo));
- std::move(closure).Run();
-}
-
} // namespace
-class GamepadStructTraitsTest : public testing::Test,
- public mojom::GamepadStructTraitsTest {
+class GamepadStructTraitsTest : public testing::Test {
protected:
- GamepadStructTraitsTest() : binding_(this) {}
-
- void PassGamepad(const Gamepad& send, PassGamepadCallback callback) override {
- std::move(callback).Run(send);
- }
-
- mojom::GamepadStructTraitsTestPtr GetGamepadStructTraitsTestProxy() {
- mojom::GamepadStructTraitsTestPtr proxy;
- binding_.Bind(mojo::MakeRequest(&proxy));
- return proxy;
- }
+ GamepadStructTraitsTest() {}
private:
base::MessageLoop message_loop_;
- mojo::Binding<mojom::GamepadStructTraitsTest> binding_;
DISALLOW_COPY_AND_ASSIGN(GamepadStructTraitsTest);
};
TEST_F(GamepadStructTraitsTest, GamepadCommon) {
- Gamepad send = GetWebGamepadInstance(GamepadCommon);
- base::RunLoop loop;
- mojom::GamepadStructTraitsTestPtr proxy = GetGamepadStructTraitsTestProxy();
- proxy->PassGamepad(
- send, base::BindOnce(&ExpectWebGamepad, send, loop.QuitClosure()));
- loop.Run();
+ Gamepad gamepad_in = GetWebGamepadInstance(GamepadCommon);
+ Gamepad gamepad_out;
+
+ ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Gamepad>(
+ &gamepad_in, &gamepad_out));
+ EXPECT_EQ(true, isWebGamepadEqual(gamepad_in, gamepad_out));
}
TEST_F(GamepadStructTraitsTest, GamepadPose_HasOrientation) {
- Gamepad send = GetWebGamepadInstance(GamepadPose_HasOrientation);
- base::RunLoop loop;
- mojom::GamepadStructTraitsTestPtr proxy = GetGamepadStructTraitsTestProxy();
- proxy->PassGamepad(
- send, base::BindOnce(&ExpectWebGamepad, send, loop.QuitClosure()));
- loop.Run();
+ Gamepad gamepad_in = GetWebGamepadInstance(GamepadPose_HasOrientation);
+ Gamepad gamepad_out;
+
+ ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Gamepad>(
+ &gamepad_in, &gamepad_out));
+ EXPECT_EQ(true, isWebGamepadEqual(gamepad_in, gamepad_out));
}
TEST_F(GamepadStructTraitsTest, GamepadPose_HasPosition) {
- Gamepad send = GetWebGamepadInstance(GamepadPose_HasPosition);
- base::RunLoop loop;
- mojom::GamepadStructTraitsTestPtr proxy = GetGamepadStructTraitsTestProxy();
- proxy->PassGamepad(
- send, base::BindOnce(&ExpectWebGamepad, send, loop.QuitClosure()));
- loop.Run();
+ Gamepad gamepad_in = GetWebGamepadInstance(GamepadPose_HasPosition);
+ Gamepad gamepad_out;
+
+ ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Gamepad>(
+ &gamepad_in, &gamepad_out));
+ EXPECT_EQ(true, isWebGamepadEqual(gamepad_in, gamepad_out));
}
TEST_F(GamepadStructTraitsTest, GamepadPose_Null) {
- Gamepad send = GetWebGamepadInstance(GamepadPose_Null);
- base::RunLoop loop;
- mojom::GamepadStructTraitsTestPtr proxy = GetGamepadStructTraitsTestProxy();
- proxy->PassGamepad(
- send, base::BindOnce(&ExpectWebGamepad, send, loop.QuitClosure()));
- loop.Run();
-}
+ Gamepad gamepad_in = GetWebGamepadInstance(GamepadPose_Null);
+ Gamepad gamepad_out;
+ ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Gamepad>(
+ &gamepad_in, &gamepad_out));
+ EXPECT_EQ(true, isWebGamepadEqual(gamepad_in, gamepad_out));
+}
} // namespace device
diff --git a/chromium/device/gamepad/public/cpp/gamepad_switches.cc b/chromium/device/gamepad/public/cpp/gamepad_switches.cc
index 1dd28a0717b..9185c7726b8 100644
--- a/chromium/device/gamepad/public/cpp/gamepad_switches.cc
+++ b/chromium/device/gamepad/public/cpp/gamepad_switches.cc
@@ -6,6 +6,11 @@
namespace switches {
+// Enables gamepadbuttondown, gamepadbuttonup, gamepadbuttonchange,
+// gamepadaxismove non-standard gamepad events.
+const char kEnableGamepadButtonAxisEvents[] =
+ "enable-gamepad-button-axis-events";
+
// Overrides the gamepad polling interval. Decreasing the interval improves
// input latency of buttons and axes but may negatively affect performance due
// to more CPU time spent in the input polling thread.
diff --git a/chromium/device/gamepad/public/cpp/gamepad_switches.h b/chromium/device/gamepad/public/cpp/gamepad_switches.h
index 18460ec70f1..bbbbb18ea07 100644
--- a/chromium/device/gamepad/public/cpp/gamepad_switches.h
+++ b/chromium/device/gamepad/public/cpp/gamepad_switches.h
@@ -9,6 +9,7 @@ namespace switches {
// All switches in alphabetical order. The switches should be documented
// alongside the definition of their values in the .cc file.
+extern const char kEnableGamepadButtonAxisEvents[];
extern const char kGamepadPollingInterval[];
} // namespace switches
diff --git a/chromium/device/gamepad/public/cpp/gamepads.cc b/chromium/device/gamepad/public/cpp/gamepads.cc
new file mode 100644
index 00000000000..82f8c70eb4f
--- /dev/null
+++ b/chromium/device/gamepad/public/cpp/gamepads.cc
@@ -0,0 +1,11 @@
+// Copyright 2018 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/gamepad/public/cpp/gamepads.h"
+
+namespace device {
+
+const size_t Gamepads::kItemsLengthCap;
+
+} // namespace device
diff --git a/chromium/device/gamepad/public/mojom/BUILD.gn b/chromium/device/gamepad/public/mojom/BUILD.gn
index a44ae8db51a..1bd367aed26 100644
--- a/chromium/device/gamepad/public/mojom/BUILD.gn
+++ b/chromium/device/gamepad/public/mojom/BUILD.gn
@@ -16,13 +16,3 @@ mojom_component("mojom") {
output_prefix = "gamepad_mojom"
macro_prefix = "GAMEPAD_MOJOM"
}
-
-mojom("gamepad_mojom_traits_test") {
- sources = [
- "gamepad_mojom_traits_test.mojom",
- ]
-
- public_deps = [
- ":mojom",
- ]
-}
diff --git a/chromium/device/gamepad/public/mojom/gamepad.mojom b/chromium/device/gamepad/public/mojom/gamepad.mojom
index 415395f788c..1d51cdac13d 100644
--- a/chromium/device/gamepad/public/mojom/gamepad.mojom
+++ b/chromium/device/gamepad/public/mojom/gamepad.mojom
@@ -67,8 +67,19 @@ struct Gamepad {
};
interface GamepadObserver {
- GamepadConnected(int32 index, Gamepad gamepad);
- GamepadDisconnected(int32 index, Gamepad gamepad);
+ // Called when a gamepad is connected. |index| is the index of the gamepad in
+ // the gamepad array, and |gamepad| is a reference to the connected gamepad.
+ GamepadConnected(uint32 index, Gamepad gamepad);
+
+ // Called when a gamepad is disconnected. |index| is the former index of the
+ // gamepad in the gamepad array, and |gamepad| is a reference to the
+ // connected gamepad.
+ GamepadDisconnected(uint32 index, Gamepad gamepad);
+
+ // Called when a button or axis is changed on a connected gamepad. |index| is
+ // the index of the gamepad in the gamepad array, and |gamepad| is a reference
+ // to the gamepad.
+ GamepadButtonOrAxisChanged(uint32 index, Gamepad gamepad);
};
// Asks the browser process to start polling, and return a shared memory
@@ -102,9 +113,9 @@ enum GamepadHapticsResult {
};
interface GamepadHapticsManager {
- PlayVibrationEffectOnce(int32 pad_index,
+ PlayVibrationEffectOnce(uint32 pad_index,
GamepadHapticEffectType type,
GamepadEffectParameters params)
=> (GamepadHapticsResult result);
- ResetVibrationActuator(int32 pad_index) => (GamepadHapticsResult result);
+ ResetVibrationActuator(uint32 pad_index) => (GamepadHapticsResult result);
};
diff --git a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_test.mojom b/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_test.mojom
deleted file mode 100644
index 2dcc07855db..00000000000
--- a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_test.mojom
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 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.
-
-module device.mojom;
-
-import "device/gamepad/public/mojom/gamepad.mojom";
-
-interface GamepadStructTraitsTest {
- PassGamepad(Gamepad send) => (Gamepad echo);
-};
diff --git a/chromium/device/serial/BUILD.gn b/chromium/device/serial/BUILD.gn
index 77132bfc86d..0ab6e1acc95 100644
--- a/chromium/device/serial/BUILD.gn
+++ b/chromium/device/serial/BUILD.gn
@@ -20,7 +20,6 @@ if (is_win || is_linux || is_mac) {
":test_support",
"//device:device_unittests",
"//services/device/serial",
- "//tools/battor_agent:battor_agent_lib",
]
output_name = "device_serial"
@@ -78,19 +77,4 @@ if (is_win || is_linux || is_mac) {
]
}
}
-
- static_library("test_support") {
- # TODO(leonhsl): Merge necessary parts of TestSerialIoHandler into
- # battor_connection_impl_unittest.cc to hide serial impl completely.
- visibility = [ "//tools/battor_agent:battor_agent_unittests" ]
-
- sources = [
- "test_serial_io_handler.cc",
- "test_serial_io_handler.h",
- ]
-
- public_deps = [
- ":serial",
- ]
- }
}
diff --git a/chromium/device/serial/serial_io_handler.cc b/chromium/device/serial/serial_io_handler.cc
index de1d8c178d7..e8219537e08 100644
--- a/chromium/device/serial/serial_io_handler.cc
+++ b/chromium/device/serial/serial_io_handler.cc
@@ -11,8 +11,8 @@
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/strings/string_util.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/task_scheduler/task_traits.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
#include "build/build_config.h"
#if defined(OS_CHROMEOS)
diff --git a/chromium/device/serial/test_serial_io_handler.cc b/chromium/device/serial/test_serial_io_handler.cc
deleted file mode 100644
index c04c2a95606..00000000000
--- a/chromium/device/serial/test_serial_io_handler.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2014 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/serial/test_serial_io_handler.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "services/device/public/mojom/serial.mojom.h"
-
-namespace device {
-
-TestSerialIoHandler::TestSerialIoHandler()
- : SerialIoHandler(NULL),
- opened_(false),
- dtr_(false),
- rts_(false),
- flushes_(0) {}
-
-scoped_refptr<SerialIoHandler> TestSerialIoHandler::Create() {
- return scoped_refptr<SerialIoHandler>(new TestSerialIoHandler);
-}
-
-void TestSerialIoHandler::Open(const std::string& port,
- const mojom::SerialConnectionOptions& options,
- OpenCompleteCallback callback) {
- DCHECK(!opened_);
- opened_ = true;
- ConfigurePort(options);
- std::move(callback).Run(true);
-}
-
-void TestSerialIoHandler::ReadImpl() {
- if (!pending_read_buffer())
- return;
- if (buffer_.empty())
- return;
-
- size_t num_bytes =
- std::min(buffer_.size(), static_cast<size_t>(pending_read_buffer_len()));
- memcpy(pending_read_buffer(), buffer_.data(), num_bytes);
- buffer_.erase(buffer_.begin(), buffer_.begin() + num_bytes);
- ReadCompleted(static_cast<uint32_t>(num_bytes),
- mojom::SerialReceiveError::NONE);
-}
-
-void TestSerialIoHandler::CancelReadImpl() {
- ReadCompleted(0, read_cancel_reason());
-}
-
-void TestSerialIoHandler::WriteImpl() {
- if (send_callback_) {
- std::move(send_callback_).Run();
- return;
- }
- buffer_.insert(buffer_.end(), pending_write_buffer(),
- pending_write_buffer() + pending_write_buffer_len());
- WriteCompleted(pending_write_buffer_len(), mojom::SerialSendError::NONE);
- if (pending_read_buffer())
- ReadImpl();
-}
-
-void TestSerialIoHandler::CancelWriteImpl() {
- WriteCompleted(0, write_cancel_reason());
-}
-
-bool TestSerialIoHandler::ConfigurePortImpl() {
- info_.bitrate = options().bitrate;
- info_.data_bits = options().data_bits;
- info_.parity_bit = options().parity_bit;
- info_.stop_bits = options().stop_bits;
- info_.cts_flow_control = options().cts_flow_control;
- return true;
-}
-
-mojom::SerialDeviceControlSignalsPtr TestSerialIoHandler::GetControlSignals()
- const {
- auto signals = mojom::SerialDeviceControlSignals::New();
- *signals = device_control_signals_;
- return signals;
-}
-
-mojom::SerialConnectionInfoPtr TestSerialIoHandler::GetPortInfo() const {
- auto info = mojom::SerialConnectionInfo::New();
- *info = info_;
- return info;
-}
-
-bool TestSerialIoHandler::Flush() const {
- flushes_++;
- return true;
-}
-
-bool TestSerialIoHandler::SetControlSignals(
- const mojom::SerialHostControlSignals& signals) {
- if (signals.has_dtr)
- dtr_ = signals.dtr;
- if (signals.has_rts)
- rts_ = signals.rts;
- return true;
-}
-
-bool TestSerialIoHandler::SetBreak() {
- return true;
-}
-
-bool TestSerialIoHandler::ClearBreak() {
- return true;
-}
-
-void TestSerialIoHandler::ForceReceiveError(
- device::mojom::SerialReceiveError error) {
- ReadCompleted(0, error);
-}
-
-void TestSerialIoHandler::ForceSendError(device::mojom::SerialSendError error) {
- WriteCompleted(0, error);
-}
-
-TestSerialIoHandler::~TestSerialIoHandler() = default;
-
-} // namespace device
diff --git a/chromium/device/serial/test_serial_io_handler.h b/chromium/device/serial/test_serial_io_handler.h
deleted file mode 100644
index d60be3a7db8..00000000000
--- a/chromium/device/serial/test_serial_io_handler.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2014 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_SERIAL_TEST_SERIAL_IO_HANDLER_H_
-#define DEVICE_SERIAL_TEST_SERIAL_IO_HANDLER_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "device/serial/serial_io_handler.h"
-#include "services/device/public/mojom/serial.mojom.h"
-
-namespace device {
-
-class TestSerialIoHandler : public SerialIoHandler {
- public:
- TestSerialIoHandler();
-
- static scoped_refptr<SerialIoHandler> Create();
-
- // SerialIoHandler overrides.
- void Open(const std::string& port,
- const mojom::SerialConnectionOptions& options,
- OpenCompleteCallback callback) override;
- void ReadImpl() override;
- void CancelReadImpl() override;
- void WriteImpl() override;
- void CancelWriteImpl() override;
- bool ConfigurePortImpl() override;
- mojom::SerialDeviceControlSignalsPtr GetControlSignals() const override;
- mojom::SerialConnectionInfoPtr GetPortInfo() const override;
- bool Flush() const override;
- bool SetControlSignals(
- const mojom::SerialHostControlSignals& signals) override;
- bool SetBreak() override;
- bool ClearBreak() override;
- void ForceReceiveError(device::mojom::SerialReceiveError error);
- void ForceSendError(device::mojom::SerialSendError error);
-
- mojom::SerialConnectionInfo* connection_info() { return &info_; }
- mojom::SerialDeviceControlSignals* device_control_signals() {
- return &device_control_signals_;
- }
- bool dtr() { return dtr_; }
- bool rts() { return rts_; }
- int flushes() { return flushes_; }
- // This callback will be called when this IoHandler processes its next write,
- // instead of the normal behavior of echoing the data to reads.
- void set_send_callback(base::OnceClosure callback) {
- send_callback_ = std::move(callback);
- }
-
- protected:
- ~TestSerialIoHandler() override;
-
- private:
- bool opened_;
- mojom::SerialConnectionInfo info_;
- mojom::SerialDeviceControlSignals device_control_signals_;
- bool dtr_;
- bool rts_;
- mutable int flushes_;
- std::vector<uint8_t> buffer_;
- base::OnceClosure send_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(TestSerialIoHandler);
-};
-
-} // namespace device
-
-#endif // DEVICE_SERIAL_TEST_SERIAL_IO_HANDLER_H_
diff --git a/chromium/device/usb/BUILD.gn b/chromium/device/usb/BUILD.gn
index 5b183fcc4c8..3248fe6b4ca 100644
--- a/chromium/device/usb/BUILD.gn
+++ b/chromium/device/usb/BUILD.gn
@@ -81,6 +81,9 @@ static_library("usb") {
if (is_win || is_mac) {
sources += [
+ "scoped_libusb_device_handle.cc",
+ "scoped_libusb_device_handle.h",
+ "scoped_libusb_device_ref.cc",
"scoped_libusb_device_ref.h",
"usb_context.cc",
"usb_context.h",
diff --git a/chromium/device/usb/mojo/BUILD.gn b/chromium/device/usb/mojo/BUILD.gn
index 0a556a864c5..f3f3ee94406 100644
--- a/chromium/device/usb/mojo/BUILD.gn
+++ b/chromium/device/usb/mojo/BUILD.gn
@@ -8,8 +8,6 @@ source_set("mojo") {
"device_impl.h",
"device_manager_impl.cc",
"device_manager_impl.h",
- "permission_provider.cc",
- "permission_provider.h",
"type_converters.cc",
"type_converters.h",
]
diff --git a/chromium/device/usb/mojo/device_impl.cc b/chromium/device/usb/mojo/device_impl.cc
index 4de3ee0a35a..4c53767435e 100644
--- a/chromium/device/usb/mojo/device_impl.cc
+++ b/chromium/device/usb/mojo/device_impl.cc
@@ -103,10 +103,9 @@ void OnIsochronousTransferOut(
// static
void DeviceImpl::Create(scoped_refptr<device::UsbDevice> device,
- base::WeakPtr<PermissionProvider> permission_provider,
- mojom::UsbDeviceRequest request) {
- auto* device_impl =
- new DeviceImpl(std::move(device), std::move(permission_provider));
+ mojom::UsbDeviceRequest request,
+ mojom::UsbDeviceClientPtr client) {
+ auto* device_impl = new DeviceImpl(std::move(device), std::move(client));
device_impl->binding_ = mojo::MakeStrongBinding(base::WrapUnique(device_impl),
std::move(request));
}
@@ -116,10 +115,10 @@ DeviceImpl::~DeviceImpl() {
}
DeviceImpl::DeviceImpl(scoped_refptr<device::UsbDevice> device,
- base::WeakPtr<PermissionProvider> permission_provider)
+ mojom::UsbDeviceClientPtr client)
: device_(std::move(device)),
- permission_provider_(std::move(permission_provider)),
observer_(this),
+ client_(std::move(client)),
weak_factory_(this) {
DCHECK(device_);
observer_.Add(device_.get());
@@ -128,8 +127,8 @@ DeviceImpl::DeviceImpl(scoped_refptr<device::UsbDevice> device,
void DeviceImpl::CloseHandle() {
if (device_handle_) {
device_handle_->Close();
- if (permission_provider_)
- permission_provider_->DecrementConnectionCount();
+ if (client_)
+ client_->OnDeviceClosed();
}
device_handle_ = nullptr;
}
@@ -175,8 +174,9 @@ void DeviceImpl::OnOpen(base::WeakPtr<DeviceImpl> self,
}
self->device_handle_ = std::move(handle);
- if (self->device_handle_ && self->permission_provider_)
- self->permission_provider_->IncrementConnectionCount();
+ if (self->device_handle_ && self->client_)
+ self->client_->OnDeviceOpened();
+
std::move(callback).Run(self->device_handle_
? mojom::UsbOpenDeviceError::OK
: mojom::UsbOpenDeviceError::ACCESS_DENIED);
diff --git a/chromium/device/usb/mojo/device_impl.h b/chromium/device/usb/mojo/device_impl.h
index 4a82e065a58..74062646312 100644
--- a/chromium/device/usb/mojo/device_impl.h
+++ b/chromium/device/usb/mojo/device_impl.h
@@ -13,7 +13,6 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
-#include "device/usb/mojo/permission_provider.h"
#include "device/usb/public/mojom/device.mojom.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h"
@@ -22,22 +21,20 @@
namespace device {
namespace usb {
-class PermissionProvider;
-
// Implementation of the public Device interface. Instances of this class are
// constructed by DeviceManagerImpl and are strongly bound to their MessagePipe
// lifetime.
class DeviceImpl : public mojom::UsbDevice, public device::UsbDevice::Observer {
public:
static void Create(scoped_refptr<device::UsbDevice> device,
- base::WeakPtr<PermissionProvider> permission_provider,
- mojom::UsbDeviceRequest request);
+ mojom::UsbDeviceRequest request,
+ mojom::UsbDeviceClientPtr client);
~DeviceImpl() override;
private:
DeviceImpl(scoped_refptr<device::UsbDevice> device,
- base::WeakPtr<PermissionProvider> permission_provider);
+ mojom::UsbDeviceClientPtr client);
// Closes the device if it's open. This will always set |device_handle_| to
// null.
@@ -99,7 +96,6 @@ class DeviceImpl : public mojom::UsbDevice, public device::UsbDevice::Observer {
void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override;
const scoped_refptr<device::UsbDevice> device_;
- base::WeakPtr<PermissionProvider> permission_provider_;
ScopedObserver<device::UsbDevice, device::UsbDevice::Observer> observer_;
// The device handle. Will be null before the device is opened and after it
@@ -107,6 +103,7 @@ class DeviceImpl : public mojom::UsbDevice, public device::UsbDevice::Observer {
scoped_refptr<UsbDeviceHandle> device_handle_;
mojo::StrongBindingPtr<mojom::UsbDevice> binding_;
+ device::mojom::UsbDeviceClientPtr client_;
base::WeakPtrFactory<DeviceImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceImpl);
diff --git a/chromium/device/usb/mojo/device_impl_unittest.cc b/chromium/device/usb/mojo/device_impl_unittest.cc
index 0c707a06d0a..0dd2eed8ecb 100644
--- a/chromium/device/usb/mojo/device_impl_unittest.cc
+++ b/chromium/device/usb/mojo/device_impl_unittest.cc
@@ -24,7 +24,6 @@
#include "base/stl_util.h"
#include "device/usb/mock_usb_device.h"
#include "device/usb/mock_usb_device_handle.h"
-#include "device/usb/mojo/mock_permission_provider.h"
#include "device/usb/mojo/type_converters.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
@@ -138,6 +137,24 @@ void ExpectTransferStatusAndThen(mojom::UsbTransferStatus expected_status,
continuation.Run();
}
+class MockUsbDeviceClient : public mojom::UsbDeviceClient {
+ public:
+ MockUsbDeviceClient() : binding_(this) {}
+ ~MockUsbDeviceClient() override = default;
+
+ mojom::UsbDeviceClientPtr CreateInterfacePtrAndBind() {
+ mojom::UsbDeviceClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client));
+ return client;
+ }
+
+ MOCK_METHOD0(OnDeviceOpened, void());
+ MOCK_METHOD0(OnDeviceClosed, void());
+
+ private:
+ mojo::Binding<mojom::UsbDeviceClient> binding_;
+};
+
class USBDeviceImplTest : public testing::Test {
public:
USBDeviceImplTest()
@@ -150,7 +167,6 @@ class USBDeviceImplTest : public testing::Test {
void TearDown() override { base::RunLoop().RunUntilIdle(); }
protected:
- MockPermissionProvider& permission_provider() { return permission_provider_; }
MockUsbDevice& mock_device() { return *mock_device_.get(); }
bool is_device_open() const { return is_device_open_; }
MockUsbDeviceHandle& mock_handle() { return *mock_handle_.get(); }
@@ -163,14 +179,15 @@ class USBDeviceImplTest : public testing::Test {
uint16_t product_id,
const std::string& manufacturer,
const std::string& product,
- const std::string& serial) {
+ const std::string& serial,
+ mojom::UsbDeviceClientPtr client) {
mock_device_ =
new MockUsbDevice(vendor_id, product_id, manufacturer, product, serial);
mock_handle_ = new MockUsbDeviceHandle(mock_device_.get());
UsbDevicePtr proxy;
- DeviceImpl::Create(mock_device_, permission_provider_.GetWeakPtr(),
- mojo::MakeRequest(&proxy));
+ DeviceImpl::Create(mock_device_, mojo::MakeRequest(&proxy),
+ std::move(client));
// Set up mock handle calls to respond based on mock device configs
// established by the test.
@@ -202,10 +219,13 @@ class USBDeviceImplTest : public testing::Test {
return proxy;
}
- UsbDevicePtr GetMockDeviceProxy() {
- return GetMockDeviceProxy(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF");
+ UsbDevicePtr GetMockDeviceProxy(mojom::UsbDeviceClientPtr client) {
+ return GetMockDeviceProxy(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF",
+ std::move(client));
}
+ UsbDevicePtr GetMockDeviceProxy() { return GetMockDeviceProxy(nullptr); }
+
void AddMockConfig(const ConfigBuilder& builder) {
const UsbConfigDescriptor& config = builder.config();
DCHECK(!base::ContainsKey(mock_configs_, config.configuration_value));
@@ -419,8 +439,6 @@ class USBDeviceImplTest : public testing::Test {
std::set<uint8_t> claimed_interfaces_;
- MockPermissionProvider permission_provider_;
-
DISALLOW_COPY_AND_ASSIGN(USBDeviceImplTest);
};
@@ -436,12 +454,14 @@ TEST_F(USBDeviceImplTest, Disconnect) {
}
TEST_F(USBDeviceImplTest, Open) {
- UsbDevicePtr device = GetMockDeviceProxy();
+ MockUsbDeviceClient device_client;
+ UsbDevicePtr device =
+ GetMockDeviceProxy(device_client.CreateInterfacePtrAndBind());
EXPECT_FALSE(is_device_open());
EXPECT_CALL(mock_device(), OpenInternal(_));
- EXPECT_CALL(permission_provider(), IncrementConnectionCount());
+ EXPECT_CALL(device_client, OnDeviceOpened());
{
base::RunLoop loop;
@@ -459,7 +479,10 @@ TEST_F(USBDeviceImplTest, Open) {
}
EXPECT_CALL(mock_handle(), Close());
- EXPECT_CALL(permission_provider(), DecrementConnectionCount());
+ EXPECT_CALL(device_client, OnDeviceClosed());
+
+ device.reset();
+ base::RunLoop().RunUntilIdle();
}
TEST_F(USBDeviceImplTest, OpenFailure) {
diff --git a/chromium/device/usb/mojo/device_manager_impl.cc b/chromium/device/usb/mojo/device_manager_impl.cc
index 5f611f2ffdd..8240e533eb6 100644
--- a/chromium/device/usb/mojo/device_manager_impl.cc
+++ b/chromium/device/usb/mojo/device_manager_impl.cc
@@ -15,7 +15,6 @@
#include "base/memory/ptr_util.h"
#include "device/base/device_client.h"
#include "device/usb/mojo/device_impl.h"
-#include "device/usb/mojo/permission_provider.h"
#include "device/usb/mojo/type_converters.h"
#include "device/usb/public/cpp/filter_utils.h"
#include "device/usb/public/mojom/device.mojom.h"
@@ -27,26 +26,19 @@ namespace usb {
// static
void DeviceManagerImpl::Create(
- base::WeakPtr<PermissionProvider> permission_provider,
mojom::UsbDeviceManagerRequest request) {
DCHECK(DeviceClient::Get());
UsbService* service = DeviceClient::Get()->GetUsbService();
if (!service)
return;
- auto* device_manager_impl =
- new DeviceManagerImpl(std::move(permission_provider), service);
+ auto* device_manager_impl = new DeviceManagerImpl(service);
device_manager_impl->binding_ = mojo::MakeStrongBinding(
base::WrapUnique(device_manager_impl), std::move(request));
}
-DeviceManagerImpl::DeviceManagerImpl(
- base::WeakPtr<PermissionProvider> permission_provider,
- UsbService* usb_service)
- : permission_provider_(permission_provider),
- usb_service_(usb_service),
- observer_(this),
- weak_factory_(this) {
+DeviceManagerImpl::DeviceManagerImpl(UsbService* usb_service)
+ : usb_service_(usb_service), observer_(this), weak_factory_(this) {
// This object owns itself and will be destroyed if the message pipe it is
// bound to is closed, the message loop is destructed, or the UsbService is
// shut down.
@@ -63,16 +55,14 @@ void DeviceManagerImpl::GetDevices(mojom::UsbEnumerationOptionsPtr options,
}
void DeviceManagerImpl::GetDevice(const std::string& guid,
- mojom::UsbDeviceRequest device_request) {
+ mojom::UsbDeviceRequest device_request,
+ mojom::UsbDeviceClientPtr device_client) {
scoped_refptr<UsbDevice> device = usb_service_->GetDevice(guid);
if (!device)
return;
- if (permission_provider_ &&
- permission_provider_->HasDevicePermission(device)) {
- DeviceImpl::Create(std::move(device), permission_provider_,
- std::move(device_request));
- }
+ DeviceImpl::Create(std::move(device), std::move(device_request),
+ std::move(device_client));
}
void DeviceManagerImpl::SetClient(mojom::UsbDeviceManagerClientPtr client) {
@@ -90,10 +80,7 @@ void DeviceManagerImpl::OnGetDevices(
std::vector<mojom::UsbDeviceInfoPtr> device_infos;
for (const auto& device : devices) {
if (UsbDeviceFilterMatchesAny(filters, *device)) {
- if (permission_provider_ &&
- permission_provider_->HasDevicePermission(device)) {
- device_infos.push_back(mojom::UsbDeviceInfo::From(*device));
- }
+ device_infos.push_back(mojom::UsbDeviceInfo::From(*device));
}
}
@@ -101,14 +88,12 @@ void DeviceManagerImpl::OnGetDevices(
}
void DeviceManagerImpl::OnDeviceAdded(scoped_refptr<UsbDevice> device) {
- if (client_ && permission_provider_ &&
- permission_provider_->HasDevicePermission(device))
+ if (client_)
client_->OnDeviceAdded(mojom::UsbDeviceInfo::From(*device));
}
void DeviceManagerImpl::OnDeviceRemoved(scoped_refptr<UsbDevice> device) {
- if (client_ && permission_provider_ &&
- permission_provider_->HasDevicePermission(device))
+ if (client_)
client_->OnDeviceRemoved(mojom::UsbDeviceInfo::From(*device));
}
diff --git a/chromium/device/usb/mojo/device_manager_impl.h b/chromium/device/usb/mojo/device_manager_impl.h
index 095419dbb6d..43224dda71d 100644
--- a/chromium/device/usb/mojo/device_manager_impl.h
+++ b/chromium/device/usb/mojo/device_manager_impl.h
@@ -8,6 +8,8 @@
#include <memory>
#include <queue>
#include <set>
+#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/macros.h"
@@ -24,27 +26,24 @@ class UsbDevice;
namespace usb {
-class PermissionProvider;
-
// Implements the public Mojo UsbDeviceManager interface by wrapping the
// UsbService instance.
class DeviceManagerImpl : public mojom::UsbDeviceManager,
public UsbService::Observer {
public:
- static void Create(base::WeakPtr<PermissionProvider> permission_provider,
- mojom::UsbDeviceManagerRequest request);
+ static void Create(mojom::UsbDeviceManagerRequest request);
~DeviceManagerImpl() override;
private:
- DeviceManagerImpl(base::WeakPtr<PermissionProvider> permission_provider,
- UsbService* usb_service);
+ explicit DeviceManagerImpl(UsbService* usb_service);
// DeviceManager implementation:
void GetDevices(mojom::UsbEnumerationOptionsPtr options,
GetDevicesCallback callback) override;
void GetDevice(const std::string& guid,
- mojom::UsbDeviceRequest device_request) override;
+ mojom::UsbDeviceRequest device_request,
+ mojom::UsbDeviceClientPtr device_client) override;
void SetClient(mojom::UsbDeviceManagerClientPtr client) override;
// Callbacks to handle the async responses from the underlying UsbService.
@@ -60,7 +59,6 @@ class DeviceManagerImpl : public mojom::UsbDeviceManager,
void MaybeRunDeviceChangesCallback();
mojo::StrongBindingPtr<mojom::UsbDeviceManager> binding_;
- base::WeakPtr<PermissionProvider> permission_provider_;
UsbService* usb_service_;
ScopedObserver<UsbService, UsbService::Observer> observer_;
diff --git a/chromium/device/usb/mojo/device_manager_impl_unittest.cc b/chromium/device/usb/mojo/device_manager_impl_unittest.cc
index ed3ed79e95e..d81db9d250b 100644
--- a/chromium/device/usb/mojo/device_manager_impl_unittest.cc
+++ b/chromium/device/usb/mojo/device_manager_impl_unittest.cc
@@ -22,7 +22,6 @@
#include "device/usb/mock_usb_device_handle.h"
#include "device/usb/mock_usb_service.h"
#include "device/usb/mojo/device_impl.h"
-#include "device/usb/mojo/mock_permission_provider.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -56,15 +55,13 @@ class USBDeviceManagerImplTest : public testing::Test {
protected:
UsbDeviceManagerPtr ConnectToDeviceManager() {
UsbDeviceManagerPtr device_manager;
- DeviceManagerImpl::Create(permission_provider_.GetWeakPtr(),
- mojo::MakeRequest(&device_manager));
+ DeviceManagerImpl::Create(mojo::MakeRequest(&device_manager));
return device_manager;
}
MockDeviceClient device_client_;
private:
- MockPermissionProvider permission_provider_;
std::unique_ptr<base::MessageLoop> message_loop_;
};
@@ -152,7 +149,8 @@ TEST_F(USBDeviceManagerImplTest, GetDevice) {
{
base::RunLoop loop;
UsbDevicePtr device;
- device_manager->GetDevice(mock_device->guid(), mojo::MakeRequest(&device));
+ device_manager->GetDevice(mock_device->guid(), mojo::MakeRequest(&device),
+ /*device_client=*/nullptr);
// Close is a no-op if the device hasn't been opened but ensures that the
// pipe was successfully connected.
device->Close(loop.QuitClosure());
@@ -160,7 +158,8 @@ TEST_F(USBDeviceManagerImplTest, GetDevice) {
}
UsbDevicePtr bad_device;
- device_manager->GetDevice("not a real guid", mojo::MakeRequest(&bad_device));
+ device_manager->GetDevice("not a real guid", mojo::MakeRequest(&bad_device),
+ /*device_client=*/nullptr);
{
base::RunLoop loop;
diff --git a/chromium/device/usb/mojo/mock_permission_provider.cc b/chromium/device/usb/mojo/mock_permission_provider.cc
deleted file mode 100644
index 690688026a5..00000000000
--- a/chromium/device/usb/mojo/mock_permission_provider.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2015 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/usb/mojo/mock_permission_provider.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "device/usb/public/mojom/device.mojom.h"
-
-using ::testing::Return;
-using ::testing::_;
-
-namespace device {
-namespace usb {
-
-MockPermissionProvider::MockPermissionProvider() : weak_factory_(this) {
- ON_CALL(*this, HasDevicePermission(_)).WillByDefault(Return(true));
-}
-
-MockPermissionProvider::~MockPermissionProvider() = default;
-
-base::WeakPtr<PermissionProvider> MockPermissionProvider::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
-}
-
-} // namespace usb
-} // namespace device
diff --git a/chromium/device/usb/mojo/mock_permission_provider.h b/chromium/device/usb/mojo/mock_permission_provider.h
deleted file mode 100644
index c558323a271..00000000000
--- a/chromium/device/usb/mojo/mock_permission_provider.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2015 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_USB_MOJO_MOCK_PERMISSION_PROVIDER_H_
-#define DEVICE_USB_MOJO_MOCK_PERMISSION_PROVIDER_H_
-
-#include <stdint.h>
-
-#include "base/memory/weak_ptr.h"
-#include "device/usb/mojo/permission_provider.h"
-#include "device/usb/usb_device.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace device {
-namespace usb {
-
-class MockPermissionProvider : public PermissionProvider {
- public:
- MockPermissionProvider();
- ~MockPermissionProvider() override;
-
- base::WeakPtr<PermissionProvider> GetWeakPtr();
- MOCK_CONST_METHOD1(HasDevicePermission,
- bool(scoped_refptr<const UsbDevice> device));
-
- MOCK_METHOD0(IncrementConnectionCount, void());
- MOCK_METHOD0(DecrementConnectionCount, void());
-
- private:
- base::WeakPtrFactory<PermissionProvider> weak_factory_;
-};
-
-} // namespace usb
-} // namespace device
-
-#endif // DEVICE_USB_MOCK_MOJO_PERMISSION_PROVIDER_H_
diff --git a/chromium/device/usb/mojo/permission_provider.cc b/chromium/device/usb/mojo/permission_provider.cc
deleted file mode 100644
index 806993de682..00000000000
--- a/chromium/device/usb/mojo/permission_provider.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2016 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/usb/mojo/permission_provider.h"
-
-namespace device {
-namespace usb {
-
-PermissionProvider::PermissionProvider() = default;
-
-PermissionProvider::~PermissionProvider() = default;
-
-} // namespace usb
-} // namespace device
diff --git a/chromium/device/usb/mojo/permission_provider.h b/chromium/device/usb/mojo/permission_provider.h
deleted file mode 100644
index 5bb6a716670..00000000000
--- a/chromium/device/usb/mojo/permission_provider.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 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_USB_MOJO_PERMISSION_PROVIDER_H_
-#define DEVICE_USB_MOJO_PERMISSION_PROVIDER_H_
-
-#include "base/memory/ref_counted.h"
-
-namespace device {
-
-class UsbDevice;
-
-namespace usb {
-
-// An implementation of this interface must be provided to a DeviceManager in
-// order to implement device permission checks.
-class PermissionProvider {
- public:
- PermissionProvider();
- virtual ~PermissionProvider();
-
- virtual bool HasDevicePermission(
- scoped_refptr<const UsbDevice> device) const = 0;
- virtual void IncrementConnectionCount() = 0;
- virtual void DecrementConnectionCount() = 0;
-};
-
-} // namespace usb
-} // namespace device
-
-#endif // DEVICE_USB_MOJO_PERMISSION_PROVIDER_H_
diff --git a/chromium/device/usb/public/mojom/BUILD.gn b/chromium/device/usb/public/mojom/BUILD.gn
index 1c496ed638a..703ff1e77d6 100644
--- a/chromium/device/usb/public/mojom/BUILD.gn
+++ b/chromium/device/usb/public/mojom/BUILD.gn
@@ -6,7 +6,6 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [
- "chooser_service.mojom",
"device.mojom",
"device_manager.mojom",
]
@@ -19,4 +18,18 @@ mojom("mojom") {
# prepackaged redistributable JS bindings. It is therefore not desirable to
# scramble these messages.
scramble_message_ids = false
+
+ # The blink variant of the usb mojom is depended on by the blink platform
+ # target. All blink variant mojoms use WTF types, which are part of the
+ # blink platform component. In order to avoid a dependency cycle, these
+ # targets must be part of that component.
+ export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
+ export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+ export_header_blink = "third_party/blink/public/platform/web_common.h"
+
+ visibility_blink = [
+ "//components/arc/common:common_blink",
+ "//third_party/blink/public/mojom/usb:usb_blink",
+ "//third_party/blink/renderer/modules/webusb",
+ ]
}
diff --git a/chromium/device/usb/public/mojom/chooser_service.mojom b/chromium/device/usb/public/mojom/chooser_service.mojom
deleted file mode 100644
index 0b08daecbbf..00000000000
--- a/chromium/device/usb/public/mojom/chooser_service.mojom
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015 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.
-
-module device.mojom;
-
-import "device/usb/public/mojom/device.mojom";
-import "device/usb/public/mojom/device_manager.mojom";
-
-interface UsbChooserService {
- // Get permission from user to use the device.
- //
- // |device_filters| filters the available devices and the filtered
- // devices will be listed for user to grant permission.
- //
- // |result| is the device that user grants permission to use.
- GetPermission(array<device.mojom.UsbDeviceFilter> device_filters)
- => (device.mojom.UsbDeviceInfo? result);
-};
diff --git a/chromium/device/usb/public/mojom/device.mojom b/chromium/device/usb/public/mojom/device.mojom
index 4967853a0a0..17b2b498442 100644
--- a/chromium/device/usb/public/mojom/device.mojom
+++ b/chromium/device/usb/public/mojom/device.mojom
@@ -271,3 +271,19 @@ interface UsbDevice {
uint32 timeout)
=> (array<UsbIsochronousPacket> packets);
};
+
+// This client is introduced for keeping connection count for a tab through
+// WebUSBPermissionProvider. It will be implemented by WebUsbServiceImpl and
+// passed to the UsbDevice via UsbDeviceManager::GetDevice method.
+interface UsbDeviceClient {
+ // Called when a device is opened successfully.
+ // This event may be triggered once more than OnDeviceClosed while the device
+ // is in use.
+ OnDeviceOpened();
+
+ // Called when a device is closed successfully.
+ // This event may not be triggered when the device is in use. But eventually,
+ // after the whole lifecycle, it is expected to have the same number of this
+ // call as that of OnDeviceOpened.
+ OnDeviceClosed();
+};
diff --git a/chromium/device/usb/public/mojom/device_manager.mojom b/chromium/device/usb/public/mojom/device_manager.mojom
index 2ef620e6c7e..9c783d498cb 100644
--- a/chromium/device/usb/public/mojom/device_manager.mojom
+++ b/chromium/device/usb/public/mojom/device_manager.mojom
@@ -36,7 +36,8 @@ interface UsbDeviceManager {
GetDevices(UsbEnumerationOptions? options) => (array<UsbDeviceInfo> results);
// Requests a device by guid.
- GetDevice(string guid, UsbDevice& device_request);
+ GetDevice(string guid, UsbDevice& device_request,
+ UsbDeviceClient? device_client);
// Sets the client for this DeviceManager service. The service will notify its
// client of device events such as connection and disconnection.
diff --git a/chromium/device/usb/scoped_libusb_device_handle.cc b/chromium/device/usb/scoped_libusb_device_handle.cc
new file mode 100644
index 00000000000..4223a8136b7
--- /dev/null
+++ b/chromium/device/usb/scoped_libusb_device_handle.cc
@@ -0,0 +1,37 @@
+// Copyright 2018 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/usb/scoped_libusb_device_handle.h"
+
+#include "device/usb/usb_context.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+ScopedLibusbDeviceHandle::ScopedLibusbDeviceHandle(
+ libusb_device_handle* handle,
+ scoped_refptr<UsbContext> context)
+ : handle_(handle), context_(std::move(context)) {}
+
+ScopedLibusbDeviceHandle::ScopedLibusbDeviceHandle(
+ ScopedLibusbDeviceHandle&& other)
+ : handle_(other.handle_), context_(std::move(other.context_)) {
+ other.handle_ = nullptr;
+}
+
+ScopedLibusbDeviceHandle::~ScopedLibusbDeviceHandle() {
+ Reset();
+}
+
+void ScopedLibusbDeviceHandle::Reset() {
+ libusb_close(handle_);
+ handle_ = nullptr;
+ context_.reset();
+}
+
+bool ScopedLibusbDeviceHandle::IsValid() const {
+ return handle_ != nullptr;
+}
+
+} // namespace device
diff --git a/chromium/device/usb/scoped_libusb_device_handle.h b/chromium/device/usb/scoped_libusb_device_handle.h
new file mode 100644
index 00000000000..a4eb378b3d7
--- /dev/null
+++ b/chromium/device/usb/scoped_libusb_device_handle.h
@@ -0,0 +1,41 @@
+// Copyright 2018 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_USB_SCOPED_LIBUSB_DEVICE_HANDLE_H_
+#define DEVICE_USB_SCOPED_LIBUSB_DEVICE_HANDLE_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+
+struct libusb_device_handle;
+
+namespace device {
+
+class UsbContext;
+
+// This class owns a reference to a libusb_device_handle as well as a reference
+// to the libusb_context. The libusb_context must outlive any
+// libusb_device_handle instances created from it.
+class ScopedLibusbDeviceHandle {
+ public:
+ ScopedLibusbDeviceHandle(libusb_device_handle* handle,
+ scoped_refptr<UsbContext> context);
+ ScopedLibusbDeviceHandle(ScopedLibusbDeviceHandle&& other);
+ ~ScopedLibusbDeviceHandle();
+
+ libusb_device_handle* get() const { return handle_; }
+
+ void Reset();
+ bool IsValid() const;
+
+ private:
+ libusb_device_handle* handle_;
+ scoped_refptr<UsbContext> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedLibusbDeviceHandle);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_SCOPED_LIBUSB_DEVICE_HANDLE_H_
diff --git a/chromium/device/usb/scoped_libusb_device_ref.cc b/chromium/device/usb/scoped_libusb_device_ref.cc
new file mode 100644
index 00000000000..b3ac5e8eb08
--- /dev/null
+++ b/chromium/device/usb/scoped_libusb_device_ref.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 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/usb/scoped_libusb_device_ref.h"
+
+#include "device/usb/usb_context.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+ScopedLibusbDeviceRef::ScopedLibusbDeviceRef(libusb_device* device,
+ scoped_refptr<UsbContext> context)
+ : device_(device), context_(std::move(context)) {}
+
+ScopedLibusbDeviceRef::ScopedLibusbDeviceRef(ScopedLibusbDeviceRef&& other)
+ : device_(other.device_), context_(std::move(other.context_)) {
+ other.device_ = nullptr;
+}
+
+ScopedLibusbDeviceRef::~ScopedLibusbDeviceRef() {
+ Reset();
+}
+
+void ScopedLibusbDeviceRef::Reset() {
+ libusb_unref_device(device_);
+ device_ = nullptr;
+ context_.reset();
+}
+
+bool ScopedLibusbDeviceRef::IsValid() const {
+ return device_ != nullptr;
+}
+
+bool operator==(const ScopedLibusbDeviceRef& ref, libusb_device* device) {
+ return ref.get() == device;
+}
+
+} // namespace device
diff --git a/chromium/device/usb/scoped_libusb_device_ref.h b/chromium/device/usb/scoped_libusb_device_ref.h
index 05a21b237b4..73a37310895 100644
--- a/chromium/device/usb/scoped_libusb_device_ref.h
+++ b/chromium/device/usb/scoped_libusb_device_ref.h
@@ -5,19 +5,40 @@
#ifndef DEVICE_USB_SCOPED_LIBUSB_DEVICE_REF_H_
#define DEVICE_USB_SCOPED_LIBUSB_DEVICE_REF_H_
-#include "base/scoped_generic.h"
-#include "third_party/libusb/src/libusb/libusb.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+
+struct libusb_device;
namespace device {
-struct LibusbDeviceRefTraits {
- static libusb_device* InvalidValue() { return nullptr; }
+class UsbContext;
+
+// This class owns a reference to a libusb_device as well as a reference to
+// the libusb_context. The libusb_context must outlive any libusb_device
+// instances created from it.
+class ScopedLibusbDeviceRef {
+ public:
+ ScopedLibusbDeviceRef(libusb_device* device,
+ scoped_refptr<UsbContext> context);
+ ScopedLibusbDeviceRef(ScopedLibusbDeviceRef&& other);
+ ~ScopedLibusbDeviceRef();
+
+ libusb_device* get() const { return device_; }
+
+ scoped_refptr<UsbContext> GetContext() const { return context_; }
+
+ void Reset();
+ bool IsValid() const;
+
+ private:
+ libusb_device* device_;
+ scoped_refptr<UsbContext> context_;
- static void Free(libusb_device* device) { libusb_unref_device(device); }
+ DISALLOW_COPY_AND_ASSIGN(ScopedLibusbDeviceRef);
};
-using ScopedLibusbDeviceRef =
- base::ScopedGeneric<libusb_device*, LibusbDeviceRefTraits>;
+bool operator==(const ScopedLibusbDeviceRef& ref, libusb_device* device);
} // namespace device
diff --git a/chromium/device/usb/usb_device.h b/chromium/device/usb/usb_device.h
index e6427af4212..b9cdc68046f 100644
--- a/chromium/device/usb/usb_device.h
+++ b/chromium/device/usb/usb_device.h
@@ -147,7 +147,7 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
// is freed.
std::list<UsbDeviceHandle*> handles_;
- base::ObserverList<Observer, true> observer_list_;
+ base::ObserverList<Observer, true>::Unchecked observer_list_;
DISALLOW_COPY_AND_ASSIGN(UsbDevice);
};
diff --git a/chromium/device/usb/usb_device_handle_impl.cc b/chromium/device/usb/usb_device_handle_impl.cc
index cce0691d14d..b8c2307a690 100644
--- a/chromium/device/usb/usb_device_handle_impl.cc
+++ b/chromium/device/usb/usb_device_handle_impl.cc
@@ -311,7 +311,7 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer(
libusb_fill_control_setup(buffer->front(), type, request, value, index,
length);
libusb_fill_control_transfer(transfer->platform_transfer_,
- device_handle->handle_, buffer->front(),
+ device_handle->handle(), buffer->front(),
&UsbDeviceHandleImpl::Transfer::PlatformCallback,
transfer.get(), timeout);
@@ -341,7 +341,7 @@ UsbDeviceHandleImpl::Transfer::CreateBulkTransfer(
}
libusb_fill_bulk_transfer(
- transfer->platform_transfer_, device_handle->handle_, endpoint,
+ transfer->platform_transfer_, device_handle->handle(), endpoint,
buffer->front(), length, &UsbDeviceHandleImpl::Transfer::PlatformCallback,
transfer.get(), timeout);
@@ -371,7 +371,7 @@ UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer(
}
libusb_fill_interrupt_transfer(
- transfer->platform_transfer_, device_handle->handle_, endpoint,
+ transfer->platform_transfer_, device_handle->handle(), endpoint,
buffer->front(), length, &UsbDeviceHandleImpl::Transfer::PlatformCallback,
transfer.get(), timeout);
@@ -401,10 +401,10 @@ UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer(
return nullptr;
}
- libusb_fill_iso_transfer(transfer->platform_transfer_, device_handle->handle_,
- endpoint, buffer->front(), static_cast<int>(length),
- num_packets, &Transfer::PlatformCallback,
- transfer.get(), timeout);
+ libusb_fill_iso_transfer(
+ transfer->platform_transfer_, device_handle->handle(), endpoint,
+ buffer->front(), static_cast<int>(length), num_packets,
+ &Transfer::PlatformCallback, transfer.get(), timeout);
for (size_t i = 0; i < packet_lengths.size(); ++i)
transfer->platform_transfer_->iso_packet_desc[i].length = packet_lengths[i];
@@ -798,16 +798,14 @@ const UsbInterfaceDescriptor* UsbDeviceHandleImpl::FindInterfaceByEndpoint(
}
UsbDeviceHandleImpl::UsbDeviceHandleImpl(
- scoped_refptr<UsbContext> context,
scoped_refptr<UsbDeviceImpl> device,
- PlatformUsbDeviceHandle handle,
+ ScopedLibusbDeviceHandle handle,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
- : device_(device),
- handle_(handle),
- context_(context),
+ : device_(std::move(device)),
+ handle_(std::move(handle)),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
blocking_task_runner_(blocking_task_runner) {
- DCHECK(handle) << "Cannot create device with NULL handle.";
+ DCHECK(handle_.IsValid()) << "Cannot create device with an invalid handle.";
}
UsbDeviceHandleImpl::~UsbDeviceHandleImpl() {
@@ -817,17 +815,19 @@ UsbDeviceHandleImpl::~UsbDeviceHandleImpl() {
// any thread. libusb is not safe to reentrancy so be sure not to try to close
// the device from inside a transfer completion callback.
if (blocking_task_runner_->RunsTasksInCurrentSequence()) {
- libusb_close(handle_);
+ handle_.Reset();
} else {
- blocking_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&libusb_close, handle_));
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(base::DoNothing::Once<ScopedLibusbDeviceHandle>(),
+ std::move(handle_)));
}
}
void UsbDeviceHandleImpl::SetConfigurationOnBlockingThread(
int configuration_value,
ResultCallback callback) {
- int rv = libusb_set_configuration(handle_, configuration_value);
+ int rv = libusb_set_configuration(handle(), configuration_value);
if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to set configuration " << configuration_value
<< ": " << ConvertPlatformUsbErrorToString(rv);
@@ -855,7 +855,7 @@ void UsbDeviceHandleImpl::SetConfigurationComplete(bool success,
void UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread(
int interface_number,
ResultCallback callback) {
- int rv = libusb_claim_interface(handle_, interface_number);
+ int rv = libusb_claim_interface(handle(), interface_number);
scoped_refptr<InterfaceClaimer> interface_claimer;
if (rv == LIBUSB_SUCCESS) {
interface_claimer =
@@ -897,7 +897,7 @@ void UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread(
int interface_number,
int alternate_setting,
ResultCallback callback) {
- int rv = libusb_set_interface_alt_setting(handle_, interface_number,
+ int rv = libusb_set_interface_alt_setting(handle(), interface_number,
alternate_setting);
if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to set interface " << interface_number
@@ -930,7 +930,7 @@ void UsbDeviceHandleImpl::SetInterfaceAlternateSettingComplete(
}
void UsbDeviceHandleImpl::ResetDeviceOnBlockingThread(ResultCallback callback) {
- int rv = libusb_reset_device(handle_);
+ int rv = libusb_reset_device(handle());
if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to reset device: "
<< ConvertPlatformUsbErrorToString(rv);
@@ -941,7 +941,7 @@ void UsbDeviceHandleImpl::ResetDeviceOnBlockingThread(ResultCallback callback) {
void UsbDeviceHandleImpl::ClearHaltOnBlockingThread(uint8_t endpoint,
ResultCallback callback) {
- int rv = libusb_clear_halt(handle_, endpoint);
+ int rv = libusb_clear_halt(handle(), endpoint);
if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to clear halt: "
<< ConvertPlatformUsbErrorToString(rv);
diff --git a/chromium/device/usb/usb_device_handle_impl.h b/chromium/device/usb/usb_device_handle_impl.h
index acf0332d11e..32dbe04a375 100644
--- a/chromium/device/usb/usb_device_handle_impl.h
+++ b/chromium/device/usb/usb_device_handle_impl.h
@@ -17,6 +17,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
+#include "device/usb/scoped_libusb_device_handle.h"
#include "device/usb/usb_device_handle.h"
#include "third_party/libusb/src/libusb/libusb.h"
@@ -34,10 +35,8 @@ struct EndpointMapValue {
const UsbEndpointDescriptor* endpoint;
};
-class UsbContext;
class UsbDeviceImpl;
-typedef libusb_device_handle* PlatformUsbDeviceHandle;
typedef libusb_iso_packet_descriptor* PlatformUsbIsoPacketDescriptor;
typedef libusb_transfer* PlatformUsbTransferHandle;
@@ -90,14 +89,13 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
// This constructor is called by UsbDeviceImpl.
UsbDeviceHandleImpl(
- scoped_refptr<UsbContext> context,
scoped_refptr<UsbDeviceImpl> device,
- PlatformUsbDeviceHandle handle,
+ ScopedLibusbDeviceHandle handle,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
~UsbDeviceHandleImpl() override;
- PlatformUsbDeviceHandle handle() const { return handle_; }
+ libusb_device_handle* handle() const { return handle_.get(); }
private:
class InterfaceClaimer;
@@ -174,7 +172,7 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
scoped_refptr<UsbDeviceImpl> device_;
- PlatformUsbDeviceHandle handle_;
+ ScopedLibusbDeviceHandle handle_;
typedef std::map<int, scoped_refptr<InterfaceClaimer>> ClaimedInterfaceMap;
ClaimedInterfaceMap claimed_interfaces_;
@@ -186,10 +184,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
typedef std::map<int, EndpointMapValue> EndpointMap;
EndpointMap endpoint_map_;
- // Retain the UsbContext so that the platform context will not be destroyed
- // before this handle.
- scoped_refptr<UsbContext> context_;
-
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
diff --git a/chromium/device/usb/usb_device_impl.cc b/chromium/device/usb/usb_device_impl.cc
index 056c6c34f64..ab734eabe15 100644
--- a/chromium/device/usb/usb_device_impl.cc
+++ b/chromium/device/usb/usb_device_impl.cc
@@ -28,8 +28,7 @@
namespace device {
-UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context,
- ScopedLibusbDeviceRef platform_device,
+UsbDeviceImpl::UsbDeviceImpl(ScopedLibusbDeviceRef platform_device,
const libusb_device_descriptor& descriptor)
: UsbDevice(descriptor.bcdUSB,
descriptor.bDeviceClass,
@@ -41,9 +40,8 @@ UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context,
base::string16(),
base::string16(),
base::string16()),
- context_(std::move(context)),
platform_device_(std::move(platform_device)) {
- CHECK(platform_device_.is_valid()) << "platform_device must be valid";
+ CHECK(platform_device_.IsValid()) << "platform_device must be valid";
ReadAllConfigurations();
RefreshActiveConfiguration();
}
@@ -104,12 +102,15 @@ void UsbDeviceImpl::OpenOnBlockingThread(
scoped_refptr<base::TaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
base::AssertBlockingAllowed();
- PlatformUsbDeviceHandle handle;
+ libusb_device_handle* handle = nullptr;
const int rv = libusb_open(platform_device(), &handle);
if (LIBUSB_SUCCESS == rv) {
+ ScopedLibusbDeviceHandle scoped_handle(handle,
+ platform_device_.GetContext());
task_runner->PostTask(
- FROM_HERE, base::BindOnce(&UsbDeviceImpl::Opened, this, handle,
- std::move(callback), blocking_task_runner));
+ FROM_HERE,
+ base::BindOnce(&UsbDeviceImpl::Opened, this, std::move(scoped_handle),
+ std::move(callback), blocking_task_runner));
} else {
USB_LOG(EVENT) << "Failed to open device: "
<< ConvertPlatformUsbErrorToString(rv);
@@ -119,12 +120,12 @@ void UsbDeviceImpl::OpenOnBlockingThread(
}
void UsbDeviceImpl::Opened(
- PlatformUsbDeviceHandle platform_handle,
+ ScopedLibusbDeviceHandle platform_handle,
OpenCallback callback,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
DCHECK(thread_checker_.CalledOnValidThread());
scoped_refptr<UsbDeviceHandle> device_handle = new UsbDeviceHandleImpl(
- context_, this, platform_handle, blocking_task_runner);
+ this, std::move(platform_handle), blocking_task_runner);
handles().push_back(device_handle.get());
std::move(callback).Run(device_handle);
}
diff --git a/chromium/device/usb/usb_device_impl.h b/chromium/device/usb/usb_device_impl.h
index 16ab891600e..9e3d2c7db91 100644
--- a/chromium/device/usb/usb_device_impl.h
+++ b/chromium/device/usb/usb_device_impl.h
@@ -21,9 +21,7 @@
#include "device/usb/usb_descriptors.h"
#include "device/usb/usb_device.h"
-struct libusb_device;
struct libusb_device_descriptor;
-struct libusb_device_handle;
namespace base {
class SequencedTaskRunner;
@@ -31,15 +29,12 @@ class SequencedTaskRunner;
namespace device {
+class ScopedLibusbDeviceHandle;
class UsbDeviceHandleImpl;
-class UsbContext;
-
-typedef struct libusb_device_handle* PlatformUsbDeviceHandle;
class UsbDeviceImpl : public UsbDevice {
public:
- UsbDeviceImpl(scoped_refptr<UsbContext> context,
- ScopedLibusbDeviceRef platform_device,
+ UsbDeviceImpl(ScopedLibusbDeviceRef platform_device,
const libusb_device_descriptor& descriptor);
// UsbDevice implementation:
@@ -79,15 +74,13 @@ class UsbDeviceImpl : public UsbDevice {
OpenCallback callback,
scoped_refptr<base::TaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
- void Opened(PlatformUsbDeviceHandle platform_handle,
+ void Opened(ScopedLibusbDeviceHandle platform_handle,
OpenCallback callback,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
base::ThreadChecker thread_checker_;
bool visited_ = false;
- // The libusb_context must not be released before the libusb_device.
- const scoped_refptr<UsbContext> context_;
const ScopedLibusbDeviceRef platform_device_;
DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl);
diff --git a/chromium/device/usb/usb_service.cc b/chromium/device/usb/usb_service.cc
index 457b0e1145a..bfea0c0bd73 100644
--- a/chromium/device/usb/usb_service.cc
+++ b/chromium/device/usb/usb_service.cc
@@ -8,7 +8,7 @@
#include "base/feature_list.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
-#include "base/task_scheduler/post_task.h"
+#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/device_event_log/device_event_log.h"
diff --git a/chromium/device/usb/usb_service.h b/chromium/device/usb/usb_service.h
index c4728cc768a..8e613436b59 100644
--- a/chromium/device/usb/usb_service.h
+++ b/chromium/device/usb/usb_service.h
@@ -19,7 +19,7 @@
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
-#include "base/task_scheduler/task_traits.h"
+#include "base/task/task_traits.h"
namespace device {
@@ -103,7 +103,7 @@ class UsbService {
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
std::unordered_map<std::string, scoped_refptr<UsbDevice>> devices_;
std::unordered_set<std::string> testing_devices_;
- base::ObserverList<Observer, true> observer_list_;
+ base::ObserverList<Observer, true>::Unchecked observer_list_;
DISALLOW_COPY_AND_ASSIGN(UsbService);
};
diff --git a/chromium/device/usb/usb_service_impl.cc b/chromium/device/usb/usb_service_impl.cc
index 83e14c6f0c1..6b7a8036a35 100644
--- a/chromium/device/usb/usb_service_impl.cc
+++ b/chromium/device/usb/usb_service_impl.cc
@@ -21,7 +21,7 @@
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/task_scheduler/post_task.h"
+#include "base/task/post_task.h"
#include "build/build_config.h"
#include "components/device_event_log/device_event_log.h"
#include "device/usb/usb_device_handle.h"
@@ -131,7 +131,7 @@ void GetDeviceListOnBlockingThread(
std::vector<ScopedLibusbDeviceRef> scoped_devices;
scoped_devices.reserve(device_count);
for (ssize_t i = 0; i < device_count; ++i)
- scoped_devices.emplace_back(platform_devices[i]);
+ scoped_devices.emplace_back(platform_devices[i], usb_context);
// Free the list but don't unref the devices because ownership has been
// been transfered to the elements of |scoped_devices|.
@@ -231,6 +231,7 @@ UsbServiceImpl::UsbServiceImpl()
device_observer_(this),
#endif
weak_factory_(this) {
+ weak_self_ = weak_factory_.GetWeakPtr();
base::PostTaskWithTraits(
FROM_HERE, kBlockingTaskTraits,
base::Bind(&InitializeUsbContextOnBlockingThread, task_runner(),
@@ -361,7 +362,7 @@ void UsbServiceImpl::OnDeviceList(
// Mark the existing device object visited and remove it from the list so
// it will not be ignored.
it->second->set_visited(true);
- device.reset();
+ device.Reset();
}
}
@@ -382,7 +383,7 @@ void UsbServiceImpl::OnDeviceList(
// that have been removed don't remain in |ignored_devices_| indefinitely.
ignored_devices_.clear();
for (auto& device : *devices) {
- if (device.is_valid())
+ if (device.IsValid())
ignored_devices_.push_back(std::move(device));
}
@@ -441,8 +442,8 @@ void UsbServiceImpl::EnumerateDevice(ScopedLibusbDeviceRef platform_device,
devices_being_enumerated_.insert(platform_device.get());
- auto device = base::MakeRefCounted<UsbDeviceImpl>(
- context_, std::move(platform_device), descriptor);
+ auto device = base::MakeRefCounted<UsbDeviceImpl>(std::move(platform_device),
+ descriptor);
base::OnceClosure add_device =
base::BindOnce(&UsbServiceImpl::AddDevice, weak_factory_.GetWeakPtr(),
refresh_complete, device);
@@ -458,7 +459,8 @@ void UsbServiceImpl::EnumerateDevice(ScopedLibusbDeviceRef platform_device,
libusb_ref_device(device->platform_device());
base::OnceClosure enumeration_failed = base::BindOnce(
&UsbServiceImpl::EnumerationFailed, weak_factory_.GetWeakPtr(),
- ScopedLibusbDeviceRef(device->platform_device()), refresh_complete);
+ ScopedLibusbDeviceRef(device->platform_device(), context_),
+ refresh_complete);
device->Open(base::BindOnce(
&OnDeviceOpenedReadDescriptors, descriptor.iManufacturer,
@@ -518,18 +520,18 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
// libusb does not transfer ownership of |device_raw| to this function so a
// reference must be taken here.
libusb_ref_device(device_raw);
- ScopedLibusbDeviceRef device(device_raw);
+ ScopedLibusbDeviceRef device(device_raw, self->context_);
switch (event) {
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
self->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&UsbServiceImpl::OnPlatformDeviceAdded,
- base::Unretained(self), std::move(device)));
+ self->weak_self_, std::move(device)));
break;
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
self->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&UsbServiceImpl::OnPlatformDeviceRemoved,
- base::Unretained(self), std::move(device)));
+ self->weak_self_, std::move(device)));
break;
default:
NOTREACHED();
diff --git a/chromium/device/usb/usb_service_impl.h b/chromium/device/usb/usb_service_impl.h
index e7e89d0f545..36f71154f9c 100644
--- a/chromium/device/usb/usb_service_impl.h
+++ b/chromium/device/usb/usb_service_impl.h
@@ -122,6 +122,10 @@ class UsbServiceImpl :
ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
#endif // OS_WIN
+ // This WeakPtr is used to safely post hotplug events back to the thread this
+ // object lives on.
+ base::WeakPtr<UsbServiceImpl> weak_self_;
+
base::WeakPtrFactory<UsbServiceImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl);
diff --git a/chromium/device/vr/BUILD.gn b/chromium/device/vr/BUILD.gn
index f0a7cb1fe1f..1f2e6a4b585 100644
--- a/chromium/device/vr/BUILD.gn
+++ b/chromium/device/vr/BUILD.gn
@@ -96,8 +96,8 @@ if (enable_vr) {
"openvr/openvr_device.h",
"openvr/openvr_device_provider.cc",
"openvr/openvr_device_provider.h",
- "openvr/openvr_gamepad_data_fetcher.cc",
- "openvr/openvr_gamepad_data_fetcher.h",
+ "openvr/openvr_gamepad_helper.cc",
+ "openvr/openvr_gamepad_helper.h",
"openvr/openvr_render_loop.cc",
"openvr/openvr_render_loop.h",
"openvr/openvr_type_converters.cc",
@@ -113,6 +113,8 @@ if (enable_vr) {
]
sources += [
+ "isolated_gamepad_data_fetcher.cc",
+ "isolated_gamepad_data_fetcher.h",
"windows/d3d11_texture_helper.cc",
"windows/d3d11_texture_helper.h",
"windows/flip_pixel_shader.h",
@@ -132,8 +134,8 @@ if (enable_vr) {
"oculus/oculus_device.h",
"oculus/oculus_device_provider.cc",
"oculus/oculus_device_provider.h",
- "oculus/oculus_gamepad_data_fetcher.cc",
- "oculus/oculus_gamepad_data_fetcher.h",
+ "oculus/oculus_gamepad_helper.cc",
+ "oculus/oculus_gamepad_helper.h",
"oculus/oculus_render_loop.cc",
"oculus/oculus_render_loop.h",
"oculus/oculus_type_converters.cc",
@@ -160,8 +162,6 @@ if (enable_vr) {
"test/fake_vr_display_impl_client.h",
"test/fake_vr_service_client.cc",
"test/fake_vr_service_client.h",
- "test/mock_vr_display_impl.cc",
- "test/mock_vr_display_impl.h",
"vr_export.h",
]
diff --git a/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc b/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
index 03e7d72dece..b151a27ef58 100644
--- a/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
+++ b/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
@@ -26,7 +26,7 @@ void CopyToUString(UChar* dest, size_t dest_length, base::string16 src) {
CardboardGamepadDataFetcher::Factory::Factory(
CardboardGamepadDataProvider* data_provider,
- unsigned int display_id)
+ device::mojom::XRDeviceId display_id)
: data_provider_(data_provider), display_id_(display_id) {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
@@ -47,7 +47,7 @@ GamepadSource CardboardGamepadDataFetcher::Factory::source() {
CardboardGamepadDataFetcher::CardboardGamepadDataFetcher(
CardboardGamepadDataProvider* data_provider,
- unsigned int display_id)
+ device::mojom::XRDeviceId display_id)
: display_id_(display_id) {
// Called on UI thread.
DVLOG(1) << __FUNCTION__ << "=" << this;
@@ -93,7 +93,7 @@ void CardboardGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
pad.buttons_length = 1;
pad.axes_length = 0;
- pad.display_id = display_id_;
+ pad.display_id = static_cast<unsigned int>(display_id_);
pad.hand = GamepadHand::kNone;
}
diff --git a/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.h b/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.h
index 0b6193faf8d..0fe4651849f 100644
--- a/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.h
+++ b/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.h
@@ -9,6 +9,7 @@
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/vr/android/gvr/cardboard_gamepad_data_provider.h"
+#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_export.h"
namespace device {
@@ -17,18 +18,19 @@ class DEVICE_VR_EXPORT CardboardGamepadDataFetcher : public GamepadDataFetcher {
public:
class Factory : public GamepadDataFetcherFactory {
public:
- Factory(CardboardGamepadDataProvider*, unsigned int display_id);
+ Factory(CardboardGamepadDataProvider*,
+ device::mojom::XRDeviceId display_id);
~Factory() override;
std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
GamepadSource source() override;
private:
CardboardGamepadDataProvider* data_provider_;
- unsigned int display_id_;
+ device::mojom::XRDeviceId display_id_;
};
CardboardGamepadDataFetcher(CardboardGamepadDataProvider*,
- unsigned int display_id);
+ device::mojom::XRDeviceId display_id);
~CardboardGamepadDataFetcher() override;
GamepadSource source() override;
@@ -41,7 +43,7 @@ class DEVICE_VR_EXPORT CardboardGamepadDataFetcher : public GamepadDataFetcher {
void SetGamepadData(CardboardGamepadData);
private:
- unsigned int display_id_;
+ device::mojom::XRDeviceId display_id_;
CardboardGamepadData gamepad_data_;
DISALLOW_COPY_AND_ASSIGN(CardboardGamepadDataFetcher);
diff --git a/chromium/device/vr/android/gvr/gvr_delegate_provider.h b/chromium/device/vr/android/gvr/gvr_delegate_provider.h
index 9bd7708c62b..60c3419e363 100644
--- a/chromium/device/vr/android/gvr/gvr_delegate_provider.h
+++ b/chromium/device/vr/android/gvr/gvr_delegate_provider.h
@@ -19,10 +19,10 @@ class DEVICE_VR_EXPORT GvrDelegateProvider {
public:
GvrDelegateProvider() = default;
virtual bool ShouldDisableGvrDevice() = 0;
- virtual void SetDeviceId(unsigned int device_id) = 0;
+ virtual void SetDeviceId(mojom::XRDeviceId device_id) = 0;
virtual void StartWebXRPresentation(
mojom::VRDisplayInfoPtr display_info,
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
base::OnceCallback<void(device::mojom::XRSessionPtr)> callback) = 0;
virtual void ExitWebVRPresent() = 0;
virtual void OnListeningForActivateChanged(bool listening) = 0;
diff --git a/chromium/device/vr/android/gvr/gvr_device.cc b/chromium/device/vr/android/gvr/gvr_device.cc
index 20166c658f6..c47946f2093 100644
--- a/chromium/device/vr/android/gvr/gvr_device.cc
+++ b/chromium/device/vr/android/gvr/gvr_device.cc
@@ -93,12 +93,12 @@ mojom::VREyeParametersPtr CreateEyeParamater(
}
mojom::VRDisplayInfoPtr CreateVRDisplayInfo(gvr::GvrApi* gvr_api,
- uint32_t device_id) {
+ mojom::XRDeviceId device_id) {
TRACE_EVENT0("input", "GvrDelegate::CreateVRDisplayInfo");
mojom::VRDisplayInfoPtr device = mojom::VRDisplayInfo::New();
- device->index = device_id;
+ device->id = device_id;
device->capabilities = mojom::VRDisplayCapabilities::New();
device->capabilities->hasPosition = false;
@@ -143,7 +143,7 @@ std::unique_ptr<GvrDevice> GvrDevice::Create() {
}
GvrDevice::GvrDevice()
- : VRDeviceBase(VRDeviceId::GVR_DEVICE_ID),
+ : VRDeviceBase(mojom::XRDeviceId::GVR_DEVICE_ID),
exclusive_controller_binding_(this),
weak_ptr_factory_(this) {
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
@@ -177,34 +177,33 @@ GvrDevice::~GvrDevice() {
}
void GvrDevice::RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
+ if (!options->immersive) {
+ // TODO(https://crbug.com/695937): This should be NOTREACHED() once we no
+ // longer need the hacked GRV non-immersive mode.
+ ReturnNonImmersiveSession(std::move(callback));
+ return;
+ }
+
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider) {
std::move(callback).Run(nullptr, nullptr);
return;
}
- if (options->immersive) {
- // StartWebXRPresentation is async as we may trigger a DON (Device ON) flow
- // that pauses Chrome.
- delegate_provider->StartWebXRPresentation(
- GetVRDisplayInfo(), std::move(options),
- base::BindOnce(&GvrDevice::OnStartPresentResult,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
- } else {
- // TODO(https://crbug.com/695937): This should be NOTREACHED() once
- // orientation device handles non-immersive VR sessions.
- // TODO(https://crbug.com/842025): Handle this when RequestSession is called
- // for non-immersive sessions.
- NOTREACHED();
- }
+ // StartWebXRPresentation is async as we may trigger a DON (Device ON) flow
+ // that pauses Chrome.
+ delegate_provider->StartWebXRPresentation(
+ GetVRDisplayInfo(), std::move(options),
+ base::BindOnce(&GvrDevice::OnStartPresentResult,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void GvrDevice::OnStartPresentResult(
mojom::XRRuntime::RequestSessionCallback callback,
mojom::XRSessionPtr session) {
- if (!session || !session->connection) {
+ if (!session) {
std::move(callback).Run(nullptr, nullptr);
return;
}
@@ -223,8 +222,7 @@ void GvrDevice::OnStartPresentResult(
base::BindOnce(&GvrDevice::OnPresentingControllerMojoConnectionError,
base::Unretained(this)));
- std::move(callback).Run(std::move(session->connection),
- std::move(session_controller));
+ std::move(callback).Run(std::move(session), std::move(session_controller));
}
// XRSessionController
@@ -246,7 +244,7 @@ void GvrDevice::StopPresenting() {
}
void GvrDevice::OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
frame_data->pose =
GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), nullptr);
diff --git a/chromium/device/vr/android/gvr/gvr_device.h b/chromium/device/vr/android/gvr/gvr_device.h
index 6061fcbed79..1d802b230c1 100644
--- a/chromium/device/vr/android/gvr/gvr_device.h
+++ b/chromium/device/vr/android/gvr/gvr_device.h
@@ -26,7 +26,7 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase,
// VRDeviceBase
void RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) override;
void PauseTracking() override;
void ResumeTracking() override;
@@ -42,7 +42,7 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase,
// VRDeviceBase
void OnListeningForActivate(bool listening) override;
void OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
void OnStartPresentResult(mojom::XRRuntime::RequestSessionCallback callback,
mojom::XRSessionPtr session);
diff --git a/chromium/device/vr/android/gvr/gvr_device_provider.cc b/chromium/device/vr/android/gvr/gvr_device_provider.cc
index ea77025b1e8..56f8e792a57 100644
--- a/chromium/device/vr/android/gvr/gvr_device_provider.cc
+++ b/chromium/device/vr/android/gvr/gvr_device_provider.cc
@@ -12,10 +12,10 @@ GvrDeviceProvider::GvrDeviceProvider() = default;
GvrDeviceProvider::~GvrDeviceProvider() = default;
void GvrDeviceProvider::Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback,
base::OnceClosure initialization_complete) {
vr_device_ = GvrDevice::Create();
if (vr_device_)
diff --git a/chromium/device/vr/android/gvr/gvr_device_provider.h b/chromium/device/vr/android/gvr/gvr_device_provider.h
index c833e473404..527b84715cb 100644
--- a/chromium/device/vr/android/gvr/gvr_device_provider.h
+++ b/chromium/device/vr/android/gvr/gvr_device_provider.h
@@ -21,10 +21,10 @@ class DEVICE_VR_EXPORT GvrDeviceProvider : public VRDeviceProvider {
~GvrDeviceProvider() override;
void Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback,
base::OnceClosure initialization_complete) override;
bool Initialized() override;
diff --git a/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc b/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
index f48fcd31133..0d50dd8cbe2 100644
--- a/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
+++ b/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
@@ -26,7 +26,7 @@ void CopyToUString(UChar* dest, size_t dest_length, base::string16 src) {
} // namespace
GvrGamepadDataFetcher::Factory::Factory(GvrGamepadDataProvider* data_provider,
- unsigned int display_id)
+ mojom::XRDeviceId display_id)
: data_provider_(data_provider), display_id_(display_id) {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
@@ -46,7 +46,7 @@ GamepadSource GvrGamepadDataFetcher::Factory::source() {
GvrGamepadDataFetcher::GvrGamepadDataFetcher(
GvrGamepadDataProvider* data_provider,
- unsigned int display_id)
+ mojom::XRDeviceId display_id)
: display_id_(display_id) {
// Called on UI thread.
DVLOG(1) << __FUNCTION__ << "=" << this;
@@ -93,7 +93,7 @@ void GvrGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
pad.buttons_length = 1;
pad.axes_length = 2;
- pad.display_id = display_id_;
+ pad.display_id = static_cast<unsigned int>(display_id_);
pad.is_xr = true;
diff --git a/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.h b/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.h
index 816046e51b9..e4a0da53e22 100644
--- a/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.h
+++ b/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.h
@@ -9,6 +9,7 @@
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
+#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_export.h"
namespace device {
@@ -17,17 +18,17 @@ class DEVICE_VR_EXPORT GvrGamepadDataFetcher : public GamepadDataFetcher {
public:
class Factory : public GamepadDataFetcherFactory {
public:
- Factory(GvrGamepadDataProvider*, unsigned int display_id);
+ Factory(GvrGamepadDataProvider*, mojom::XRDeviceId display_id);
~Factory() override;
std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
GamepadSource source() override;
private:
GvrGamepadDataProvider* data_provider_;
- unsigned int display_id_;
+ mojom::XRDeviceId display_id_;
};
- GvrGamepadDataFetcher(GvrGamepadDataProvider*, unsigned int display_id);
+ GvrGamepadDataFetcher(GvrGamepadDataProvider*, mojom::XRDeviceId display_id);
~GvrGamepadDataFetcher() override;
GamepadSource source() override;
@@ -40,7 +41,7 @@ class DEVICE_VR_EXPORT GvrGamepadDataFetcher : public GamepadDataFetcher {
void SetGamepadData(GvrGamepadData);
private:
- unsigned int display_id_;
+ mojom::XRDeviceId display_id_;
GvrGamepadData gamepad_data_;
DISALLOW_COPY_AND_ASSIGN(GvrGamepadDataFetcher);
diff --git a/chromium/device/vr/buildflags/BUILD.gn b/chromium/device/vr/buildflags/BUILD.gn
index fa4c3d95ca4..2bbefac3108 100644
--- a/chromium/device/vr/buildflags/BUILD.gn
+++ b/chromium/device/vr/buildflags/BUILD.gn
@@ -10,8 +10,9 @@ buildflag_header("buildflags") {
header = "buildflags.h"
flags = [
"ENABLE_ARCORE=$enable_arcore",
- "ENABLE_VR=$enable_vr",
- "ENABLE_OPENVR=$enable_openvr",
+ "ENABLE_ISOLATED_XR_SERVICE=$enable_isolated_xr_service",
"ENABLE_OCULUS_VR=$enable_oculus_vr",
+ "ENABLE_OPENVR=$enable_openvr",
+ "ENABLE_VR=$enable_vr",
]
}
diff --git a/chromium/device/vr/buildflags/buildflags.gni b/chromium/device/vr/buildflags/buildflags.gni
index 7e663a3e315..13f05de973b 100644
--- a/chromium/device/vr/buildflags/buildflags.gni
+++ b/chromium/device/vr/buildflags/buildflags.gni
@@ -37,10 +37,10 @@ declare_args() {
# we are limiting to canary and dev until binary size issues are resolved.
# TODO(crbug.com/836524): once we've refactored AR code out from vr
# directories, we can stop requiring |enable_vr| here.
- package_arcore =
- enable_vr && is_android && !is_chromecast && current_cpu == "arm" &&
- (android_channel == "default" || android_channel == "canary" ||
- android_channel == "dev")
+ package_arcore = enable_vr && is_android && !is_chromecast &&
+ (current_cpu == "arm" || current_cpu == "arm64") &&
+ (android_channel == "default" ||
+ android_channel == "canary" || android_channel == "dev")
# TODO(crbug.com/841389): We should eventually have a single flag for
# enabling arcore, but we currently don't support ARCore in 64bit, and we do
@@ -50,4 +50,11 @@ declare_args() {
# directories, we can stop requiring |enable_vr| here.
enable_arcore = enable_vr && is_android && !is_chromecast &&
(current_cpu == "arm" || current_cpu == "arm64")
+
+ # When enabled, host Desktop VR devices (OpenVR and Oculus) in a separate
+ # process. When disabled, and Oculus/OpenVR are enabled, they are hosted
+ # in the browser process.
+ # TODO(billorr): Enable by default when OpenVR or Oculus are enabled after
+ # tests and controllers are working with the isolated process.
+ enable_isolated_xr_service = false
}
diff --git a/chromium/device/vr/isolated_gamepad_data_fetcher.cc b/chromium/device/vr/isolated_gamepad_data_fetcher.cc
new file mode 100644
index 00000000000..b78f5d5aeb0
--- /dev/null
+++ b/chromium/device/vr/isolated_gamepad_data_fetcher.cc
@@ -0,0 +1,225 @@
+// Copyright 2018 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/vr/isolated_gamepad_data_fetcher.h"
+#include "device/vr/vr_device.h"
+
+namespace device {
+
+IsolatedGamepadDataFetcher::Factory::Factory(
+ device::mojom::XRDeviceId display_id,
+ device::mojom::IsolatedXRGamepadProviderFactoryPtr factory)
+ : display_id_(display_id), factory_(std::move(factory)) {}
+
+IsolatedGamepadDataFetcher::Factory::~Factory() {}
+
+std::unique_ptr<GamepadDataFetcher>
+IsolatedGamepadDataFetcher::Factory::CreateDataFetcher() {
+ device::mojom::IsolatedXRGamepadProviderPtr provider;
+ factory_->GetIsolatedXRGamepadProvider(mojo::MakeRequest(&provider));
+ return std::make_unique<IsolatedGamepadDataFetcher>(display_id_,
+ std::move(provider));
+}
+
+GamepadSource IsolatedGamepadDataFetcher::Factory::source() {
+ return (display_id_ == device::mojom::XRDeviceId::OPENVR_DEVICE_ID)
+ ? GAMEPAD_SOURCE_OPENVR
+ : GAMEPAD_SOURCE_OCULUS;
+}
+
+IsolatedGamepadDataFetcher::IsolatedGamepadDataFetcher(
+ device::mojom::XRDeviceId display_id,
+ device::mojom::IsolatedXRGamepadProviderPtr provider)
+ : display_id_(display_id) {
+ // We bind provider_ on the poling thread, but we're created on the main UI
+ // thread.
+ provider_info_ = provider.PassInterface();
+}
+
+IsolatedGamepadDataFetcher::~IsolatedGamepadDataFetcher() = default;
+
+GamepadSource IsolatedGamepadDataFetcher::source() {
+ return (display_id_ == device::mojom::XRDeviceId::OPENVR_DEVICE_ID)
+ ? GAMEPAD_SOURCE_OPENVR
+ : GAMEPAD_SOURCE_OCULUS;
+}
+
+void IsolatedGamepadDataFetcher::OnDataUpdated(
+ device::mojom::XRGamepadDataPtr data) {
+ data_ = std::move(data);
+ have_outstanding_request_ = false;
+}
+
+GamepadPose GamepadPoseFromXRPose(device::mojom::VRPose* pose) {
+ GamepadPose ret = {};
+ ret.not_null = pose;
+ if (!pose) {
+ return ret;
+ }
+
+ if (pose->position) {
+ ret.position.not_null = true;
+ ret.position.x = (*pose->position)[0];
+ ret.position.y = (*pose->position)[1];
+ ret.position.z = (*pose->position)[2];
+ }
+
+ if (pose->orientation) {
+ ret.orientation.not_null = true;
+ ret.orientation.x = (*pose->orientation)[0];
+ ret.orientation.y = (*pose->orientation)[1];
+ ret.orientation.z = (*pose->orientation)[2];
+ ret.orientation.w = (*pose->orientation)[3];
+ }
+
+ if (pose->angularVelocity) {
+ ret.angular_velocity.not_null = true;
+ ret.angular_velocity.x = (*pose->angularVelocity)[0];
+ ret.angular_velocity.y = (*pose->angularVelocity)[1];
+ ret.angular_velocity.z = (*pose->angularVelocity)[2];
+ }
+
+ if (pose->linearVelocity) {
+ ret.linear_velocity.not_null = true;
+ ret.linear_velocity.x = (*pose->linearVelocity)[0];
+ ret.linear_velocity.y = (*pose->linearVelocity)[1];
+ ret.linear_velocity.z = (*pose->linearVelocity)[2];
+ }
+
+ if (pose->angularAcceleration) {
+ ret.angular_acceleration.not_null = true;
+ ret.angular_acceleration.x = (*pose->angularAcceleration)[0];
+ ret.angular_acceleration.y = (*pose->angularAcceleration)[1];
+ ret.angular_acceleration.z = (*pose->angularAcceleration)[2];
+ }
+
+ if (pose->linearAcceleration) {
+ ret.linear_acceleration.not_null = true;
+ ret.linear_acceleration.x = (*pose->linearAcceleration)[0];
+ ret.linear_acceleration.y = (*pose->linearAcceleration)[1];
+ ret.linear_acceleration.z = (*pose->linearAcceleration)[2];
+ }
+
+ return ret;
+}
+
+void IsolatedGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
+ if (!provider_ && provider_info_) {
+ provider_.Bind(std::move(provider_info_));
+ }
+
+ // If we don't have a provider, we can't give out data.
+ if (!provider_) {
+ return;
+ }
+
+ // Request new data.
+ if (!have_outstanding_request_) {
+ have_outstanding_request_ = true;
+ provider_->RequestUpdate(base::BindOnce(
+ &IsolatedGamepadDataFetcher::OnDataUpdated, base::Unretained(this)));
+ }
+
+ // If we have no data to give out, nothing to do.
+ if (!data_) {
+ return;
+ }
+
+ // Keep track of gamepads that go missing.
+ std::set<unsigned int> seen_gamepads;
+
+ // Give out data_, but we need to translate it.
+ for (size_t i = 0; i < data_->gamepads.size(); ++i) {
+ auto& source = data_->gamepads[i];
+ PadState* state = GetPadState(source->controller_id);
+ if (!state)
+ continue;
+ Gamepad& dest = state->data;
+ dest.connected = true;
+
+ seen_gamepads.insert(source->controller_id);
+ dest.timestamp = CurrentTimeInMicroseconds();
+ dest.pose = GamepadPoseFromXRPose(source->pose.get());
+ dest.pose.has_position = source->can_provide_position;
+ dest.pose.has_orientation = source->can_provide_orientation;
+ dest.hand = source->hand == device::mojom::XRHandedness::LEFT
+ ? GamepadHand::kLeft
+ : source->hand == device::mojom::XRHandedness::RIGHT
+ ? GamepadHand::kRight
+ : GamepadHand::kNone;
+ dest.display_id = static_cast<unsigned int>(display_id_);
+ dest.is_xr = true;
+
+ dest.axes_length = source->axes.size();
+ for (size_t j = 0; j < source->axes.size(); ++j) {
+ dest.axes[j] = source->axes[j];
+ }
+
+ dest.buttons_length = source->buttons.size();
+ for (size_t j = 0; j < source->buttons.size(); ++j) {
+ dest.buttons[j] =
+ GamepadButton(source->buttons[j]->pressed,
+ source->buttons[j]->touched, source->buttons[j]->value);
+ }
+
+ // Gamepad extensions refer to display_id, corresponding to the WebVR
+ // VRDisplay's dipslayId property.
+ // As WebVR is deprecated, and we only hand out a maximum of one "display"
+ // per runtime, we use the XRDeviceId now to associate the controller with
+ // a headset. This doesn't change behavior, but the device/display naming
+ // could be confusing here.
+ if (display_id_ == device::mojom::XRDeviceId::OPENVR_DEVICE_ID) {
+ swprintf(dest.id, Gamepad::kIdLengthCap, L"OpenVR Gamepad");
+ } else {
+ if (dest.hand == GamepadHand::kLeft) {
+ swprintf(dest.id, Gamepad::kIdLengthCap, L"Oculus Touch (Left)");
+ } else if (dest.hand == GamepadHand::kRight) {
+ swprintf(dest.id, Gamepad::kIdLengthCap, L"Oculus Touch (Right)");
+ } else {
+ swprintf(dest.id, Gamepad::kIdLengthCap, L"Oculus Remote");
+ }
+ }
+
+ dest.mapping[0] = 0;
+ }
+
+ // Remove any gamepads that aren't connected.
+ for (auto id : active_gamepads_) {
+ if (seen_gamepads.find(id) == seen_gamepads.end()) {
+ PadState* state = GetPadState(id);
+ if (state) {
+ state->data.connected = false;
+ }
+ }
+ }
+ active_gamepads_ = std::move(seen_gamepads);
+}
+
+void IsolatedGamepadDataFetcher::PauseHint(bool paused) {}
+
+void IsolatedGamepadDataFetcher::OnAddedToProvider() {}
+
+void IsolatedGamepadDataFetcher::Factory::RemoveGamepad(
+ device::mojom::XRDeviceId id) {
+ using namespace device;
+ // TODO(crbug.com/868101) - Remove this.
+ std::map<device::mojom::XRDeviceId, GamepadSource>
+ device_id_to_gamepad_source = {
+ {device::mojom::XRDeviceId::OCULUS_DEVICE_ID, GAMEPAD_SOURCE_OCULUS},
+ {device::mojom::XRDeviceId::OPENVR_DEVICE_ID, GAMEPAD_SOURCE_OPENVR},
+ };
+
+ GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory(
+ device_id_to_gamepad_source[id]);
+}
+
+void IsolatedGamepadDataFetcher::Factory::AddGamepad(
+ device::mojom::XRDeviceId device_id,
+ device::mojom::IsolatedXRGamepadProviderFactoryPtr gamepad_factory) {
+ device::GamepadDataFetcherManager::GetInstance()->AddFactory(
+ new device::IsolatedGamepadDataFetcher::Factory(
+ device_id, std::move(gamepad_factory)));
+}
+
+} // namespace device
diff --git a/chromium/device/vr/isolated_gamepad_data_fetcher.h b/chromium/device/vr/isolated_gamepad_data_fetcher.h
new file mode 100644
index 00000000000..39511405498
--- /dev/null
+++ b/chromium/device/vr/isolated_gamepad_data_fetcher.h
@@ -0,0 +1,61 @@
+// Copyright 2018 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_VR_ISOLATED_GAMEPAD_DATA_FETCHER_H_
+#define DEVICE_VR_ISOLATED_GAMEPAD_DATA_FETCHER_H_
+
+#include "device/gamepad/gamepad_data_fetcher.h"
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
+#include "device/vr/vr_device.h"
+
+namespace device {
+
+class IsolatedGamepadDataFetcher : public GamepadDataFetcher {
+ public:
+ class DEVICE_VR_EXPORT Factory : public GamepadDataFetcherFactory {
+ public:
+ Factory(device::mojom::XRDeviceId display_id,
+ device::mojom::IsolatedXRGamepadProviderFactoryPtr factory);
+ ~Factory() override;
+ std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
+ GamepadSource source() override;
+
+ static void AddGamepad(
+ device::mojom::XRDeviceId device_id,
+ device::mojom::IsolatedXRGamepadProviderFactoryPtr gamepad_factory);
+ static void RemoveGamepad(device::mojom::XRDeviceId device_id);
+
+ private:
+ device::mojom::XRDeviceId display_id_;
+ device::mojom::IsolatedXRGamepadProviderFactoryPtr factory_;
+ };
+
+ IsolatedGamepadDataFetcher(
+ device::mojom::XRDeviceId display_id,
+ device::mojom::IsolatedXRGamepadProviderPtr provider);
+ ~IsolatedGamepadDataFetcher() override;
+
+ GamepadSource source() override;
+
+ void GetGamepadData(bool devices_changed_hint) override;
+ void PauseHint(bool paused) override;
+ void OnAddedToProvider() override;
+
+ void OnDataUpdated(device::mojom::XRGamepadDataPtr data);
+
+ private:
+ device::mojom::XRDeviceId display_id_;
+ bool have_outstanding_request_ = false;
+ std::set<unsigned int> active_gamepads_;
+ device::mojom::XRGamepadDataPtr data_;
+ device::mojom::IsolatedXRGamepadProviderPtr
+ provider_; // Bound on the polling thread.
+ device::mojom::IsolatedXRGamepadProviderPtrInfo
+ provider_info_; // Received on the UI thread, bound when polled.
+
+ DISALLOW_COPY_AND_ASSIGN(IsolatedGamepadDataFetcher);
+};
+
+} // namespace device
+#endif // DEVICE_VR_ISOLATED_GAMEPAD_DATA_FETCHER_H_
diff --git a/chromium/device/vr/oculus/oculus_device.cc b/chromium/device/vr/oculus/oculus_device.cc
index f3d816d1e94..6f55e6669d3 100644
--- a/chromium/device/vr/oculus/oculus_device.cc
+++ b/chromium/device/vr/oculus/oculus_device.cc
@@ -48,10 +48,10 @@ mojom::VREyeParametersPtr GetEyeDetails(ovrSession session,
return eye_parameters;
}
-mojom::VRDisplayInfoPtr CreateVRDisplayInfo(unsigned int id,
+mojom::VRDisplayInfoPtr CreateVRDisplayInfo(mojom::XRDeviceId id,
ovrSession session) {
mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
- display_info->index = id;
+ display_info->id = id;
display_info->displayName = std::string("Oculus");
display_info->capabilities = mojom::VRDisplayCapabilities::New();
display_info->capabilities->hasPosition = true;
@@ -84,9 +84,10 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(unsigned int id,
} // namespace
OculusDevice::OculusDevice()
- : VRDeviceBase(VRDeviceId::OCULUS_DEVICE_ID),
+ : VRDeviceBase(mojom::XRDeviceId::OCULUS_DEVICE_ID),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
exclusive_controller_binding_(this),
+ gamepad_provider_factory_binding_(this),
weak_ptr_factory_(this) {
StartOvrSession();
if (!session_) {
@@ -97,36 +98,51 @@ OculusDevice::OculusDevice()
render_loop_ = std::make_unique<OculusRenderLoop>(
base::BindRepeating(&OculusDevice::OnPresentationEnded,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindRepeating(&OculusDevice::OnControllerUpdated,
weak_ptr_factory_.GetWeakPtr()));
}
-OculusDevice::~OculusDevice() {
- StopOvrSession();
+mojom::IsolatedXRGamepadProviderFactoryPtr OculusDevice::BindGamepadFactory() {
+ mojom::IsolatedXRGamepadProviderFactoryPtr ret;
+ gamepad_provider_factory_binding_.Bind(mojo::MakeRequest(&ret));
+ return ret;
+}
+OculusDevice::~OculusDevice() {
// Wait for the render loop to stop before completing destruction. This will
// ensure that bindings are closed on the correct thread.
if (render_loop_ && render_loop_->IsRunning())
render_loop_->Stop();
- device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory(
- device::GAMEPAD_SOURCE_OCULUS);
- data_fetcher_ = nullptr;
+ StopOvrSession();
}
void OculusDevice::RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
+ if (!options->immersive) {
+ ReturnNonImmersiveSession(std::move(callback));
+ return;
+ }
+
StopOvrSession();
- if (!render_loop_->IsRunning())
+ if (!render_loop_->IsRunning()) {
render_loop_->Start();
- if (!render_loop_->IsRunning()) {
- std::move(callback).Run(nullptr, nullptr);
- StartOvrSession();
- return;
+ if (!render_loop_->IsRunning()) {
+ std::move(callback).Run(nullptr, nullptr);
+ StartOvrSession();
+ return;
+ }
+
+ // If we have a pending gamepad provider request when starting the render
+ // loop, post the request over to the render loop to be bound.
+ if (provider_request_) {
+ render_loop_->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&OculusRenderLoop::RequestGamepadProvider,
+ render_loop_->GetWeakPtr(),
+ std::move(provider_request_)));
+ }
}
auto on_request_present_result =
@@ -142,9 +158,7 @@ void OculusDevice::RequestSession(
void OculusDevice::OnRequestSessionResult(
mojom::XRRuntime::RequestSessionCallback callback,
bool result,
- mojom::VRSubmitFrameClientRequest request,
- mojom::VRPresentationProviderPtrInfo provider_info,
- mojom::VRDisplayFrameTransportOptionsPtr transport_options) {
+ mojom::XRSessionPtr session) {
if (!result) {
std::move(callback).Run(nullptr, nullptr);
@@ -155,11 +169,6 @@ void OculusDevice::OnRequestSessionResult(
OnStartPresenting();
- auto connection = mojom::XRPresentationConnection::New();
- connection->client_request = std::move(request);
- connection->provider = std::move(provider_info);
- connection->transport_options = std::move(transport_options);
-
mojom::XRSessionControllerPtr session_controller;
exclusive_controller_binding_.Bind(mojo::MakeRequest(&session_controller));
@@ -169,24 +178,9 @@ void OculusDevice::OnRequestSessionResult(
base::BindOnce(&OculusDevice::OnPresentingControllerMojoConnectionError,
base::Unretained(this)));
- std::move(callback).Run(std::move(connection), std::move(session_controller));
+ session->display_info = display_info_.Clone();
- if (!oculus_gamepad_factory_) {
- oculus_gamepad_factory_ =
- new OculusGamepadDataFetcher::Factory(GetId(), this);
- GamepadDataFetcherManager::GetInstance()->AddFactory(
- oculus_gamepad_factory_);
- }
-}
-
-void OculusDevice::OnControllerUpdated(ovrInputState input,
- ovrInputState remote,
- ovrTrackingState tracking,
- bool has_touch,
- bool has_remote) {
- if (data_fetcher_)
- data_fetcher_->UpdateGamepadData(
- {input, remote, tracking, has_touch, has_remote});
+ std::move(callback).Run(std::move(session), std::move(session_controller));
}
// XRSessionController
@@ -232,7 +226,7 @@ void OculusDevice::StopOvrSession() {
}
void OculusDevice::OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
if (!session_) {
std::move(callback).Run(nullptr);
return;
@@ -245,8 +239,20 @@ void OculusDevice::OnMagicWindowFrameDataRequest(
std::move(callback).Run(std::move(frame_data));
}
-void OculusDevice::RegisterDataFetcher(OculusGamepadDataFetcher* data_fetcher) {
- data_fetcher_ = data_fetcher;
+void OculusDevice::GetIsolatedXRGamepadProvider(
+ mojom::IsolatedXRGamepadProviderRequest provider_request) {
+ // We bind the provider_request on the render loop thread, so gamepad data is
+ // updated at the rendering rate.
+ // If we haven't started the render loop yet, postpone binding the request
+ // until we do.
+ if (render_loop_->IsRunning()) {
+ render_loop_->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&OculusRenderLoop::RequestGamepadProvider,
+ render_loop_->GetWeakPtr(),
+ std::move(provider_request)));
+ } else {
+ provider_request_ = std::move(provider_request);
+ }
}
} // namespace device
diff --git a/chromium/device/vr/oculus/oculus_device.h b/chromium/device/vr/oculus/oculus_device.h
index cff84ccdc6a..6449d9e5f53 100644
--- a/chromium/device/vr/oculus/oculus_device.h
+++ b/chromium/device/vr/oculus/oculus_device.h
@@ -9,7 +9,6 @@
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
-#include "device/vr/oculus/oculus_gamepad_data_fetcher.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_device_base.h"
#include "mojo/public/cpp/bindings/binding.h"
@@ -19,55 +18,50 @@ namespace device {
class OculusRenderLoop;
-class OculusDevice : public VRDeviceBase,
- public mojom::XRSessionController,
- public OculusGamepadDataProvider {
+class DEVICE_VR_EXPORT OculusDevice
+ : public VRDeviceBase,
+ public mojom::XRSessionController,
+ public mojom::IsolatedXRGamepadProviderFactory {
public:
explicit OculusDevice();
~OculusDevice() override;
// VRDeviceBase
void RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) override;
void OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
- void OnRequestSessionResult(
- mojom::XRRuntime::RequestSessionCallback callback,
- bool result,
- mojom::VRSubmitFrameClientRequest request,
- mojom::VRPresentationProviderPtrInfo provider_info,
- mojom::VRDisplayFrameTransportOptionsPtr transport_options);
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
+ void OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback,
+ bool result,
+ mojom::XRSessionPtr session);
bool IsInitialized() { return !!session_; }
+ mojom::IsolatedXRGamepadProviderFactoryPtr BindGamepadFactory();
+
private:
// XRSessionController
void SetFrameDataRestricted(bool restricted) override;
void OnPresentingControllerMojoConnectionError();
- // OculusGamepadDataProvider
- void RegisterDataFetcher(OculusGamepadDataFetcher*) override;
+ // mojom::IsolatedXRGamepadProviderFactory
+ void GetIsolatedXRGamepadProvider(
+ mojom::IsolatedXRGamepadProviderRequest provider_request) override;
void OnPresentationEnded();
void StartOvrSession();
void StopOvrSession();
- void OnControllerUpdated(ovrInputState input,
- ovrInputState remote,
- ovrTrackingState tracking,
- bool has_touch,
- bool has_remote);
-
std::unique_ptr<OculusRenderLoop> render_loop_;
- OculusGamepadDataFetcher* data_fetcher_ = nullptr;
- mojom::VRDisplayInfoPtr display_info_;
ovrSession session_ = nullptr;
- OculusGamepadDataFetcher::Factory* oculus_gamepad_factory_ = nullptr;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_;
+ mojo::Binding<mojom::IsolatedXRGamepadProviderFactory>
+ gamepad_provider_factory_binding_;
+ mojom::IsolatedXRGamepadProviderRequest provider_request_;
base::WeakPtrFactory<OculusDevice> weak_ptr_factory_;
diff --git a/chromium/device/vr/oculus/oculus_device_provider.cc b/chromium/device/vr/oculus/oculus_device_provider.cc
index cda8a0a898f..b91253c247a 100644
--- a/chromium/device/vr/oculus/oculus_device_provider.cc
+++ b/chromium/device/vr/oculus/oculus_device_provider.cc
@@ -5,8 +5,8 @@
#include "device/vr/oculus/oculus_device_provider.h"
#include "device/gamepad/gamepad_data_fetcher_manager.h"
+#include "device/vr/isolated_gamepad_data_fetcher.h"
#include "device/vr/oculus/oculus_device.h"
-#include "device/vr/oculus/oculus_gamepad_data_fetcher.h"
#include "third_party/libovr/src/Include/OVR_CAPI.h"
namespace device {
@@ -14,18 +14,32 @@ namespace device {
OculusVRDeviceProvider::OculusVRDeviceProvider() : initialized_(false) {}
OculusVRDeviceProvider::~OculusVRDeviceProvider() {
+ // Removing gamepad factory corresponding to VRDeviceId::OCULUS_DEVICE_ID,
+ // added during initialization.
+ device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory(
+ device::GAMEPAD_SOURCE_OCULUS);
}
void OculusVRDeviceProvider::Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId)>
+ remove_device_callback,
base::OnceClosure initialization_complete) {
CreateDevice();
- if (device_)
- add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(),
+ if (device_) {
+ add_device_callback.Run(device::mojom::XRDeviceId::OCULUS_DEVICE_ID,
+ device_->GetVRDisplayInfo(),
device_->BindXRRuntimePtr());
+
+ // Removed in our destructor, as VRDeviceId::OCULUS_DEVICE_ID corresponds to
+ // device::GAMEPAD_SOURCE_OCULUS.
+ GamepadDataFetcherManager::GetInstance()->AddFactory(
+ new IsolatedGamepadDataFetcher::Factory(
+ device::mojom::XRDeviceId::OCULUS_DEVICE_ID,
+ device_->BindGamepadFactory()));
+ }
initialized_ = true;
std::move(initialization_complete).Run();
}
diff --git a/chromium/device/vr/oculus/oculus_device_provider.h b/chromium/device/vr/oculus/oculus_device_provider.h
index 24791e1d617..6451c09ec71 100644
--- a/chromium/device/vr/oculus/oculus_device_provider.h
+++ b/chromium/device/vr/oculus/oculus_device_provider.h
@@ -12,8 +12,6 @@
#include "device/vr/vr_device_provider.h"
#include "device/vr/vr_export.h"
-typedef struct ovrHmdStruct* ovrSession;
-
namespace device {
class OculusDevice;
@@ -24,10 +22,11 @@ class DEVICE_VR_EXPORT OculusVRDeviceProvider : public VRDeviceProvider {
~OculusVRDeviceProvider() override;
void Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId)>
+ remove_device_callback,
base::OnceClosure initialization_complete) override;
bool Initialized() override;
diff --git a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc b/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc
deleted file mode 100644
index aec6d3c16fe..00000000000
--- a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// 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/vr/oculus/oculus_gamepad_data_fetcher.h"
-
-#include <memory>
-
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "device/gamepad/public/cpp/gamepads.h"
-#include "third_party/libovr/src/Include/OVR_CAPI.h"
-#include "ui/gfx/transform.h"
-#include "ui/gfx/transform_util.h"
-
-namespace device {
-
-namespace {
-
-float ApplyTriggerDeadzone(float value) {
- // Trigger value should be between 0 and 1. We apply a deadzone for small
- // values so a loose controller still reports a value of 0 when not in use.
- float kTriggerDeadzone = 0.01f;
-
- return (value < kTriggerDeadzone) ? 0 : value;
-}
-
-void SetGamepadButton(Gamepad* pad,
- const ovrInputState& input_state,
- ovrButton button_id) {
- bool pressed = (input_state.Buttons & button_id) != 0;
- bool touched = (input_state.Touches & button_id) != 0;
- pad->buttons[pad->buttons_length].pressed = pressed;
- pad->buttons[pad->buttons_length].touched = touched;
- pad->buttons[pad->buttons_length].value = pressed ? 1.0f : 0.0f;
- pad->buttons_length++;
-}
-
-void SetGamepadTouchTrigger(Gamepad* pad,
- const ovrInputState& input_state,
- ovrTouch touch_id,
- float value) {
- bool touched = (input_state.Touches & touch_id) != 0;
- value = ApplyTriggerDeadzone(value);
- pad->buttons[pad->buttons_length].pressed = value != 0;
- pad->buttons[pad->buttons_length].touched = touched;
- pad->buttons[pad->buttons_length].value = value;
- pad->buttons_length++;
-}
-
-void SetGamepadTrigger(Gamepad* pad, float value) {
- value = ApplyTriggerDeadzone(value);
- pad->buttons[pad->buttons_length].pressed = value != 0;
- pad->buttons[pad->buttons_length].touched = value != 0;
- pad->buttons[pad->buttons_length].value = value;
- pad->buttons_length++;
-}
-
-void SetGamepadTouch(Gamepad* pad,
- const ovrInputState& input_state,
- ovrTouch touch_id) {
- bool touched = (input_state.Touches & touch_id) != 0;
- pad->buttons[pad->buttons_length].pressed = false;
- pad->buttons[pad->buttons_length].touched = touched;
- pad->buttons[pad->buttons_length].value = 0.0f;
- pad->buttons_length++;
-}
-
-void SetTouchData(PadState* state,
- const ovrPoseStatef& pose,
- const ovrInputState& input_state,
- ovrHandType hand,
- unsigned int display_id) {
- if (!state)
- return;
- Gamepad& pad = state->data;
- if (!state->is_initialized) {
- state->is_initialized = true;
- pad.connected = true;
- pad.is_xr = true;
- pad.pose.not_null = true;
- pad.pose.has_orientation = true;
- pad.pose.has_position = true;
- pad.display_id = display_id;
- switch (hand) {
- case ovrHand_Left:
- swprintf(pad.id, Gamepad::kIdLengthCap, L"Oculus Touch (Left)");
- pad.hand = GamepadHand::kLeft;
- break;
- case ovrHand_Right:
- swprintf(pad.id, Gamepad::kIdLengthCap, L"Oculus Touch (Right)");
- pad.hand = GamepadHand::kRight;
- break;
- default:
- NOTREACHED();
- return;
- }
- }
- pad.timestamp = OculusGamepadDataFetcher::CurrentTimeInMicroseconds();
- pad.axes_length = 0;
- pad.buttons_length = 0;
- pad.axes[pad.axes_length++] = input_state.Thumbstick[hand].x;
- pad.axes[pad.axes_length++] = -input_state.Thumbstick[hand].y;
-
- // This gamepad layout is the defacto standard, but can be adjusted for WebXR.
- switch (hand) {
- case ovrHand_Left:
- SetGamepadButton(&pad, input_state, ovrButton_LThumb);
- break;
- case ovrHand_Right:
- SetGamepadButton(&pad, input_state, ovrButton_RThumb);
- break;
- default:
- NOTREACHED();
- break;
- }
- SetGamepadTouchTrigger(
- &pad, input_state,
- hand == ovrHand_Left ? ovrTouch_LIndexTrigger : ovrTouch_RIndexTrigger,
- input_state.IndexTrigger[hand]);
- SetGamepadTrigger(&pad, input_state.HandTrigger[hand]);
- switch (hand) {
- case ovrHand_Left:
- SetGamepadButton(&pad, input_state, ovrButton_X);
- SetGamepadButton(&pad, input_state, ovrButton_Y);
- SetGamepadTouch(&pad, input_state, ovrTouch_LThumbRest);
- break;
- case ovrHand_Right:
- SetGamepadButton(&pad, input_state, ovrButton_A);
- SetGamepadButton(&pad, input_state, ovrButton_B);
- SetGamepadTouch(&pad, input_state, ovrTouch_RThumbRest);
- break;
- default:
- NOTREACHED();
- break;
- }
-
- pad.pose.orientation.not_null = true;
- pad.pose.orientation.x = pose.ThePose.Orientation.x;
- pad.pose.orientation.y = pose.ThePose.Orientation.y;
- pad.pose.orientation.z = pose.ThePose.Orientation.z;
- pad.pose.orientation.w = pose.ThePose.Orientation.w;
-
- pad.pose.position.not_null = true;
- pad.pose.position.x = pose.ThePose.Position.x;
- pad.pose.position.y = pose.ThePose.Position.y;
- pad.pose.position.z = pose.ThePose.Position.z;
-
- pad.pose.angular_velocity.not_null = true;
- pad.pose.angular_velocity.x = pose.AngularVelocity.x;
- pad.pose.angular_velocity.y = pose.AngularVelocity.y;
- pad.pose.angular_velocity.z = pose.AngularVelocity.z;
-
- pad.pose.linear_velocity.not_null = true;
- pad.pose.linear_velocity.x = pose.LinearVelocity.x;
- pad.pose.linear_velocity.y = pose.LinearVelocity.y;
- pad.pose.linear_velocity.z = pose.LinearVelocity.z;
-
- pad.pose.angular_acceleration.not_null = true;
- pad.pose.angular_acceleration.x = pose.AngularAcceleration.x;
- pad.pose.angular_acceleration.y = pose.AngularAcceleration.y;
- pad.pose.angular_acceleration.z = pose.AngularAcceleration.z;
-
- pad.pose.linear_acceleration.not_null = true;
- pad.pose.linear_acceleration.x = pose.LinearAcceleration.x;
- pad.pose.linear_acceleration.y = pose.LinearAcceleration.y;
- pad.pose.linear_acceleration.z = pose.LinearAcceleration.z;
-}
-
-} // namespace
-
-OculusGamepadDataFetcher::Factory::Factory(unsigned int display_id,
- OculusGamepadDataProvider* provider)
- : display_id_(display_id), provider_(provider) {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-}
-
-OculusGamepadDataFetcher::Factory::~Factory() {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-}
-
-std::unique_ptr<GamepadDataFetcher>
-OculusGamepadDataFetcher::Factory::CreateDataFetcher() {
- return std::make_unique<OculusGamepadDataFetcher>(display_id_, provider_);
-}
-
-GamepadSource OculusGamepadDataFetcher::Factory::source() {
- return GAMEPAD_SOURCE_OCULUS;
-}
-
-OculusGamepadDataFetcher::OculusGamepadDataFetcher(
- unsigned int display_id,
- OculusGamepadDataProvider* provider)
- : display_id_(display_id), weak_ptr_factory_(this) {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-
- // Register for updates.
- provider->RegisterDataFetcher(this);
-}
-
-OculusGamepadDataFetcher::~OculusGamepadDataFetcher() {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-}
-
-GamepadSource OculusGamepadDataFetcher::source() {
- return GAMEPAD_SOURCE_OCULUS;
-}
-
-void OculusGamepadDataFetcher::OnAddedToProvider() {}
-
-void OculusGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
- base::AutoLock lock(lock_);
- if (data_.have_input_touch) {
- SetTouchData(GetPadState(ovrControllerType_LTouch),
- data_.tracking.HandPoses[ovrHand_Left], data_.input_touch,
- ovrHand_Left, display_id_);
- SetTouchData(GetPadState(ovrControllerType_RTouch),
- data_.tracking.HandPoses[ovrHand_Right], data_.input_touch,
- ovrHand_Right, display_id_);
- }
-
- if (data_.have_input_remote) {
- PadState* state = GetPadState(ovrControllerType_Remote);
- if (state) {
- Gamepad& pad = state->data;
- if (!state->is_initialized) {
- state->is_initialized = true;
- swprintf(pad.id, Gamepad::kIdLengthCap, L"Oculus Remote");
- pad.connected = true;
- pad.is_xr = true;
- pad.display_id = display_id_;
- }
- pad.timestamp = CurrentTimeInMicroseconds();
- pad.axes_length = 0;
- pad.buttons_length = 0;
- SetGamepadButton(&pad, data_.input_remote, ovrButton_Enter);
- SetGamepadButton(&pad, data_.input_remote, ovrButton_Back);
- SetGamepadButton(&pad, data_.input_remote, ovrButton_Up);
- SetGamepadButton(&pad, data_.input_remote, ovrButton_Down);
- SetGamepadButton(&pad, data_.input_remote, ovrButton_Left);
- SetGamepadButton(&pad, data_.input_remote, ovrButton_Right);
- }
- }
-}
-
-void OculusGamepadDataFetcher::UpdateGamepadData(OculusInputData data) {
- base::AutoLock lock(lock_);
- data_ = data;
-}
-
-void OculusGamepadDataFetcher::PauseHint(bool paused) {}
-
-} // namespace device
diff --git a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h b/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h
deleted file mode 100644
index e3430e6163b..00000000000
--- a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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_VR_OCULUS_GAMEPAD_DATA_FETCHER_H_
-#define DEVICE_VR_OCULUS_GAMEPAD_DATA_FETCHER_H_
-
-#include "base/synchronization/lock.h"
-#include "device/gamepad/gamepad_data_fetcher.h"
-#include "third_party/libovr/src/Include/OVR_CAPI.h"
-
-namespace device {
-
-class OculusGamepadDataFetcher;
-
-class OculusGamepadDataProvider {
- public:
- virtual void RegisterDataFetcher(OculusGamepadDataFetcher*) = 0;
-};
-
-struct OculusInputData {
- ovrInputState input_touch = {};
- ovrInputState input_remote = {};
- ovrTrackingState tracking = {};
- bool have_input_touch = false;
- bool have_input_remote = false;
-};
-
-class OculusGamepadDataFetcher : public GamepadDataFetcher {
- public:
- class Factory : public GamepadDataFetcherFactory {
- public:
- Factory(unsigned int display_id, OculusGamepadDataProvider* provider);
- ~Factory() override;
- std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
- GamepadSource source() override;
-
- private:
- unsigned int display_id_;
- OculusGamepadDataProvider* provider_;
- };
-
- OculusGamepadDataFetcher(unsigned int display_id,
- OculusGamepadDataProvider* provider);
- ~OculusGamepadDataFetcher() override;
-
- GamepadSource source() override;
-
- void GetGamepadData(bool devices_changed_hint) override;
- void PauseHint(bool paused) override;
- void OnAddedToProvider() override;
-
- void UpdateGamepadData(OculusInputData data); // Called on UI thread.
-
- private:
- unsigned int display_id_;
-
- // Protects access to data_, which is read/written on different threads.
- base::Lock lock_;
-
- // have_input_* are initialized to false, so we won't try to read uninialized
- // data from this if we read data_ before UpdateGamepadData is called.
- OculusInputData data_;
-
- base::WeakPtrFactory<OculusGamepadDataFetcher> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(OculusGamepadDataFetcher);
-};
-
-} // namespace device
-
-#endif // DEVICE_VR_OCULUS_GAMEPAD_DATA_FETCHER_H_
diff --git a/chromium/device/vr/oculus/oculus_gamepad_helper.cc b/chromium/device/vr/oculus/oculus_gamepad_helper.cc
new file mode 100644
index 00000000000..a4a1520ff17
--- /dev/null
+++ b/chromium/device/vr/oculus/oculus_gamepad_helper.cc
@@ -0,0 +1,210 @@
+// 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/vr/oculus/oculus_gamepad_helper.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "device/gamepad/public/cpp/gamepads.h"
+#include "device/vr/vr_device.h"
+#include "third_party/libovr/src/Include/OVR_CAPI.h"
+#include "ui/gfx/transform.h"
+#include "ui/gfx/transform_util.h"
+
+namespace device {
+
+namespace {
+
+enum GamepadIndex : unsigned int {
+ kLeftTouchController = 0x0,
+ kRightTouchController = 0x1,
+ kRemote = 0x2,
+};
+
+float ApplyTriggerDeadzone(float value) {
+ // Trigger value should be between 0 and 1. We apply a deadzone for small
+ // values so a loose controller still reports a value of 0 when not in use.
+ float kTriggerDeadzone = 0.01f;
+
+ return (value < kTriggerDeadzone) ? 0 : value;
+}
+
+mojom::XRGamepadButtonPtr GetGamepadButton(const ovrInputState& input_state,
+ ovrButton button_id) {
+ bool pressed = (input_state.Buttons & button_id) != 0;
+ bool touched = (input_state.Touches & button_id) != 0;
+
+ auto button = mojom::XRGamepadButton::New();
+ button->pressed = pressed;
+ button->touched = touched;
+ button->value = pressed ? 1.0f : 0.0f;
+
+ return button;
+}
+
+mojom::XRGamepadButtonPtr GetGamepadTouchTrigger(
+ const ovrInputState& input_state,
+ ovrTouch touch_id,
+ float value) {
+ bool touched = (input_state.Touches & touch_id) != 0;
+ value = ApplyTriggerDeadzone(value);
+
+ auto button = mojom::XRGamepadButton::New();
+ button->pressed = value != 0;
+ button->touched = touched;
+ button->value = value;
+
+ return button;
+}
+
+mojom::XRGamepadButtonPtr GetGamepadTrigger(float value) {
+ value = ApplyTriggerDeadzone(value);
+ auto button = mojom::XRGamepadButton::New();
+ button->pressed = value != 0;
+ button->touched = value != 0;
+ button->value = value;
+
+ return button;
+}
+
+mojom::XRGamepadButtonPtr GetGamepadTouch(const ovrInputState& input_state,
+ ovrTouch touch_id) {
+ bool touched = (input_state.Touches & touch_id) != 0;
+
+ auto button = mojom::XRGamepadButton::New();
+ button->pressed = false;
+ button->touched = touched;
+ button->value = 0.0f;
+
+ return button;
+}
+
+void AddTouchData(mojom::XRGamepadDataPtr& data,
+ const ovrTrackingState& tracking,
+ const ovrInputState& input_state,
+ ovrHandType hand) {
+ auto gamepad = mojom::XRGamepad::New();
+ // This gamepad layout is the defacto standard, but can be adjusted for WebXR.
+ switch (hand) {
+ case ovrHand_Left:
+ gamepad->hand = device::mojom::XRHandedness::LEFT;
+ gamepad->controller_id = kLeftTouchController;
+ gamepad->buttons.push_back(
+ GetGamepadButton(input_state, ovrButton_LThumb));
+ break;
+ case ovrHand_Right:
+ gamepad->hand = device::mojom::XRHandedness::RIGHT;
+ gamepad->controller_id = kRightTouchController;
+ gamepad->buttons.push_back(
+ GetGamepadButton(input_state, ovrButton_RThumb));
+ break;
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ gamepad->axes.push_back(input_state.Thumbstick[hand].x);
+ gamepad->axes.push_back(-input_state.Thumbstick[hand].y);
+
+ gamepad->buttons.push_back(GetGamepadTouchTrigger(
+ input_state,
+ hand == ovrHand_Left ? ovrTouch_LIndexTrigger : ovrTouch_RIndexTrigger,
+ input_state.IndexTrigger[hand]));
+ gamepad->buttons.push_back(GetGamepadTrigger(input_state.HandTrigger[hand]));
+
+ switch (hand) {
+ case ovrHand_Left:
+ gamepad->buttons.push_back(GetGamepadButton(input_state, ovrButton_X));
+ gamepad->buttons.push_back(GetGamepadButton(input_state, ovrButton_Y));
+ gamepad->buttons.push_back(
+ GetGamepadTouch(input_state, ovrTouch_LThumbRest));
+ break;
+ case ovrHand_Right:
+ gamepad->buttons.push_back(GetGamepadButton(input_state, ovrButton_A));
+ gamepad->buttons.push_back(GetGamepadButton(input_state, ovrButton_B));
+ gamepad->buttons.push_back(
+ GetGamepadTouch(input_state, ovrTouch_RThumbRest));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ auto dst_pose = mojom::VRPose::New();
+ const ovrPoseStatef& src_pose = tracking.HandPoses[hand];
+
+ dst_pose->orientation = std::vector<float>(
+ {src_pose.ThePose.Orientation.x, src_pose.ThePose.Orientation.y,
+ src_pose.ThePose.Orientation.z, src_pose.ThePose.Orientation.w});
+
+ dst_pose->position = std::vector<float>({src_pose.ThePose.Position.x,
+ src_pose.ThePose.Position.y,
+ src_pose.ThePose.Position.z});
+
+ dst_pose->angularVelocity = std::vector<float>({src_pose.AngularVelocity.x,
+ src_pose.AngularVelocity.y,
+ src_pose.AngularVelocity.z});
+
+ dst_pose->linearVelocity =
+ std::vector<float>({src_pose.LinearVelocity.x, src_pose.LinearVelocity.y,
+ src_pose.LinearVelocity.z});
+
+ dst_pose->angularAcceleration = std::vector<float>(
+ {src_pose.AngularAcceleration.x, src_pose.AngularAcceleration.y,
+ src_pose.AngularAcceleration.z});
+
+ dst_pose->linearAcceleration = std::vector<float>(
+ {src_pose.LinearAcceleration.x, src_pose.LinearAcceleration.y,
+ src_pose.LinearAcceleration.z});
+
+ gamepad->pose = std::move(dst_pose);
+ gamepad->can_provide_position = true;
+ gamepad->can_provide_orientation = true;
+ data->gamepads.push_back(std::move(gamepad));
+}
+
+void AddRemoteData(mojom::XRGamepadDataPtr& data,
+ const ovrInputState& input_remote) {
+ auto remote = mojom::XRGamepad::New();
+ remote->buttons.push_back(GetGamepadButton(input_remote, ovrButton_Enter));
+ remote->buttons.push_back(GetGamepadButton(input_remote, ovrButton_Back));
+ remote->buttons.push_back(GetGamepadButton(input_remote, ovrButton_Up));
+ remote->buttons.push_back(GetGamepadButton(input_remote, ovrButton_Down));
+ remote->buttons.push_back(GetGamepadButton(input_remote, ovrButton_Left));
+ remote->buttons.push_back(GetGamepadButton(input_remote, ovrButton_Right));
+ remote->controller_id = kRemote;
+ remote->hand = device::mojom::XRHandedness::NONE;
+ data->gamepads.push_back(std::move(remote));
+}
+
+} // namespace
+
+mojom::XRGamepadDataPtr OculusGamepadHelper::GetGamepadData(
+ ovrSession session) {
+ ovrInputState input_touch;
+ bool have_touch = OVR_SUCCESS(
+ ovr_GetInputState(session, ovrControllerType_Touch, &input_touch));
+
+ ovrInputState input_remote;
+ bool have_remote = OVR_SUCCESS(
+ ovr_GetInputState(session, ovrControllerType_Remote, &input_remote));
+
+ ovrTrackingState tracking = ovr_GetTrackingState(session, 0, false);
+
+ mojom::XRGamepadDataPtr data = mojom::XRGamepadData::New();
+
+ if (have_touch) {
+ AddTouchData(data, tracking, input_touch, ovrHand_Left);
+ AddTouchData(data, tracking, input_touch, ovrHand_Right);
+ }
+
+ if (have_remote)
+ AddRemoteData(data, input_remote);
+
+ return data;
+}
+
+} // namespace device
diff --git a/chromium/device/vr/oculus/oculus_gamepad_helper.h b/chromium/device/vr/oculus/oculus_gamepad_helper.h
new file mode 100644
index 00000000000..0e4f690072c
--- /dev/null
+++ b/chromium/device/vr/oculus/oculus_gamepad_helper.h
@@ -0,0 +1,20 @@
+// 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_VR_OCULUS_OCULUS_GAMEPAD_HELPER_H_
+#define DEVICE_VR_OCULUS_OCULUS_GAMEPAD_HELPER_H_
+
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
+#include "third_party/libovr/src/Include/OVR_CAPI.h"
+
+namespace device {
+
+class OculusGamepadHelper {
+ public:
+ static mojom::XRGamepadDataPtr GetGamepadData(ovrSession session);
+};
+
+} // namespace device
+
+#endif // DEVICE_VR_OCULUS_OCULUS_GAMEPAD_HELPER_H_
diff --git a/chromium/device/vr/oculus/oculus_render_loop.cc b/chromium/device/vr/oculus/oculus_render_loop.cc
index 80a37b6ec6e..d4348ae246c 100644
--- a/chromium/device/vr/oculus/oculus_render_loop.cc
+++ b/chromium/device/vr/oculus/oculus_render_loop.cc
@@ -4,6 +4,7 @@
#include "device/vr/oculus/oculus_render_loop.h"
+#include "device/vr/oculus/oculus_gamepad_helper.h"
#include "device/vr/oculus/oculus_type_converters.h"
#include "third_party/libovr/src/Include/Extras/OVR_Math.h"
#include "third_party/libovr/src/Include/OVR_CAPI.h"
@@ -42,15 +43,13 @@ gfx::Transform PoseToTransform(const ovrPosef& pose) {
} // namespace
OculusRenderLoop::OculusRenderLoop(
- base::RepeatingCallback<void()> on_presentation_ended,
- base::RepeatingCallback<
- void(ovrInputState, ovrInputState, ovrTrackingState, bool, bool)>
- on_controller_updated)
+ base::RepeatingCallback<void()> on_presentation_ended)
: base::Thread("OculusRenderLoop"),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- binding_(this),
+ presentation_binding_(this),
+ frame_data_binding_(this),
on_presentation_ended_(on_presentation_ended),
- on_controller_updated_(on_controller_updated),
+ gamepad_provider_(this),
weak_ptr_factory_(this) {
DCHECK(main_thread_task_runner_);
}
@@ -69,7 +68,9 @@ void OculusRenderLoop::ClearPendingFrame() {
void OculusRenderLoop::CleanUp() {
submit_client_ = nullptr;
StopOvrSession();
- binding_.Close();
+ presentation_binding_.Close();
+ frame_data_binding_.Close();
+ gamepad_provider_.Close();
}
void OculusRenderLoop::SubmitFrameMissing(int16_t frame_index,
@@ -228,9 +229,8 @@ void OculusRenderLoop::UpdateLayerBounds(int16_t frame_id,
DestroyOvrSwapChain();
};
-void OculusRenderLoop::RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
- RequestSessionCallback callback) {
+void OculusRenderLoop::RequestSession(mojom::XRRuntimeSessionOptionsPtr options,
+ RequestSessionCallback callback) {
DCHECK(options->immersive);
StartOvrSession();
@@ -241,28 +241,36 @@ void OculusRenderLoop::RequestSession(
#endif
) {
main_thread_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), false, nullptr, nullptr, nullptr));
+ FROM_HERE, base::BindOnce(std::move(callback), false, nullptr));
return;
}
- binding_.Close();
- device::mojom::VRPresentationProviderPtr provider;
- binding_.Bind(mojo::MakeRequest(&provider));
+ presentation_binding_.Close();
+ frame_data_binding_.Close();
+ device::mojom::XRPresentationProviderPtr presentation_provider;
+ device::mojom::XRFrameDataProviderPtr frame_data_provider;
+ presentation_binding_.Bind(mojo::MakeRequest(&presentation_provider));
+ frame_data_binding_.Bind(mojo::MakeRequest(&frame_data_provider));
- device::mojom::VRDisplayFrameTransportOptionsPtr transport_options =
- device::mojom::VRDisplayFrameTransportOptions::New();
+ device::mojom::XRPresentationTransportOptionsPtr transport_options =
+ device::mojom::XRPresentationTransportOptions::New();
transport_options->transport_method =
- device::mojom::VRDisplayFrameTransportMethod::SUBMIT_AS_TEXTURE_HANDLE;
+ device::mojom::XRPresentationTransportMethod::SUBMIT_AS_TEXTURE_HANDLE;
// Only set boolean options that we need. Default is false, and we should be
// able to safely ignore ones that our implementation doesn't care about.
transport_options->wait_for_transfer_notification = true;
+ auto submit_frame_sink = device::mojom::XRPresentationConnection::New();
+ submit_frame_sink->provider = presentation_provider.PassInterface();
+ submit_frame_sink->client_request = mojo::MakeRequest(&submit_client_);
+ submit_frame_sink->transport_options = std::move(transport_options);
+
+ auto session = device::mojom::XRSession::New();
+ session->data_provider = frame_data_provider.PassInterface();
+ session->submit_frame_sink = std::move(submit_frame_sink);
+
main_thread_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), true,
- mojo::MakeRequest(&submit_client_),
- provider.PassInterface(), std::move(transport_options)));
+ FROM_HERE, base::BindOnce(std::move(callback), true, std::move(session)));
is_presenting_ = true;
}
@@ -296,7 +304,8 @@ void OculusRenderLoop::StopOvrSession() {
void OculusRenderLoop::ExitPresent() {
is_presenting_ = false;
- binding_.Close();
+ presentation_binding_.Close();
+ frame_data_binding_.Close();
submit_client_ = nullptr;
ClearPendingFrame();
@@ -312,7 +321,7 @@ base::WeakPtr<OculusRenderLoop> OculusRenderLoop::GetWeakPtr() {
}
void OculusRenderLoop::GetFrameData(
- mojom::VRPresentationProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
DCHECK(is_presenting_);
if (has_outstanding_frame_) {
@@ -352,6 +361,14 @@ void OculusRenderLoop::GetFrameData(
std::move(callback).Run(std::move(frame_data));
}
+void OculusRenderLoop::RequestGamepadProvider(
+ mojom::IsolatedXRGamepadProviderRequest request) {
+ gamepad_provider_.Close();
+ // We just close the binding, so the other side won't expect callbacks.
+ gamepad_callback_.Reset();
+ gamepad_provider_.Bind(std::move(request));
+}
+
std::vector<mojom::XRInputSourceStatePtr> OculusRenderLoop::GetInputState(
const ovrTrackingState& tracking_state) {
std::vector<mojom::XRInputSourceStatePtr> input_states;
@@ -405,28 +422,18 @@ std::vector<mojom::XRInputSourceStatePtr> OculusRenderLoop::GetInputState(
}
void OculusRenderLoop::UpdateControllerState() {
- if (!session_) {
- ovrInputState input = {};
- ovrTrackingState tracking = {};
- main_thread_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(on_controller_updated_, input, input,
- tracking, false, false));
+ if (!gamepad_callback_) {
+ // Nobody is listening to updates, so bail early.
+ return;
}
- ovrInputState input_touch;
- bool have_touch = OVR_SUCCESS(
- ovr_GetInputState(session_, ovrControllerType_Touch, &input_touch));
-
- ovrInputState input_remote;
- bool have_remote = OVR_SUCCESS(
- ovr_GetInputState(session_, ovrControllerType_Remote, &input_remote));
-
- ovrTrackingState tracking = ovr_GetTrackingState(session_, 0, false);
+ if (!session_) {
+ std::move(gamepad_callback_).Run(nullptr);
+ return;
+ }
- main_thread_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(on_controller_updated_, input_touch, input_remote,
- tracking, have_touch, have_remote));
+ std::move(gamepad_callback_)
+ .Run(OculusGamepadHelper::GetGamepadData(session_));
}
device::mojom::XRInputSourceStatePtr OculusRenderLoop::GetTouchData(
@@ -487,4 +494,22 @@ device::mojom::XRInputSourceStatePtr OculusRenderLoop::GetTouchData(
return state;
}
+void OculusRenderLoop::RequestUpdate(
+ mojom::IsolatedXRGamepadProvider::RequestUpdateCallback callback) {
+ DCHECK(!gamepad_callback_);
+ if (gamepad_callback_) {
+ std::move(gamepad_callback_).Run(nullptr);
+ }
+
+ // If we aren't presenting, reply now saying that we have no controllers.
+ if (!is_presenting_) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
+ // Otherwise, save the callback to resolve next time we update (typically on
+ // vsync).
+ gamepad_callback_ = std::move(callback);
+}
+
} // namespace device
diff --git a/chromium/device/vr/oculus/oculus_render_loop.h b/chromium/device/vr/oculus/oculus_render_loop.h
index c8186b71aea..18ac6946d94 100644
--- a/chromium/device/vr/oculus/oculus_render_loop.h
+++ b/chromium/device/vr/oculus/oculus_render_loop.h
@@ -23,27 +23,27 @@ namespace device {
const int kMaxOculusRenderLoopInputId = (ovrControllerType_Remote + 1);
-class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
+class OculusRenderLoop : public base::Thread,
+ mojom::XRPresentationProvider,
+ mojom::XRFrameDataProvider,
+ mojom::IsolatedXRGamepadProvider {
public:
using RequestSessionCallback =
- base::OnceCallback<void(bool result,
- mojom::VRSubmitFrameClientRequest,
- mojom::VRPresentationProviderPtrInfo,
- mojom::VRDisplayFrameTransportOptionsPtr)>;
-
- OculusRenderLoop(
- base::RepeatingCallback<void()> on_presentation_ended,
- base::RepeatingCallback<
- void(ovrInputState, ovrInputState, ovrTrackingState, bool, bool)>
- on_controller_updated);
+ base::OnceCallback<void(bool result, mojom::XRSessionPtr)>;
+
+ OculusRenderLoop(base::RepeatingCallback<void()> on_presentation_ended);
~OculusRenderLoop() override;
- void RequestSession(mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ void RequestSession(mojom::XRRuntimeSessionOptionsPtr options,
RequestSessionCallback callback);
void ExitPresent();
base::WeakPtr<OculusRenderLoop> GetWeakPtr();
- // VRPresentationProvider overrides:
+ // IsolatedXRGamepadProvider
+ void RequestUpdate(mojom::IsolatedXRGamepadProvider::RequestUpdateCallback
+ callback) override;
+
+ // XRPresentationProvider overrides:
void SubmitFrameMissing(int16_t frame_index, const gpu::SyncToken&) override;
void SubmitFrame(int16_t frame_index,
const gpu::MailboxHolder& mailbox,
@@ -58,7 +58,11 @@ class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
const gfx::RectF& right_bounds,
const gfx::Size& source_size) override;
void GetFrameData(
- mojom::VRPresentationProvider::GetFrameDataCallback callback) override;
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
+
+ // Bind a gamepad provider on the render loop thread, so we can provide
+ // updates with the latest poses used for rendering.
+ void RequestGamepadProvider(mojom::IsolatedXRGamepadProviderRequest request);
private:
// base::Thread overrides:
@@ -91,6 +95,8 @@ class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
base::OnceCallback<void()> delayed_get_frame_data_callback_;
bool has_outstanding_frame_ = false;
+ mojom::IsolatedXRGamepadProvider::RequestUpdateCallback gamepad_callback_;
+
long long ovr_frame_index_ = 0;
int16_t next_frame_id_ = 0;
bool is_presenting_ = false;
@@ -98,18 +104,18 @@ class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
gfx::RectF right_bounds_;
gfx::Size source_size_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
- mojom::VRSubmitFrameClientPtr submit_client_;
+ mojom::XRPresentationClientPtr submit_client_;
ovrSession session_ = nullptr;
ovrGraphicsLuid luid_ = {};
ovrPosef last_render_pose_;
ovrTextureSwapChain texture_swap_chain_ = 0;
double sensor_time_;
- mojo::Binding<mojom::VRPresentationProvider> binding_;
+ mojo::Binding<mojom::XRPresentationProvider> presentation_binding_;
+ mojo::Binding<mojom::XRFrameDataProvider> frame_data_binding_;
bool primary_input_pressed[kMaxOculusRenderLoopInputId];
base::RepeatingCallback<void()> on_presentation_ended_;
- base::RepeatingCallback<
- void(ovrInputState, ovrInputState, ovrTrackingState, bool, bool)>
- on_controller_updated_;
+
+ mojo::Binding<mojom::IsolatedXRGamepadProvider> gamepad_provider_;
base::WeakPtrFactory<OculusRenderLoop> weak_ptr_factory_;
diff --git a/chromium/device/vr/openvr/openvr_device.cc b/chromium/device/vr/openvr/openvr_device.cc
index cbab3b27b52..8334f97e40a 100644
--- a/chromium/device/vr/openvr/openvr_device.cc
+++ b/chromium/device/vr/openvr/openvr_device.cc
@@ -10,7 +10,6 @@
#include "base/memory/ptr_util.h"
#include "base/numerics/math_constants.h"
#include "build/build_config.h"
-#include "device/vr/openvr/openvr_gamepad_data_fetcher.h"
#include "device/vr/openvr/openvr_render_loop.h"
#include "device/vr/openvr/openvr_type_converters.h"
#include "third_party/openvr/src/headers/openvr.h"
@@ -63,9 +62,9 @@ std::vector<float> HmdMatrix34ToWebVRTransformMatrix(
}
mojom::VRDisplayInfoPtr CreateVRDisplayInfo(vr::IVRSystem* vr_system,
- unsigned int id) {
+ device::mojom::XRDeviceId id) {
mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
- display_info->index = id;
+ display_info->id = id;
display_info->displayName =
GetOpenVRString(vr_system, vr::Prop_ManufacturerName_String) + " " +
GetOpenVRString(vr_system, vr::Prop_ModelNumber_String);
@@ -129,9 +128,10 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(vr::IVRSystem* vr_system,
} // namespace
OpenVRDevice::OpenVRDevice()
- : VRDeviceBase(VRDeviceId::OPENVR_DEVICE_ID),
+ : VRDeviceBase(device::mojom::XRDeviceId::OPENVR_DEVICE_ID),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
exclusive_controller_binding_(this),
+ gamepad_provider_factory_binding_(this),
weak_ptr_factory_(this) {
// Initialize OpenVR.
openvr_ = std::make_unique<OpenVRWrapper>(false /* presenting */);
@@ -142,20 +142,15 @@ OpenVRDevice::OpenVRDevice()
SetVRDisplayInfo(CreateVRDisplayInfo(openvr_->GetSystem(), GetId()));
- render_loop_ = std::make_unique<OpenVRRenderLoop>(base::BindRepeating(
- &OpenVRDevice::OnGamepadUpdated, weak_ptr_factory_.GetWeakPtr()));
+ render_loop_ = std::make_unique<OpenVRRenderLoop>();
OnPollingEvents();
}
-void OpenVRDevice::OnGamepadUpdated(OpenVRGamepadState state) {
- if (gamepad_data_fetcher_) {
- gamepad_data_fetcher_->UpdateGamepadData(state);
- }
-}
-
-void OpenVRDevice::RegisterDataFetcher(OpenVRGamepadDataFetcher* fetcher) {
- gamepad_data_fetcher_ = fetcher;
+mojom::IsolatedXRGamepadProviderFactoryPtr OpenVRDevice::BindGamepadFactory() {
+ mojom::IsolatedXRGamepadProviderFactoryPtr ret;
+ gamepad_provider_factory_binding_.Bind(mojo::MakeRequest(&ret));
+ return ret;
}
OpenVRDevice::~OpenVRDevice() {
@@ -171,14 +166,27 @@ void OpenVRDevice::Shutdown() {
}
void OpenVRDevice::RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
- if (!render_loop_->IsRunning())
- render_loop_->Start();
+ if (!options->immersive) {
+ ReturnNonImmersiveSession(std::move(callback));
+ return;
+ }
if (!render_loop_->IsRunning()) {
- std::move(callback).Run(nullptr, nullptr);
- return;
+ render_loop_->Start();
+
+ if (!render_loop_->IsRunning()) {
+ std::move(callback).Run(nullptr, nullptr);
+ return;
+ }
+
+ if (provider_request_) {
+ render_loop_->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&OpenVRRenderLoop::RequestGamepadProvider,
+ render_loop_->GetWeakPtr(),
+ std::move(provider_request_)));
+ }
}
// We are done using OpenVR until the presentation session ends.
@@ -211,9 +219,7 @@ void OpenVRDevice::OnPresentationEnded() {
void OpenVRDevice::OnRequestSessionResult(
mojom::XRRuntime::RequestSessionCallback callback,
bool result,
- mojom::VRSubmitFrameClientRequest request,
- mojom::VRPresentationProviderPtrInfo provider_info,
- mojom::VRDisplayFrameTransportOptionsPtr transport_options) {
+ mojom::XRSessionPtr session) {
if (!result) {
OnPresentationEnded();
std::move(callback).Run(nullptr, nullptr);
@@ -222,11 +228,6 @@ void OpenVRDevice::OnRequestSessionResult(
OnStartPresenting();
- auto connection = mojom::XRPresentationConnection::New();
- connection->client_request = std::move(request);
- connection->provider = std::move(provider_info);
- connection->transport_options = std::move(transport_options);
-
mojom::XRSessionControllerPtr session_controller;
exclusive_controller_binding_.Bind(mojo::MakeRequest(&session_controller));
@@ -236,7 +237,21 @@ void OpenVRDevice::OnRequestSessionResult(
base::BindOnce(&OpenVRDevice::OnPresentingControllerMojoConnectionError,
base::Unretained(this)));
- std::move(callback).Run(std::move(connection), std::move(session_controller));
+ session->display_info = display_info_.Clone();
+
+ std::move(callback).Run(std::move(session), std::move(session_controller));
+}
+
+void OpenVRDevice::GetIsolatedXRGamepadProvider(
+ mojom::IsolatedXRGamepadProviderRequest provider_request) {
+ if (render_loop_->IsRunning()) {
+ render_loop_->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&OpenVRRenderLoop::RequestGamepadProvider,
+ render_loop_->GetWeakPtr(),
+ std::move(provider_request)));
+ } else {
+ provider_request_ = std::move(provider_request);
+ }
}
// XRSessionController
@@ -249,13 +264,16 @@ void OpenVRDevice::OnPresentingControllerMojoConnectionError() {
render_loop_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&OpenVRRenderLoop::ExitPresent, render_loop_->GetWeakPtr()));
- render_loop_->Stop();
+ // Don't stop the render loop here. We need to keep the gamepad provider alive
+ // so that we don't lose a pending mojo gamepad_callback_.
+ // TODO(https://crbug.com/875187): Alternatively, we could recreate the
+ // provider on the next session, or look into why the callback gets lost.
OnExitPresent();
exclusive_controller_binding_.Close();
}
void OpenVRDevice::OnMagicWindowFrameDataRequest(
- mojom::VRPresentationProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
if (!openvr_) {
std::move(callback).Run(nullptr);
return;
diff --git a/chromium/device/vr/openvr/openvr_device.h b/chromium/device/vr/openvr/openvr_device.h
index c5af92f2451..6a873889e44 100644
--- a/chromium/device/vr/openvr/openvr_device.h
+++ b/chromium/device/vr/openvr/openvr_device.h
@@ -10,7 +10,6 @@
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "device/vr/openvr/openvr_api_wrapper.h"
-#include "device/vr/openvr/openvr_gamepad_data_fetcher.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_device_base.h"
#include "mojo/public/cpp/bindings/binding.h"
@@ -18,11 +17,11 @@
namespace device {
class OpenVRRenderLoop;
-struct OpenVRGamepadState;
-class OpenVRDevice : public VRDeviceBase,
- public mojom::XRSessionController,
- public OpenVRGamepadDataProvider {
+class DEVICE_VR_EXPORT OpenVRDevice
+ : public VRDeviceBase,
+ public mojom::XRSessionController,
+ public mojom::IsolatedXRGamepadProviderFactory {
public:
OpenVRDevice();
~OpenVRDevice() override;
@@ -31,41 +30,43 @@ class OpenVRDevice : public VRDeviceBase,
// VRDeviceBase
void RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) override;
void OnPollingEvents();
- void OnRequestSessionResult(
- mojom::XRRuntime::RequestSessionCallback callback,
- bool result,
- mojom::VRSubmitFrameClientRequest request,
- mojom::VRPresentationProviderPtrInfo provider_info,
- mojom::VRDisplayFrameTransportOptionsPtr transport_options);
+ void OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback,
+ bool result,
+ mojom::XRSessionPtr session);
bool IsInitialized() { return !!openvr_; }
+ mojom::IsolatedXRGamepadProviderFactoryPtr BindGamepadFactory();
+
private:
// VRDeviceBase
void OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
// XRSessionController
void SetFrameDataRestricted(bool restricted) override;
+ // mojom::IsolatedXRGamepadProviderFactory
+ void GetIsolatedXRGamepadProvider(
+ mojom::IsolatedXRGamepadProviderRequest provider_request) override;
+
void OnPresentingControllerMojoConnectionError();
void OnPresentationEnded();
- void RegisterDataFetcher(OpenVRGamepadDataFetcher*) override;
- void OnGamepadUpdated(OpenVRGamepadState state);
-
std::unique_ptr<OpenVRRenderLoop> render_loop_;
- mojom::VRDisplayInfoPtr display_info_;
std::unique_ptr<OpenVRWrapper> openvr_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_;
- OpenVRGamepadDataFetcher* gamepad_data_fetcher_ = nullptr;
+
+ mojo::Binding<mojom::IsolatedXRGamepadProviderFactory>
+ gamepad_provider_factory_binding_;
+ mojom::IsolatedXRGamepadProviderRequest provider_request_;
base::WeakPtrFactory<OpenVRDevice> weak_ptr_factory_;
diff --git a/chromium/device/vr/openvr/openvr_device_provider.cc b/chromium/device/vr/openvr/openvr_device_provider.cc
index 2e1e55a7e73..377eca334ba 100644
--- a/chromium/device/vr/openvr/openvr_device_provider.cc
+++ b/chromium/device/vr/openvr/openvr_device_provider.cc
@@ -6,9 +6,9 @@
#include "base/metrics/histogram_macros.h"
#include "device/gamepad/gamepad_data_fetcher_manager.h"
+#include "device/vr/isolated_gamepad_data_fetcher.h"
#include "device/vr/openvr/openvr_api_wrapper.h"
#include "device/vr/openvr/openvr_device.h"
-#include "device/vr/openvr/openvr_gamepad_data_fetcher.h"
#include "device/vr/openvr/test/test_hook.h"
#include "third_party/openvr/src/headers/openvr.h"
@@ -37,15 +37,17 @@ OpenVRDeviceProvider::~OpenVRDeviceProvider() {
}
void OpenVRDeviceProvider::Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId)>
+ remove_device_callback,
base::OnceClosure initialization_complete) {
CreateDevice();
- if (device_)
+ if (device_) {
add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(),
device_->BindXRRuntimePtr());
+ }
initialized_ = true;
std::move(initialization_complete).Run();
}
@@ -61,7 +63,9 @@ void OpenVRDeviceProvider::CreateDevice() {
device_ = std::make_unique<OpenVRDevice>();
if (device_->IsInitialized()) {
GamepadDataFetcherManager::GetInstance()->AddFactory(
- new OpenVRGamepadDataFetcher::Factory(device_->GetId(), device_.get()));
+ new IsolatedGamepadDataFetcher::Factory(
+ device::mojom::XRDeviceId::OPENVR_DEVICE_ID,
+ device_->BindGamepadFactory()));
} else {
device_ = nullptr;
}
diff --git a/chromium/device/vr/openvr/openvr_device_provider.h b/chromium/device/vr/openvr/openvr_device_provider.h
index 7dc8229ddc4..ed9f42855a1 100644
--- a/chromium/device/vr/openvr/openvr_device_provider.h
+++ b/chromium/device/vr/openvr/openvr_device_provider.h
@@ -23,10 +23,11 @@ class DEVICE_VR_EXPORT OpenVRDeviceProvider : public VRDeviceProvider {
~OpenVRDeviceProvider() override;
void Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(device::mojom::XRDeviceId)>
+ remove_device_callback,
base::OnceClosure initialization_complete) override;
bool Initialized() override;
diff --git a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc b/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc
deleted file mode 100644
index 0f301411ee0..00000000000
--- a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-// 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/vr/openvr/openvr_gamepad_data_fetcher.h"
-
-#include <memory>
-
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "device/gamepad/public/cpp/gamepads.h"
-#include "third_party/openvr/src/headers/openvr.h"
-#include "ui/gfx/transform.h"
-#include "ui/gfx/transform_util.h"
-
-namespace device {
-
-namespace {
-
-void SetGamepadButton(Gamepad* pad,
- const vr::VRControllerState_t& controller_state,
- uint64_t supported_buttons,
- vr::EVRButtonId button_id) {
- uint64_t button_mask = vr::ButtonMaskFromId(button_id);
- if ((supported_buttons & button_mask) != 0) {
- bool button_pressed = (controller_state.ulButtonPressed & button_mask) != 0;
- bool button_touched = (controller_state.ulButtonTouched & button_mask) != 0;
- pad->buttons[pad->buttons_length].touched = button_touched;
- pad->buttons[pad->buttons_length].pressed = button_pressed;
- pad->buttons[pad->buttons_length].value = button_pressed ? 1.0 : 0.0;
- pad->buttons_length++;
- }
-}
-
-} // namespace
-
-OpenVRGamepadDataFetcher::Factory::Factory(unsigned int display_id,
- OpenVRGamepadDataProvider* provider)
- : display_id_(display_id), provider_(provider) {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-}
-
-OpenVRGamepadDataFetcher::Factory::~Factory() {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-}
-
-std::unique_ptr<GamepadDataFetcher>
-OpenVRGamepadDataFetcher::Factory::CreateDataFetcher() {
- return std::make_unique<OpenVRGamepadDataFetcher>(display_id_, provider_);
-}
-
-GamepadSource OpenVRGamepadDataFetcher::Factory::source() {
- return GAMEPAD_SOURCE_OPENVR;
-}
-
-OpenVRGamepadDataFetcher::OpenVRGamepadDataFetcher(
- unsigned int display_id,
- OpenVRGamepadDataProvider* provider)
- : display_id_(display_id) {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-
- // Register for updates.
- provider->RegisterDataFetcher(this);
-}
-
-OpenVRGamepadDataFetcher::~OpenVRGamepadDataFetcher() {
- DVLOG(1) << __FUNCTION__ << "=" << this;
-}
-
-GamepadSource OpenVRGamepadDataFetcher::source() {
- return GAMEPAD_SOURCE_OPENVR;
-}
-
-void OpenVRGamepadDataFetcher::OnAddedToProvider() {}
-
-void OpenVRGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
- base::AutoLock lock(lock_);
-
- vr::TrackedDevicePose_t* tracked_devices_poses = data_.tracked_devices_poses;
- for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) {
- if (data_.device_class[i] != vr::TrackedDeviceClass_Controller)
- continue;
-
- PadState* state = GetPadState(i);
- if (!state)
- continue;
-
- Gamepad& pad = state->data;
-
- vr::VRControllerState_t& controller_state = data_.controller_state[i];
- if (data_.have_controller_state[i]) {
- pad.timestamp = CurrentTimeInMicroseconds();
- pad.connected = true;
- pad.is_xr = true;
-
- pad.pose.not_null = true;
-
- pad.pose.has_orientation = true;
- pad.pose.has_position = true;
-
- // The defacto standard says we set id to "OpenVR Gamepad". WebXR input
- // will provide a better solution.
- swprintf(pad.id, Gamepad::kIdLengthCap, L"OpenVR Gamepad");
- swprintf(pad.mapping, Gamepad::kMappingLengthCap, L"");
-
- pad.display_id = display_id_;
-
- vr::ETrackedControllerRole hand = data_.hand[i];
-
- switch (hand) {
- case vr::TrackedControllerRole_Invalid:
- pad.hand = GamepadHand::kNone;
- break;
- case vr::TrackedControllerRole_LeftHand:
- pad.hand = GamepadHand::kLeft;
- break;
- case vr::TrackedControllerRole_RightHand:
- pad.hand = GamepadHand::kRight;
- break;
- }
-
- uint64_t supported_buttons = data_.supported_buttons[i];
-
- pad.buttons_length = 0;
- pad.axes_length = 0;
-
- for (unsigned int j = 0; j < vr::k_unControllerStateAxisCount; ++j) {
- int32_t axis_type = data_.axis_type[i][j];
- switch (axis_type) {
- case vr::k_eControllerAxis_Joystick:
- case vr::k_eControllerAxis_TrackPad:
- pad.axes[pad.axes_length++] = controller_state.rAxis[j].x;
- pad.axes[pad.axes_length++] = controller_state.rAxis[j].y;
-
- SetGamepadButton(
- &pad, controller_state, supported_buttons,
- static_cast<vr::EVRButtonId>(vr::k_EButton_Axis0 + j));
-
- break;
- case vr::k_eControllerAxis_Trigger:
- pad.buttons[pad.buttons_length].value = controller_state.rAxis[j].x;
-
- uint64_t button_mask = vr::ButtonMaskFromId(
- static_cast<vr::EVRButtonId>(vr::k_EButton_Axis0 + j));
- if ((supported_buttons & button_mask) != 0) {
- pad.buttons[pad.buttons_length].pressed =
- (controller_state.ulButtonPressed & button_mask) != 0;
- }
-
- pad.buttons_length++;
- break;
- }
- }
-
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_A);
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_Grip);
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_ApplicationMenu);
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_DPad_Left);
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_DPad_Up);
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_DPad_Right);
- SetGamepadButton(&pad, controller_state, supported_buttons,
- vr::k_EButton_DPad_Down);
- }
-
- const vr::TrackedDevicePose_t& pose = tracked_devices_poses[i];
- if (pose.bPoseIsValid) {
- const vr::HmdMatrix34_t& mat = pose.mDeviceToAbsoluteTracking;
- gfx::Transform transform(
- mat.m[0][0], mat.m[0][1], mat.m[0][2], mat.m[0][3], mat.m[1][0],
- mat.m[1][1], mat.m[1][2], mat.m[1][3], mat.m[2][0], mat.m[2][1],
- mat.m[2][2], mat.m[2][3], 0, 0, 0, 1);
-
- gfx::DecomposedTransform decomposed_transform;
- gfx::DecomposeTransform(&decomposed_transform, transform);
-
- pad.pose.orientation.not_null = true;
- pad.pose.orientation.x = decomposed_transform.quaternion.x();
- pad.pose.orientation.y = decomposed_transform.quaternion.y();
- pad.pose.orientation.z = decomposed_transform.quaternion.z();
- pad.pose.orientation.w = decomposed_transform.quaternion.w();
-
- pad.pose.position.not_null = true;
- pad.pose.position.x = decomposed_transform.translate[0];
- pad.pose.position.y = decomposed_transform.translate[1];
- pad.pose.position.z = decomposed_transform.translate[2];
-
- pad.pose.angular_velocity.not_null = true;
- pad.pose.angular_velocity.x = pose.vAngularVelocity.v[0];
- pad.pose.angular_velocity.y = pose.vAngularVelocity.v[1];
- pad.pose.angular_velocity.z = pose.vAngularVelocity.v[2];
-
- pad.pose.linear_velocity.not_null = true;
- pad.pose.linear_velocity.x = pose.vVelocity.v[0];
- pad.pose.linear_velocity.y = pose.vVelocity.v[1];
- pad.pose.linear_velocity.z = pose.vVelocity.v[2];
- } else {
- pad.pose.orientation.not_null = false;
- pad.pose.position.not_null = false;
- pad.pose.angular_velocity.not_null = false;
- pad.pose.linear_velocity.not_null = false;
- }
- }
-}
-
-void OpenVRGamepadDataFetcher::UpdateGamepadData(OpenVRGamepadState data) {
- base::AutoLock lock(lock_);
- data_ = data;
-}
-
-void OpenVRGamepadDataFetcher::PauseHint(bool paused) {}
-
-} // namespace device
diff --git a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h b/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h
deleted file mode 100644
index 4df8cb612b1..00000000000
--- a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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_VR_OPENVR_GAMEPAD_DATA_FETCHER_H_
-#define DEVICE_VR_OPENVR_GAMEPAD_DATA_FETCHER_H_
-
-#include "device/gamepad/gamepad_data_fetcher.h"
-#include "third_party/openvr/src/headers/openvr.h"
-
-namespace device {
-
-class OpenVRGamepadDataFetcher;
-
-class OpenVRGamepadDataProvider {
- public:
- virtual void RegisterDataFetcher(OpenVRGamepadDataFetcher*) = 0;
-};
-
-struct OpenVRGamepadState {
- int32_t axis_type[vr::k_unMaxTrackedDeviceCount]
- [vr::k_unControllerStateAxisCount];
- int32_t ButtonMaskFromId[vr::k_unMaxTrackedDeviceCount]
- [vr::k_unControllerStateAxisCount];
-
- vr::TrackedDevicePose_t tracked_devices_poses[vr::k_unMaxTrackedDeviceCount];
- vr::ETrackedDeviceClass device_class[vr::k_unMaxTrackedDeviceCount];
- vr::VRControllerState_t controller_state[vr::k_unMaxTrackedDeviceCount];
- vr::ETrackedControllerRole hand[vr::k_unMaxTrackedDeviceCount];
- uint64_t supported_buttons[vr::k_unMaxTrackedDeviceCount];
-
- bool have_controller_state[vr::k_unMaxTrackedDeviceCount] = {};
-};
-
-class OpenVRGamepadDataFetcher : public GamepadDataFetcher {
- public:
- class Factory : public GamepadDataFetcherFactory {
- public:
- Factory(unsigned int display_id, OpenVRGamepadDataProvider*);
- ~Factory() override;
- std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
- GamepadSource source() override;
-
- private:
- unsigned int display_id_;
- OpenVRGamepadDataProvider* provider_;
- };
-
- OpenVRGamepadDataFetcher(unsigned int display_id, OpenVRGamepadDataProvider*);
- ~OpenVRGamepadDataFetcher() override;
-
- GamepadSource source() override;
-
- void GetGamepadData(bool devices_changed_hint) override;
- void PauseHint(bool paused) override;
- void OnAddedToProvider() override;
-
- void UpdateGamepadData(OpenVRGamepadState); // Called on UI thread.
-
- private:
- unsigned int display_id_;
-
- // Protects access to data_, which is read/written on different threads.
- base::Lock lock_;
-
- OpenVRGamepadState data_;
-
- DISALLOW_COPY_AND_ASSIGN(OpenVRGamepadDataFetcher);
-};
-
-} // namespace device
-#endif // DEVICE_VR_OPENVR_GAMEPAD_DATA_FETCHER_H_
diff --git a/chromium/device/vr/openvr/openvr_gamepad_helper.cc b/chromium/device/vr/openvr/openvr_gamepad_helper.cc
new file mode 100644
index 00000000000..2dd4d2f07dd
--- /dev/null
+++ b/chromium/device/vr/openvr/openvr_gamepad_helper.cc
@@ -0,0 +1,171 @@
+// 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/vr/openvr/openvr_gamepad_helper.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "device/gamepad/public/cpp/gamepads.h"
+#include "device/vr/vr_device.h"
+#include "third_party/openvr/src/headers/openvr.h"
+#include "ui/gfx/transform.h"
+#include "ui/gfx/transform_util.h"
+
+namespace device {
+
+namespace {
+
+mojom::XRGamepadButtonPtr GetGamepadButton(
+ const vr::VRControllerState_t& controller_state,
+ uint64_t supported_buttons,
+ vr::EVRButtonId button_id) {
+ uint64_t button_mask = vr::ButtonMaskFromId(button_id);
+ if ((supported_buttons & button_mask) != 0) {
+ auto ret = mojom::XRGamepadButton::New();
+ bool button_pressed = (controller_state.ulButtonPressed & button_mask) != 0;
+ bool button_touched = (controller_state.ulButtonTouched & button_mask) != 0;
+ ret->touched = button_touched;
+ ret->pressed = button_pressed;
+ ret->value = button_pressed ? 1.0 : 0.0;
+ return ret;
+ }
+
+ return nullptr;
+}
+
+} // namespace
+
+mojom::XRGamepadDataPtr OpenVRGamepadHelper::GetGamepadData(
+ vr::IVRSystem* vr_system) {
+ mojom::XRGamepadDataPtr ret = mojom::XRGamepadData::New();
+
+ vr::TrackedDevicePose_t tracked_devices_poses[vr::k_unMaxTrackedDeviceCount];
+ vr_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0.0f,
+ tracked_devices_poses,
+ vr::k_unMaxTrackedDeviceCount);
+ for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) {
+ if (vr_system->GetTrackedDeviceClass(i) !=
+ vr::TrackedDeviceClass_Controller)
+ continue;
+
+ vr::VRControllerState_t controller_state;
+ bool have_state = vr_system->GetControllerState(i, &controller_state,
+ sizeof(controller_state));
+ if (!have_state)
+ continue;
+
+ auto gamepad = mojom::XRGamepad::New();
+ gamepad->controller_id = i;
+
+ vr::ETrackedControllerRole hand =
+ vr_system->GetControllerRoleForTrackedDeviceIndex(i);
+ switch (hand) {
+ case vr::TrackedControllerRole_Invalid:
+ gamepad->hand = device::mojom::XRHandedness::NONE;
+ break;
+ case vr::TrackedControllerRole_LeftHand:
+ gamepad->hand = device::mojom::XRHandedness::LEFT;
+ break;
+ case vr::TrackedControllerRole_RightHand:
+ gamepad->hand = device::mojom::XRHandedness::RIGHT;
+ break;
+ }
+
+ uint64_t supported_buttons = vr_system->GetUint64TrackedDeviceProperty(
+ i, vr::Prop_SupportedButtons_Uint64);
+ for (uint32_t j = 0; j < vr::k_unControllerStateAxisCount; ++j) {
+ int32_t axis_type = vr_system->GetInt32TrackedDeviceProperty(
+ i,
+ static_cast<vr::TrackedDeviceProperty>(vr::Prop_Axis0Type_Int32 + j));
+ switch (axis_type) {
+ case vr::k_eControllerAxis_Joystick:
+ case vr::k_eControllerAxis_TrackPad: {
+ gamepad->axes.push_back(controller_state.rAxis[j].x);
+ gamepad->axes.push_back(controller_state.rAxis[j].y);
+ auto button = GetGamepadButton(
+ controller_state, supported_buttons,
+ static_cast<vr::EVRButtonId>(vr::k_EButton_Axis0 + j));
+ if (button) {
+ gamepad->buttons.push_back(std::move(button));
+ }
+ } break;
+ case vr::k_eControllerAxis_Trigger: {
+ auto button = mojom::XRGamepadButton::New();
+ button->value = controller_state.rAxis[j].x;
+ uint64_t button_mask = vr::ButtonMaskFromId(
+ static_cast<vr::EVRButtonId>(vr::k_EButton_Axis0 + j));
+ if ((supported_buttons & button_mask) != 0) {
+ button->pressed =
+ (controller_state.ulButtonPressed & button_mask) != 0;
+ }
+ gamepad->buttons.push_back(std::move(button));
+ } break;
+ }
+ }
+
+ auto button =
+ GetGamepadButton(controller_state, supported_buttons, vr::k_EButton_A);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+ button = GetGamepadButton(controller_state, supported_buttons,
+ vr::k_EButton_Grip);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+ button = GetGamepadButton(controller_state, supported_buttons,
+ vr::k_EButton_ApplicationMenu);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+ button = GetGamepadButton(controller_state, supported_buttons,
+ vr::k_EButton_DPad_Left);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+ button = GetGamepadButton(controller_state, supported_buttons,
+ vr::k_EButton_DPad_Up);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+ button = GetGamepadButton(controller_state, supported_buttons,
+ vr::k_EButton_DPad_Right);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+ button = GetGamepadButton(controller_state, supported_buttons,
+ vr::k_EButton_DPad_Down);
+ if (button)
+ gamepad->buttons.push_back(std::move(button));
+
+ const vr::TrackedDevicePose_t& pose = tracked_devices_poses[i];
+ if (pose.bPoseIsValid) {
+ const vr::HmdMatrix34_t& mat = pose.mDeviceToAbsoluteTracking;
+ gfx::Transform transform(
+ mat.m[0][0], mat.m[0][1], mat.m[0][2], mat.m[0][3], mat.m[1][0],
+ mat.m[1][1], mat.m[1][2], mat.m[1][3], mat.m[2][0], mat.m[2][1],
+ mat.m[2][2], mat.m[2][3], 0, 0, 0, 1);
+
+ gfx::DecomposedTransform src_pose;
+ gfx::DecomposeTransform(&src_pose, transform);
+ auto dst_pose = mojom::VRPose::New();
+
+ dst_pose->orientation = std::vector<float>(
+ {src_pose.quaternion.x(), src_pose.quaternion.y(),
+ src_pose.quaternion.z(), src_pose.quaternion.w()});
+ dst_pose->position =
+ std::vector<float>({src_pose.translate[0], src_pose.translate[1],
+ src_pose.translate[2]});
+ dst_pose->angularVelocity = std::vector<float>(
+ {pose.vAngularVelocity.v[0], pose.vAngularVelocity.v[1],
+ pose.vAngularVelocity.v[2]});
+ dst_pose->linearVelocity = std::vector<float>(
+ {pose.vVelocity.v[0], pose.vVelocity.v[1], pose.vVelocity.v[2]});
+
+ gamepad->pose = std::move(dst_pose);
+ }
+
+ ret->gamepads.push_back(std::move(gamepad));
+ }
+
+ return ret;
+}
+
+} // namespace device
diff --git a/chromium/device/vr/openvr/openvr_gamepad_helper.h b/chromium/device/vr/openvr/openvr_gamepad_helper.h
new file mode 100644
index 00000000000..3bc7879de6a
--- /dev/null
+++ b/chromium/device/vr/openvr/openvr_gamepad_helper.h
@@ -0,0 +1,19 @@
+// 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_VR_OPENVR_OPENVR_GAMEPAD_HELPER_H_
+#define DEVICE_VR_OPENVR_OPENVR_GAMEPAD_HELPER_H_
+
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
+#include "third_party/openvr/src/headers/openvr.h"
+
+namespace device {
+
+class OpenVRGamepadHelper {
+ public:
+ static mojom::XRGamepadDataPtr GetGamepadData(vr::IVRSystem* system);
+};
+
+} // namespace device
+#endif // DEVICE_VR_OPENVR_OPENVR_GAMEPAD_HELPER_H_
diff --git a/chromium/device/vr/openvr/openvr_render_loop.cc b/chromium/device/vr/openvr/openvr_render_loop.cc
index fa37e4db58e..2fae5df382d 100644
--- a/chromium/device/vr/openvr/openvr_render_loop.cc
+++ b/chromium/device/vr/openvr/openvr_render_loop.cc
@@ -6,7 +6,7 @@
#include "base/metrics/histogram_functions.h"
#include "device/vr/openvr/openvr_api_wrapper.h"
-#include "device/vr/openvr/openvr_gamepad_data_fetcher.h"
+#include "device/vr/openvr/openvr_gamepad_helper.h"
#include "device/vr/openvr/openvr_type_converters.h"
#include "ui/gfx/geometry/angle_conversions.h"
#include "ui/gfx/transform.h"
@@ -45,12 +45,12 @@ gfx::Transform HmdMatrix34ToTransform(const vr::HmdMatrix34_t& mat) {
} // namespace
-OpenVRRenderLoop::OpenVRRenderLoop(
- base::RepeatingCallback<void(OpenVRGamepadState)> update_gamepad)
+OpenVRRenderLoop::OpenVRRenderLoop()
: base::Thread("OpenVRRenderLoop"),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- update_gamepad_(std::move(update_gamepad)),
- binding_(this),
+ presentation_binding_(this),
+ frame_data_binding_(this),
+ gamepad_provider_(this),
weak_ptr_factory_(this) {
DCHECK(main_thread_task_runner_);
}
@@ -148,7 +148,15 @@ void OpenVRRenderLoop::SubmitFrameWithTextureHandle(
void OpenVRRenderLoop::CleanUp() {
submit_client_ = nullptr;
- binding_.Close();
+ presentation_binding_.Close();
+ frame_data_binding_.Close();
+ gamepad_provider_.Close();
+}
+
+void OpenVRRenderLoop::RequestGamepadProvider(
+ mojom::IsolatedXRGamepadProviderRequest request) {
+ gamepad_provider_.Close();
+ gamepad_provider_.Bind(std::move(request));
}
void OpenVRRenderLoop::UpdateLayerBounds(int16_t frame_id,
@@ -166,18 +174,18 @@ void OpenVRRenderLoop::UpdateLayerBounds(int16_t frame_id,
void OpenVRRenderLoop::RequestSession(
base::OnceCallback<void()> on_presentation_ended,
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
RequestSessionCallback callback) {
DCHECK(options->immersive);
- binding_.Close();
+ presentation_binding_.Close();
+ frame_data_binding_.Close();
if (!openvr_) {
openvr_ = std::make_unique<OpenVRWrapper>(true);
if (!openvr_->IsInitialized()) {
openvr_ = nullptr;
main_thread_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), false, nullptr,
- nullptr, nullptr));
+ FROM_HERE, base::BindOnce(std::move(callback), false, nullptr));
return;
}
@@ -193,20 +201,22 @@ void OpenVRRenderLoop::RequestSession(
!texture_helper_.EnsureInitialized()) {
openvr_ = nullptr;
main_thread_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), false, nullptr, nullptr, nullptr));
+ FROM_HERE, base::BindOnce(std::move(callback), false, nullptr));
return;
}
#endif
DCHECK(!on_presentation_ended_);
on_presentation_ended_ = std::move(on_presentation_ended);
- device::mojom::VRPresentationProviderPtr provider;
- binding_.Bind(mojo::MakeRequest(&provider));
- device::mojom::VRDisplayFrameTransportOptionsPtr transport_options =
- device::mojom::VRDisplayFrameTransportOptions::New();
+ device::mojom::XRPresentationProviderPtr presentation_provider;
+ device::mojom::XRFrameDataProviderPtr frame_data_provider;
+ presentation_binding_.Bind(mojo::MakeRequest(&presentation_provider));
+ frame_data_binding_.Bind(mojo::MakeRequest(&frame_data_provider));
+
+ device::mojom::XRPresentationTransportOptionsPtr transport_options =
+ device::mojom::XRPresentationTransportOptions::New();
transport_options->transport_method =
- device::mojom::VRDisplayFrameTransportMethod::SUBMIT_AS_TEXTURE_HANDLE;
+ device::mojom::XRPresentationTransportMethod::SUBMIT_AS_TEXTURE_HANDLE;
// Only set boolean options that we need. Default is false, and we should be
// able to safely ignore ones that our implementation doesn't care about.
transport_options->wait_for_transfer_notification = true;
@@ -220,11 +230,17 @@ void OpenVRRenderLoop::RequestSession(
input_active_state.controller_role = vr::TrackedControllerRole_Invalid;
}
+ auto submit_frame_sink = device::mojom::XRPresentationConnection::New();
+ submit_frame_sink->provider = presentation_provider.PassInterface();
+ submit_frame_sink->client_request = mojo::MakeRequest(&submit_client_);
+ submit_frame_sink->transport_options = std::move(transport_options);
+
+ auto session = device::mojom::XRSession::New();
+ session->data_provider = frame_data_provider.PassInterface();
+ session->submit_frame_sink = std::move(submit_frame_sink);
+
main_thread_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), true,
- mojo::MakeRequest(&submit_client_),
- provider.PassInterface(), std::move(transport_options)));
+ FROM_HERE, base::BindOnce(std::move(callback), true, std::move(session)));
is_presenting_ = true;
openvr_->GetCompositor()->SuspendRendering(false);
@@ -247,7 +263,8 @@ void OpenVRRenderLoop::RequestSession(
void OpenVRRenderLoop::ExitPresent() {
is_presenting_ = false;
- binding_.Close();
+ presentation_binding_.Close();
+ frame_data_binding_.Close();
submit_client_ = nullptr;
if (openvr_)
openvr_->GetCompositor()->SuspendRendering(true);
@@ -266,37 +283,29 @@ void OpenVRRenderLoop::ExitPresent() {
}
void OpenVRRenderLoop::UpdateControllerState() {
- OpenVRGamepadState state = {};
+ if (!gamepad_callback_) {
+ // Nobody is listening to updates, so bail early.
+ return;
+ }
- if (openvr_) {
- vr::IVRSystem* vr_system = openvr_->GetSystem();
- vr_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0.0f,
- state.tracked_devices_poses,
- vr::k_unMaxTrackedDeviceCount);
-
- for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) {
- state.device_class[i] = vr_system->GetTrackedDeviceClass(i);
- if (state.device_class[i] == vr::TrackedDeviceClass_Controller) {
- state.have_controller_state[i] = vr_system->GetControllerState(
- i, &(state.controller_state[i]), sizeof(state.controller_state[i]));
- if (state.have_controller_state[i]) {
- state.hand[i] = vr_system->GetControllerRoleForTrackedDeviceIndex(i);
- state.supported_buttons[i] =
- vr_system->GetUint64TrackedDeviceProperty(
- i, vr::Prop_SupportedButtons_Uint64);
-
- for (int j = 0; j < vr::k_unControllerStateAxisCount; ++j) {
- state.axis_type[i][j] = vr_system->GetInt32TrackedDeviceProperty(
- i, static_cast<vr::TrackedDeviceProperty>(
- vr::Prop_Axis0Type_Int32 + j));
- }
- }
- }
- }
+ if (!openvr_) {
+ std::move(gamepad_callback_).Run(nullptr);
+ return;
}
- main_thread_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(update_gamepad_, state));
+ std::move(gamepad_callback_)
+ .Run(OpenVRGamepadHelper::GetGamepadData(openvr_->GetSystem()));
+}
+
+void OpenVRRenderLoop::RequestUpdate(
+ mojom::IsolatedXRGamepadProvider::RequestUpdateCallback callback) {
+ // Save the callback to resolve next time we update (typically on vsync).
+ gamepad_callback_ = std::move(callback);
+
+ // If we aren't presenting, reply now saying that we have no controllers.
+ if (!is_presenting_) {
+ UpdateControllerState();
+ }
}
mojom::VRPosePtr OpenVRRenderLoop::GetPose() {
@@ -325,7 +334,7 @@ base::WeakPtr<OpenVRRenderLoop> OpenVRRenderLoop::GetWeakPtr() {
}
void OpenVRRenderLoop::GetFrameData(
- mojom::VRPresentationProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
DCHECK(is_presenting_);
if (has_outstanding_frame_) {
diff --git a/chromium/device/vr/openvr/openvr_render_loop.h b/chromium/device/vr/openvr/openvr_render_loop.h
index df7fdc35e1f..bf0715d4abc 100644
--- a/chromium/device/vr/openvr/openvr_render_loop.h
+++ b/chromium/device/vr/openvr/openvr_render_loop.h
@@ -25,25 +25,24 @@ namespace device {
class OpenVRWrapper;
struct OpenVRGamepadState;
-class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
+class OpenVRRenderLoop : public base::Thread,
+ mojom::XRPresentationProvider,
+ mojom::XRFrameDataProvider,
+ mojom::IsolatedXRGamepadProvider {
public:
using RequestSessionCallback =
- base::OnceCallback<void(bool result,
- mojom::VRSubmitFrameClientRequest,
- mojom::VRPresentationProviderPtrInfo,
- mojom::VRDisplayFrameTransportOptionsPtr)>;
+ base::OnceCallback<void(bool result, mojom::XRSessionPtr)>;
- OpenVRRenderLoop(
- base::RepeatingCallback<void(OpenVRGamepadState)> update_gamepad);
+ OpenVRRenderLoop();
~OpenVRRenderLoop() override;
void RequestSession(base::OnceCallback<void()> on_presentation_ended,
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
RequestSessionCallback callback);
void ExitPresent();
base::WeakPtr<OpenVRRenderLoop> GetWeakPtr();
- // VRPresentationProvider overrides:
+ // XRPresentationProvider overrides:
void SubmitFrameMissing(int16_t frame_index, const gpu::SyncToken&) override;
void SubmitFrame(int16_t frame_index,
const gpu::MailboxHolder& mailbox,
@@ -58,7 +57,9 @@ class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
const gfx::RectF& right_bounds,
const gfx::Size& source_size) override;
void GetFrameData(
- VRPresentationProvider::GetFrameDataCallback callback) override;
+ XRFrameDataProvider::GetFrameDataCallback callback) override;
+
+ void RequestGamepadProvider(mojom::IsolatedXRGamepadProviderRequest request);
private:
// base::Thread overrides:
@@ -68,6 +69,10 @@ class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
void ClearPendingFrame();
void UpdateControllerState();
+ // IsolatedXRGamepadProvider
+ void RequestUpdate(mojom::IsolatedXRGamepadProvider::RequestUpdateCallback
+ callback) override;
+
mojom::VRPosePtr GetPose();
std::vector<mojom::XRInputSourceStatePtr> GetInputState(
vr::TrackedDevicePose_t* poses,
@@ -94,11 +99,15 @@ class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
gfx::RectF right_bounds_;
gfx::Size source_size_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
- mojom::VRSubmitFrameClientPtr submit_client_;
+ mojom::XRPresentationClientPtr submit_client_;
base::RepeatingCallback<void(OpenVRGamepadState)> update_gamepad_;
base::OnceCallback<void()> on_presentation_ended_;
+ mojom::IsolatedXRGamepadProvider::RequestUpdateCallback gamepad_callback_;
std::unique_ptr<OpenVRWrapper> openvr_;
- mojo::Binding<mojom::VRPresentationProvider> binding_;
+ mojo::Binding<mojom::XRPresentationProvider> presentation_binding_;
+ mojo::Binding<mojom::XRFrameDataProvider> frame_data_binding_;
+ mojo::Binding<mojom::IsolatedXRGamepadProvider> gamepad_provider_;
+
base::WeakPtrFactory<OpenVRRenderLoop> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OpenVRRenderLoop);
diff --git a/chromium/device/vr/orientation/orientation_device.cc b/chromium/device/vr/orientation/orientation_device.cc
index ee322ddddc6..0d7fb6947ba 100644
--- a/chromium/device/vr/orientation/orientation_device.cc
+++ b/chromium/device/vr/orientation/orientation_device.cc
@@ -23,11 +23,11 @@ using gfx::Vector3dF;
namespace {
static constexpr int kDefaultPumpFrequencyHz = 60;
-mojom::VRDisplayInfoPtr CreateVRDisplayInfo(unsigned int id) {
+mojom::VRDisplayInfoPtr CreateVRDisplayInfo(mojom::XRDeviceId id) {
static const char DEVICE_NAME[] = "VR Orientation Device";
mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
- display_info->index = id;
+ display_info->id = id;
display_info->displayName = DEVICE_NAME;
display_info->capabilities = mojom::VRDisplayCapabilities::New();
display_info->capabilities->hasPosition = false;
@@ -52,7 +52,7 @@ display::Display::Rotation GetRotation() {
VROrientationDevice::VROrientationDevice(
mojom::SensorProviderPtr* sensor_provider,
base::OnceClosure ready_callback)
- : VRDeviceBase(VRDeviceId::ORIENTATION_DEVICE_ID),
+ : VRDeviceBase(mojom::XRDeviceId::ORIENTATION_DEVICE_ID),
ready_callback_(std::move(ready_callback)),
binding_(this) {
(*sensor_provider)
@@ -139,16 +139,16 @@ void VROrientationDevice::HandleSensorError() {
}
void VROrientationDevice::RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
DCHECK(!options->immersive);
- // TODO(offenwanger): Perform a check to see if sensors are available when
- // RequestSession is called for non-immersive sessions.
- std::move(callback).Run(nullptr, nullptr);
+ // TODO(http://crbug.com/695937): Perform a check to see if sensors are
+ // available when RequestSession is called for non-immersive sessions.
+ ReturnNonImmersiveSession(std::move(callback));
}
void VROrientationDevice::OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
mojom::VRPosePtr pose = mojom::VRPose::New();
pose->orientation.emplace(4);
diff --git a/chromium/device/vr/orientation/orientation_device.h b/chromium/device/vr/orientation/orientation_device.h
index f9f3b19a41e..d6f3c329142 100644
--- a/chromium/device/vr/orientation/orientation_device.h
+++ b/chromium/device/vr/orientation/orientation_device.h
@@ -44,12 +44,12 @@ class DEVICE_VR_EXPORT VROrientationDevice : public VRDeviceBase,
// VRDevice
void RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) override;
// VRDeviceBase
void OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
// Indicates whether the device was able to connect to orientation events.
bool IsAvailable() const { return available_; }
diff --git a/chromium/device/vr/orientation/orientation_device_provider.cc b/chromium/device/vr/orientation/orientation_device_provider.cc
index 7f3271569be..5cdf236eb3d 100644
--- a/chromium/device/vr/orientation/orientation_device_provider.cc
+++ b/chromium/device/vr/orientation/orientation_device_provider.cc
@@ -21,10 +21,10 @@ VROrientationDeviceProvider::VROrientationDeviceProvider(
VROrientationDeviceProvider::~VROrientationDeviceProvider() = default;
void VROrientationDeviceProvider::Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback,
base::OnceClosure initialization_complete) {
if (device_ && device_->IsAvailable()) {
add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(),
diff --git a/chromium/device/vr/orientation/orientation_device_provider.h b/chromium/device/vr/orientation/orientation_device_provider.h
index d7772325430..a260c5ba38c 100644
--- a/chromium/device/vr/orientation/orientation_device_provider.h
+++ b/chromium/device/vr/orientation/orientation_device_provider.h
@@ -24,10 +24,10 @@ class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider {
~VROrientationDeviceProvider() override;
void Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(mojom::XRDeviceId,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback,
base::OnceClosure initialization_complete) override;
bool Initialized() override;
@@ -42,7 +42,7 @@ class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider {
std::unique_ptr<VROrientationDevice> device_;
base::RepeatingCallback<
- void(unsigned int, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr)>
+ void(mojom::XRDeviceId, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr)>
add_device_callback_;
base::OnceClosure initialized_callback_;
diff --git a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc
index bccfdc37de1..2909230cf07 100644
--- a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc
+++ b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc
@@ -83,22 +83,26 @@ class VROrientationDeviceProviderTest : public testing::Test {
return init_params;
}
- base::RepeatingCallback<
- void(unsigned int, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr device)>
+ base::RepeatingCallback<void(device::mojom::XRDeviceId,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr device)>
DeviceAndIdCallbackFailIfCalled() {
- return base::BindRepeating([](unsigned int id, mojom::VRDisplayInfoPtr,
+ return base::BindRepeating([](device::mojom::XRDeviceId id,
+ mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr device) { FAIL(); });
};
- base::RepeatingCallback<void(unsigned int)> DeviceIdCallbackFailIfCalled() {
- return base::BindRepeating([](unsigned int id) { FAIL(); });
+ base::RepeatingCallback<void(device::mojom::XRDeviceId)>
+ DeviceIdCallbackFailIfCalled() {
+ return base::BindRepeating([](device::mojom::XRDeviceId id) { FAIL(); });
};
- base::RepeatingCallback<
- void(unsigned int, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr device)>
+ base::RepeatingCallback<void(device::mojom::XRDeviceId,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr device)>
DeviceAndIdCallbackMustBeCalled(base::RunLoop* loop) {
return base::BindRepeating(
- [](base::OnceClosure quit_closure, unsigned int id,
+ [](base::OnceClosure quit_closure, device::mojom::XRDeviceId id,
mojom::VRDisplayInfoPtr info, mojom::XRRuntimePtr device) {
ASSERT_TRUE(device);
ASSERT_TRUE(info);
@@ -107,10 +111,10 @@ class VROrientationDeviceProviderTest : public testing::Test {
loop->QuitClosure());
};
- base::RepeatingCallback<void(unsigned int)> DeviceIdCallbackMustBeCalled(
- base::RunLoop* loop) {
+ base::RepeatingCallback<void(device::mojom::XRDeviceId)>
+ DeviceIdCallbackMustBeCalled(base::RunLoop* loop) {
return base::BindRepeating(
- [](base::OnceClosure quit_closure, unsigned int id) {
+ [](base::OnceClosure quit_closure, device::mojom::XRDeviceId id) {
std::move(quit_closure).Run();
},
loop->QuitClosure());
diff --git a/chromium/device/vr/public/mojom/BUILD.gn b/chromium/device/vr/public/mojom/BUILD.gn
index 06cc4f2320d..a66e48534df 100644
--- a/chromium/device/vr/public/mojom/BUILD.gn
+++ b/chromium/device/vr/public/mojom/BUILD.gn
@@ -14,6 +14,7 @@ mojom_component("mojom") {
]
public_deps = [
+ "//device/gamepad/public/mojom",
"//gpu/ipc/common:interfaces",
"//mojo/public/mojom/base",
"//ui/display/mojo:interfaces",
diff --git a/chromium/device/vr/public/mojom/README.md b/chromium/device/vr/public/mojom/README.md
index 273b0d5e2da..ad481a1b3e8 100644
--- a/chromium/device/vr/public/mojom/README.md
+++ b/chromium/device/vr/public/mojom/README.md
@@ -6,12 +6,31 @@ Some XRRuntimes must live in the browser process, while others must not live in
the browser process. The ones that cannot live in the browser, are hosted in a
service.
+# Supported Session types
+
+## Magic-window:
+Magic window sessions are requested by sites that request poses, but render
+through the normal Chrome compositor pipeline.
+It serves as a basic mode that requires only some way to get orientation poses.
+
+## Immersive:
+Immersive sessions are where the site wishes to request poses, then render
+content back to a display other than chrome. The common case for this is Head
+Mounted Displays (HMD), like Vive, Oculus, or Daydream.
+
+## Environment Integration
+This type of session allows for environment integration by providing functions
+that allow the site to query the environment, such as HitTest. A Environment
+Integration session may also supply data in addition to the pose, such as a
+camera frame.
+
# Renderer <-> Browser interfaces (defined in vr_service.mojom)
VRService - lives in the browser process, corresponds to a single frame. Root
object to obtain other XR objects.
-VRDisplayHost - lives in the browser process. Allows a client to start a
-session (either immersive/exclusive/presenting or magic window).
+XRDevice - lives in the browser process, implemented as XRDeviceImpl. Allows a
+client to start a session (either immersive/exclusive/presenting or
+non-immersive).
VRServiceClient - lives in the renderer process. Is notified when VRDisplays
are connected.
@@ -24,22 +43,22 @@ These interfaces allow communication betwee an XRRuntime and the renderer
process. They may live in the browser process or may live in the isolated
service.
-## Presentation-related:
-Presentation is exclusive access to a headset where a site may display
-a stereo view to the user.
-VRPresentationProvider - lives in the XRDevice process. Implements the details
-for a presentation session, such as submitting frames to the underlying VR API.
+## Data related:
+All sessions need to be able to get data from a XR device.
-VRSubmitFrameClient - lives in the renderer process. Is notified when various
-rendering events occur, so it can reclaim/reuse textures.
+XRFrameDataProvider - lives in the XRDevice process. Provides a way to obtain
+poses and other forms of data needed to render frames.
-## Magic-window related:
-Magic window is a mode where a site may request poses, but renders through the
-normal Chrome compositor pipeline.
+## Presentation related:
+Presentation is exclusive access to a device, where the experience takes over
+the device's display, such as presenting a stereo view in an HMD.
-VRMagicWindowProvider - lives in the XRDevice process. Provides a way to obtain
-poses.
+XRPresentationProvider - lives in the XRDevice process. Implements the details
+for a presentation session, such as submitting frames to the underlying VR API.
+
+XRPresentationClient - lives in the renderer process. Is notified when various
+rendering events occur, so it can reclaim/reuse textures.
# Browser <-> Device interfaces (defined in isolated_xr_service.mojom)
The XRDevice process may be the browser process or an isolated service for
@@ -55,3 +74,11 @@ pause or stop a session (MagicWindow or Presentation).
XRRuntimeEventListener - Lives in the browser process. Exposes runtime events
to the browser.
+
+# Browser <-> XRInput interfaces (defined in isolated_xr_service.mojom)
+IsolatedXRGamepadProvider and IsolatedXRGamepadProviderFactory - Live in the
+XRInput process, and allow GamepadDataFetchers living in the browser process
+to expose data from gamepads that cannot be queried from the browser process.
+
+The XRInput process may be the browser process or a separate process depending
+on the platform.
diff --git a/chromium/device/vr/public/mojom/isolated_xr_service.mojom b/chromium/device/vr/public/mojom/isolated_xr_service.mojom
index a1bcacda7c0..035c4a16a26 100644
--- a/chromium/device/vr/public/mojom/isolated_xr_service.mojom
+++ b/chromium/device/vr/public/mojom/isolated_xr_service.mojom
@@ -6,10 +6,12 @@ module device.mojom;
import "device/vr/public/mojom/vr_service.mojom";
-// The XRSessionController lives in the vr device service, and corresponds to a
-// VRPresentationProvider or a VRMagicWindowProvider. The client is the browser
-// process, which will pause or stop sessions depending events/state such as
-// focus or other tabs requesting presentation.
+const string kVrIsolatedServiceName = "xr_device_service";
+
+// The XRSessionController lives in the xr runtime service, and corresponds to
+// a set of the XRSession bindings. The client is the browser process, which
+// will pause or stop sessions depending events/state such as focus or other
+// tabs requesting immersive sessions.
// Sessions are stopped by closing the mojo connection.
interface XRSessionController {
// A session may be paused temporarily for example when a non-presenting
@@ -18,7 +20,7 @@ interface XRSessionController {
SetFrameDataRestricted(bool restricted);
};
-// The XRRuntimeEventListener lives in the vr device service, and allows the
+// The XRRuntimeEventListener lives in the xr runtime service, and allows the
// browser to listen to state changes about a device.
interface XRRuntimeEventListener {
// A device has changed its display information.
@@ -35,7 +37,7 @@ interface XRRuntimeEventListener {
OnExitPresent();
};
-struct XRDeviceRuntimeSessionOptions {
+struct XRRuntimeSessionOptions {
bool immersive;
bool provide_passthrough_camera;
@@ -56,24 +58,16 @@ struct XRDeviceRuntimeSessionOptions {
// An XRRuntime may live in the browser process or a utility process. The
// browser process is the client, and may in turn expose device information to
-// render processes using vr_service interfaces, such as VRDisplayHost.
+// render processes using vr_service interfaces, such as XRDevice.
interface XRRuntime {
- // Attempt to start a presentation session. Clients may submit graphics to be
- // displayed in the headset. Called by the browser process, but the
- // VRPresentationProvider may be passed to the renderer process to allow
- // submitting graphics without going through an extra IPC hop through the
- // browser process.
- RequestSession(XRDeviceRuntimeSessionOptions options) => (
- device.mojom.XRPresentationConnection? connection,
+ // Attempt to start a session. Called by the browser process, but the result
+ // will probably be passed to the renderer process to allow getting data and
+ // possibly submitting graphics without going through an extra IPC hop through
+ // the browser process.
+ RequestSession(XRRuntimeSessionOptions options) => (
+ XRSession? session,
XRSessionController? controller);
- // Attempt to start a "magic window" session. Magic window sessions allow
- // Clients to obtain poses (device position and orientation), but rendering
- // goes through the standard Chrome compositor.
- RequestMagicWindowSession() =>
- (device.mojom.VRMagicWindowProvider? session,
- device.mojom.XRSessionController? controller);
-
// The browser may register for changes to a device. Initial VRDisplayInfo
// will immediately be returned to the listener to prevent races.
ListenToDeviceChanges(XRRuntimeEventListener listener) =>
@@ -81,3 +75,93 @@ interface XRRuntime {
SetListeningForActivate(bool listen_for_activation);
};
+
+// Represents the state of a single button or trigger.
+struct XRGamepadButton {
+ bool pressed; // Is the button currently pressed from its default position?
+ bool touched; // Is the user in contact with a button (always true if pressed)
+ double value; // How far pressed is it, from 0 to 1?
+};
+
+// Represents the state of a single controller.
+struct XRGamepad {
+ bool can_provide_orientation; // Is the controller capable of orientation?
+ bool can_provide_position; // Is the controller capable of position?
+ array<double> axes;
+ array<XRGamepadButton> buttons;
+
+ // The position/orientation of a controller, and its velocity and acceleration
+ // if available. Members inside this may be null if not available currently.
+ VRPose? pose;
+
+ // Left/Right handed controller, or none if unknown.
+ XRHandedness hand;
+
+ // A unique (per device_id) id that allows controllers to be tracked between
+ // updates. Useful to identify controllers as they are added/removed.
+ uint32 controller_id;
+};
+
+// Represents the state of a set of controllers driven by some runtime API.
+struct XRGamepadData {
+ array<XRGamepad> gamepads;
+};
+
+// OpenVR and Oculus APIs can't run in the browser process, but the gamepad
+// polling happens there. This interface allows gamepad polling to request
+// data from out-of-process gamepad providers, at the cost of some extra IPC
+// latency. IsolatedXRGamepadProvider is currently implemented in the XRRuntime
+// process, and consumed by the gamepad polling thread in the browser process.
+// It will move to live in a separate XRInput process in the future.
+interface IsolatedXRGamepadProvider {
+ // Consumers should not call RequestUpdate until the previous request returns
+ // to avoid queuing up extra requests if polling and rendering are happening
+ // at different rates. If called while an outstanding request is queued, it
+ // returns immediately with null data.
+ // Returned data is null if we aren't currently getting data from the runtime.
+ RequestUpdate() => (XRGamepadData? data);
+};
+
+// Gamepad providers may come and go as pages request or stop requesting gamepad
+// data. IsolatedXRGamepadProviderFactory allows GamepadDataFetchers to acquire
+// new IsolatedXRGamepadProviders when needed.
+// IsolatedXRGamepadProvider is consumed in the browser process. It is
+// currently implemented in the XRRuntime process, but will move to a separate
+// XRInput process.
+interface IsolatedXRGamepadProviderFactory {
+ // Get the IsolatedXRGamepadProvider for a specific XR runtime API (Oculus, or
+ // OpenVR, which are currently the only two that are hosted outside of the
+ // browser process).
+ GetIsolatedXRGamepadProvider(IsolatedXRGamepadProvider& provider);
+};
+
+// Notify the browser process about a set of runtimes. The browser process
+// implements this interface to be notified about runtime changes from the XR
+// device service.
+interface IsolatedXRRuntimeProviderClient {
+ // Called when runtimes are initially enumerated, or when devices are later
+ // attached and become available.
+ OnDeviceAdded(XRRuntime runtime,
+ IsolatedXRGamepadProviderFactory gamepad_factory,
+ device.mojom.VRDisplayInfo display_info);
+
+ // Called when runtimes become unavailable - for example if the hardware is
+ // disconnected or the APIs notify us that they are shutting down.
+ // device_index corresponds to display_info->index for a previously added
+ // device.
+ OnDeviceRemoved(device.mojom.XRDeviceId device_index);
+
+ // Called once after all the initial OnDeviceAdded calls are completed.
+ // This is a signal to the browser that it knows what hardware is available,
+ // and can unblock any callbacks/promises that WebXR APIs are blocked.
+ OnDevicesEnumerated();
+};
+
+// Provides access to XRRuntimes. This is implemented in the XR device service,
+// and consumed by the browser.
+interface IsolatedXRRuntimeProvider {
+ // Register a client, and triggers OnDeviceAdded for all available runtimes,
+ // followed by OnDevicesEnumerated.
+ // Should only be called once.
+ RequestDevices(IsolatedXRRuntimeProviderClient client);
+};
diff --git a/chromium/device/vr/public/mojom/vr_service.mojom b/chromium/device/vr/public/mojom/vr_service.mojom
index 108c4291434..e752a0729b3 100644
--- a/chromium/device/vr/public/mojom/vr_service.mojom
+++ b/chromium/device/vr/public/mojom/vr_service.mojom
@@ -16,6 +16,19 @@ import "ui/gfx/mojo/transform.mojom";
// WebXR interfaces
//
+// TODO: Use EnableIf to only define values on platforms that have
+// implementations.
+enum XRDeviceId {
+ LAYOUT_TEST_DEVICE_ID = 0, // Fake device used by layout tests.
+ GVR_DEVICE_ID = 1,
+ OPENVR_DEVICE_ID = 2,
+ OCULUS_DEVICE_ID = 3,
+ ARCORE_DEVICE_ID = 4,
+ ORIENTATION_DEVICE_ID = 5,
+ FAKE_DEVICE_ID = 6, // Fake device used by unit tests.
+};
+
+
enum XRHandedness {
NONE = 0,
LEFT = 1,
@@ -41,17 +54,35 @@ struct XRSessionOptions {
bool use_legacy_webvr_render_path;
};
-// TODO(offenwanger) Rearrange these two interfaces to merge duplicate
-// functionality.
+// This structure contains all the mojo interfaces for different kinds of
+// XRSession. The only interface required by all sessions is the
+// XRFrameDataProvider. It must always be present. Other interfaces are set as
+// apropriate based on the session creation options. (for example, an immersive
+// session ought to have a XRPresentationConnection to submit the frames to the
+// immersive environment).
+// The XRSessionClient request must be fulfilled for the session to get
+// information about the device it is connected to, such as focus and blur
+// events, changes to view or stage parameters, or exit present calls initiated
+// by the device.
struct XRSession {
- VRMagicWindowProvider? magic_window_provider;
- XRPresentationConnection? connection;
+ XRFrameDataProvider data_provider;
+ // TODO(http://crbug.com/842025) Move where the client_request gets set to the
+ // device process then mark this as non-optional.
+ XRSessionClient&? client_request;
+ // TODO(http://crbug.com/842025) Move the information that is sent in display
+ // info to more sensible places so that this doesn't need to be sent here.
+ VRDisplayInfo display_info;
+ XRPresentationConnection? submit_frame_sink;
+ XREnvironmentIntegrationProvider? environment_provider;
};
+// This structure contains the infomation and interfaces needed to create a two
+// way connection between the renderer and a device to synchronize and submit
+// frames to a sink outside of Chrome.
struct XRPresentationConnection {
- VRSubmitFrameClient& client_request;
- VRPresentationProvider provider;
- VRDisplayFrameTransportOptions transport_options;
+ XRPresentationProvider provider;
+ XRPresentationClient& client_request;
+ XRPresentationTransportOptions transport_options;
};
struct XRInputSourceDescription {
@@ -116,10 +147,8 @@ struct VRPose {
};
struct XRRay {
- // TODO(https://crbug.com/845293): use Point3F and Vector3F from
- // ui/gfx/geometry and inline directly in requestHitTest().
- array<float, 3> origin;
- array<float, 3> direction;
+ gfx.mojom.Point3F origin;
+ gfx.mojom.Vector3dF direction;
};
struct XRHitResult {
@@ -158,7 +187,7 @@ struct VRStageParameters {
};
struct VRDisplayInfo {
- uint32 index;
+ XRDeviceId id;
string displayName;
VRDisplayCapabilities capabilities;
VRStageParameters? stageParameters;
@@ -171,7 +200,7 @@ struct VRDisplayInfo {
};
// Frame transport method from the Renderer's point of view.
-enum VRDisplayFrameTransportMethod {
+enum XRPresentationTransportMethod {
NONE = 0,
// Renderer should create a new texture handle (Windows) or
@@ -185,10 +214,10 @@ enum VRDisplayFrameTransportMethod {
DRAW_INTO_TEXTURE_MAILBOX = 3,
};
-struct VRDisplayFrameTransportOptions {
- VRDisplayFrameTransportMethod transport_method;
+struct XRPresentationTransportOptions {
+ XRPresentationTransportMethod transport_method;
- // Booleans indicating which of the VRSubmitFrameClient callbacks
+ // Booleans indicating which of the XRPresentationClient callbacks
// are in use. Default is false, the device implementation should set
// the ones to true that it needs and can ignore the rest.
bool wait_for_transfer_notification;
@@ -232,113 +261,86 @@ enum VRDisplayEventReason {
UNMOUNTED = 3
};
-// TODO(shaobo.yan@intel.com) : Add comments to describe these interfaces about
-// how to use and where they live.
+// Interface for requesting XRDevice interfaces and registering for
+// notifications that the XRDevice has changed
interface VRService {
- // TODO(shaobo.yan@intel.com, https://crbug.com/701027): Use a factory
- // function which takes a VRServiceClient so we will never have a
- // half-initialized VRService.
- SetClient(VRServiceClient client) => ();
- // Inform the service that the page is listening for vrdisplayactivate events.
- // TODO(mthiesse): Move SetListeningForActivate onto VRDisplay.
- SetListeningForActivate(bool listening);
+ // Returns the XRDevice interface which is used for creating XRSessions. This
+ // is not expected to be called once per renderer, unless the returned
+ // XRDevice is destroyed, then it might be called again to get another one.
+ RequestDevice() => (XRDevice? device);
+ // Optionally supply a VRServiceClient to listen for changes to the XRDevice.
+ // The last provided listener will have events called on it.
+ SetClient(VRServiceClient client);
+
+ // WebVR 1.1 functionality compatibility method. To stop listening pass a null
+ // client.
+ SetListeningForActivate(VRDisplayClient? client);
};
+// The interface for the renderer to listen to top level XR events, events that
+// can be listened to and triggered without the renderer calling requestDevice.
interface VRServiceClient {
- OnDisplayConnected(VRDisplayHost display, VRDisplayClient& request,
- VRDisplayInfo display_info);
+ // Signals changes to the available physical device runtimes.
+ OnDeviceChanged();
};
-// After submitting a frame, the VRPresentationProvider will notify the client
-// about several stages of the render pipeline. This allows pipelining
-// efficiency. Following VRPresentationProvider::Submit*, the submitted frame
-// will be transferred (read from, perhaps copied to another texture), and then
-// rendered (submitted to the underlying VR API).
-// The client lives in the render process, implemented by VRDisplay.
-//
-// See VRDisplayFrameTransportConfiguration which configures which of these
-// callbacks are in use.
-interface VRSubmitFrameClient {
- // The VRPresentationProvider calls this to indicate that the submitted frame
- // has been transferred, so the backing data (mailbox or GpuMemoryBuffer) can
- // be reused or discarded. Note that this is a convenience/optimization
- // feature, not a security feature - if a site discards the data early we may
- // drop a frame, but nothing will otherwise misbehave.
- // When the frame wasn't successfully transferred, the client should create a
- // new mailbox/GpuMemoryBuffer rather than reusing an existing one to recover
- // for subsequent frames.
- OnSubmitFrameTransferred(bool success);
-
- // The VRPresentationProvider calls this after the frame was handed off to the
- // underlying VR API. This allows some pipelining of CPU/GPU work, while
- // delaying expensive tasks for a subsequent frame until the current frame has
- // completed.
- OnSubmitFrameRendered();
-
- // This callback provides a GpuFence corresponding to the previous frame's
- // rendering completion, intended for use with a server wait issued before
- // the following wait to prevent its rendering work from competing with
- // the previous frame.
- OnSubmitFrameGpuFence(gfx.mojom.GpuFenceHandle gpu_fence_handle);
-};
-
-
-// Provides a communication channel from the renderer to the browser-side host
-// of a (device/) VrDisplayImpl.
-interface VRDisplayHost {
- // Request to initialize a session in the browser process. The return value
- // indicates if the session was successfully initialized or not.
- // TODO(https://crbug.com/842025): Refactor VR device interfaces to better
- // reflect WebXR.
+// Supplies XRSessions and session support information. Implemented in the
+// browser process, consumed in the renderer process.
+interface XRDevice {
+ // Request to initialize a session in the browser process. If successful, the
+ // XRSession struct with the requisite interfaces will be returned.
RequestSession(
XRSessionOptions options,
bool triggered_by_displayactive) => (XRSession? session);
SupportsSession(XRSessionOptions options) => (bool supports_session);
+ // WebVR 1.1 functionality compatibility method. Returns VRDisplayInfo for an
+ // immersive session if immersive is supported. If (and only if) immersive is
+ // not supported, will return a nullptr. This call might cause device specific
+ // UI to appear.
+ GetImmersiveVRDisplayInfo() => (VRDisplayInfo? info);
+
ExitPresent();
};
-// Provides the necessary functionality for a non-presenting WebXR session to
-// draw magic window content.
+// Provides the necessary functionality for a WebXR session to get data for
+// drawing frames. The kind of data it gets depends on what kind of session was
+// requested.
// This interface is hosted in the Browser process, but will move to a sandboxed
// utility process on Windows. The render process communicates with it.
-// For AR displays (VRDisplayCapabilities.can_provide_pass_through_images
-// is true), clients can use GetFrameData to get images.
-// TODO(836478): rename VRMagicWindowProvider to NonImmersiveWindowProvider or
-// similar.
-interface VRMagicWindowProvider {
+interface XRFrameDataProvider {
// frame_data is optional and will not be set if and only if the call fails
// for some reason, such as device disconnection.
GetFrameData() => (XRFrameData? frame_data);
+};
- // Different devices can have different native orientations - 0
- // is the native orientation, and then increments of 90 degrees
- // from there.
+// Provides functionality for integrating environment information into an
+// XRSession. For example, some AR sessions would implement hit test to allow
+// developers to get the information about the world that its sensors supply.
+interface XREnvironmentIntegrationProvider {
+ // Different devices can have different native orientations - 0 is the native
+ // orientation, and then increments of 90 degrees from there. Session geometry
+ // is needed by the device when integrating environment image data, i.e.
+ // camera feeds, into a session.
UpdateSessionGeometry(
gfx.mojom.Size frame_size,
display.mojom.Rotation display_rotation);
- // Performs a raycast into the magic window scene and returns a list of
- // XRHitResults sorted from closest to furthest hit from the ray. Each
- // hit result contains a hit_matrix containing the transform of the hit
- // where the rotation represents the normal of the surface hit.
+ // Performs a raycast into the scene and returns a list of XRHitResults sorted
+ // from closest to furthest hit from the ray. Each hit result contains a
+ // hit_matrix containing the transform of the hit where the rotation
+ // represents the normal of the surface hit.
// An empty result list means there were no hits. If a nullopt is returned,
// there was an error.
- // TODO(https://crbug.com/842025): have one "session" type, merging
- // VRMagicWindowProvider and VRPresentationProvider because RequestHitTest
- // makes sense for both types of sessions.
RequestHitTest(XRRay ray) => (array<XRHitResult>? results);
};
-// Provides the necessary functionality for a presenting WebVR page to draw
-// frames for a VrDisplay.
+// Provides the necessary functionality for sending frames to a headset.
// This interface is hosted in the Browser process, but will move to a sandboxed
// utility process on Windows. The render process communicates with it.
-interface VRPresentationProvider {
- // frame_data is optional and will not be set if and only if the call fails
- // for some reason, such as device disconnection.
- GetFrameData() => (XRFrameData? frame_data);
-
+interface XRPresentationProvider {
+ // This function tells the device which parts of the canvas should be rendered
+ // to which view.
UpdateLayerBounds(int16 frame_id, gfx.mojom.RectF left_bounds,
gfx.mojom.RectF right_bounds, gfx.mojom.Size source_size);
@@ -346,31 +348,75 @@ interface VRPresentationProvider {
// ensure that every GetFrameData has a matching Submit call. This happens for
// WebXR if there were no drawing operations to the opaque framebuffer, and
// for WebVR 1.1 if the application didn't call SubmitFrame. Usable with any
- // VRDisplayFrameTransportMethod. This path does *not* call the
+ // XRPresentationTransportMethod. This path does *not* call the
// SubmitFrameClient methods such as OnSubmitFrameTransferred. This is
// intended to help separate frames while presenting, it may or may not
// be called for the last animating frame when presentation ends.
SubmitFrameMissing(int16 frame_id, gpu.mojom.SyncToken sync_token);
- // VRDisplayFrameTransportMethod SUBMIT_AS_MAILBOX_HOLDER
+ // XRPresentationTransportMethod SUBMIT_AS_MAILBOX_HOLDER
SubmitFrame(int16 frame_id, gpu.mojom.MailboxHolder mailbox_holder,
mojo_base.mojom.TimeDelta time_waited);
- // VRDisplayFrameTransportMethod SUBMIT_AS_TEXTURE_HANDLE
+ // XRPresentationTransportMethod SUBMIT_AS_TEXTURE_HANDLE
// TODO(https://crbug.com/676224): Support preprocessing of mojom files, since
// this is Windows only.
SubmitFrameWithTextureHandle(int16 frameId, handle texture);
- // VRDisplayFrameTransportMethod DRAW_INTO_TEXTURE_MAILBOX
+ // XRPresentationTransportMethod DRAW_INTO_TEXTURE_MAILBOX
SubmitFrameDrawnIntoTexture(int16 frameId, gpu.mojom.SyncToken sync_token,
mojo_base.mojom.TimeDelta time_waited);
};
-interface VRDisplayClient {
+// After submitting a frame, the XRPresentationProvider will notify the client
+// about several stages of the render pipeline. This allows pipelining
+// efficiency. Following XRPresentationProvider::Submit*, the submitted frame
+// will be transferred (read from, perhaps copied to another texture), and then
+// rendered (submitted to the underlying VR API).
+// The client lives in the render process.
+//
+// See XRPresentationTransportOptions which configures which of these
+// callbacks are in use.
+interface XRPresentationClient {
+ // The XRPresentationProvider calls this to indicate that the submitted frame
+ // has been transferred, so the backing data (mailbox or GpuMemoryBuffer) can
+ // be reused or discarded. Note that this is a convenience/optimization
+ // feature, not a security feature - if a site discards the data early we may
+ // drop a frame, but nothing will otherwise misbehave.
+ // When the frame wasn't successfully transferred, the client should create a
+ // new mailbox/GpuMemoryBuffer rather than reusing an existing one to recover
+ // for subsequent frames.
+ OnSubmitFrameTransferred(bool success);
+
+ // The XRPresentationProvider calls this after the frame was handed off to the
+ // underlying VR API. This allows some pipelining of CPU/GPU work, while
+ // delaying expensive tasks for a subsequent frame until the current frame has
+ // completed.
+ OnSubmitFrameRendered();
+
+ // This callback provides a GpuFence corresponding to the previous frame's
+ // rendering completion, intended for use with a server wait issued before
+ // the following wait to prevent its rendering work from competing with
+ // the previous frame.
+ OnSubmitFrameGpuFence(gfx.mojom.GpuFenceHandle gpu_fence_handle);
+};
+
+// Functions for pushing device information to the sessions.
+interface XRSessionClient {
OnChanged(VRDisplayInfo display);
OnExitPresent();
OnBlur();
OnFocus();
+};
+
+// Backwards compatibility events for WebVR 1.1. These are expected to not be
+// used for WebXR.
+interface VRDisplayClient {
+ // Inform the renderer that a headset has sent a signal indicating that the
+ // user has put it on. Returns an indicator of whether or not the page
+ // actually started a WebVR 1.1 presentation.
OnActivate(VRDisplayEventReason reason) => (bool will_not_present);
+ // Inform the renderer that a headset has sent a signal indicating that the
+ // user stopped using a headset.
OnDeactivate(VRDisplayEventReason reason);
};
diff --git a/chromium/device/vr/vr_device.h b/chromium/device/vr/vr_device.h
index aaa2294fa79..abc3dffe3b1 100644
--- a/chromium/device/vr/vr_device.h
+++ b/chromium/device/vr/vr_device.h
@@ -27,16 +27,6 @@ enum class VrViewerType {
VIEWER_TYPE_COUNT,
};
-// Hardcoded list of ids for each device type.
-enum class VRDeviceId : unsigned int {
- GVR_DEVICE_ID = 1,
- OPENVR_DEVICE_ID = 2,
- OCULUS_DEVICE_ID = 3,
- ARCORE_DEVICE_ID = 4,
- ORIENTATION_DEVICE_ID = 5,
- FAKE_DEVICE_ID = 6,
-};
-
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class XrRuntimeAvailable {
diff --git a/chromium/device/vr/vr_device_base.cc b/chromium/device/vr/vr_device_base.cc
index 1c7703ad6d3..af19fab1000 100644
--- a/chromium/device/vr/vr_device_base.cc
+++ b/chromium/device/vr/vr_device_base.cc
@@ -9,12 +9,12 @@
namespace device {
-VRDeviceBase::VRDeviceBase(VRDeviceId id)
- : id_(static_cast<unsigned int>(id)), runtime_binding_(this) {}
+VRDeviceBase::VRDeviceBase(mojom::XRDeviceId id)
+ : id_(id), runtime_binding_(this) {}
VRDeviceBase::~VRDeviceBase() = default;
-unsigned int VRDeviceBase::GetId() const {
+mojom::XRDeviceId VRDeviceBase::GetId() const {
return id_;
}
@@ -53,7 +53,7 @@ void VRDeviceBase::ListenToDeviceChanges(
}
void VRDeviceBase::GetFrameData(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
if (!magic_window_enabled_) {
std::move(callback).Run(nullptr);
return;
@@ -64,7 +64,7 @@ void VRDeviceBase::GetFrameData(
void VRDeviceBase::SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info) {
DCHECK(display_info);
- DCHECK(display_info->index == id_);
+ DCHECK(display_info->id == id_);
bool initialized = !!display_info_;
display_info_ = std::move(display_info);
@@ -95,7 +95,7 @@ bool VRDeviceBase::ShouldPauseTrackingWhenFrameDataRestricted() {
void VRDeviceBase::OnListeningForActivate(bool listening) {}
void VRDeviceBase::OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
std::move(callback).Run(nullptr);
}
@@ -105,18 +105,31 @@ void VRDeviceBase::SetListeningForActivate(bool is_listening) {
void VRDeviceBase::RequestHitTest(
mojom::XRRayPtr ray,
- mojom::VRMagicWindowProvider::RequestHitTestCallback callback) {
+ mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback) {
NOTREACHED() << "Unexpected call to a device without hit-test support";
std::move(callback).Run(base::nullopt);
}
-void VRDeviceBase::RequestMagicWindowSession(
- mojom::XRRuntime::RequestMagicWindowSessionCallback callback) {
- mojom::VRMagicWindowProviderPtr provider;
+void VRDeviceBase::ReturnNonImmersiveSession(
+ mojom::XRRuntime::RequestSessionCallback callback) {
+ mojom::XRFrameDataProviderPtr data_provider;
+ mojom::XREnvironmentIntegrationProviderPtr environment_provider;
mojom::XRSessionControllerPtr controller;
- magic_window_sessions_.push_back(std::make_unique<VRDisplayImpl>(
- this, mojo::MakeRequest(&provider), mojo::MakeRequest(&controller)));
- std::move(callback).Run(std::move(provider), std::move(controller));
+ magic_window_sessions_.push_back(
+ std::make_unique<VRDisplayImpl>(this, mojo::MakeRequest(&data_provider),
+ mojo::MakeRequest(&environment_provider),
+ mojo::MakeRequest(&controller)));
+
+ auto session = mojom::XRSession::New();
+ session->data_provider = data_provider.PassInterface();
+ // TODO(http://crbug.com/876135) Not all sessions want the environment
+ // provider. This should be refactored to only be passed when requested.
+ session->environment_provider = environment_provider.PassInterface();
+ if (display_info_) {
+ session->display_info = display_info_.Clone();
+ }
+
+ std::move(callback).Run(std::move(session), std::move(controller));
}
void VRDeviceBase::EndMagicWindowSession(VRDisplayImpl* session) {
diff --git a/chromium/device/vr/vr_device_base.h b/chromium/device/vr/vr_device_base.h
index 04f6c5066d6..8d10ef56e65 100644
--- a/chromium/device/vr/vr_device_base.h
+++ b/chromium/device/vr/vr_device_base.h
@@ -22,7 +22,7 @@ class VRDisplayImpl;
// TODO(mthiesse, crbug.com/769373): Remove DEVICE_VR_EXPORT.
class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime {
public:
- explicit VRDeviceBase(VRDeviceId id);
+ explicit VRDeviceBase(mojom::XRDeviceId id);
~VRDeviceBase() override;
// VRDevice Implementation
@@ -31,13 +31,12 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime {
mojom::XRRuntime::ListenToDeviceChangesCallback callback) final;
void SetListeningForActivate(bool is_listening) override;
- void GetFrameData(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback);
+ void GetFrameData(mojom::XRFrameDataProvider::GetFrameDataCallback callback);
virtual void RequestHitTest(
mojom::XRRayPtr ray,
- mojom::VRMagicWindowProvider::RequestHitTestCallback callback);
- unsigned int GetId() const;
+ mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback);
+ device::mojom::XRDeviceId GetId() const;
bool HasExclusiveSession();
void EndMagicWindowSession(VRDisplayImpl* session);
@@ -75,25 +74,23 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime {
void OnActivate(mojom::VRDisplayEventReason reason,
base::Callback<void(bool)> on_handled);
+ void ReturnNonImmersiveSession(
+ mojom::XRRuntime::RequestSessionCallback callback);
+
+ mojom::VRDisplayInfoPtr display_info_;
std::vector<std::unique_ptr<VRDisplayImpl>> magic_window_sessions_;
private:
// TODO(https://crbug.com/842227): Rename methods to HandleOnXXX
virtual void OnListeningForActivate(bool listening);
virtual void OnMagicWindowFrameDataRequest(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback);
-
- // XRRuntime
- void RequestMagicWindowSession(
- mojom::XRRuntime::RequestMagicWindowSessionCallback callback) override;
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback);
mojom::XRRuntimeEventListenerPtr listener_;
- mojom::VRDisplayInfoPtr display_info_;
-
bool presenting_ = false;
- unsigned int id_;
+ device::mojom::XRDeviceId id_;
bool magic_window_enabled_ = true;
mojo::Binding<mojom::XRRuntime> runtime_binding_;
diff --git a/chromium/device/vr/vr_device_base_unittest.cc b/chromium/device/vr/vr_device_base_unittest.cc
index 5025d3ed0e3..318b292b971 100644
--- a/chromium/device/vr/vr_device_base_unittest.cc
+++ b/chromium/device/vr/vr_device_base_unittest.cc
@@ -11,8 +11,8 @@
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/test/fake_vr_device.h"
#include "device/vr/test/fake_vr_service_client.h"
-#include "device/vr/test/mock_vr_display_impl.h"
#include "device/vr/vr_device_base.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
@@ -21,7 +21,7 @@ namespace {
class VRDeviceBaseForTesting : public VRDeviceBase {
public:
- VRDeviceBaseForTesting() : VRDeviceBase(VRDeviceId::FAKE_DEVICE_ID) {}
+ VRDeviceBaseForTesting() : VRDeviceBase(mojom::XRDeviceId::FAKE_DEVICE_ID) {}
~VRDeviceBaseForTesting() override = default;
void SetVRDisplayInfoForTest(mojom::VRDisplayInfoPtr display_info) {
@@ -35,7 +35,7 @@ class VRDeviceBaseForTesting : public VRDeviceBase {
bool ListeningForActivate() { return listening_for_activate; }
void RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) override {}
private:
@@ -95,14 +95,6 @@ class VRDeviceTest : public testing::Test {
client_ = std::make_unique<FakeVRServiceClient>(mojo::MakeRequest(&proxy));
}
- std::unique_ptr<MockVRDisplayImpl> MakeMockDisplay(VRDeviceBase* device) {
- mojom::VRMagicWindowProviderPtr session;
- mojom::XRSessionControllerPtr controller;
- return std::make_unique<testing::NiceMock<MockVRDisplayImpl>>(
- device, mojo::MakeRequest(&session), mojo::MakeRequest(&controller),
- false);
- }
-
std::unique_ptr<VRDeviceBaseForTesting> MakeVRDevice() {
std::unique_ptr<VRDeviceBaseForTesting> device =
std::make_unique<VRDeviceBaseForTesting>();
@@ -110,9 +102,9 @@ class VRDeviceTest : public testing::Test {
return device;
}
- mojom::VRDisplayInfoPtr MakeVRDisplayInfo(unsigned int device_id) {
+ mojom::VRDisplayInfoPtr MakeVRDisplayInfo(mojom::XRDeviceId device_id) {
mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
- display_info->index = device_id;
+ display_info->id = device_id;
display_info->capabilities = mojom::VRDisplayCapabilities::New();
return display_info;
}
@@ -158,7 +150,8 @@ TEST_F(VRDeviceTest, DisplayActivateRegsitered) {
}
TEST_F(VRDeviceTest, NoMagicWindowPosesWhileBrowsing) {
- auto device = std::make_unique<FakeVRDevice>(1);
+ auto device =
+ std::make_unique<FakeVRDevice>(static_cast<device::mojom::XRDeviceId>(1));
device->SetPose(mojom::VRPose::New());
device->GetFrameData(base::BindOnce(
diff --git a/chromium/device/vr/vr_device_provider.h b/chromium/device/vr/vr_device_provider.h
index 26732491088..27616caa4f4 100644
--- a/chromium/device/vr/vr_device_provider.h
+++ b/chromium/device/vr/vr_device_provider.h
@@ -19,10 +19,11 @@ class VRDeviceProvider {
// If the VR API requires initialization that should happen here.
virtual void Initialize(
- base::RepeatingCallback<void(unsigned int,
+ base::RepeatingCallback<void(mojom::XRDeviceId id,
mojom::VRDisplayInfoPtr,
mojom::XRRuntimePtr)> add_device_callback,
- base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::RepeatingCallback<void(mojom::XRDeviceId id)>
+ remove_device_callback,
base::OnceClosure initialization_complete) = 0;
// Returns true if initialization is complete.
diff --git a/chromium/device/vr/vr_display_impl.cc b/chromium/device/vr/vr_display_impl.cc
index b4e482b3de3..ce0c958e77e 100644
--- a/chromium/device/vr/vr_display_impl.cc
+++ b/chromium/device/vr/vr_display_impl.cc
@@ -17,14 +17,13 @@ namespace device {
VRDisplayImpl::VRDisplayImpl(
VRDeviceBase* device,
- mojom::VRMagicWindowProviderRequest magic_window_request,
+ mojom::XRFrameDataProviderRequest magic_window_request,
+ mojom::XREnvironmentIntegrationProviderRequest environment_request,
mojom::XRSessionControllerRequest session_request)
- : magic_window_binding_(this),
- session_controller_binding_(this),
+ : magic_window_binding_(this, std::move(magic_window_request)),
+ environment_binding_(this, std::move(environment_request)),
+ session_controller_binding_(this, std::move(session_request)),
device_(device) {
- magic_window_binding_.Bind(std::move(magic_window_request));
- session_controller_binding_.Bind(std::move(session_request));
-
// Unretained is safe because the binding will close when we are destroyed,
// so we won't receive any more callbacks after that.
session_controller_binding_.set_connection_error_handler(base::BindOnce(
@@ -35,7 +34,7 @@ VRDisplayImpl::~VRDisplayImpl() = default;
// Gets frame data for sessions.
void VRDisplayImpl::GetFrameData(
- mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
if (device_->HasExclusiveSession() || restrict_frame_data_) {
std::move(callback).Run(nullptr);
return;
@@ -63,7 +62,7 @@ void VRDisplayImpl::UpdateSessionGeometry(const gfx::Size& frame_size,
void VRDisplayImpl::RequestHitTest(
mojom::XRRayPtr ray,
- mojom::VRMagicWindowProvider::RequestHitTestCallback callback) {
+ mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback) {
if (restrict_frame_data_) {
std::move(callback).Run(base::nullopt);
return;
diff --git a/chromium/device/vr/vr_display_impl.h b/chromium/device/vr/vr_display_impl.h
index 0d0bd8fd080..219c9c8c611 100644
--- a/chromium/device/vr/vr_display_impl.h
+++ b/chromium/device/vr/vr_display_impl.h
@@ -20,15 +20,18 @@ namespace device {
class VRDeviceBase;
-// VR device process implementation of a VRMagicWindowProvider within a WebVR
+// VR device process implementation of a XRFrameDataProvider within a WebVR
// or WebXR site session.
// VRDisplayImpl objects are owned by their respective XRRuntime instances.
-// TODO(offenwanger): Rename this.
-class DEVICE_VR_EXPORT VRDisplayImpl : public mojom::VRMagicWindowProvider,
- public mojom::XRSessionController {
+// TODO(http://crbug.com/842025): Rename this.
+class DEVICE_VR_EXPORT VRDisplayImpl
+ : public mojom::XRFrameDataProvider,
+ public mojom::XREnvironmentIntegrationProvider,
+ public mojom::XRSessionController {
public:
VRDisplayImpl(VRDeviceBase* device,
- mojom::VRMagicWindowProviderRequest,
+ mojom::XRFrameDataProviderRequest,
+ mojom::XREnvironmentIntegrationProviderRequest,
mojom::XRSessionControllerRequest);
~VRDisplayImpl() override;
@@ -39,7 +42,7 @@ class DEVICE_VR_EXPORT VRDisplayImpl : public mojom::VRMagicWindowProvider,
// Accessible to tests.
protected:
- // mojom::VRMagicWindowProvider
+ // mojom::XRFrameDataProvider
void GetFrameData(GetFrameDataCallback callback) override;
void UpdateSessionGeometry(const gfx::Size& frame_size,
display::Display::Rotation rotation) override;
@@ -51,7 +54,8 @@ class DEVICE_VR_EXPORT VRDisplayImpl : public mojom::VRMagicWindowProvider,
void OnMojoConnectionError();
- mojo::Binding<mojom::VRMagicWindowProvider> magic_window_binding_;
+ mojo::Binding<mojom::XRFrameDataProvider> magic_window_binding_;
+ mojo::Binding<mojom::XREnvironmentIntegrationProvider> environment_binding_;
mojo::Binding<mojom::XRSessionController> session_controller_binding_;
device::VRDeviceBase* device_;
bool restrict_frame_data_ = true;
diff --git a/chromium/device/vr/vr_display_impl_unittest.cc b/chromium/device/vr/vr_display_impl_unittest.cc
index 6735be754f8..fc8ad17219b 100644
--- a/chromium/device/vr/vr_display_impl_unittest.cc
+++ b/chromium/device/vr/vr_display_impl_unittest.cc
@@ -21,17 +21,10 @@ class VRDisplayImplTest : public testing::Test {
VRDisplayImplTest() {}
~VRDisplayImplTest() override {}
void onDisplaySynced() {}
- void onPresentComplete(
- device::mojom::XRPresentationConnectionPtr connection,
- mojom::XRSessionControllerPtr immersive_session_controller) {
- is_request_presenting_success_ = connection ? true : false;
-
- immersive_session_controller_ = std::move(immersive_session_controller);
- }
protected:
void SetUp() override {
- device_ = std::make_unique<FakeVRDevice>(1);
+ device_ = std::make_unique<FakeVRDevice>(static_cast<mojom::XRDeviceId>(1));
device_->SetPose(mojom::VRPose::New());
mojom::VRServiceClientPtr proxy;
client_ = std::make_unique<FakeVRServiceClient>(mojo::MakeRequest(&proxy));
@@ -39,9 +32,12 @@ class VRDisplayImplTest : public testing::Test {
std::unique_ptr<VRDisplayImpl> MakeDisplay(
mojom::XRSessionControllerPtr* controller) {
- mojom::VRMagicWindowProviderPtr session;
+ mojom::XRFrameDataProviderPtr data_provider;
+ mojom::XREnvironmentIntegrationProviderPtr environment_provider;
auto display = std::make_unique<VRDisplayImpl>(
- device(), mojo::MakeRequest(&session), mojo::MakeRequest(controller));
+ device(), mojo::MakeRequest(&data_provider),
+ mojo::MakeRequest(&environment_provider),
+ mojo::MakeRequest(controller));
static_cast<mojom::XRSessionController*>(display.get())
->SetFrameDataRestricted(true);
return display;
@@ -49,14 +45,14 @@ class VRDisplayImplTest : public testing::Test {
void RequestSession(VRDisplayImpl* display_impl) {
device_->RequestSession(
- mojom::XRDeviceRuntimeSessionOptionsPtr(),
- base::BindOnce(&VRDisplayImplTest::onPresentComplete,
- base::Unretained(this)));
+ mojom::XRRuntimeSessionOptionsPtr(),
+ base::BindOnce(
+ [](device::mojom::XRSessionPtr session,
+ mojom::XRSessionControllerPtr immersive_session_controller) {}));
}
void ExitPresent() {
device_->StopSession();
- immersive_session_controller_ = nullptr;
}
bool presenting() { return device_->IsPresenting(); }
@@ -64,10 +60,8 @@ class VRDisplayImplTest : public testing::Test {
FakeVRServiceClient* client() { return client_.get(); }
base::MessageLoop message_loop_;
- bool is_request_presenting_success_ = false;
std::unique_ptr<FakeVRDevice> device_;
std::unique_ptr<FakeVRServiceClient> client_;
- mojom::XRSessionControllerPtr immersive_session_controller_;
DISALLOW_COPY_AND_ASSIGN(VRDisplayImplTest);
};
@@ -77,6 +71,7 @@ TEST_F(VRDisplayImplTest, DevicePresentationIsolation) {
std::unique_ptr<VRDisplayImpl> display_1 = MakeDisplay(&controller1);
static_cast<mojom::XRSessionController*>(display_1.get())
->SetFrameDataRestricted(false);
+
mojom::XRSessionControllerPtr controller2;
std::unique_ptr<VRDisplayImpl> display_2 = MakeDisplay(&controller2);
static_cast<mojom::XRSessionController*>(display_2.get())
@@ -92,31 +87,36 @@ TEST_F(VRDisplayImplTest, DevicePresentationIsolation) {
EXPECT_EQ(expect_null, !data);
};
- static_cast<mojom::VRMagicWindowProvider*>(display_1.get())
+ static_cast<mojom::XRFrameDataProvider*>(display_1.get())
->GetFrameData(base::BindOnce(callback, false, &was_called));
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
was_called = false;
- static_cast<mojom::VRMagicWindowProvider*>(display_2.get())
+
+ static_cast<mojom::XRFrameDataProvider*>(display_2.get())
->GetFrameData(base::BindOnce(callback, false, &was_called));
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
was_called = false;
// Attempt to present.
RequestSession(display_1.get());
- EXPECT_TRUE(is_request_presenting_success_);
EXPECT_TRUE(presenting());
EXPECT_TRUE(device()->HasExclusiveSession());
- // While a device is presenting, noone should have access to magic window.
- static_cast<mojom::VRMagicWindowProvider*>(display_1.get())
+ // While a device is presenting, no one should have access to magic window.
+ static_cast<mojom::XRFrameDataProvider*>(display_1.get())
->GetFrameData(base::BindOnce(callback, true, &was_called));
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
was_called = false;
- static_cast<mojom::VRMagicWindowProvider*>(display_2.get())
+
+ static_cast<mojom::XRFrameDataProvider*>(display_2.get())
->GetFrameData(base::BindOnce(callback, true, &was_called));
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
was_called = false;
@@ -128,13 +128,16 @@ TEST_F(VRDisplayImplTest, DevicePresentationIsolation) {
// Once presentation had ended both services should be able to access the
// device.
- static_cast<mojom::VRMagicWindowProvider*>(display_1.get())
+ static_cast<mojom::XRFrameDataProvider*>(display_1.get())
->GetFrameData(base::BindOnce(callback, false, &was_called));
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
was_called = false;
- static_cast<mojom::VRMagicWindowProvider*>(display_2.get())
+
+ static_cast<mojom::XRFrameDataProvider*>(display_2.get())
->GetFrameData(base::BindOnce(callback, false, &was_called));
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
was_called = false;